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/cli.cjs
CHANGED
|
@@ -3496,7 +3496,7 @@ var init_executor = __esm({
|
|
|
3496
3496
|
init_exceptions();
|
|
3497
3497
|
init_parser();
|
|
3498
3498
|
GadgetExecutor = class {
|
|
3499
|
-
constructor(registry, requestHumanInput, logger, defaultGadgetTimeoutMs, errorFormatterOptions, client, mediaStore, agentConfig, subagentConfig) {
|
|
3499
|
+
constructor(registry, requestHumanInput, logger, defaultGadgetTimeoutMs, errorFormatterOptions, client, mediaStore, agentConfig, subagentConfig, onNestedEvent) {
|
|
3500
3500
|
this.registry = registry;
|
|
3501
3501
|
this.requestHumanInput = requestHumanInput;
|
|
3502
3502
|
this.defaultGadgetTimeoutMs = defaultGadgetTimeoutMs;
|
|
@@ -3504,6 +3504,7 @@ var init_executor = __esm({
|
|
|
3504
3504
|
this.mediaStore = mediaStore;
|
|
3505
3505
|
this.agentConfig = agentConfig;
|
|
3506
3506
|
this.subagentConfig = subagentConfig;
|
|
3507
|
+
this.onNestedEvent = onNestedEvent;
|
|
3507
3508
|
this.logger = logger ?? createLogger({ name: "llmist:executor" });
|
|
3508
3509
|
this.errorFormatter = new GadgetExecutionErrorFormatter(errorFormatterOptions);
|
|
3509
3510
|
this.argPrefix = errorFormatterOptions?.argPrefix ?? GADGET_ARG_PREFIX;
|
|
@@ -3649,7 +3650,9 @@ var init_executor = __esm({
|
|
|
3649
3650
|
llmist: this.client ? new CostReportingLLMistWrapper(this.client, reportCost) : void 0,
|
|
3650
3651
|
signal: abortController.signal,
|
|
3651
3652
|
agentConfig: this.agentConfig,
|
|
3652
|
-
subagentConfig: this.subagentConfig
|
|
3653
|
+
subagentConfig: this.subagentConfig,
|
|
3654
|
+
invocationId: call.invocationId,
|
|
3655
|
+
onNestedEvent: this.onNestedEvent
|
|
3653
3656
|
};
|
|
3654
3657
|
let rawResult;
|
|
3655
3658
|
if (timeoutMs && timeoutMs > 0) {
|
|
@@ -3888,14 +3891,21 @@ var init_stream_processor = __esm({
|
|
|
3888
3891
|
options.client,
|
|
3889
3892
|
options.mediaStore,
|
|
3890
3893
|
options.agentConfig,
|
|
3891
|
-
options.subagentConfig
|
|
3894
|
+
options.subagentConfig,
|
|
3895
|
+
options.onNestedEvent
|
|
3892
3896
|
);
|
|
3893
3897
|
}
|
|
3894
3898
|
/**
|
|
3895
|
-
* Process an LLM stream and
|
|
3899
|
+
* Process an LLM stream and yield events in real-time.
|
|
3900
|
+
*
|
|
3901
|
+
* This is an async generator that yields events immediately as they occur:
|
|
3902
|
+
* - Text events are yielded as text is streamed from the LLM
|
|
3903
|
+
* - gadget_call events are yielded immediately when a gadget call is parsed
|
|
3904
|
+
* - gadget_result events are yielded when gadget execution completes
|
|
3905
|
+
*
|
|
3906
|
+
* The final event is always a StreamCompletionEvent containing metadata.
|
|
3896
3907
|
*/
|
|
3897
|
-
async process(stream2) {
|
|
3898
|
-
const outputs = [];
|
|
3908
|
+
async *process(stream2) {
|
|
3899
3909
|
let finishReason = null;
|
|
3900
3910
|
let usage;
|
|
3901
3911
|
let didExecuteGadgets = false;
|
|
@@ -3941,14 +3951,13 @@ var init_stream_processor = __esm({
|
|
|
3941
3951
|
continue;
|
|
3942
3952
|
}
|
|
3943
3953
|
for (const event of this.parser.feed(processedChunk)) {
|
|
3944
|
-
const
|
|
3945
|
-
|
|
3946
|
-
|
|
3947
|
-
|
|
3948
|
-
|
|
3949
|
-
|
|
3950
|
-
|
|
3951
|
-
shouldBreakLoop = true;
|
|
3954
|
+
for await (const processedEvent of this.processEventGenerator(event)) {
|
|
3955
|
+
yield processedEvent;
|
|
3956
|
+
if (processedEvent.type === "gadget_result") {
|
|
3957
|
+
didExecuteGadgets = true;
|
|
3958
|
+
if (processedEvent.result.breaksLoop) {
|
|
3959
|
+
shouldBreakLoop = true;
|
|
3960
|
+
}
|
|
3952
3961
|
}
|
|
3953
3962
|
}
|
|
3954
3963
|
}
|
|
@@ -3959,25 +3968,23 @@ var init_stream_processor = __esm({
|
|
|
3959
3968
|
}
|
|
3960
3969
|
if (!this.executionHalted) {
|
|
3961
3970
|
for (const event of this.parser.finalize()) {
|
|
3962
|
-
const
|
|
3963
|
-
|
|
3964
|
-
|
|
3965
|
-
|
|
3966
|
-
|
|
3967
|
-
|
|
3968
|
-
|
|
3969
|
-
shouldBreakLoop = true;
|
|
3971
|
+
for await (const processedEvent of this.processEventGenerator(event)) {
|
|
3972
|
+
yield processedEvent;
|
|
3973
|
+
if (processedEvent.type === "gadget_result") {
|
|
3974
|
+
didExecuteGadgets = true;
|
|
3975
|
+
if (processedEvent.result.breaksLoop) {
|
|
3976
|
+
shouldBreakLoop = true;
|
|
3977
|
+
}
|
|
3970
3978
|
}
|
|
3971
3979
|
}
|
|
3972
3980
|
}
|
|
3973
|
-
const
|
|
3974
|
-
|
|
3975
|
-
|
|
3976
|
-
|
|
3977
|
-
|
|
3978
|
-
|
|
3979
|
-
|
|
3980
|
-
shouldBreakLoop = true;
|
|
3981
|
+
for await (const evt of this.processPendingGadgetsGenerator()) {
|
|
3982
|
+
yield evt;
|
|
3983
|
+
if (evt.type === "gadget_result") {
|
|
3984
|
+
didExecuteGadgets = true;
|
|
3985
|
+
if (evt.result.breaksLoop) {
|
|
3986
|
+
shouldBreakLoop = true;
|
|
3987
|
+
}
|
|
3981
3988
|
}
|
|
3982
3989
|
}
|
|
3983
3990
|
}
|
|
@@ -3990,8 +3997,8 @@ var init_stream_processor = __esm({
|
|
|
3990
3997
|
};
|
|
3991
3998
|
finalMessage = this.hooks.interceptors.interceptAssistantMessage(finalMessage, context);
|
|
3992
3999
|
}
|
|
3993
|
-
|
|
3994
|
-
|
|
4000
|
+
const completionEvent = {
|
|
4001
|
+
type: "stream_complete",
|
|
3995
4002
|
shouldBreakLoop,
|
|
3996
4003
|
didExecuteGadgets,
|
|
3997
4004
|
finishReason,
|
|
@@ -3999,9 +4006,11 @@ var init_stream_processor = __esm({
|
|
|
3999
4006
|
rawResponse: this.responseText,
|
|
4000
4007
|
finalMessage
|
|
4001
4008
|
};
|
|
4009
|
+
yield completionEvent;
|
|
4002
4010
|
}
|
|
4003
4011
|
/**
|
|
4004
4012
|
* Process a single parsed event (text or gadget call).
|
|
4013
|
+
* @deprecated Use processEventGenerator for real-time streaming
|
|
4005
4014
|
*/
|
|
4006
4015
|
async processEvent(event) {
|
|
4007
4016
|
if (event.type === "text") {
|
|
@@ -4011,6 +4020,23 @@ var init_stream_processor = __esm({
|
|
|
4011
4020
|
}
|
|
4012
4021
|
return [event];
|
|
4013
4022
|
}
|
|
4023
|
+
/**
|
|
4024
|
+
* Process a single parsed event, yielding events in real-time.
|
|
4025
|
+
* Generator version of processEvent for streaming support.
|
|
4026
|
+
*/
|
|
4027
|
+
async *processEventGenerator(event) {
|
|
4028
|
+
if (event.type === "text") {
|
|
4029
|
+
for (const e of await this.processTextEvent(event)) {
|
|
4030
|
+
yield e;
|
|
4031
|
+
}
|
|
4032
|
+
} else if (event.type === "gadget_call") {
|
|
4033
|
+
for await (const e of this.processGadgetCallGenerator(event.call)) {
|
|
4034
|
+
yield e;
|
|
4035
|
+
}
|
|
4036
|
+
} else {
|
|
4037
|
+
yield event;
|
|
4038
|
+
}
|
|
4039
|
+
}
|
|
4014
4040
|
/**
|
|
4015
4041
|
* Process a text event through interceptors.
|
|
4016
4042
|
*/
|
|
@@ -4087,9 +4113,68 @@ var init_stream_processor = __esm({
|
|
|
4087
4113
|
events.push(...triggeredEvents);
|
|
4088
4114
|
return events;
|
|
4089
4115
|
}
|
|
4116
|
+
/**
|
|
4117
|
+
* Process a gadget call, yielding events in real-time.
|
|
4118
|
+
*
|
|
4119
|
+
* Key difference from processGadgetCall: yields gadget_call event IMMEDIATELY
|
|
4120
|
+
* when parsed (before execution), enabling real-time UI feedback.
|
|
4121
|
+
*/
|
|
4122
|
+
async *processGadgetCallGenerator(call) {
|
|
4123
|
+
if (this.executionHalted) {
|
|
4124
|
+
this.logger.debug("Skipping gadget execution due to previous error", {
|
|
4125
|
+
gadgetName: call.gadgetName
|
|
4126
|
+
});
|
|
4127
|
+
return;
|
|
4128
|
+
}
|
|
4129
|
+
yield { type: "gadget_call", call };
|
|
4130
|
+
if (call.dependencies.length > 0) {
|
|
4131
|
+
if (call.dependencies.includes(call.invocationId)) {
|
|
4132
|
+
this.logger.warn("Gadget has self-referential dependency (depends on itself)", {
|
|
4133
|
+
gadgetName: call.gadgetName,
|
|
4134
|
+
invocationId: call.invocationId
|
|
4135
|
+
});
|
|
4136
|
+
this.failedInvocations.add(call.invocationId);
|
|
4137
|
+
const skipEvent = {
|
|
4138
|
+
type: "gadget_skipped",
|
|
4139
|
+
gadgetName: call.gadgetName,
|
|
4140
|
+
invocationId: call.invocationId,
|
|
4141
|
+
parameters: call.parameters ?? {},
|
|
4142
|
+
failedDependency: call.invocationId,
|
|
4143
|
+
failedDependencyError: `Gadget "${call.invocationId}" cannot depend on itself (self-referential dependency)`
|
|
4144
|
+
};
|
|
4145
|
+
yield skipEvent;
|
|
4146
|
+
return;
|
|
4147
|
+
}
|
|
4148
|
+
const failedDep = call.dependencies.find((dep) => this.failedInvocations.has(dep));
|
|
4149
|
+
if (failedDep) {
|
|
4150
|
+
const skipEvents = await this.handleFailedDependency(call, failedDep);
|
|
4151
|
+
for (const evt of skipEvents) {
|
|
4152
|
+
yield evt;
|
|
4153
|
+
}
|
|
4154
|
+
return;
|
|
4155
|
+
}
|
|
4156
|
+
const unsatisfied = call.dependencies.filter((dep) => !this.completedResults.has(dep));
|
|
4157
|
+
if (unsatisfied.length > 0) {
|
|
4158
|
+
this.logger.debug("Queueing gadget for later - waiting on dependencies", {
|
|
4159
|
+
gadgetName: call.gadgetName,
|
|
4160
|
+
invocationId: call.invocationId,
|
|
4161
|
+
waitingOn: unsatisfied
|
|
4162
|
+
});
|
|
4163
|
+
this.gadgetsAwaitingDependencies.set(call.invocationId, call);
|
|
4164
|
+
return;
|
|
4165
|
+
}
|
|
4166
|
+
}
|
|
4167
|
+
for await (const evt of this.executeGadgetGenerator(call)) {
|
|
4168
|
+
yield evt;
|
|
4169
|
+
}
|
|
4170
|
+
for await (const evt of this.processPendingGadgetsGenerator()) {
|
|
4171
|
+
yield evt;
|
|
4172
|
+
}
|
|
4173
|
+
}
|
|
4090
4174
|
/**
|
|
4091
4175
|
* Execute a gadget through the full hook lifecycle.
|
|
4092
4176
|
* This is the core execution logic, extracted from processGadgetCall.
|
|
4177
|
+
* @deprecated Use executeGadgetGenerator for real-time streaming
|
|
4093
4178
|
*/
|
|
4094
4179
|
async executeGadgetWithHooks(call) {
|
|
4095
4180
|
const events = [];
|
|
@@ -4242,6 +4327,159 @@ var init_stream_processor = __esm({
|
|
|
4242
4327
|
}
|
|
4243
4328
|
return events;
|
|
4244
4329
|
}
|
|
4330
|
+
/**
|
|
4331
|
+
* Execute a gadget and yield the result event.
|
|
4332
|
+
* Generator version that yields gadget_result immediately when execution completes.
|
|
4333
|
+
*/
|
|
4334
|
+
async *executeGadgetGenerator(call) {
|
|
4335
|
+
if (call.parseError) {
|
|
4336
|
+
this.logger.warn("Gadget has parse error", {
|
|
4337
|
+
gadgetName: call.gadgetName,
|
|
4338
|
+
error: call.parseError,
|
|
4339
|
+
rawParameters: call.parametersRaw
|
|
4340
|
+
});
|
|
4341
|
+
const shouldContinue = await this.checkCanRecoverFromError(
|
|
4342
|
+
call.parseError,
|
|
4343
|
+
call.gadgetName,
|
|
4344
|
+
"parse",
|
|
4345
|
+
call.parameters
|
|
4346
|
+
);
|
|
4347
|
+
if (!shouldContinue) {
|
|
4348
|
+
this.executionHalted = true;
|
|
4349
|
+
}
|
|
4350
|
+
}
|
|
4351
|
+
let parameters = call.parameters ?? {};
|
|
4352
|
+
if (this.hooks.interceptors?.interceptGadgetParameters) {
|
|
4353
|
+
const context = {
|
|
4354
|
+
iteration: this.iteration,
|
|
4355
|
+
gadgetName: call.gadgetName,
|
|
4356
|
+
invocationId: call.invocationId,
|
|
4357
|
+
logger: this.logger
|
|
4358
|
+
};
|
|
4359
|
+
parameters = this.hooks.interceptors.interceptGadgetParameters(parameters, context);
|
|
4360
|
+
}
|
|
4361
|
+
call.parameters = parameters;
|
|
4362
|
+
let shouldSkip = false;
|
|
4363
|
+
let syntheticResult;
|
|
4364
|
+
if (this.hooks.controllers?.beforeGadgetExecution) {
|
|
4365
|
+
const context = {
|
|
4366
|
+
iteration: this.iteration,
|
|
4367
|
+
gadgetName: call.gadgetName,
|
|
4368
|
+
invocationId: call.invocationId,
|
|
4369
|
+
parameters,
|
|
4370
|
+
logger: this.logger
|
|
4371
|
+
};
|
|
4372
|
+
const action = await this.hooks.controllers.beforeGadgetExecution(context);
|
|
4373
|
+
validateBeforeGadgetExecutionAction(action);
|
|
4374
|
+
if (action.action === "skip") {
|
|
4375
|
+
shouldSkip = true;
|
|
4376
|
+
syntheticResult = action.syntheticResult;
|
|
4377
|
+
this.logger.info("Controller skipped gadget execution", {
|
|
4378
|
+
gadgetName: call.gadgetName
|
|
4379
|
+
});
|
|
4380
|
+
}
|
|
4381
|
+
}
|
|
4382
|
+
const startObservers = [];
|
|
4383
|
+
if (this.hooks.observers?.onGadgetExecutionStart) {
|
|
4384
|
+
startObservers.push(async () => {
|
|
4385
|
+
const context = {
|
|
4386
|
+
iteration: this.iteration,
|
|
4387
|
+
gadgetName: call.gadgetName,
|
|
4388
|
+
invocationId: call.invocationId,
|
|
4389
|
+
parameters,
|
|
4390
|
+
logger: this.logger
|
|
4391
|
+
};
|
|
4392
|
+
await this.hooks.observers.onGadgetExecutionStart(context);
|
|
4393
|
+
});
|
|
4394
|
+
}
|
|
4395
|
+
await this.runObserversInParallel(startObservers);
|
|
4396
|
+
let result;
|
|
4397
|
+
if (shouldSkip) {
|
|
4398
|
+
result = {
|
|
4399
|
+
gadgetName: call.gadgetName,
|
|
4400
|
+
invocationId: call.invocationId,
|
|
4401
|
+
parameters,
|
|
4402
|
+
result: syntheticResult ?? "Execution skipped",
|
|
4403
|
+
executionTimeMs: 0
|
|
4404
|
+
};
|
|
4405
|
+
} else {
|
|
4406
|
+
result = await this.executor.execute(call);
|
|
4407
|
+
}
|
|
4408
|
+
const originalResult = result.result;
|
|
4409
|
+
if (result.result && this.hooks.interceptors?.interceptGadgetResult) {
|
|
4410
|
+
const context = {
|
|
4411
|
+
iteration: this.iteration,
|
|
4412
|
+
gadgetName: result.gadgetName,
|
|
4413
|
+
invocationId: result.invocationId,
|
|
4414
|
+
parameters,
|
|
4415
|
+
executionTimeMs: result.executionTimeMs,
|
|
4416
|
+
logger: this.logger
|
|
4417
|
+
};
|
|
4418
|
+
result.result = this.hooks.interceptors.interceptGadgetResult(result.result, context);
|
|
4419
|
+
}
|
|
4420
|
+
if (this.hooks.controllers?.afterGadgetExecution) {
|
|
4421
|
+
const context = {
|
|
4422
|
+
iteration: this.iteration,
|
|
4423
|
+
gadgetName: result.gadgetName,
|
|
4424
|
+
invocationId: result.invocationId,
|
|
4425
|
+
parameters,
|
|
4426
|
+
result: result.result,
|
|
4427
|
+
error: result.error,
|
|
4428
|
+
executionTimeMs: result.executionTimeMs,
|
|
4429
|
+
logger: this.logger
|
|
4430
|
+
};
|
|
4431
|
+
const action = await this.hooks.controllers.afterGadgetExecution(context);
|
|
4432
|
+
validateAfterGadgetExecutionAction(action);
|
|
4433
|
+
if (action.action === "recover" && result.error) {
|
|
4434
|
+
this.logger.info("Controller recovered from gadget error", {
|
|
4435
|
+
gadgetName: result.gadgetName,
|
|
4436
|
+
originalError: result.error
|
|
4437
|
+
});
|
|
4438
|
+
result = {
|
|
4439
|
+
...result,
|
|
4440
|
+
error: void 0,
|
|
4441
|
+
result: action.fallbackResult
|
|
4442
|
+
};
|
|
4443
|
+
}
|
|
4444
|
+
}
|
|
4445
|
+
const completeObservers = [];
|
|
4446
|
+
if (this.hooks.observers?.onGadgetExecutionComplete) {
|
|
4447
|
+
completeObservers.push(async () => {
|
|
4448
|
+
const context = {
|
|
4449
|
+
iteration: this.iteration,
|
|
4450
|
+
gadgetName: result.gadgetName,
|
|
4451
|
+
invocationId: result.invocationId,
|
|
4452
|
+
parameters,
|
|
4453
|
+
originalResult,
|
|
4454
|
+
finalResult: result.result,
|
|
4455
|
+
error: result.error,
|
|
4456
|
+
executionTimeMs: result.executionTimeMs,
|
|
4457
|
+
breaksLoop: result.breaksLoop,
|
|
4458
|
+
cost: result.cost,
|
|
4459
|
+
logger: this.logger
|
|
4460
|
+
};
|
|
4461
|
+
await this.hooks.observers.onGadgetExecutionComplete(context);
|
|
4462
|
+
});
|
|
4463
|
+
}
|
|
4464
|
+
await this.runObserversInParallel(completeObservers);
|
|
4465
|
+
this.completedResults.set(result.invocationId, result);
|
|
4466
|
+
if (result.error) {
|
|
4467
|
+
this.failedInvocations.add(result.invocationId);
|
|
4468
|
+
}
|
|
4469
|
+
yield { type: "gadget_result", result };
|
|
4470
|
+
if (result.error) {
|
|
4471
|
+
const errorType = this.determineErrorType(call, result);
|
|
4472
|
+
const shouldContinue = await this.checkCanRecoverFromError(
|
|
4473
|
+
result.error,
|
|
4474
|
+
result.gadgetName,
|
|
4475
|
+
errorType,
|
|
4476
|
+
result.parameters
|
|
4477
|
+
);
|
|
4478
|
+
if (!shouldContinue) {
|
|
4479
|
+
this.executionHalted = true;
|
|
4480
|
+
}
|
|
4481
|
+
}
|
|
4482
|
+
}
|
|
4245
4483
|
/**
|
|
4246
4484
|
* Handle a gadget that cannot execute because a dependency failed.
|
|
4247
4485
|
* Calls the onDependencySkipped controller to allow customization.
|
|
@@ -4398,6 +4636,99 @@ var init_stream_processor = __esm({
|
|
|
4398
4636
|
}
|
|
4399
4637
|
return events;
|
|
4400
4638
|
}
|
|
4639
|
+
/**
|
|
4640
|
+
* Process pending gadgets, yielding events in real-time.
|
|
4641
|
+
* Generator version that yields events as gadgets complete.
|
|
4642
|
+
*
|
|
4643
|
+
* Note: Gadgets are still executed in parallel for efficiency,
|
|
4644
|
+
* but results are yielded as they become available.
|
|
4645
|
+
*/
|
|
4646
|
+
async *processPendingGadgetsGenerator() {
|
|
4647
|
+
let progress = true;
|
|
4648
|
+
while (progress && this.gadgetsAwaitingDependencies.size > 0) {
|
|
4649
|
+
progress = false;
|
|
4650
|
+
const readyToExecute = [];
|
|
4651
|
+
const readyToSkip = [];
|
|
4652
|
+
for (const [_invocationId, call] of this.gadgetsAwaitingDependencies) {
|
|
4653
|
+
const failedDep = call.dependencies.find((dep) => this.failedInvocations.has(dep));
|
|
4654
|
+
if (failedDep) {
|
|
4655
|
+
readyToSkip.push({ call, failedDep });
|
|
4656
|
+
continue;
|
|
4657
|
+
}
|
|
4658
|
+
const allSatisfied = call.dependencies.every((dep) => this.completedResults.has(dep));
|
|
4659
|
+
if (allSatisfied) {
|
|
4660
|
+
readyToExecute.push(call);
|
|
4661
|
+
}
|
|
4662
|
+
}
|
|
4663
|
+
for (const { call, failedDep } of readyToSkip) {
|
|
4664
|
+
this.gadgetsAwaitingDependencies.delete(call.invocationId);
|
|
4665
|
+
const skipEvents = await this.handleFailedDependency(call, failedDep);
|
|
4666
|
+
for (const evt of skipEvents) {
|
|
4667
|
+
yield evt;
|
|
4668
|
+
}
|
|
4669
|
+
progress = true;
|
|
4670
|
+
}
|
|
4671
|
+
if (readyToExecute.length > 0) {
|
|
4672
|
+
this.logger.debug("Executing ready gadgets in parallel", {
|
|
4673
|
+
count: readyToExecute.length,
|
|
4674
|
+
invocationIds: readyToExecute.map((c) => c.invocationId)
|
|
4675
|
+
});
|
|
4676
|
+
for (const call of readyToExecute) {
|
|
4677
|
+
this.gadgetsAwaitingDependencies.delete(call.invocationId);
|
|
4678
|
+
}
|
|
4679
|
+
const eventSets = await Promise.all(
|
|
4680
|
+
readyToExecute.map(async (call) => {
|
|
4681
|
+
const events = [];
|
|
4682
|
+
for await (const evt of this.executeGadgetGenerator(call)) {
|
|
4683
|
+
events.push(evt);
|
|
4684
|
+
}
|
|
4685
|
+
return events;
|
|
4686
|
+
})
|
|
4687
|
+
);
|
|
4688
|
+
for (const events of eventSets) {
|
|
4689
|
+
for (const evt of events) {
|
|
4690
|
+
yield evt;
|
|
4691
|
+
}
|
|
4692
|
+
}
|
|
4693
|
+
progress = true;
|
|
4694
|
+
}
|
|
4695
|
+
}
|
|
4696
|
+
if (this.gadgetsAwaitingDependencies.size > 0) {
|
|
4697
|
+
const pendingIds = new Set(this.gadgetsAwaitingDependencies.keys());
|
|
4698
|
+
for (const [invocationId, call] of this.gadgetsAwaitingDependencies) {
|
|
4699
|
+
const missingDeps = call.dependencies.filter((dep) => !this.completedResults.has(dep));
|
|
4700
|
+
const circularDeps = missingDeps.filter((dep) => pendingIds.has(dep));
|
|
4701
|
+
const trulyMissingDeps = missingDeps.filter((dep) => !pendingIds.has(dep));
|
|
4702
|
+
let errorMessage;
|
|
4703
|
+
let logLevel = "warn";
|
|
4704
|
+
if (circularDeps.length > 0 && trulyMissingDeps.length > 0) {
|
|
4705
|
+
errorMessage = `Dependencies unresolvable: circular=[${circularDeps.join(", ")}], missing=[${trulyMissingDeps.join(", ")}]`;
|
|
4706
|
+
logLevel = "error";
|
|
4707
|
+
} else if (circularDeps.length > 0) {
|
|
4708
|
+
errorMessage = `Circular dependency detected: "${invocationId}" depends on "${circularDeps[0]}" which also depends on "${invocationId}" (directly or indirectly)`;
|
|
4709
|
+
} else {
|
|
4710
|
+
errorMessage = `Dependency "${missingDeps[0]}" was never executed - check that the invocation ID exists and is spelled correctly`;
|
|
4711
|
+
}
|
|
4712
|
+
this.logger[logLevel]("Gadget has unresolvable dependencies", {
|
|
4713
|
+
gadgetName: call.gadgetName,
|
|
4714
|
+
invocationId,
|
|
4715
|
+
circularDependencies: circularDeps,
|
|
4716
|
+
missingDependencies: trulyMissingDeps
|
|
4717
|
+
});
|
|
4718
|
+
this.failedInvocations.add(invocationId);
|
|
4719
|
+
const skipEvent = {
|
|
4720
|
+
type: "gadget_skipped",
|
|
4721
|
+
gadgetName: call.gadgetName,
|
|
4722
|
+
invocationId,
|
|
4723
|
+
parameters: call.parameters ?? {},
|
|
4724
|
+
failedDependency: missingDeps[0],
|
|
4725
|
+
failedDependencyError: errorMessage
|
|
4726
|
+
};
|
|
4727
|
+
yield skipEvent;
|
|
4728
|
+
}
|
|
4729
|
+
this.gadgetsAwaitingDependencies.clear();
|
|
4730
|
+
}
|
|
4731
|
+
}
|
|
4401
4732
|
/**
|
|
4402
4733
|
* Safely execute an observer, catching and logging any errors.
|
|
4403
4734
|
* Observers are non-critical, so errors are logged but don't crash the system.
|
|
@@ -4520,6 +4851,8 @@ var init_agent = __esm({
|
|
|
4520
4851
|
// Subagent configuration
|
|
4521
4852
|
agentContextConfig;
|
|
4522
4853
|
subagentConfig;
|
|
4854
|
+
// Nested event callback for subagent gadgets
|
|
4855
|
+
onNestedEvent;
|
|
4523
4856
|
/**
|
|
4524
4857
|
* Creates a new Agent instance.
|
|
4525
4858
|
* @internal This constructor is private. Use LLMist.createAgent() or AgentBuilder instead.
|
|
@@ -4597,6 +4930,7 @@ var init_agent = __esm({
|
|
|
4597
4930
|
temperature: this.temperature
|
|
4598
4931
|
};
|
|
4599
4932
|
this.subagentConfig = options.subagentConfig;
|
|
4933
|
+
this.onNestedEvent = options.onNestedEvent;
|
|
4600
4934
|
}
|
|
4601
4935
|
/**
|
|
4602
4936
|
* Get the gadget registry for this agent.
|
|
@@ -4827,12 +5161,30 @@ var init_agent = __esm({
|
|
|
4827
5161
|
client: this.client,
|
|
4828
5162
|
mediaStore: this.mediaStore,
|
|
4829
5163
|
agentConfig: this.agentContextConfig,
|
|
4830
|
-
subagentConfig: this.subagentConfig
|
|
5164
|
+
subagentConfig: this.subagentConfig,
|
|
5165
|
+
onNestedEvent: this.onNestedEvent
|
|
4831
5166
|
});
|
|
4832
|
-
|
|
4833
|
-
|
|
4834
|
-
|
|
5167
|
+
let streamMetadata = null;
|
|
5168
|
+
let gadgetCallCount = 0;
|
|
5169
|
+
const textOutputs = [];
|
|
5170
|
+
const gadgetResults = [];
|
|
5171
|
+
for await (const event of processor.process(stream2)) {
|
|
5172
|
+
if (event.type === "stream_complete") {
|
|
5173
|
+
streamMetadata = event;
|
|
5174
|
+
continue;
|
|
5175
|
+
}
|
|
5176
|
+
if (event.type === "text") {
|
|
5177
|
+
textOutputs.push(event.content);
|
|
5178
|
+
} else if (event.type === "gadget_result") {
|
|
5179
|
+
gadgetCallCount++;
|
|
5180
|
+
gadgetResults.push(event);
|
|
5181
|
+
}
|
|
5182
|
+
yield event;
|
|
5183
|
+
}
|
|
5184
|
+
if (!streamMetadata) {
|
|
5185
|
+
throw new Error("Stream processing completed without metadata event");
|
|
4835
5186
|
}
|
|
5187
|
+
const result = streamMetadata;
|
|
4836
5188
|
this.logger.info("LLM response completed", {
|
|
4837
5189
|
finishReason: result.finishReason,
|
|
4838
5190
|
usage: result.usage,
|
|
@@ -4857,9 +5209,6 @@ var init_agent = __esm({
|
|
|
4857
5209
|
});
|
|
4858
5210
|
let finalMessage = result.finalMessage;
|
|
4859
5211
|
if (this.hooks.controllers?.afterLLMCall) {
|
|
4860
|
-
const gadgetCallCount = result.outputs.filter(
|
|
4861
|
-
(output) => output.type === "gadget_result"
|
|
4862
|
-
).length;
|
|
4863
5212
|
const context = {
|
|
4864
5213
|
iteration: currentIteration,
|
|
4865
5214
|
maxIterations: this.maxIterations,
|
|
@@ -4889,9 +5238,7 @@ var init_agent = __esm({
|
|
|
4889
5238
|
}
|
|
4890
5239
|
if (result.didExecuteGadgets) {
|
|
4891
5240
|
if (this.textWithGadgetsHandler) {
|
|
4892
|
-
const textContent =
|
|
4893
|
-
(output) => output.type === "text"
|
|
4894
|
-
).map((output) => output.content).join("");
|
|
5241
|
+
const textContent = textOutputs.join("");
|
|
4895
5242
|
if (textContent.trim()) {
|
|
4896
5243
|
const { gadgetName, parameterMapping, resultMapping } = this.textWithGadgetsHandler;
|
|
4897
5244
|
this.conversation.addGadgetCallResult(
|
|
@@ -4901,7 +5248,7 @@ var init_agent = __esm({
|
|
|
4901
5248
|
);
|
|
4902
5249
|
}
|
|
4903
5250
|
}
|
|
4904
|
-
for (const output of
|
|
5251
|
+
for (const output of gadgetResults) {
|
|
4905
5252
|
if (output.type === "gadget_result") {
|
|
4906
5253
|
const gadgetResult = output.result;
|
|
4907
5254
|
this.conversation.addGadgetCallResult(
|
|
@@ -8409,6 +8756,8 @@ var init_builder = __esm({
|
|
|
8409
8756
|
signal;
|
|
8410
8757
|
trailingMessage;
|
|
8411
8758
|
subagentConfig;
|
|
8759
|
+
nestedEventCallback;
|
|
8760
|
+
parentContext;
|
|
8412
8761
|
constructor(client) {
|
|
8413
8762
|
this.client = client;
|
|
8414
8763
|
}
|
|
@@ -8905,6 +9254,74 @@ var init_builder = __esm({
|
|
|
8905
9254
|
this.subagentConfig = config;
|
|
8906
9255
|
return this;
|
|
8907
9256
|
}
|
|
9257
|
+
/**
|
|
9258
|
+
* Set the callback for nested subagent events.
|
|
9259
|
+
*
|
|
9260
|
+
* Subagent gadgets (like BrowseWeb) can use ExecutionContext.onNestedEvent
|
|
9261
|
+
* to report their internal LLM calls and gadget executions in real-time.
|
|
9262
|
+
* This callback receives those events, enabling hierarchical progress display.
|
|
9263
|
+
*
|
|
9264
|
+
* @param callback - Function to handle nested agent events
|
|
9265
|
+
* @returns This builder for chaining
|
|
9266
|
+
*
|
|
9267
|
+
* @example
|
|
9268
|
+
* ```typescript
|
|
9269
|
+
* .withNestedEventCallback((event) => {
|
|
9270
|
+
* if (event.type === "llm_call_start") {
|
|
9271
|
+
* console.log(` Nested LLM #${event.event.iteration} starting...`);
|
|
9272
|
+
* } else if (event.type === "gadget_call") {
|
|
9273
|
+
* console.log(` ⏵ ${event.event.call.gadgetName}...`);
|
|
9274
|
+
* }
|
|
9275
|
+
* })
|
|
9276
|
+
* ```
|
|
9277
|
+
*/
|
|
9278
|
+
withNestedEventCallback(callback) {
|
|
9279
|
+
this.nestedEventCallback = callback;
|
|
9280
|
+
return this;
|
|
9281
|
+
}
|
|
9282
|
+
/**
|
|
9283
|
+
* Enable automatic nested event forwarding to parent agent.
|
|
9284
|
+
*
|
|
9285
|
+
* When building a subagent inside a gadget, call this method to automatically
|
|
9286
|
+
* forward all LLM calls and gadget events to the parent agent. This enables
|
|
9287
|
+
* hierarchical progress display without any manual event handling.
|
|
9288
|
+
*
|
|
9289
|
+
* The method extracts `invocationId` and `onNestedEvent` from the execution
|
|
9290
|
+
* context and sets up automatic forwarding via hooks and event wrapping.
|
|
9291
|
+
*
|
|
9292
|
+
* @param ctx - ExecutionContext passed to the gadget's execute() method
|
|
9293
|
+
* @param depth - Nesting depth (default: 1 for direct child)
|
|
9294
|
+
* @returns This builder for chaining
|
|
9295
|
+
*
|
|
9296
|
+
* @example
|
|
9297
|
+
* ```typescript
|
|
9298
|
+
* // In a subagent gadget like BrowseWeb - ONE LINE enables auto-forwarding:
|
|
9299
|
+
* execute: async (params, ctx) => {
|
|
9300
|
+
* const agent = new AgentBuilder(client)
|
|
9301
|
+
* .withModel(model)
|
|
9302
|
+
* .withGadgets(Navigate, Click, Screenshot)
|
|
9303
|
+
* .withParentContext(ctx) // <-- This is all you need!
|
|
9304
|
+
* .ask(params.task);
|
|
9305
|
+
*
|
|
9306
|
+
* for await (const event of agent.run()) {
|
|
9307
|
+
* // Events automatically forwarded - just process normally
|
|
9308
|
+
* if (event.type === "text") {
|
|
9309
|
+
* result = event.content;
|
|
9310
|
+
* }
|
|
9311
|
+
* }
|
|
9312
|
+
* }
|
|
9313
|
+
* ```
|
|
9314
|
+
*/
|
|
9315
|
+
withParentContext(ctx, depth = 1) {
|
|
9316
|
+
if (ctx.onNestedEvent && ctx.invocationId) {
|
|
9317
|
+
this.parentContext = {
|
|
9318
|
+
invocationId: ctx.invocationId,
|
|
9319
|
+
onNestedEvent: ctx.onNestedEvent,
|
|
9320
|
+
depth
|
|
9321
|
+
};
|
|
9322
|
+
}
|
|
9323
|
+
return this;
|
|
9324
|
+
}
|
|
8908
9325
|
/**
|
|
8909
9326
|
* Add an ephemeral trailing message that appears at the end of each LLM request.
|
|
8910
9327
|
*
|
|
@@ -8972,14 +9389,58 @@ ${endPrefix}`
|
|
|
8972
9389
|
return this;
|
|
8973
9390
|
}
|
|
8974
9391
|
/**
|
|
8975
|
-
* Compose the final hooks, including
|
|
9392
|
+
* Compose the final hooks, including:
|
|
9393
|
+
* - Trailing message injection (if configured)
|
|
9394
|
+
* - Nested event forwarding for LLM calls (if parentContext is set)
|
|
8976
9395
|
*/
|
|
8977
9396
|
composeHooks() {
|
|
9397
|
+
let hooks = this.hooks;
|
|
9398
|
+
if (this.parentContext) {
|
|
9399
|
+
const { invocationId, onNestedEvent, depth } = this.parentContext;
|
|
9400
|
+
const existingOnLLMCallStart = hooks?.observers?.onLLMCallStart;
|
|
9401
|
+
const existingOnLLMCallComplete = hooks?.observers?.onLLMCallComplete;
|
|
9402
|
+
hooks = {
|
|
9403
|
+
...hooks,
|
|
9404
|
+
observers: {
|
|
9405
|
+
...hooks?.observers,
|
|
9406
|
+
onLLMCallStart: async (context) => {
|
|
9407
|
+
onNestedEvent({
|
|
9408
|
+
type: "llm_call_start",
|
|
9409
|
+
gadgetInvocationId: invocationId,
|
|
9410
|
+
depth,
|
|
9411
|
+
event: {
|
|
9412
|
+
iteration: context.iteration,
|
|
9413
|
+
model: context.options.model
|
|
9414
|
+
}
|
|
9415
|
+
});
|
|
9416
|
+
if (existingOnLLMCallStart) {
|
|
9417
|
+
await existingOnLLMCallStart(context);
|
|
9418
|
+
}
|
|
9419
|
+
},
|
|
9420
|
+
onLLMCallComplete: async (context) => {
|
|
9421
|
+
onNestedEvent({
|
|
9422
|
+
type: "llm_call_end",
|
|
9423
|
+
gadgetInvocationId: invocationId,
|
|
9424
|
+
depth,
|
|
9425
|
+
event: {
|
|
9426
|
+
iteration: context.iteration,
|
|
9427
|
+
model: context.options.model,
|
|
9428
|
+
outputTokens: context.usage?.outputTokens,
|
|
9429
|
+
finishReason: context.finishReason
|
|
9430
|
+
}
|
|
9431
|
+
});
|
|
9432
|
+
if (existingOnLLMCallComplete) {
|
|
9433
|
+
await existingOnLLMCallComplete(context);
|
|
9434
|
+
}
|
|
9435
|
+
}
|
|
9436
|
+
}
|
|
9437
|
+
};
|
|
9438
|
+
}
|
|
8978
9439
|
if (!this.trailingMessage) {
|
|
8979
|
-
return
|
|
9440
|
+
return hooks;
|
|
8980
9441
|
}
|
|
8981
9442
|
const trailingMsg = this.trailingMessage;
|
|
8982
|
-
const existingBeforeLLMCall =
|
|
9443
|
+
const existingBeforeLLMCall = hooks?.controllers?.beforeLLMCall;
|
|
8983
9444
|
const trailingMessageController = async (ctx) => {
|
|
8984
9445
|
const result = existingBeforeLLMCall ? await existingBeforeLLMCall(ctx) : { action: "proceed" };
|
|
8985
9446
|
if (result.action === "skip") {
|
|
@@ -8994,9 +9455,9 @@ ${endPrefix}`
|
|
|
8994
9455
|
};
|
|
8995
9456
|
};
|
|
8996
9457
|
return {
|
|
8997
|
-
...
|
|
9458
|
+
...hooks,
|
|
8998
9459
|
controllers: {
|
|
8999
|
-
...
|
|
9460
|
+
...hooks?.controllers,
|
|
9000
9461
|
beforeLLMCall: trailingMessageController
|
|
9001
9462
|
}
|
|
9002
9463
|
};
|
|
@@ -9057,6 +9518,19 @@ ${endPrefix}`
|
|
|
9057
9518
|
this.client = new LLMistClass();
|
|
9058
9519
|
}
|
|
9059
9520
|
const registry = GadgetRegistry.from(this.gadgets);
|
|
9521
|
+
let onNestedEvent = this.nestedEventCallback;
|
|
9522
|
+
if (this.parentContext) {
|
|
9523
|
+
const { invocationId, onNestedEvent: parentCallback, depth } = this.parentContext;
|
|
9524
|
+
const existingCallback = this.nestedEventCallback;
|
|
9525
|
+
onNestedEvent = (event) => {
|
|
9526
|
+
parentCallback({
|
|
9527
|
+
...event,
|
|
9528
|
+
gadgetInvocationId: invocationId,
|
|
9529
|
+
depth: event.depth + depth
|
|
9530
|
+
});
|
|
9531
|
+
existingCallback?.(event);
|
|
9532
|
+
};
|
|
9533
|
+
}
|
|
9060
9534
|
return {
|
|
9061
9535
|
client: this.client,
|
|
9062
9536
|
model: this.model ?? "openai:gpt-5-nano",
|
|
@@ -9082,7 +9556,8 @@ ${endPrefix}`
|
|
|
9082
9556
|
gadgetOutputLimitPercent: this.gadgetOutputLimitPercent,
|
|
9083
9557
|
compactionConfig: this.compactionConfig,
|
|
9084
9558
|
signal: this.signal,
|
|
9085
|
-
subagentConfig: this.subagentConfig
|
|
9559
|
+
subagentConfig: this.subagentConfig,
|
|
9560
|
+
onNestedEvent
|
|
9086
9561
|
};
|
|
9087
9562
|
}
|
|
9088
9563
|
ask(userPrompt) {
|
|
@@ -9239,6 +9714,19 @@ ${endPrefix}`
|
|
|
9239
9714
|
this.client = new LLMistClass();
|
|
9240
9715
|
}
|
|
9241
9716
|
const registry = GadgetRegistry.from(this.gadgets);
|
|
9717
|
+
let onNestedEvent = this.nestedEventCallback;
|
|
9718
|
+
if (this.parentContext) {
|
|
9719
|
+
const { invocationId, onNestedEvent: parentCallback, depth } = this.parentContext;
|
|
9720
|
+
const existingCallback = this.nestedEventCallback;
|
|
9721
|
+
onNestedEvent = (event) => {
|
|
9722
|
+
parentCallback({
|
|
9723
|
+
...event,
|
|
9724
|
+
gadgetInvocationId: invocationId,
|
|
9725
|
+
depth: event.depth + depth
|
|
9726
|
+
});
|
|
9727
|
+
existingCallback?.(event);
|
|
9728
|
+
};
|
|
9729
|
+
}
|
|
9242
9730
|
const options = {
|
|
9243
9731
|
client: this.client,
|
|
9244
9732
|
model: this.model ?? "openai:gpt-5-nano",
|
|
@@ -9264,7 +9752,8 @@ ${endPrefix}`
|
|
|
9264
9752
|
gadgetOutputLimitPercent: this.gadgetOutputLimitPercent,
|
|
9265
9753
|
compactionConfig: this.compactionConfig,
|
|
9266
9754
|
signal: this.signal,
|
|
9267
|
-
subagentConfig: this.subagentConfig
|
|
9755
|
+
subagentConfig: this.subagentConfig,
|
|
9756
|
+
onNestedEvent
|
|
9268
9757
|
};
|
|
9269
9758
|
return new Agent(AGENT_INTERNAL_KEY, options);
|
|
9270
9759
|
}
|
|
@@ -12420,7 +12909,9 @@ function formatMediaLine(media) {
|
|
|
12420
12909
|
}
|
|
12421
12910
|
function formatGadgetSummary2(result) {
|
|
12422
12911
|
const gadgetLabel = import_chalk3.default.magenta.bold(result.gadgetName);
|
|
12423
|
-
const timeLabel = import_chalk3.default.dim(
|
|
12912
|
+
const timeLabel = import_chalk3.default.dim(
|
|
12913
|
+
result.executionTimeMs >= 1e3 ? `${(result.executionTimeMs / 1e3).toFixed(1)}s` : `${Math.round(result.executionTimeMs)}ms`
|
|
12914
|
+
);
|
|
12424
12915
|
const paramsStr = formatParametersInline(result.parameters);
|
|
12425
12916
|
const paramsLabel = paramsStr ? `${import_chalk3.default.dim("(")}${paramsStr}${import_chalk3.default.dim(")")}` : "";
|
|
12426
12917
|
if (result.error) {
|
|
@@ -12588,6 +13079,8 @@ var StreamProgress = class {
|
|
|
12588
13079
|
delayTimeout = null;
|
|
12589
13080
|
isRunning = false;
|
|
12590
13081
|
hasRendered = false;
|
|
13082
|
+
lastRenderLineCount = 0;
|
|
13083
|
+
// Track lines rendered for multi-line clearing
|
|
12591
13084
|
// Current call stats (streaming mode)
|
|
12592
13085
|
mode = "cumulative";
|
|
12593
13086
|
model = "";
|
|
@@ -12607,6 +13100,99 @@ var StreamProgress = class {
|
|
|
12607
13100
|
totalCost = 0;
|
|
12608
13101
|
iterations = 0;
|
|
12609
13102
|
currentIteration = 0;
|
|
13103
|
+
// In-flight gadget tracking for concurrent status display
|
|
13104
|
+
inFlightGadgets = /* @__PURE__ */ new Map();
|
|
13105
|
+
// Nested agent tracking for hierarchical subagent display
|
|
13106
|
+
nestedAgents = /* @__PURE__ */ new Map();
|
|
13107
|
+
// Nested gadget tracking for hierarchical subagent display
|
|
13108
|
+
nestedGadgets = /* @__PURE__ */ new Map();
|
|
13109
|
+
/**
|
|
13110
|
+
* Add a gadget to the in-flight tracking (called when gadget_call event received).
|
|
13111
|
+
* Triggers re-render to show the gadget in the status display.
|
|
13112
|
+
*/
|
|
13113
|
+
addGadget(invocationId, name, params) {
|
|
13114
|
+
this.inFlightGadgets.set(invocationId, { name, params, startTime: Date.now() });
|
|
13115
|
+
if (this.isRunning && this.isTTY) {
|
|
13116
|
+
this.render();
|
|
13117
|
+
}
|
|
13118
|
+
}
|
|
13119
|
+
/**
|
|
13120
|
+
* Remove a gadget from in-flight tracking (called when gadget_result event received).
|
|
13121
|
+
* Triggers re-render to update the status display.
|
|
13122
|
+
*/
|
|
13123
|
+
removeGadget(invocationId) {
|
|
13124
|
+
this.inFlightGadgets.delete(invocationId);
|
|
13125
|
+
if (this.isRunning && this.isTTY) {
|
|
13126
|
+
this.render();
|
|
13127
|
+
}
|
|
13128
|
+
}
|
|
13129
|
+
/**
|
|
13130
|
+
* Check if there are any gadgets currently in flight.
|
|
13131
|
+
*/
|
|
13132
|
+
hasInFlightGadgets() {
|
|
13133
|
+
return this.inFlightGadgets.size > 0;
|
|
13134
|
+
}
|
|
13135
|
+
/**
|
|
13136
|
+
* Add a nested agent LLM call (called when nested llm_call_start event received).
|
|
13137
|
+
* Used to display hierarchical progress for subagent gadgets.
|
|
13138
|
+
*/
|
|
13139
|
+
addNestedAgent(id, parentInvocationId, depth, model, iteration, inputTokens) {
|
|
13140
|
+
this.nestedAgents.set(id, {
|
|
13141
|
+
parentInvocationId,
|
|
13142
|
+
depth,
|
|
13143
|
+
model,
|
|
13144
|
+
iteration,
|
|
13145
|
+
startTime: Date.now(),
|
|
13146
|
+
inputTokens
|
|
13147
|
+
});
|
|
13148
|
+
if (this.isRunning && this.isTTY) {
|
|
13149
|
+
this.render();
|
|
13150
|
+
}
|
|
13151
|
+
}
|
|
13152
|
+
/**
|
|
13153
|
+
* Update a nested agent with completion info (called when nested llm_call_end event received).
|
|
13154
|
+
*/
|
|
13155
|
+
updateNestedAgent(id, outputTokens) {
|
|
13156
|
+
const agent = this.nestedAgents.get(id);
|
|
13157
|
+
if (agent) {
|
|
13158
|
+
agent.outputTokens = outputTokens;
|
|
13159
|
+
if (this.isRunning && this.isTTY) {
|
|
13160
|
+
this.render();
|
|
13161
|
+
}
|
|
13162
|
+
}
|
|
13163
|
+
}
|
|
13164
|
+
/**
|
|
13165
|
+
* Remove a nested agent (called when the nested LLM call completes).
|
|
13166
|
+
*/
|
|
13167
|
+
removeNestedAgent(id) {
|
|
13168
|
+
this.nestedAgents.delete(id);
|
|
13169
|
+
if (this.isRunning && this.isTTY) {
|
|
13170
|
+
this.render();
|
|
13171
|
+
}
|
|
13172
|
+
}
|
|
13173
|
+
/**
|
|
13174
|
+
* Add a nested gadget call (called when nested gadget_call event received).
|
|
13175
|
+
*/
|
|
13176
|
+
addNestedGadget(id, depth, parentInvocationId, name) {
|
|
13177
|
+
this.nestedGadgets.set(id, {
|
|
13178
|
+
depth,
|
|
13179
|
+
parentInvocationId,
|
|
13180
|
+
name,
|
|
13181
|
+
startTime: Date.now()
|
|
13182
|
+
});
|
|
13183
|
+
if (this.isRunning && this.isTTY) {
|
|
13184
|
+
this.render();
|
|
13185
|
+
}
|
|
13186
|
+
}
|
|
13187
|
+
/**
|
|
13188
|
+
* Remove a nested gadget (called when nested gadget_result event received).
|
|
13189
|
+
*/
|
|
13190
|
+
removeNestedGadget(id) {
|
|
13191
|
+
this.nestedGadgets.delete(id);
|
|
13192
|
+
if (this.isRunning && this.isTTY) {
|
|
13193
|
+
this.render();
|
|
13194
|
+
}
|
|
13195
|
+
}
|
|
12610
13196
|
/**
|
|
12611
13197
|
* Starts a new LLM call. Switches to streaming mode.
|
|
12612
13198
|
* @param model - Model name being used
|
|
@@ -12733,15 +13319,57 @@ var StreamProgress = class {
|
|
|
12733
13319
|
this.isStreaming = true;
|
|
12734
13320
|
}
|
|
12735
13321
|
render() {
|
|
13322
|
+
this.clearRenderedLines();
|
|
12736
13323
|
const spinner = SPINNER_FRAMES[this.frameIndex++ % SPINNER_FRAMES.length];
|
|
13324
|
+
const lines = [];
|
|
12737
13325
|
if (this.mode === "streaming") {
|
|
12738
|
-
this.
|
|
13326
|
+
lines.push(this.formatStreamingLine(spinner));
|
|
12739
13327
|
} else {
|
|
12740
|
-
this.
|
|
12741
|
-
}
|
|
13328
|
+
lines.push(this.formatCumulativeLine(spinner));
|
|
13329
|
+
}
|
|
13330
|
+
if (this.isTTY) {
|
|
13331
|
+
for (const [gadgetId, gadget] of this.inFlightGadgets) {
|
|
13332
|
+
const elapsed = ((Date.now() - gadget.startTime) / 1e3).toFixed(1);
|
|
13333
|
+
const gadgetLine = ` ${import_chalk4.default.blue("\u23F5")} ${import_chalk4.default.magenta.bold(gadget.name)}${import_chalk4.default.dim("(...)")} ${import_chalk4.default.dim(elapsed + "s")}`;
|
|
13334
|
+
lines.push(gadgetLine);
|
|
13335
|
+
for (const [_agentId, nested] of this.nestedAgents) {
|
|
13336
|
+
if (nested.parentInvocationId !== gadgetId) continue;
|
|
13337
|
+
const indent = " ".repeat(nested.depth + 1);
|
|
13338
|
+
const nestedElapsed = ((Date.now() - nested.startTime) / 1e3).toFixed(1);
|
|
13339
|
+
const tokens = nested.inputTokens ? ` ${import_chalk4.default.dim("\u2191")}${import_chalk4.default.yellow(formatTokens(nested.inputTokens))}` : "";
|
|
13340
|
+
const outTokens = nested.outputTokens ? ` ${import_chalk4.default.dim("\u2193")}${import_chalk4.default.green(formatTokens(nested.outputTokens))}` : "";
|
|
13341
|
+
const nestedLine = `${indent}${import_chalk4.default.cyan(`#${nested.iteration}`)} ${import_chalk4.default.dim(nested.model)}${tokens}${outTokens} ${import_chalk4.default.dim(nestedElapsed + "s")} ${import_chalk4.default.cyan(spinner)}`;
|
|
13342
|
+
lines.push(nestedLine);
|
|
13343
|
+
}
|
|
13344
|
+
for (const [_nestedId, nestedGadget] of this.nestedGadgets) {
|
|
13345
|
+
if (nestedGadget.parentInvocationId === gadgetId) {
|
|
13346
|
+
const indent = " ".repeat(nestedGadget.depth + 1);
|
|
13347
|
+
const nestedElapsed = ((Date.now() - nestedGadget.startTime) / 1e3).toFixed(1);
|
|
13348
|
+
const nestedGadgetLine = `${indent}${import_chalk4.default.blue("\u23F5")} ${import_chalk4.default.dim(nestedGadget.name + "(...)")} ${import_chalk4.default.dim(nestedElapsed + "s")}`;
|
|
13349
|
+
lines.push(nestedGadgetLine);
|
|
13350
|
+
}
|
|
13351
|
+
}
|
|
13352
|
+
}
|
|
13353
|
+
}
|
|
13354
|
+
this.lastRenderLineCount = lines.length;
|
|
13355
|
+
this.target.write("\r" + lines.join("\n"));
|
|
12742
13356
|
this.hasRendered = true;
|
|
12743
13357
|
}
|
|
12744
|
-
|
|
13358
|
+
/**
|
|
13359
|
+
* Clears the previously rendered lines (for multi-line status display).
|
|
13360
|
+
*/
|
|
13361
|
+
clearRenderedLines() {
|
|
13362
|
+
if (!this.hasRendered || this.lastRenderLineCount === 0) return;
|
|
13363
|
+
this.target.write("\r\x1B[K");
|
|
13364
|
+
for (let i = 1; i < this.lastRenderLineCount; i++) {
|
|
13365
|
+
this.target.write("\x1B[1A\x1B[K");
|
|
13366
|
+
}
|
|
13367
|
+
this.target.write("\r");
|
|
13368
|
+
}
|
|
13369
|
+
/**
|
|
13370
|
+
* Format the streaming mode progress line (returns string, doesn't write).
|
|
13371
|
+
*/
|
|
13372
|
+
formatStreamingLine(spinner) {
|
|
12745
13373
|
const elapsed = ((Date.now() - this.callStartTime) / 1e3).toFixed(1);
|
|
12746
13374
|
const outTokens = this.callOutputTokensEstimated ? Math.round(this.callOutputChars / FALLBACK_CHARS_PER_TOKEN) : this.callOutputTokens;
|
|
12747
13375
|
const parts = [];
|
|
@@ -12775,7 +13403,7 @@ var StreamProgress = class {
|
|
|
12775
13403
|
if (callCost > 0) {
|
|
12776
13404
|
parts.push(import_chalk4.default.cyan(`$${formatCost(callCost)}`));
|
|
12777
13405
|
}
|
|
12778
|
-
|
|
13406
|
+
return `${parts.join(import_chalk4.default.dim(" | "))} ${import_chalk4.default.cyan(spinner)}`;
|
|
12779
13407
|
}
|
|
12780
13408
|
/**
|
|
12781
13409
|
* Calculates live cost estimate for the current streaming call.
|
|
@@ -12812,7 +13440,10 @@ var StreamProgress = class {
|
|
|
12812
13440
|
}
|
|
12813
13441
|
return this.callInputTokens / limits.contextWindow * 100;
|
|
12814
13442
|
}
|
|
12815
|
-
|
|
13443
|
+
/**
|
|
13444
|
+
* Format the cumulative mode progress line (returns string, doesn't write).
|
|
13445
|
+
*/
|
|
13446
|
+
formatCumulativeLine(spinner) {
|
|
12816
13447
|
const elapsed = ((Date.now() - this.totalStartTime) / 1e3).toFixed(1);
|
|
12817
13448
|
const parts = [];
|
|
12818
13449
|
if (this.model) {
|
|
@@ -12828,10 +13459,10 @@ var StreamProgress = class {
|
|
|
12828
13459
|
parts.push(import_chalk4.default.dim("cost:") + import_chalk4.default.cyan(` $${formatCost(this.totalCost)}`));
|
|
12829
13460
|
}
|
|
12830
13461
|
parts.push(import_chalk4.default.dim(`${elapsed}s`));
|
|
12831
|
-
|
|
13462
|
+
return `${parts.join(import_chalk4.default.dim(" | "))} ${import_chalk4.default.cyan(spinner)}`;
|
|
12832
13463
|
}
|
|
12833
13464
|
/**
|
|
12834
|
-
* Pauses the progress indicator and clears
|
|
13465
|
+
* Pauses the progress indicator and clears all rendered lines.
|
|
12835
13466
|
* Can be resumed with start().
|
|
12836
13467
|
*/
|
|
12837
13468
|
pause() {
|
|
@@ -12845,10 +13476,9 @@ var StreamProgress = class {
|
|
|
12845
13476
|
this.interval = null;
|
|
12846
13477
|
}
|
|
12847
13478
|
this.isRunning = false;
|
|
12848
|
-
|
|
12849
|
-
|
|
12850
|
-
|
|
12851
|
-
}
|
|
13479
|
+
this.clearRenderedLines();
|
|
13480
|
+
this.hasRendered = false;
|
|
13481
|
+
this.lastRenderLineCount = 0;
|
|
12852
13482
|
}
|
|
12853
13483
|
/**
|
|
12854
13484
|
* Completes the progress indicator and clears the line.
|
|
@@ -13425,6 +14055,38 @@ Denied: ${result.reason ?? "by user"}`
|
|
|
13425
14055
|
"Maximize efficiency by batching independent operations in a single response."
|
|
13426
14056
|
].join(" ")
|
|
13427
14057
|
);
|
|
14058
|
+
if (!options.quiet) {
|
|
14059
|
+
builder.withNestedEventCallback((event) => {
|
|
14060
|
+
if (event.type === "llm_call_start") {
|
|
14061
|
+
const info = event.event;
|
|
14062
|
+
const nestedId = `${event.gadgetInvocationId}:${info.iteration}`;
|
|
14063
|
+
progress.addNestedAgent(
|
|
14064
|
+
nestedId,
|
|
14065
|
+
event.gadgetInvocationId,
|
|
14066
|
+
event.depth,
|
|
14067
|
+
info.model,
|
|
14068
|
+
info.iteration,
|
|
14069
|
+
info.inputTokens
|
|
14070
|
+
);
|
|
14071
|
+
} else if (event.type === "llm_call_end") {
|
|
14072
|
+
const info = event.event;
|
|
14073
|
+
const nestedId = `${event.gadgetInvocationId}:${info.iteration}`;
|
|
14074
|
+
progress.updateNestedAgent(nestedId, info.outputTokens);
|
|
14075
|
+
setTimeout(() => progress.removeNestedAgent(nestedId), 100);
|
|
14076
|
+
} else if (event.type === "gadget_call") {
|
|
14077
|
+
const gadgetEvent = event.event;
|
|
14078
|
+
progress.addNestedGadget(
|
|
14079
|
+
gadgetEvent.call.invocationId,
|
|
14080
|
+
event.depth,
|
|
14081
|
+
event.gadgetInvocationId,
|
|
14082
|
+
gadgetEvent.call.gadgetName
|
|
14083
|
+
);
|
|
14084
|
+
} else if (event.type === "gadget_result") {
|
|
14085
|
+
const resultEvent = event.event;
|
|
14086
|
+
progress.removeNestedGadget(resultEvent.result.invocationId);
|
|
14087
|
+
}
|
|
14088
|
+
});
|
|
14089
|
+
}
|
|
13428
14090
|
let agent;
|
|
13429
14091
|
if (options.image || options.audio) {
|
|
13430
14092
|
const parts = [text(prompt)];
|
|
@@ -13449,10 +14111,22 @@ Denied: ${result.reason ?? "by user"}`
|
|
|
13449
14111
|
try {
|
|
13450
14112
|
for await (const event of agent.run()) {
|
|
13451
14113
|
if (event.type === "text") {
|
|
13452
|
-
progress.pause();
|
|
13453
14114
|
textBuffer += event.content;
|
|
14115
|
+
} else if (event.type === "gadget_call") {
|
|
14116
|
+
flushTextBuffer();
|
|
14117
|
+
if (!options.quiet) {
|
|
14118
|
+
progress.addGadget(
|
|
14119
|
+
event.call.invocationId,
|
|
14120
|
+
event.call.gadgetName,
|
|
14121
|
+
event.call.parameters
|
|
14122
|
+
);
|
|
14123
|
+
progress.start();
|
|
14124
|
+
}
|
|
13454
14125
|
} else if (event.type === "gadget_result") {
|
|
13455
14126
|
flushTextBuffer();
|
|
14127
|
+
if (!options.quiet) {
|
|
14128
|
+
progress.removeGadget(event.result.invocationId);
|
|
14129
|
+
}
|
|
13456
14130
|
progress.pause();
|
|
13457
14131
|
if (options.quiet) {
|
|
13458
14132
|
if (event.result.gadgetName === "TellUser" && event.result.parameters?.message) {
|
|
@@ -13467,6 +14141,9 @@ Denied: ${result.reason ?? "by user"}`
|
|
|
13467
14141
|
`
|
|
13468
14142
|
);
|
|
13469
14143
|
}
|
|
14144
|
+
if (progress.hasInFlightGadgets()) {
|
|
14145
|
+
progress.start();
|
|
14146
|
+
}
|
|
13470
14147
|
}
|
|
13471
14148
|
}
|
|
13472
14149
|
} catch (error) {
|