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/testing/index.cjs
CHANGED
|
@@ -3485,7 +3485,7 @@ var init_executor = __esm({
|
|
|
3485
3485
|
init_exceptions();
|
|
3486
3486
|
init_parser();
|
|
3487
3487
|
GadgetExecutor = class {
|
|
3488
|
-
constructor(registry, requestHumanInput, logger, defaultGadgetTimeoutMs, errorFormatterOptions, client, mediaStore, agentConfig, subagentConfig) {
|
|
3488
|
+
constructor(registry, requestHumanInput, logger, defaultGadgetTimeoutMs, errorFormatterOptions, client, mediaStore, agentConfig, subagentConfig, onNestedEvent) {
|
|
3489
3489
|
this.registry = registry;
|
|
3490
3490
|
this.requestHumanInput = requestHumanInput;
|
|
3491
3491
|
this.defaultGadgetTimeoutMs = defaultGadgetTimeoutMs;
|
|
@@ -3493,6 +3493,7 @@ var init_executor = __esm({
|
|
|
3493
3493
|
this.mediaStore = mediaStore;
|
|
3494
3494
|
this.agentConfig = agentConfig;
|
|
3495
3495
|
this.subagentConfig = subagentConfig;
|
|
3496
|
+
this.onNestedEvent = onNestedEvent;
|
|
3496
3497
|
this.logger = logger ?? createLogger({ name: "llmist:executor" });
|
|
3497
3498
|
this.errorFormatter = new GadgetExecutionErrorFormatter(errorFormatterOptions);
|
|
3498
3499
|
this.argPrefix = errorFormatterOptions?.argPrefix ?? GADGET_ARG_PREFIX;
|
|
@@ -3638,7 +3639,9 @@ var init_executor = __esm({
|
|
|
3638
3639
|
llmist: this.client ? new CostReportingLLMistWrapper(this.client, reportCost) : void 0,
|
|
3639
3640
|
signal: abortController.signal,
|
|
3640
3641
|
agentConfig: this.agentConfig,
|
|
3641
|
-
subagentConfig: this.subagentConfig
|
|
3642
|
+
subagentConfig: this.subagentConfig,
|
|
3643
|
+
invocationId: call.invocationId,
|
|
3644
|
+
onNestedEvent: this.onNestedEvent
|
|
3642
3645
|
};
|
|
3643
3646
|
let rawResult;
|
|
3644
3647
|
if (timeoutMs && timeoutMs > 0) {
|
|
@@ -3877,14 +3880,21 @@ var init_stream_processor = __esm({
|
|
|
3877
3880
|
options.client,
|
|
3878
3881
|
options.mediaStore,
|
|
3879
3882
|
options.agentConfig,
|
|
3880
|
-
options.subagentConfig
|
|
3883
|
+
options.subagentConfig,
|
|
3884
|
+
options.onNestedEvent
|
|
3881
3885
|
);
|
|
3882
3886
|
}
|
|
3883
3887
|
/**
|
|
3884
|
-
* Process an LLM stream and
|
|
3888
|
+
* Process an LLM stream and yield events in real-time.
|
|
3889
|
+
*
|
|
3890
|
+
* This is an async generator that yields events immediately as they occur:
|
|
3891
|
+
* - Text events are yielded as text is streamed from the LLM
|
|
3892
|
+
* - gadget_call events are yielded immediately when a gadget call is parsed
|
|
3893
|
+
* - gadget_result events are yielded when gadget execution completes
|
|
3894
|
+
*
|
|
3895
|
+
* The final event is always a StreamCompletionEvent containing metadata.
|
|
3885
3896
|
*/
|
|
3886
|
-
async process(stream2) {
|
|
3887
|
-
const outputs = [];
|
|
3897
|
+
async *process(stream2) {
|
|
3888
3898
|
let finishReason = null;
|
|
3889
3899
|
let usage;
|
|
3890
3900
|
let didExecuteGadgets = false;
|
|
@@ -3930,14 +3940,13 @@ var init_stream_processor = __esm({
|
|
|
3930
3940
|
continue;
|
|
3931
3941
|
}
|
|
3932
3942
|
for (const event of this.parser.feed(processedChunk)) {
|
|
3933
|
-
const
|
|
3934
|
-
|
|
3935
|
-
|
|
3936
|
-
|
|
3937
|
-
|
|
3938
|
-
|
|
3939
|
-
|
|
3940
|
-
shouldBreakLoop = true;
|
|
3943
|
+
for await (const processedEvent of this.processEventGenerator(event)) {
|
|
3944
|
+
yield processedEvent;
|
|
3945
|
+
if (processedEvent.type === "gadget_result") {
|
|
3946
|
+
didExecuteGadgets = true;
|
|
3947
|
+
if (processedEvent.result.breaksLoop) {
|
|
3948
|
+
shouldBreakLoop = true;
|
|
3949
|
+
}
|
|
3941
3950
|
}
|
|
3942
3951
|
}
|
|
3943
3952
|
}
|
|
@@ -3948,25 +3957,23 @@ var init_stream_processor = __esm({
|
|
|
3948
3957
|
}
|
|
3949
3958
|
if (!this.executionHalted) {
|
|
3950
3959
|
for (const event of this.parser.finalize()) {
|
|
3951
|
-
const
|
|
3952
|
-
|
|
3953
|
-
|
|
3954
|
-
|
|
3955
|
-
|
|
3956
|
-
|
|
3957
|
-
|
|
3958
|
-
shouldBreakLoop = true;
|
|
3960
|
+
for await (const processedEvent of this.processEventGenerator(event)) {
|
|
3961
|
+
yield processedEvent;
|
|
3962
|
+
if (processedEvent.type === "gadget_result") {
|
|
3963
|
+
didExecuteGadgets = true;
|
|
3964
|
+
if (processedEvent.result.breaksLoop) {
|
|
3965
|
+
shouldBreakLoop = true;
|
|
3966
|
+
}
|
|
3959
3967
|
}
|
|
3960
3968
|
}
|
|
3961
3969
|
}
|
|
3962
|
-
const
|
|
3963
|
-
|
|
3964
|
-
|
|
3965
|
-
|
|
3966
|
-
|
|
3967
|
-
|
|
3968
|
-
|
|
3969
|
-
shouldBreakLoop = true;
|
|
3970
|
+
for await (const evt of this.processPendingGadgetsGenerator()) {
|
|
3971
|
+
yield evt;
|
|
3972
|
+
if (evt.type === "gadget_result") {
|
|
3973
|
+
didExecuteGadgets = true;
|
|
3974
|
+
if (evt.result.breaksLoop) {
|
|
3975
|
+
shouldBreakLoop = true;
|
|
3976
|
+
}
|
|
3970
3977
|
}
|
|
3971
3978
|
}
|
|
3972
3979
|
}
|
|
@@ -3979,8 +3986,8 @@ var init_stream_processor = __esm({
|
|
|
3979
3986
|
};
|
|
3980
3987
|
finalMessage = this.hooks.interceptors.interceptAssistantMessage(finalMessage, context);
|
|
3981
3988
|
}
|
|
3982
|
-
|
|
3983
|
-
|
|
3989
|
+
const completionEvent = {
|
|
3990
|
+
type: "stream_complete",
|
|
3984
3991
|
shouldBreakLoop,
|
|
3985
3992
|
didExecuteGadgets,
|
|
3986
3993
|
finishReason,
|
|
@@ -3988,9 +3995,11 @@ var init_stream_processor = __esm({
|
|
|
3988
3995
|
rawResponse: this.responseText,
|
|
3989
3996
|
finalMessage
|
|
3990
3997
|
};
|
|
3998
|
+
yield completionEvent;
|
|
3991
3999
|
}
|
|
3992
4000
|
/**
|
|
3993
4001
|
* Process a single parsed event (text or gadget call).
|
|
4002
|
+
* @deprecated Use processEventGenerator for real-time streaming
|
|
3994
4003
|
*/
|
|
3995
4004
|
async processEvent(event) {
|
|
3996
4005
|
if (event.type === "text") {
|
|
@@ -4000,6 +4009,23 @@ var init_stream_processor = __esm({
|
|
|
4000
4009
|
}
|
|
4001
4010
|
return [event];
|
|
4002
4011
|
}
|
|
4012
|
+
/**
|
|
4013
|
+
* Process a single parsed event, yielding events in real-time.
|
|
4014
|
+
* Generator version of processEvent for streaming support.
|
|
4015
|
+
*/
|
|
4016
|
+
async *processEventGenerator(event) {
|
|
4017
|
+
if (event.type === "text") {
|
|
4018
|
+
for (const e of await this.processTextEvent(event)) {
|
|
4019
|
+
yield e;
|
|
4020
|
+
}
|
|
4021
|
+
} else if (event.type === "gadget_call") {
|
|
4022
|
+
for await (const e of this.processGadgetCallGenerator(event.call)) {
|
|
4023
|
+
yield e;
|
|
4024
|
+
}
|
|
4025
|
+
} else {
|
|
4026
|
+
yield event;
|
|
4027
|
+
}
|
|
4028
|
+
}
|
|
4003
4029
|
/**
|
|
4004
4030
|
* Process a text event through interceptors.
|
|
4005
4031
|
*/
|
|
@@ -4076,9 +4102,68 @@ var init_stream_processor = __esm({
|
|
|
4076
4102
|
events.push(...triggeredEvents);
|
|
4077
4103
|
return events;
|
|
4078
4104
|
}
|
|
4105
|
+
/**
|
|
4106
|
+
* Process a gadget call, yielding events in real-time.
|
|
4107
|
+
*
|
|
4108
|
+
* Key difference from processGadgetCall: yields gadget_call event IMMEDIATELY
|
|
4109
|
+
* when parsed (before execution), enabling real-time UI feedback.
|
|
4110
|
+
*/
|
|
4111
|
+
async *processGadgetCallGenerator(call) {
|
|
4112
|
+
if (this.executionHalted) {
|
|
4113
|
+
this.logger.debug("Skipping gadget execution due to previous error", {
|
|
4114
|
+
gadgetName: call.gadgetName
|
|
4115
|
+
});
|
|
4116
|
+
return;
|
|
4117
|
+
}
|
|
4118
|
+
yield { type: "gadget_call", call };
|
|
4119
|
+
if (call.dependencies.length > 0) {
|
|
4120
|
+
if (call.dependencies.includes(call.invocationId)) {
|
|
4121
|
+
this.logger.warn("Gadget has self-referential dependency (depends on itself)", {
|
|
4122
|
+
gadgetName: call.gadgetName,
|
|
4123
|
+
invocationId: call.invocationId
|
|
4124
|
+
});
|
|
4125
|
+
this.failedInvocations.add(call.invocationId);
|
|
4126
|
+
const skipEvent = {
|
|
4127
|
+
type: "gadget_skipped",
|
|
4128
|
+
gadgetName: call.gadgetName,
|
|
4129
|
+
invocationId: call.invocationId,
|
|
4130
|
+
parameters: call.parameters ?? {},
|
|
4131
|
+
failedDependency: call.invocationId,
|
|
4132
|
+
failedDependencyError: `Gadget "${call.invocationId}" cannot depend on itself (self-referential dependency)`
|
|
4133
|
+
};
|
|
4134
|
+
yield skipEvent;
|
|
4135
|
+
return;
|
|
4136
|
+
}
|
|
4137
|
+
const failedDep = call.dependencies.find((dep) => this.failedInvocations.has(dep));
|
|
4138
|
+
if (failedDep) {
|
|
4139
|
+
const skipEvents = await this.handleFailedDependency(call, failedDep);
|
|
4140
|
+
for (const evt of skipEvents) {
|
|
4141
|
+
yield evt;
|
|
4142
|
+
}
|
|
4143
|
+
return;
|
|
4144
|
+
}
|
|
4145
|
+
const unsatisfied = call.dependencies.filter((dep) => !this.completedResults.has(dep));
|
|
4146
|
+
if (unsatisfied.length > 0) {
|
|
4147
|
+
this.logger.debug("Queueing gadget for later - waiting on dependencies", {
|
|
4148
|
+
gadgetName: call.gadgetName,
|
|
4149
|
+
invocationId: call.invocationId,
|
|
4150
|
+
waitingOn: unsatisfied
|
|
4151
|
+
});
|
|
4152
|
+
this.gadgetsAwaitingDependencies.set(call.invocationId, call);
|
|
4153
|
+
return;
|
|
4154
|
+
}
|
|
4155
|
+
}
|
|
4156
|
+
for await (const evt of this.executeGadgetGenerator(call)) {
|
|
4157
|
+
yield evt;
|
|
4158
|
+
}
|
|
4159
|
+
for await (const evt of this.processPendingGadgetsGenerator()) {
|
|
4160
|
+
yield evt;
|
|
4161
|
+
}
|
|
4162
|
+
}
|
|
4079
4163
|
/**
|
|
4080
4164
|
* Execute a gadget through the full hook lifecycle.
|
|
4081
4165
|
* This is the core execution logic, extracted from processGadgetCall.
|
|
4166
|
+
* @deprecated Use executeGadgetGenerator for real-time streaming
|
|
4082
4167
|
*/
|
|
4083
4168
|
async executeGadgetWithHooks(call) {
|
|
4084
4169
|
const events = [];
|
|
@@ -4231,6 +4316,159 @@ var init_stream_processor = __esm({
|
|
|
4231
4316
|
}
|
|
4232
4317
|
return events;
|
|
4233
4318
|
}
|
|
4319
|
+
/**
|
|
4320
|
+
* Execute a gadget and yield the result event.
|
|
4321
|
+
* Generator version that yields gadget_result immediately when execution completes.
|
|
4322
|
+
*/
|
|
4323
|
+
async *executeGadgetGenerator(call) {
|
|
4324
|
+
if (call.parseError) {
|
|
4325
|
+
this.logger.warn("Gadget has parse error", {
|
|
4326
|
+
gadgetName: call.gadgetName,
|
|
4327
|
+
error: call.parseError,
|
|
4328
|
+
rawParameters: call.parametersRaw
|
|
4329
|
+
});
|
|
4330
|
+
const shouldContinue = await this.checkCanRecoverFromError(
|
|
4331
|
+
call.parseError,
|
|
4332
|
+
call.gadgetName,
|
|
4333
|
+
"parse",
|
|
4334
|
+
call.parameters
|
|
4335
|
+
);
|
|
4336
|
+
if (!shouldContinue) {
|
|
4337
|
+
this.executionHalted = true;
|
|
4338
|
+
}
|
|
4339
|
+
}
|
|
4340
|
+
let parameters = call.parameters ?? {};
|
|
4341
|
+
if (this.hooks.interceptors?.interceptGadgetParameters) {
|
|
4342
|
+
const context = {
|
|
4343
|
+
iteration: this.iteration,
|
|
4344
|
+
gadgetName: call.gadgetName,
|
|
4345
|
+
invocationId: call.invocationId,
|
|
4346
|
+
logger: this.logger
|
|
4347
|
+
};
|
|
4348
|
+
parameters = this.hooks.interceptors.interceptGadgetParameters(parameters, context);
|
|
4349
|
+
}
|
|
4350
|
+
call.parameters = parameters;
|
|
4351
|
+
let shouldSkip = false;
|
|
4352
|
+
let syntheticResult;
|
|
4353
|
+
if (this.hooks.controllers?.beforeGadgetExecution) {
|
|
4354
|
+
const context = {
|
|
4355
|
+
iteration: this.iteration,
|
|
4356
|
+
gadgetName: call.gadgetName,
|
|
4357
|
+
invocationId: call.invocationId,
|
|
4358
|
+
parameters,
|
|
4359
|
+
logger: this.logger
|
|
4360
|
+
};
|
|
4361
|
+
const action = await this.hooks.controllers.beforeGadgetExecution(context);
|
|
4362
|
+
validateBeforeGadgetExecutionAction(action);
|
|
4363
|
+
if (action.action === "skip") {
|
|
4364
|
+
shouldSkip = true;
|
|
4365
|
+
syntheticResult = action.syntheticResult;
|
|
4366
|
+
this.logger.info("Controller skipped gadget execution", {
|
|
4367
|
+
gadgetName: call.gadgetName
|
|
4368
|
+
});
|
|
4369
|
+
}
|
|
4370
|
+
}
|
|
4371
|
+
const startObservers = [];
|
|
4372
|
+
if (this.hooks.observers?.onGadgetExecutionStart) {
|
|
4373
|
+
startObservers.push(async () => {
|
|
4374
|
+
const context = {
|
|
4375
|
+
iteration: this.iteration,
|
|
4376
|
+
gadgetName: call.gadgetName,
|
|
4377
|
+
invocationId: call.invocationId,
|
|
4378
|
+
parameters,
|
|
4379
|
+
logger: this.logger
|
|
4380
|
+
};
|
|
4381
|
+
await this.hooks.observers.onGadgetExecutionStart(context);
|
|
4382
|
+
});
|
|
4383
|
+
}
|
|
4384
|
+
await this.runObserversInParallel(startObservers);
|
|
4385
|
+
let result;
|
|
4386
|
+
if (shouldSkip) {
|
|
4387
|
+
result = {
|
|
4388
|
+
gadgetName: call.gadgetName,
|
|
4389
|
+
invocationId: call.invocationId,
|
|
4390
|
+
parameters,
|
|
4391
|
+
result: syntheticResult ?? "Execution skipped",
|
|
4392
|
+
executionTimeMs: 0
|
|
4393
|
+
};
|
|
4394
|
+
} else {
|
|
4395
|
+
result = await this.executor.execute(call);
|
|
4396
|
+
}
|
|
4397
|
+
const originalResult = result.result;
|
|
4398
|
+
if (result.result && this.hooks.interceptors?.interceptGadgetResult) {
|
|
4399
|
+
const context = {
|
|
4400
|
+
iteration: this.iteration,
|
|
4401
|
+
gadgetName: result.gadgetName,
|
|
4402
|
+
invocationId: result.invocationId,
|
|
4403
|
+
parameters,
|
|
4404
|
+
executionTimeMs: result.executionTimeMs,
|
|
4405
|
+
logger: this.logger
|
|
4406
|
+
};
|
|
4407
|
+
result.result = this.hooks.interceptors.interceptGadgetResult(result.result, context);
|
|
4408
|
+
}
|
|
4409
|
+
if (this.hooks.controllers?.afterGadgetExecution) {
|
|
4410
|
+
const context = {
|
|
4411
|
+
iteration: this.iteration,
|
|
4412
|
+
gadgetName: result.gadgetName,
|
|
4413
|
+
invocationId: result.invocationId,
|
|
4414
|
+
parameters,
|
|
4415
|
+
result: result.result,
|
|
4416
|
+
error: result.error,
|
|
4417
|
+
executionTimeMs: result.executionTimeMs,
|
|
4418
|
+
logger: this.logger
|
|
4419
|
+
};
|
|
4420
|
+
const action = await this.hooks.controllers.afterGadgetExecution(context);
|
|
4421
|
+
validateAfterGadgetExecutionAction(action);
|
|
4422
|
+
if (action.action === "recover" && result.error) {
|
|
4423
|
+
this.logger.info("Controller recovered from gadget error", {
|
|
4424
|
+
gadgetName: result.gadgetName,
|
|
4425
|
+
originalError: result.error
|
|
4426
|
+
});
|
|
4427
|
+
result = {
|
|
4428
|
+
...result,
|
|
4429
|
+
error: void 0,
|
|
4430
|
+
result: action.fallbackResult
|
|
4431
|
+
};
|
|
4432
|
+
}
|
|
4433
|
+
}
|
|
4434
|
+
const completeObservers = [];
|
|
4435
|
+
if (this.hooks.observers?.onGadgetExecutionComplete) {
|
|
4436
|
+
completeObservers.push(async () => {
|
|
4437
|
+
const context = {
|
|
4438
|
+
iteration: this.iteration,
|
|
4439
|
+
gadgetName: result.gadgetName,
|
|
4440
|
+
invocationId: result.invocationId,
|
|
4441
|
+
parameters,
|
|
4442
|
+
originalResult,
|
|
4443
|
+
finalResult: result.result,
|
|
4444
|
+
error: result.error,
|
|
4445
|
+
executionTimeMs: result.executionTimeMs,
|
|
4446
|
+
breaksLoop: result.breaksLoop,
|
|
4447
|
+
cost: result.cost,
|
|
4448
|
+
logger: this.logger
|
|
4449
|
+
};
|
|
4450
|
+
await this.hooks.observers.onGadgetExecutionComplete(context);
|
|
4451
|
+
});
|
|
4452
|
+
}
|
|
4453
|
+
await this.runObserversInParallel(completeObservers);
|
|
4454
|
+
this.completedResults.set(result.invocationId, result);
|
|
4455
|
+
if (result.error) {
|
|
4456
|
+
this.failedInvocations.add(result.invocationId);
|
|
4457
|
+
}
|
|
4458
|
+
yield { type: "gadget_result", result };
|
|
4459
|
+
if (result.error) {
|
|
4460
|
+
const errorType = this.determineErrorType(call, result);
|
|
4461
|
+
const shouldContinue = await this.checkCanRecoverFromError(
|
|
4462
|
+
result.error,
|
|
4463
|
+
result.gadgetName,
|
|
4464
|
+
errorType,
|
|
4465
|
+
result.parameters
|
|
4466
|
+
);
|
|
4467
|
+
if (!shouldContinue) {
|
|
4468
|
+
this.executionHalted = true;
|
|
4469
|
+
}
|
|
4470
|
+
}
|
|
4471
|
+
}
|
|
4234
4472
|
/**
|
|
4235
4473
|
* Handle a gadget that cannot execute because a dependency failed.
|
|
4236
4474
|
* Calls the onDependencySkipped controller to allow customization.
|
|
@@ -4387,6 +4625,99 @@ var init_stream_processor = __esm({
|
|
|
4387
4625
|
}
|
|
4388
4626
|
return events;
|
|
4389
4627
|
}
|
|
4628
|
+
/**
|
|
4629
|
+
* Process pending gadgets, yielding events in real-time.
|
|
4630
|
+
* Generator version that yields events as gadgets complete.
|
|
4631
|
+
*
|
|
4632
|
+
* Note: Gadgets are still executed in parallel for efficiency,
|
|
4633
|
+
* but results are yielded as they become available.
|
|
4634
|
+
*/
|
|
4635
|
+
async *processPendingGadgetsGenerator() {
|
|
4636
|
+
let progress = true;
|
|
4637
|
+
while (progress && this.gadgetsAwaitingDependencies.size > 0) {
|
|
4638
|
+
progress = false;
|
|
4639
|
+
const readyToExecute = [];
|
|
4640
|
+
const readyToSkip = [];
|
|
4641
|
+
for (const [_invocationId, call] of this.gadgetsAwaitingDependencies) {
|
|
4642
|
+
const failedDep = call.dependencies.find((dep) => this.failedInvocations.has(dep));
|
|
4643
|
+
if (failedDep) {
|
|
4644
|
+
readyToSkip.push({ call, failedDep });
|
|
4645
|
+
continue;
|
|
4646
|
+
}
|
|
4647
|
+
const allSatisfied = call.dependencies.every((dep) => this.completedResults.has(dep));
|
|
4648
|
+
if (allSatisfied) {
|
|
4649
|
+
readyToExecute.push(call);
|
|
4650
|
+
}
|
|
4651
|
+
}
|
|
4652
|
+
for (const { call, failedDep } of readyToSkip) {
|
|
4653
|
+
this.gadgetsAwaitingDependencies.delete(call.invocationId);
|
|
4654
|
+
const skipEvents = await this.handleFailedDependency(call, failedDep);
|
|
4655
|
+
for (const evt of skipEvents) {
|
|
4656
|
+
yield evt;
|
|
4657
|
+
}
|
|
4658
|
+
progress = true;
|
|
4659
|
+
}
|
|
4660
|
+
if (readyToExecute.length > 0) {
|
|
4661
|
+
this.logger.debug("Executing ready gadgets in parallel", {
|
|
4662
|
+
count: readyToExecute.length,
|
|
4663
|
+
invocationIds: readyToExecute.map((c) => c.invocationId)
|
|
4664
|
+
});
|
|
4665
|
+
for (const call of readyToExecute) {
|
|
4666
|
+
this.gadgetsAwaitingDependencies.delete(call.invocationId);
|
|
4667
|
+
}
|
|
4668
|
+
const eventSets = await Promise.all(
|
|
4669
|
+
readyToExecute.map(async (call) => {
|
|
4670
|
+
const events = [];
|
|
4671
|
+
for await (const evt of this.executeGadgetGenerator(call)) {
|
|
4672
|
+
events.push(evt);
|
|
4673
|
+
}
|
|
4674
|
+
return events;
|
|
4675
|
+
})
|
|
4676
|
+
);
|
|
4677
|
+
for (const events of eventSets) {
|
|
4678
|
+
for (const evt of events) {
|
|
4679
|
+
yield evt;
|
|
4680
|
+
}
|
|
4681
|
+
}
|
|
4682
|
+
progress = true;
|
|
4683
|
+
}
|
|
4684
|
+
}
|
|
4685
|
+
if (this.gadgetsAwaitingDependencies.size > 0) {
|
|
4686
|
+
const pendingIds = new Set(this.gadgetsAwaitingDependencies.keys());
|
|
4687
|
+
for (const [invocationId, call] of this.gadgetsAwaitingDependencies) {
|
|
4688
|
+
const missingDeps = call.dependencies.filter((dep) => !this.completedResults.has(dep));
|
|
4689
|
+
const circularDeps = missingDeps.filter((dep) => pendingIds.has(dep));
|
|
4690
|
+
const trulyMissingDeps = missingDeps.filter((dep) => !pendingIds.has(dep));
|
|
4691
|
+
let errorMessage;
|
|
4692
|
+
let logLevel = "warn";
|
|
4693
|
+
if (circularDeps.length > 0 && trulyMissingDeps.length > 0) {
|
|
4694
|
+
errorMessage = `Dependencies unresolvable: circular=[${circularDeps.join(", ")}], missing=[${trulyMissingDeps.join(", ")}]`;
|
|
4695
|
+
logLevel = "error";
|
|
4696
|
+
} else if (circularDeps.length > 0) {
|
|
4697
|
+
errorMessage = `Circular dependency detected: "${invocationId}" depends on "${circularDeps[0]}" which also depends on "${invocationId}" (directly or indirectly)`;
|
|
4698
|
+
} else {
|
|
4699
|
+
errorMessage = `Dependency "${missingDeps[0]}" was never executed - check that the invocation ID exists and is spelled correctly`;
|
|
4700
|
+
}
|
|
4701
|
+
this.logger[logLevel]("Gadget has unresolvable dependencies", {
|
|
4702
|
+
gadgetName: call.gadgetName,
|
|
4703
|
+
invocationId,
|
|
4704
|
+
circularDependencies: circularDeps,
|
|
4705
|
+
missingDependencies: trulyMissingDeps
|
|
4706
|
+
});
|
|
4707
|
+
this.failedInvocations.add(invocationId);
|
|
4708
|
+
const skipEvent = {
|
|
4709
|
+
type: "gadget_skipped",
|
|
4710
|
+
gadgetName: call.gadgetName,
|
|
4711
|
+
invocationId,
|
|
4712
|
+
parameters: call.parameters ?? {},
|
|
4713
|
+
failedDependency: missingDeps[0],
|
|
4714
|
+
failedDependencyError: errorMessage
|
|
4715
|
+
};
|
|
4716
|
+
yield skipEvent;
|
|
4717
|
+
}
|
|
4718
|
+
this.gadgetsAwaitingDependencies.clear();
|
|
4719
|
+
}
|
|
4720
|
+
}
|
|
4390
4721
|
/**
|
|
4391
4722
|
* Safely execute an observer, catching and logging any errors.
|
|
4392
4723
|
* Observers are non-critical, so errors are logged but don't crash the system.
|
|
@@ -4509,6 +4840,8 @@ var init_agent = __esm({
|
|
|
4509
4840
|
// Subagent configuration
|
|
4510
4841
|
agentContextConfig;
|
|
4511
4842
|
subagentConfig;
|
|
4843
|
+
// Nested event callback for subagent gadgets
|
|
4844
|
+
onNestedEvent;
|
|
4512
4845
|
/**
|
|
4513
4846
|
* Creates a new Agent instance.
|
|
4514
4847
|
* @internal This constructor is private. Use LLMist.createAgent() or AgentBuilder instead.
|
|
@@ -4586,6 +4919,7 @@ var init_agent = __esm({
|
|
|
4586
4919
|
temperature: this.temperature
|
|
4587
4920
|
};
|
|
4588
4921
|
this.subagentConfig = options.subagentConfig;
|
|
4922
|
+
this.onNestedEvent = options.onNestedEvent;
|
|
4589
4923
|
}
|
|
4590
4924
|
/**
|
|
4591
4925
|
* Get the gadget registry for this agent.
|
|
@@ -4816,12 +5150,30 @@ var init_agent = __esm({
|
|
|
4816
5150
|
client: this.client,
|
|
4817
5151
|
mediaStore: this.mediaStore,
|
|
4818
5152
|
agentConfig: this.agentContextConfig,
|
|
4819
|
-
subagentConfig: this.subagentConfig
|
|
5153
|
+
subagentConfig: this.subagentConfig,
|
|
5154
|
+
onNestedEvent: this.onNestedEvent
|
|
4820
5155
|
});
|
|
4821
|
-
|
|
4822
|
-
|
|
4823
|
-
|
|
5156
|
+
let streamMetadata = null;
|
|
5157
|
+
let gadgetCallCount = 0;
|
|
5158
|
+
const textOutputs = [];
|
|
5159
|
+
const gadgetResults = [];
|
|
5160
|
+
for await (const event of processor.process(stream2)) {
|
|
5161
|
+
if (event.type === "stream_complete") {
|
|
5162
|
+
streamMetadata = event;
|
|
5163
|
+
continue;
|
|
5164
|
+
}
|
|
5165
|
+
if (event.type === "text") {
|
|
5166
|
+
textOutputs.push(event.content);
|
|
5167
|
+
} else if (event.type === "gadget_result") {
|
|
5168
|
+
gadgetCallCount++;
|
|
5169
|
+
gadgetResults.push(event);
|
|
5170
|
+
}
|
|
5171
|
+
yield event;
|
|
4824
5172
|
}
|
|
5173
|
+
if (!streamMetadata) {
|
|
5174
|
+
throw new Error("Stream processing completed without metadata event");
|
|
5175
|
+
}
|
|
5176
|
+
const result = streamMetadata;
|
|
4825
5177
|
this.logger.info("LLM response completed", {
|
|
4826
5178
|
finishReason: result.finishReason,
|
|
4827
5179
|
usage: result.usage,
|
|
@@ -4846,9 +5198,6 @@ var init_agent = __esm({
|
|
|
4846
5198
|
});
|
|
4847
5199
|
let finalMessage = result.finalMessage;
|
|
4848
5200
|
if (this.hooks.controllers?.afterLLMCall) {
|
|
4849
|
-
const gadgetCallCount = result.outputs.filter(
|
|
4850
|
-
(output) => output.type === "gadget_result"
|
|
4851
|
-
).length;
|
|
4852
5201
|
const context = {
|
|
4853
5202
|
iteration: currentIteration,
|
|
4854
5203
|
maxIterations: this.maxIterations,
|
|
@@ -4878,9 +5227,7 @@ var init_agent = __esm({
|
|
|
4878
5227
|
}
|
|
4879
5228
|
if (result.didExecuteGadgets) {
|
|
4880
5229
|
if (this.textWithGadgetsHandler) {
|
|
4881
|
-
const textContent =
|
|
4882
|
-
(output) => output.type === "text"
|
|
4883
|
-
).map((output) => output.content).join("");
|
|
5230
|
+
const textContent = textOutputs.join("");
|
|
4884
5231
|
if (textContent.trim()) {
|
|
4885
5232
|
const { gadgetName, parameterMapping, resultMapping } = this.textWithGadgetsHandler;
|
|
4886
5233
|
this.conversation.addGadgetCallResult(
|
|
@@ -4890,7 +5237,7 @@ var init_agent = __esm({
|
|
|
4890
5237
|
);
|
|
4891
5238
|
}
|
|
4892
5239
|
}
|
|
4893
|
-
for (const output of
|
|
5240
|
+
for (const output of gadgetResults) {
|
|
4894
5241
|
if (output.type === "gadget_result") {
|
|
4895
5242
|
const gadgetResult = output.result;
|
|
4896
5243
|
this.conversation.addGadgetCallResult(
|
|
@@ -5127,6 +5474,8 @@ var init_builder = __esm({
|
|
|
5127
5474
|
signal;
|
|
5128
5475
|
trailingMessage;
|
|
5129
5476
|
subagentConfig;
|
|
5477
|
+
nestedEventCallback;
|
|
5478
|
+
parentContext;
|
|
5130
5479
|
constructor(client) {
|
|
5131
5480
|
this.client = client;
|
|
5132
5481
|
}
|
|
@@ -5623,6 +5972,74 @@ var init_builder = __esm({
|
|
|
5623
5972
|
this.subagentConfig = config;
|
|
5624
5973
|
return this;
|
|
5625
5974
|
}
|
|
5975
|
+
/**
|
|
5976
|
+
* Set the callback for nested subagent events.
|
|
5977
|
+
*
|
|
5978
|
+
* Subagent gadgets (like BrowseWeb) can use ExecutionContext.onNestedEvent
|
|
5979
|
+
* to report their internal LLM calls and gadget executions in real-time.
|
|
5980
|
+
* This callback receives those events, enabling hierarchical progress display.
|
|
5981
|
+
*
|
|
5982
|
+
* @param callback - Function to handle nested agent events
|
|
5983
|
+
* @returns This builder for chaining
|
|
5984
|
+
*
|
|
5985
|
+
* @example
|
|
5986
|
+
* ```typescript
|
|
5987
|
+
* .withNestedEventCallback((event) => {
|
|
5988
|
+
* if (event.type === "llm_call_start") {
|
|
5989
|
+
* console.log(` Nested LLM #${event.event.iteration} starting...`);
|
|
5990
|
+
* } else if (event.type === "gadget_call") {
|
|
5991
|
+
* console.log(` ⏵ ${event.event.call.gadgetName}...`);
|
|
5992
|
+
* }
|
|
5993
|
+
* })
|
|
5994
|
+
* ```
|
|
5995
|
+
*/
|
|
5996
|
+
withNestedEventCallback(callback) {
|
|
5997
|
+
this.nestedEventCallback = callback;
|
|
5998
|
+
return this;
|
|
5999
|
+
}
|
|
6000
|
+
/**
|
|
6001
|
+
* Enable automatic nested event forwarding to parent agent.
|
|
6002
|
+
*
|
|
6003
|
+
* When building a subagent inside a gadget, call this method to automatically
|
|
6004
|
+
* forward all LLM calls and gadget events to the parent agent. This enables
|
|
6005
|
+
* hierarchical progress display without any manual event handling.
|
|
6006
|
+
*
|
|
6007
|
+
* The method extracts `invocationId` and `onNestedEvent` from the execution
|
|
6008
|
+
* context and sets up automatic forwarding via hooks and event wrapping.
|
|
6009
|
+
*
|
|
6010
|
+
* @param ctx - ExecutionContext passed to the gadget's execute() method
|
|
6011
|
+
* @param depth - Nesting depth (default: 1 for direct child)
|
|
6012
|
+
* @returns This builder for chaining
|
|
6013
|
+
*
|
|
6014
|
+
* @example
|
|
6015
|
+
* ```typescript
|
|
6016
|
+
* // In a subagent gadget like BrowseWeb - ONE LINE enables auto-forwarding:
|
|
6017
|
+
* execute: async (params, ctx) => {
|
|
6018
|
+
* const agent = new AgentBuilder(client)
|
|
6019
|
+
* .withModel(model)
|
|
6020
|
+
* .withGadgets(Navigate, Click, Screenshot)
|
|
6021
|
+
* .withParentContext(ctx) // <-- This is all you need!
|
|
6022
|
+
* .ask(params.task);
|
|
6023
|
+
*
|
|
6024
|
+
* for await (const event of agent.run()) {
|
|
6025
|
+
* // Events automatically forwarded - just process normally
|
|
6026
|
+
* if (event.type === "text") {
|
|
6027
|
+
* result = event.content;
|
|
6028
|
+
* }
|
|
6029
|
+
* }
|
|
6030
|
+
* }
|
|
6031
|
+
* ```
|
|
6032
|
+
*/
|
|
6033
|
+
withParentContext(ctx, depth = 1) {
|
|
6034
|
+
if (ctx.onNestedEvent && ctx.invocationId) {
|
|
6035
|
+
this.parentContext = {
|
|
6036
|
+
invocationId: ctx.invocationId,
|
|
6037
|
+
onNestedEvent: ctx.onNestedEvent,
|
|
6038
|
+
depth
|
|
6039
|
+
};
|
|
6040
|
+
}
|
|
6041
|
+
return this;
|
|
6042
|
+
}
|
|
5626
6043
|
/**
|
|
5627
6044
|
* Add an ephemeral trailing message that appears at the end of each LLM request.
|
|
5628
6045
|
*
|
|
@@ -5690,14 +6107,58 @@ ${endPrefix}`
|
|
|
5690
6107
|
return this;
|
|
5691
6108
|
}
|
|
5692
6109
|
/**
|
|
5693
|
-
* Compose the final hooks, including
|
|
6110
|
+
* Compose the final hooks, including:
|
|
6111
|
+
* - Trailing message injection (if configured)
|
|
6112
|
+
* - Nested event forwarding for LLM calls (if parentContext is set)
|
|
5694
6113
|
*/
|
|
5695
6114
|
composeHooks() {
|
|
6115
|
+
let hooks = this.hooks;
|
|
6116
|
+
if (this.parentContext) {
|
|
6117
|
+
const { invocationId, onNestedEvent, depth } = this.parentContext;
|
|
6118
|
+
const existingOnLLMCallStart = hooks?.observers?.onLLMCallStart;
|
|
6119
|
+
const existingOnLLMCallComplete = hooks?.observers?.onLLMCallComplete;
|
|
6120
|
+
hooks = {
|
|
6121
|
+
...hooks,
|
|
6122
|
+
observers: {
|
|
6123
|
+
...hooks?.observers,
|
|
6124
|
+
onLLMCallStart: async (context) => {
|
|
6125
|
+
onNestedEvent({
|
|
6126
|
+
type: "llm_call_start",
|
|
6127
|
+
gadgetInvocationId: invocationId,
|
|
6128
|
+
depth,
|
|
6129
|
+
event: {
|
|
6130
|
+
iteration: context.iteration,
|
|
6131
|
+
model: context.options.model
|
|
6132
|
+
}
|
|
6133
|
+
});
|
|
6134
|
+
if (existingOnLLMCallStart) {
|
|
6135
|
+
await existingOnLLMCallStart(context);
|
|
6136
|
+
}
|
|
6137
|
+
},
|
|
6138
|
+
onLLMCallComplete: async (context) => {
|
|
6139
|
+
onNestedEvent({
|
|
6140
|
+
type: "llm_call_end",
|
|
6141
|
+
gadgetInvocationId: invocationId,
|
|
6142
|
+
depth,
|
|
6143
|
+
event: {
|
|
6144
|
+
iteration: context.iteration,
|
|
6145
|
+
model: context.options.model,
|
|
6146
|
+
outputTokens: context.usage?.outputTokens,
|
|
6147
|
+
finishReason: context.finishReason
|
|
6148
|
+
}
|
|
6149
|
+
});
|
|
6150
|
+
if (existingOnLLMCallComplete) {
|
|
6151
|
+
await existingOnLLMCallComplete(context);
|
|
6152
|
+
}
|
|
6153
|
+
}
|
|
6154
|
+
}
|
|
6155
|
+
};
|
|
6156
|
+
}
|
|
5696
6157
|
if (!this.trailingMessage) {
|
|
5697
|
-
return
|
|
6158
|
+
return hooks;
|
|
5698
6159
|
}
|
|
5699
6160
|
const trailingMsg = this.trailingMessage;
|
|
5700
|
-
const existingBeforeLLMCall =
|
|
6161
|
+
const existingBeforeLLMCall = hooks?.controllers?.beforeLLMCall;
|
|
5701
6162
|
const trailingMessageController = async (ctx) => {
|
|
5702
6163
|
const result = existingBeforeLLMCall ? await existingBeforeLLMCall(ctx) : { action: "proceed" };
|
|
5703
6164
|
if (result.action === "skip") {
|
|
@@ -5712,9 +6173,9 @@ ${endPrefix}`
|
|
|
5712
6173
|
};
|
|
5713
6174
|
};
|
|
5714
6175
|
return {
|
|
5715
|
-
...
|
|
6176
|
+
...hooks,
|
|
5716
6177
|
controllers: {
|
|
5717
|
-
...
|
|
6178
|
+
...hooks?.controllers,
|
|
5718
6179
|
beforeLLMCall: trailingMessageController
|
|
5719
6180
|
}
|
|
5720
6181
|
};
|
|
@@ -5775,6 +6236,19 @@ ${endPrefix}`
|
|
|
5775
6236
|
this.client = new LLMistClass();
|
|
5776
6237
|
}
|
|
5777
6238
|
const registry = GadgetRegistry.from(this.gadgets);
|
|
6239
|
+
let onNestedEvent = this.nestedEventCallback;
|
|
6240
|
+
if (this.parentContext) {
|
|
6241
|
+
const { invocationId, onNestedEvent: parentCallback, depth } = this.parentContext;
|
|
6242
|
+
const existingCallback = this.nestedEventCallback;
|
|
6243
|
+
onNestedEvent = (event) => {
|
|
6244
|
+
parentCallback({
|
|
6245
|
+
...event,
|
|
6246
|
+
gadgetInvocationId: invocationId,
|
|
6247
|
+
depth: event.depth + depth
|
|
6248
|
+
});
|
|
6249
|
+
existingCallback?.(event);
|
|
6250
|
+
};
|
|
6251
|
+
}
|
|
5778
6252
|
return {
|
|
5779
6253
|
client: this.client,
|
|
5780
6254
|
model: this.model ?? "openai:gpt-5-nano",
|
|
@@ -5800,7 +6274,8 @@ ${endPrefix}`
|
|
|
5800
6274
|
gadgetOutputLimitPercent: this.gadgetOutputLimitPercent,
|
|
5801
6275
|
compactionConfig: this.compactionConfig,
|
|
5802
6276
|
signal: this.signal,
|
|
5803
|
-
subagentConfig: this.subagentConfig
|
|
6277
|
+
subagentConfig: this.subagentConfig,
|
|
6278
|
+
onNestedEvent
|
|
5804
6279
|
};
|
|
5805
6280
|
}
|
|
5806
6281
|
ask(userPrompt) {
|
|
@@ -5957,6 +6432,19 @@ ${endPrefix}`
|
|
|
5957
6432
|
this.client = new LLMistClass();
|
|
5958
6433
|
}
|
|
5959
6434
|
const registry = GadgetRegistry.from(this.gadgets);
|
|
6435
|
+
let onNestedEvent = this.nestedEventCallback;
|
|
6436
|
+
if (this.parentContext) {
|
|
6437
|
+
const { invocationId, onNestedEvent: parentCallback, depth } = this.parentContext;
|
|
6438
|
+
const existingCallback = this.nestedEventCallback;
|
|
6439
|
+
onNestedEvent = (event) => {
|
|
6440
|
+
parentCallback({
|
|
6441
|
+
...event,
|
|
6442
|
+
gadgetInvocationId: invocationId,
|
|
6443
|
+
depth: event.depth + depth
|
|
6444
|
+
});
|
|
6445
|
+
existingCallback?.(event);
|
|
6446
|
+
};
|
|
6447
|
+
}
|
|
5960
6448
|
const options = {
|
|
5961
6449
|
client: this.client,
|
|
5962
6450
|
model: this.model ?? "openai:gpt-5-nano",
|
|
@@ -5982,7 +6470,8 @@ ${endPrefix}`
|
|
|
5982
6470
|
gadgetOutputLimitPercent: this.gadgetOutputLimitPercent,
|
|
5983
6471
|
compactionConfig: this.compactionConfig,
|
|
5984
6472
|
signal: this.signal,
|
|
5985
|
-
subagentConfig: this.subagentConfig
|
|
6473
|
+
subagentConfig: this.subagentConfig,
|
|
6474
|
+
onNestedEvent
|
|
5986
6475
|
};
|
|
5987
6476
|
return new Agent(AGENT_INTERNAL_KEY, options);
|
|
5988
6477
|
}
|