llmist 6.1.0 → 7.0.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/README.md +6 -1
- package/dist/{chunk-7BJX376V.js → chunk-5KEZ7SQX.js} +13 -25
- package/dist/chunk-5KEZ7SQX.js.map +1 -0
- package/dist/{chunk-VAJLPRJ6.js → chunk-SFZIL2VR.js} +410 -493
- package/dist/chunk-SFZIL2VR.js.map +1 -0
- package/dist/cli.cjs +11533 -11843
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +5528 -5751
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +5779 -5872
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +75 -299
- package/dist/index.d.ts +75 -299
- package/dist/index.js +5 -3
- package/dist/{mock-stream-Cq1Sxezz.d.cts → mock-stream-r5vjy2Iq.d.cts} +1103 -739
- package/dist/{mock-stream-Cq1Sxezz.d.ts → mock-stream-r5vjy2Iq.d.ts} +1103 -739
- package/dist/testing/index.cjs +401 -486
- 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 +2 -1
- package/dist/chunk-7BJX376V.js.map +0 -1
- package/dist/chunk-VAJLPRJ6.js.map +0 -1
|
@@ -44,6 +44,9 @@ function parseEnvBoolean(value) {
|
|
|
44
44
|
if (normalized === "false" || normalized === "0") return false;
|
|
45
45
|
return void 0;
|
|
46
46
|
}
|
|
47
|
+
function stripAnsi(str) {
|
|
48
|
+
return str.replace(/\x1b\[[0-9;]*m/g, "");
|
|
49
|
+
}
|
|
47
50
|
function createLogger(options = {}) {
|
|
48
51
|
const envMinLevel = parseLogLevel(process.env.LLMIST_LOG_LEVEL);
|
|
49
52
|
const envLogFile = process.env.LLMIST_LOG_FILE?.trim() ?? "";
|
|
@@ -52,36 +55,62 @@ function createLogger(options = {}) {
|
|
|
52
55
|
const defaultType = options.type ?? "pretty";
|
|
53
56
|
const name = options.name ?? "llmist";
|
|
54
57
|
const logReset = options.logReset ?? envLogReset ?? false;
|
|
55
|
-
|
|
56
|
-
let finalType = defaultType;
|
|
57
|
-
if (envLogFile) {
|
|
58
|
+
if (envLogFile && (!logFileInitialized || sharedLogFilePath !== envLogFile)) {
|
|
58
59
|
try {
|
|
60
|
+
if (sharedLogFileStream) {
|
|
61
|
+
sharedLogFileStream.end();
|
|
62
|
+
sharedLogFileStream = void 0;
|
|
63
|
+
}
|
|
59
64
|
mkdirSync(dirname(envLogFile), { recursive: true });
|
|
60
65
|
const flags = logReset ? "w" : "a";
|
|
61
|
-
|
|
62
|
-
|
|
66
|
+
sharedLogFileStream = createWriteStream(envLogFile, { flags });
|
|
67
|
+
sharedLogFilePath = envLogFile;
|
|
68
|
+
logFileInitialized = true;
|
|
69
|
+
writeErrorCount = 0;
|
|
70
|
+
writeErrorReported = false;
|
|
71
|
+
sharedLogFileStream.on("error", (error) => {
|
|
72
|
+
writeErrorCount++;
|
|
73
|
+
if (!writeErrorReported) {
|
|
74
|
+
console.error(`[llmist] Log file write error: ${error.message}`);
|
|
75
|
+
writeErrorReported = true;
|
|
76
|
+
}
|
|
77
|
+
if (writeErrorCount >= MAX_WRITE_ERRORS_BEFORE_DISABLE) {
|
|
78
|
+
console.error(
|
|
79
|
+
`[llmist] Too many log file errors (${writeErrorCount}), disabling file logging`
|
|
80
|
+
);
|
|
81
|
+
sharedLogFileStream?.end();
|
|
82
|
+
sharedLogFileStream = void 0;
|
|
83
|
+
}
|
|
84
|
+
});
|
|
63
85
|
} catch (error) {
|
|
64
86
|
console.error("Failed to initialize LLMIST_LOG_FILE output:", error);
|
|
65
87
|
}
|
|
66
88
|
}
|
|
89
|
+
const useFileLogging = Boolean(sharedLogFileStream);
|
|
67
90
|
const logger = new Logger({
|
|
68
91
|
name,
|
|
69
92
|
minLevel,
|
|
70
|
-
type:
|
|
71
|
-
//
|
|
72
|
-
hideLogPositionForProduction:
|
|
73
|
-
|
|
74
|
-
|
|
93
|
+
type: useFileLogging ? "pretty" : defaultType,
|
|
94
|
+
// Hide log position for file logging and non-pretty types
|
|
95
|
+
hideLogPositionForProduction: useFileLogging || defaultType !== "pretty",
|
|
96
|
+
prettyLogTemplate: LOG_TEMPLATE,
|
|
97
|
+
// Use overwrite to redirect tslog's formatted output to file instead of console
|
|
98
|
+
overwrite: useFileLogging ? {
|
|
99
|
+
transportFormatted: (logMetaMarkup, logArgs, _logErrors) => {
|
|
100
|
+
if (!sharedLogFileStream) return;
|
|
101
|
+
const meta = stripAnsi(logMetaMarkup);
|
|
102
|
+
const args = logArgs.map(
|
|
103
|
+
(arg) => typeof arg === "string" ? stripAnsi(arg) : JSON.stringify(arg)
|
|
104
|
+
);
|
|
105
|
+
const line = `${meta}${args.join(" ")}
|
|
106
|
+
`;
|
|
107
|
+
sharedLogFileStream.write(line);
|
|
108
|
+
}
|
|
109
|
+
} : void 0
|
|
75
110
|
});
|
|
76
|
-
if (logFileStream) {
|
|
77
|
-
logger.attachTransport((logObj) => {
|
|
78
|
-
logFileStream?.write(`${JSON.stringify(logObj)}
|
|
79
|
-
`);
|
|
80
|
-
});
|
|
81
|
-
}
|
|
82
111
|
return logger;
|
|
83
112
|
}
|
|
84
|
-
var LEVEL_NAME_TO_ID, defaultLogger;
|
|
113
|
+
var LEVEL_NAME_TO_ID, sharedLogFilePath, sharedLogFileStream, logFileInitialized, writeErrorCount, writeErrorReported, MAX_WRITE_ERRORS_BEFORE_DISABLE, LOG_TEMPLATE, defaultLogger;
|
|
85
114
|
var init_logger = __esm({
|
|
86
115
|
"src/logging/logger.ts"() {
|
|
87
116
|
"use strict";
|
|
@@ -94,6 +123,11 @@ var init_logger = __esm({
|
|
|
94
123
|
error: 5,
|
|
95
124
|
fatal: 6
|
|
96
125
|
};
|
|
126
|
+
logFileInitialized = false;
|
|
127
|
+
writeErrorCount = 0;
|
|
128
|
+
writeErrorReported = false;
|
|
129
|
+
MAX_WRITE_ERRORS_BEFORE_DISABLE = 5;
|
|
130
|
+
LOG_TEMPLATE = "{{yyyy}}-{{mm}}-{{dd}} {{hh}}:{{MM}}:{{ss}}:{{ms}} {{logLevelName}} [{{name}}] ";
|
|
97
131
|
defaultLogger = createLogger();
|
|
98
132
|
}
|
|
99
133
|
});
|
|
@@ -3859,9 +3893,8 @@ var init_cost_reporting_client = __esm({
|
|
|
3859
3893
|
*/
|
|
3860
3894
|
reportCostFromUsage(model, inputTokens, outputTokens, cachedInputTokens = 0, cacheCreationInputTokens = 0) {
|
|
3861
3895
|
if (inputTokens === 0 && outputTokens === 0) return;
|
|
3862
|
-
const modelName = model.includes(":") ? model.split(":")[1] : model;
|
|
3863
3896
|
const estimate = this.client.modelRegistry.estimateCost(
|
|
3864
|
-
|
|
3897
|
+
model,
|
|
3865
3898
|
inputTokens,
|
|
3866
3899
|
outputTokens,
|
|
3867
3900
|
cachedInputTokens,
|
|
@@ -4144,18 +4177,64 @@ var init_parser = __esm({
|
|
|
4144
4177
|
}
|
|
4145
4178
|
});
|
|
4146
4179
|
|
|
4180
|
+
// src/gadgets/typed-gadget.ts
|
|
4181
|
+
function Gadget(config) {
|
|
4182
|
+
class GadgetBase extends AbstractGadget {
|
|
4183
|
+
description = config.description;
|
|
4184
|
+
parameterSchema = config.schema;
|
|
4185
|
+
name = config.name;
|
|
4186
|
+
timeoutMs = config.timeoutMs;
|
|
4187
|
+
examples = config.examples;
|
|
4188
|
+
/**
|
|
4189
|
+
* Type helper property for accessing inferred parameter type.
|
|
4190
|
+
* This is used in the execute method signature: `execute(params: this['params'])`
|
|
4191
|
+
*
|
|
4192
|
+
* Note: This is just for type inference - the actual params in execute()
|
|
4193
|
+
* will be Record<string, unknown> which you can safely cast to this['params']
|
|
4194
|
+
*/
|
|
4195
|
+
params;
|
|
4196
|
+
}
|
|
4197
|
+
return GadgetBase;
|
|
4198
|
+
}
|
|
4199
|
+
var init_typed_gadget = __esm({
|
|
4200
|
+
"src/gadgets/typed-gadget.ts"() {
|
|
4201
|
+
"use strict";
|
|
4202
|
+
init_gadget();
|
|
4203
|
+
}
|
|
4204
|
+
});
|
|
4205
|
+
|
|
4147
4206
|
// src/gadgets/executor.ts
|
|
4148
|
-
|
|
4207
|
+
import equal from "fast-deep-equal";
|
|
4208
|
+
import { z as z4 } from "zod";
|
|
4209
|
+
function getHostExportsInternal() {
|
|
4210
|
+
if (!cachedHostExports) {
|
|
4211
|
+
cachedHostExports = {
|
|
4212
|
+
AgentBuilder,
|
|
4213
|
+
Gadget,
|
|
4214
|
+
createGadget,
|
|
4215
|
+
ExecutionTree,
|
|
4216
|
+
LLMist,
|
|
4217
|
+
z: z4
|
|
4218
|
+
};
|
|
4219
|
+
}
|
|
4220
|
+
return cachedHostExports;
|
|
4221
|
+
}
|
|
4222
|
+
var cachedHostExports, GadgetExecutor;
|
|
4149
4223
|
var init_executor = __esm({
|
|
4150
4224
|
"src/gadgets/executor.ts"() {
|
|
4151
4225
|
"use strict";
|
|
4226
|
+
init_builder();
|
|
4227
|
+
init_client();
|
|
4152
4228
|
init_constants();
|
|
4229
|
+
init_execution_tree();
|
|
4153
4230
|
init_logger();
|
|
4154
4231
|
init_block_params();
|
|
4155
4232
|
init_cost_reporting_client();
|
|
4233
|
+
init_create_gadget();
|
|
4156
4234
|
init_error_formatter();
|
|
4157
4235
|
init_exceptions();
|
|
4158
4236
|
init_parser();
|
|
4237
|
+
init_typed_gadget();
|
|
4159
4238
|
GadgetExecutor = class {
|
|
4160
4239
|
constructor(registry, requestHumanInput, logger, defaultGadgetTimeoutMs, errorFormatterOptions, client, mediaStore, agentConfig, subagentConfig, onSubagentEvent, tree, parentNodeId, baseDepth) {
|
|
4161
4240
|
this.registry = registry;
|
|
@@ -4253,7 +4332,7 @@ var init_executor = __esm({
|
|
|
4253
4332
|
try {
|
|
4254
4333
|
const cleanedRaw = stripMarkdownFences(call.parametersRaw);
|
|
4255
4334
|
const initialParse = parseBlockParams(cleanedRaw, { argPrefix: this.argPrefix });
|
|
4256
|
-
const parametersWereModified = !
|
|
4335
|
+
const parametersWereModified = !equal(rawParameters, initialParse);
|
|
4257
4336
|
if (parametersWereModified) {
|
|
4258
4337
|
this.logger.debug("Parameters modified by interceptor, skipping re-parse", {
|
|
4259
4338
|
gadgetName: call.gadgetName
|
|
@@ -4328,7 +4407,11 @@ var init_executor = __esm({
|
|
|
4328
4407
|
// Tree context for subagent support - use gadget's own node ID
|
|
4329
4408
|
tree: this.tree,
|
|
4330
4409
|
nodeId: gadgetNodeId,
|
|
4331
|
-
depth: gadgetDepth
|
|
4410
|
+
depth: gadgetDepth,
|
|
4411
|
+
// Host exports for external gadgets to use host's llmist classes
|
|
4412
|
+
hostExports: getHostExportsInternal(),
|
|
4413
|
+
// Logger for structured logging (respects CLI's log level/file config)
|
|
4414
|
+
logger: this.logger
|
|
4332
4415
|
};
|
|
4333
4416
|
let rawResult;
|
|
4334
4417
|
if (timeoutMs && timeoutMs > 0) {
|
|
@@ -4498,27 +4581,6 @@ var init_executor = __esm({
|
|
|
4498
4581
|
async executeAll(calls) {
|
|
4499
4582
|
return Promise.all(calls.map((call) => this.execute(call)));
|
|
4500
4583
|
}
|
|
4501
|
-
/**
|
|
4502
|
-
* Deep equality check for objects/arrays.
|
|
4503
|
-
* Used to detect if parameters were modified by an interceptor.
|
|
4504
|
-
*/
|
|
4505
|
-
deepEquals(a, b) {
|
|
4506
|
-
if (a === b) return true;
|
|
4507
|
-
if (a === null || b === null) return a === b;
|
|
4508
|
-
if (typeof a !== typeof b) return false;
|
|
4509
|
-
if (typeof a !== "object") return a === b;
|
|
4510
|
-
if (Array.isArray(a) !== Array.isArray(b)) return false;
|
|
4511
|
-
if (Array.isArray(a) && Array.isArray(b)) {
|
|
4512
|
-
if (a.length !== b.length) return false;
|
|
4513
|
-
return a.every((val, i) => this.deepEquals(val, b[i]));
|
|
4514
|
-
}
|
|
4515
|
-
const aObj = a;
|
|
4516
|
-
const bObj = b;
|
|
4517
|
-
const aKeys = Object.keys(aObj);
|
|
4518
|
-
const bKeys = Object.keys(bObj);
|
|
4519
|
-
if (aKeys.length !== bKeys.length) return false;
|
|
4520
|
-
return aKeys.every((key) => this.deepEquals(aObj[key], bObj[key]));
|
|
4521
|
-
}
|
|
4522
4584
|
};
|
|
4523
4585
|
}
|
|
4524
4586
|
});
|
|
@@ -4556,6 +4618,11 @@ var init_stream_processor = __esm({
|
|
|
4556
4618
|
inFlightExecutions = /* @__PURE__ */ new Map();
|
|
4557
4619
|
/** Queue of completed gadget results ready to be yielded (for real-time streaming) */
|
|
4558
4620
|
completedResultsQueue = [];
|
|
4621
|
+
// Cross-iteration dependency tracking
|
|
4622
|
+
/** Invocation IDs completed in previous iterations (read-only reference from Agent) */
|
|
4623
|
+
priorCompletedInvocations;
|
|
4624
|
+
/** Invocation IDs that failed in previous iterations (read-only reference from Agent) */
|
|
4625
|
+
priorFailedInvocations;
|
|
4559
4626
|
constructor(options) {
|
|
4560
4627
|
this.iteration = options.iteration;
|
|
4561
4628
|
this.registry = options.registry;
|
|
@@ -4564,6 +4631,8 @@ var init_stream_processor = __esm({
|
|
|
4564
4631
|
this.tree = options.tree;
|
|
4565
4632
|
this.parentNodeId = options.parentNodeId ?? null;
|
|
4566
4633
|
this.baseDepth = options.baseDepth ?? 0;
|
|
4634
|
+
this.priorCompletedInvocations = options.priorCompletedInvocations ?? /* @__PURE__ */ new Set();
|
|
4635
|
+
this.priorFailedInvocations = options.priorFailedInvocations ?? /* @__PURE__ */ new Set();
|
|
4567
4636
|
this.parser = new GadgetCallParser({
|
|
4568
4637
|
startPrefix: options.gadgetStartPrefix,
|
|
4569
4638
|
endPrefix: options.gadgetEndPrefix,
|
|
@@ -4762,62 +4831,11 @@ var init_stream_processor = __esm({
|
|
|
4762
4831
|
}
|
|
4763
4832
|
return [{ type: "text", content }];
|
|
4764
4833
|
}
|
|
4765
|
-
/**
|
|
4766
|
-
* Process a gadget call through the full lifecycle, handling dependencies.
|
|
4767
|
-
*
|
|
4768
|
-
* Gadgets without dependencies (or with all dependencies satisfied) execute immediately.
|
|
4769
|
-
* Gadgets with unsatisfied dependencies are queued for later execution.
|
|
4770
|
-
* After each execution, pending gadgets are checked to see if they can now run.
|
|
4771
|
-
*/
|
|
4772
|
-
async processGadgetCall(call) {
|
|
4773
|
-
const events = [];
|
|
4774
|
-
events.push({ type: "gadget_call", call });
|
|
4775
|
-
if (call.dependencies.length > 0) {
|
|
4776
|
-
if (call.dependencies.includes(call.invocationId)) {
|
|
4777
|
-
this.logger.warn("Gadget has self-referential dependency (depends on itself)", {
|
|
4778
|
-
gadgetName: call.gadgetName,
|
|
4779
|
-
invocationId: call.invocationId
|
|
4780
|
-
});
|
|
4781
|
-
this.failedInvocations.add(call.invocationId);
|
|
4782
|
-
const skipEvent = {
|
|
4783
|
-
type: "gadget_skipped",
|
|
4784
|
-
gadgetName: call.gadgetName,
|
|
4785
|
-
invocationId: call.invocationId,
|
|
4786
|
-
parameters: call.parameters ?? {},
|
|
4787
|
-
failedDependency: call.invocationId,
|
|
4788
|
-
failedDependencyError: `Gadget "${call.invocationId}" cannot depend on itself (self-referential dependency)`
|
|
4789
|
-
};
|
|
4790
|
-
events.push(skipEvent);
|
|
4791
|
-
return events;
|
|
4792
|
-
}
|
|
4793
|
-
const failedDep = call.dependencies.find((dep) => this.failedInvocations.has(dep));
|
|
4794
|
-
if (failedDep) {
|
|
4795
|
-
const skipEvents = await this.handleFailedDependency(call, failedDep);
|
|
4796
|
-
events.push(...skipEvents);
|
|
4797
|
-
return events;
|
|
4798
|
-
}
|
|
4799
|
-
const unsatisfied = call.dependencies.filter((dep) => !this.completedResults.has(dep));
|
|
4800
|
-
if (unsatisfied.length > 0) {
|
|
4801
|
-
this.logger.debug("Queueing gadget for later - waiting on dependencies", {
|
|
4802
|
-
gadgetName: call.gadgetName,
|
|
4803
|
-
invocationId: call.invocationId,
|
|
4804
|
-
waitingOn: unsatisfied
|
|
4805
|
-
});
|
|
4806
|
-
this.gadgetsAwaitingDependencies.set(call.invocationId, call);
|
|
4807
|
-
return events;
|
|
4808
|
-
}
|
|
4809
|
-
}
|
|
4810
|
-
const executeEvents = await this.executeGadgetWithHooks(call);
|
|
4811
|
-
events.push(...executeEvents);
|
|
4812
|
-
const triggeredEvents = await this.processPendingGadgets();
|
|
4813
|
-
events.push(...triggeredEvents);
|
|
4814
|
-
return events;
|
|
4815
|
-
}
|
|
4816
4834
|
/**
|
|
4817
4835
|
* Process a gadget call, yielding events in real-time.
|
|
4818
4836
|
*
|
|
4819
|
-
*
|
|
4820
|
-
*
|
|
4837
|
+
* Yields gadget_call event IMMEDIATELY when parsed (before execution),
|
|
4838
|
+
* enabling real-time UI feedback.
|
|
4821
4839
|
*/
|
|
4822
4840
|
async *processGadgetCallGenerator(call) {
|
|
4823
4841
|
yield { type: "gadget_call", call };
|
|
@@ -4848,7 +4866,9 @@ var init_stream_processor = __esm({
|
|
|
4848
4866
|
yield skipEvent;
|
|
4849
4867
|
return;
|
|
4850
4868
|
}
|
|
4851
|
-
const failedDep = call.dependencies.find(
|
|
4869
|
+
const failedDep = call.dependencies.find(
|
|
4870
|
+
(dep) => this.failedInvocations.has(dep) || this.priorFailedInvocations.has(dep)
|
|
4871
|
+
);
|
|
4852
4872
|
if (failedDep) {
|
|
4853
4873
|
const skipEvents = await this.handleFailedDependency(call, failedDep);
|
|
4854
4874
|
for (const evt of skipEvents) {
|
|
@@ -4856,7 +4876,9 @@ var init_stream_processor = __esm({
|
|
|
4856
4876
|
}
|
|
4857
4877
|
return;
|
|
4858
4878
|
}
|
|
4859
|
-
const unsatisfied = call.dependencies.filter(
|
|
4879
|
+
const unsatisfied = call.dependencies.filter(
|
|
4880
|
+
(dep) => !this.completedResults.has(dep) && !this.priorCompletedInvocations.has(dep)
|
|
4881
|
+
);
|
|
4860
4882
|
if (unsatisfied.length > 0) {
|
|
4861
4883
|
this.logger.debug("Queueing gadget for later - waiting on dependencies", {
|
|
4862
4884
|
gadgetName: call.gadgetName,
|
|
@@ -4878,143 +4900,9 @@ var init_stream_processor = __esm({
|
|
|
4878
4900
|
this.inFlightExecutions.set(call.invocationId, executionPromise);
|
|
4879
4901
|
}
|
|
4880
4902
|
/**
|
|
4881
|
-
* Execute a gadget through the full hook lifecycle.
|
|
4882
|
-
*
|
|
4883
|
-
*
|
|
4884
|
-
*/
|
|
4885
|
-
async executeGadgetWithHooks(call) {
|
|
4886
|
-
const events = [];
|
|
4887
|
-
if (call.parseError) {
|
|
4888
|
-
this.logger.warn("Gadget has parse error", {
|
|
4889
|
-
gadgetName: call.gadgetName,
|
|
4890
|
-
error: call.parseError,
|
|
4891
|
-
rawParameters: call.parametersRaw
|
|
4892
|
-
});
|
|
4893
|
-
}
|
|
4894
|
-
let parameters = call.parameters ?? {};
|
|
4895
|
-
if (this.hooks.interceptors?.interceptGadgetParameters) {
|
|
4896
|
-
const context = {
|
|
4897
|
-
iteration: this.iteration,
|
|
4898
|
-
gadgetName: call.gadgetName,
|
|
4899
|
-
invocationId: call.invocationId,
|
|
4900
|
-
logger: this.logger
|
|
4901
|
-
};
|
|
4902
|
-
parameters = this.hooks.interceptors.interceptGadgetParameters(parameters, context);
|
|
4903
|
-
}
|
|
4904
|
-
call.parameters = parameters;
|
|
4905
|
-
let shouldSkip = false;
|
|
4906
|
-
let syntheticResult;
|
|
4907
|
-
if (this.hooks.controllers?.beforeGadgetExecution) {
|
|
4908
|
-
const context = {
|
|
4909
|
-
iteration: this.iteration,
|
|
4910
|
-
gadgetName: call.gadgetName,
|
|
4911
|
-
invocationId: call.invocationId,
|
|
4912
|
-
parameters,
|
|
4913
|
-
logger: this.logger
|
|
4914
|
-
};
|
|
4915
|
-
const action = await this.hooks.controllers.beforeGadgetExecution(context);
|
|
4916
|
-
validateBeforeGadgetExecutionAction(action);
|
|
4917
|
-
if (action.action === "skip") {
|
|
4918
|
-
shouldSkip = true;
|
|
4919
|
-
syntheticResult = action.syntheticResult;
|
|
4920
|
-
this.logger.info("Controller skipped gadget execution", {
|
|
4921
|
-
gadgetName: call.gadgetName
|
|
4922
|
-
});
|
|
4923
|
-
}
|
|
4924
|
-
}
|
|
4925
|
-
const startObservers = [];
|
|
4926
|
-
if (this.hooks.observers?.onGadgetExecutionStart) {
|
|
4927
|
-
startObservers.push(async () => {
|
|
4928
|
-
const context = {
|
|
4929
|
-
iteration: this.iteration,
|
|
4930
|
-
gadgetName: call.gadgetName,
|
|
4931
|
-
invocationId: call.invocationId,
|
|
4932
|
-
parameters,
|
|
4933
|
-
logger: this.logger
|
|
4934
|
-
};
|
|
4935
|
-
await this.hooks.observers?.onGadgetExecutionStart?.(context);
|
|
4936
|
-
});
|
|
4937
|
-
}
|
|
4938
|
-
await this.runObserversInParallel(startObservers);
|
|
4939
|
-
let result;
|
|
4940
|
-
if (shouldSkip) {
|
|
4941
|
-
result = {
|
|
4942
|
-
gadgetName: call.gadgetName,
|
|
4943
|
-
invocationId: call.invocationId,
|
|
4944
|
-
parameters,
|
|
4945
|
-
result: syntheticResult ?? "Execution skipped",
|
|
4946
|
-
executionTimeMs: 0
|
|
4947
|
-
};
|
|
4948
|
-
} else {
|
|
4949
|
-
result = await this.executor.execute(call);
|
|
4950
|
-
}
|
|
4951
|
-
const originalResult = result.result;
|
|
4952
|
-
if (result.result && this.hooks.interceptors?.interceptGadgetResult) {
|
|
4953
|
-
const context = {
|
|
4954
|
-
iteration: this.iteration,
|
|
4955
|
-
gadgetName: result.gadgetName,
|
|
4956
|
-
invocationId: result.invocationId,
|
|
4957
|
-
parameters,
|
|
4958
|
-
executionTimeMs: result.executionTimeMs,
|
|
4959
|
-
logger: this.logger
|
|
4960
|
-
};
|
|
4961
|
-
result.result = this.hooks.interceptors.interceptGadgetResult(result.result, context);
|
|
4962
|
-
}
|
|
4963
|
-
if (this.hooks.controllers?.afterGadgetExecution) {
|
|
4964
|
-
const context = {
|
|
4965
|
-
iteration: this.iteration,
|
|
4966
|
-
gadgetName: result.gadgetName,
|
|
4967
|
-
invocationId: result.invocationId,
|
|
4968
|
-
parameters,
|
|
4969
|
-
result: result.result,
|
|
4970
|
-
error: result.error,
|
|
4971
|
-
executionTimeMs: result.executionTimeMs,
|
|
4972
|
-
logger: this.logger
|
|
4973
|
-
};
|
|
4974
|
-
const action = await this.hooks.controllers.afterGadgetExecution(context);
|
|
4975
|
-
validateAfterGadgetExecutionAction(action);
|
|
4976
|
-
if (action.action === "recover" && result.error) {
|
|
4977
|
-
this.logger.info("Controller recovered from gadget error", {
|
|
4978
|
-
gadgetName: result.gadgetName,
|
|
4979
|
-
originalError: result.error
|
|
4980
|
-
});
|
|
4981
|
-
result = {
|
|
4982
|
-
...result,
|
|
4983
|
-
error: void 0,
|
|
4984
|
-
result: action.fallbackResult
|
|
4985
|
-
};
|
|
4986
|
-
}
|
|
4987
|
-
}
|
|
4988
|
-
const completeObservers = [];
|
|
4989
|
-
if (this.hooks.observers?.onGadgetExecutionComplete) {
|
|
4990
|
-
completeObservers.push(async () => {
|
|
4991
|
-
const context = {
|
|
4992
|
-
iteration: this.iteration,
|
|
4993
|
-
gadgetName: result.gadgetName,
|
|
4994
|
-
invocationId: result.invocationId,
|
|
4995
|
-
parameters,
|
|
4996
|
-
originalResult,
|
|
4997
|
-
finalResult: result.result,
|
|
4998
|
-
error: result.error,
|
|
4999
|
-
executionTimeMs: result.executionTimeMs,
|
|
5000
|
-
breaksLoop: result.breaksLoop,
|
|
5001
|
-
cost: result.cost,
|
|
5002
|
-
logger: this.logger
|
|
5003
|
-
};
|
|
5004
|
-
await this.hooks.observers?.onGadgetExecutionComplete?.(context);
|
|
5005
|
-
});
|
|
5006
|
-
}
|
|
5007
|
-
await this.runObserversInParallel(completeObservers);
|
|
5008
|
-
this.completedResults.set(result.invocationId, result);
|
|
5009
|
-
if (result.error) {
|
|
5010
|
-
this.failedInvocations.add(result.invocationId);
|
|
5011
|
-
}
|
|
5012
|
-
events.push({ type: "gadget_result", result });
|
|
5013
|
-
return events;
|
|
5014
|
-
}
|
|
5015
|
-
/**
|
|
5016
|
-
* Execute a gadget and yield the result event.
|
|
5017
|
-
* Generator version that yields gadget_result immediately when execution completes.
|
|
4903
|
+
* Execute a gadget through the full hook lifecycle and yield events.
|
|
4904
|
+
* Handles parameter interception, before/after controllers, observers,
|
|
4905
|
+
* execution, result interception, and tree tracking.
|
|
5018
4906
|
*/
|
|
5019
4907
|
async *executeGadgetGenerator(call) {
|
|
5020
4908
|
if (call.parseError) {
|
|
@@ -5280,8 +5168,9 @@ var init_stream_processor = __esm({
|
|
|
5280
5168
|
invocationId: call.invocationId,
|
|
5281
5169
|
failedDependency: failedDep
|
|
5282
5170
|
});
|
|
5283
|
-
const
|
|
5284
|
-
|
|
5171
|
+
for await (const evt of this.executeGadgetGenerator(call)) {
|
|
5172
|
+
events.push(evt);
|
|
5173
|
+
}
|
|
5285
5174
|
} else if (action.action === "use_fallback") {
|
|
5286
5175
|
const fallbackResult = {
|
|
5287
5176
|
gadgetName: call.gadgetName,
|
|
@@ -5302,90 +5191,9 @@ var init_stream_processor = __esm({
|
|
|
5302
5191
|
}
|
|
5303
5192
|
/**
|
|
5304
5193
|
* Process pending gadgets whose dependencies are now satisfied.
|
|
5305
|
-
*
|
|
5306
|
-
*/
|
|
5307
|
-
async processPendingGadgets() {
|
|
5308
|
-
const events = [];
|
|
5309
|
-
let progress = true;
|
|
5310
|
-
while (progress && this.gadgetsAwaitingDependencies.size > 0) {
|
|
5311
|
-
progress = false;
|
|
5312
|
-
const readyToExecute = [];
|
|
5313
|
-
const readyToSkip = [];
|
|
5314
|
-
for (const [invocationId, call] of this.gadgetsAwaitingDependencies) {
|
|
5315
|
-
const failedDep = call.dependencies.find((dep) => this.failedInvocations.has(dep));
|
|
5316
|
-
if (failedDep) {
|
|
5317
|
-
readyToSkip.push({ call, failedDep });
|
|
5318
|
-
continue;
|
|
5319
|
-
}
|
|
5320
|
-
const allSatisfied = call.dependencies.every((dep) => this.completedResults.has(dep));
|
|
5321
|
-
if (allSatisfied) {
|
|
5322
|
-
readyToExecute.push(call);
|
|
5323
|
-
}
|
|
5324
|
-
}
|
|
5325
|
-
for (const { call, failedDep } of readyToSkip) {
|
|
5326
|
-
this.gadgetsAwaitingDependencies.delete(call.invocationId);
|
|
5327
|
-
const skipEvents = await this.handleFailedDependency(call, failedDep);
|
|
5328
|
-
events.push(...skipEvents);
|
|
5329
|
-
progress = true;
|
|
5330
|
-
}
|
|
5331
|
-
if (readyToExecute.length > 0) {
|
|
5332
|
-
this.logger.debug("Executing ready gadgets in parallel", {
|
|
5333
|
-
count: readyToExecute.length,
|
|
5334
|
-
invocationIds: readyToExecute.map((c) => c.invocationId)
|
|
5335
|
-
});
|
|
5336
|
-
for (const call of readyToExecute) {
|
|
5337
|
-
this.gadgetsAwaitingDependencies.delete(call.invocationId);
|
|
5338
|
-
}
|
|
5339
|
-
const executePromises = readyToExecute.map((call) => this.executeGadgetWithHooks(call));
|
|
5340
|
-
const results = await Promise.all(executePromises);
|
|
5341
|
-
for (const executeEvents of results) {
|
|
5342
|
-
events.push(...executeEvents);
|
|
5343
|
-
}
|
|
5344
|
-
progress = true;
|
|
5345
|
-
}
|
|
5346
|
-
}
|
|
5347
|
-
if (this.gadgetsAwaitingDependencies.size > 0) {
|
|
5348
|
-
const pendingIds = new Set(this.gadgetsAwaitingDependencies.keys());
|
|
5349
|
-
for (const [invocationId, call] of this.gadgetsAwaitingDependencies) {
|
|
5350
|
-
const missingDeps = call.dependencies.filter((dep) => !this.completedResults.has(dep));
|
|
5351
|
-
const circularDeps = missingDeps.filter((dep) => pendingIds.has(dep));
|
|
5352
|
-
const trulyMissingDeps = missingDeps.filter((dep) => !pendingIds.has(dep));
|
|
5353
|
-
let errorMessage;
|
|
5354
|
-
let logLevel = "warn";
|
|
5355
|
-
if (circularDeps.length > 0 && trulyMissingDeps.length > 0) {
|
|
5356
|
-
errorMessage = `Dependencies unresolvable: circular=[${circularDeps.join(", ")}], missing=[${trulyMissingDeps.join(", ")}]`;
|
|
5357
|
-
logLevel = "error";
|
|
5358
|
-
} else if (circularDeps.length > 0) {
|
|
5359
|
-
errorMessage = `Circular dependency detected: "${invocationId}" depends on "${circularDeps[0]}" which also depends on "${invocationId}" (directly or indirectly)`;
|
|
5360
|
-
} else {
|
|
5361
|
-
errorMessage = `Dependency "${missingDeps[0]}" was never executed - check that the invocation ID exists and is spelled correctly`;
|
|
5362
|
-
}
|
|
5363
|
-
this.logger[logLevel]("Gadget has unresolvable dependencies", {
|
|
5364
|
-
gadgetName: call.gadgetName,
|
|
5365
|
-
invocationId,
|
|
5366
|
-
circularDependencies: circularDeps,
|
|
5367
|
-
missingDependencies: trulyMissingDeps
|
|
5368
|
-
});
|
|
5369
|
-
this.failedInvocations.add(invocationId);
|
|
5370
|
-
const skipEvent = {
|
|
5371
|
-
type: "gadget_skipped",
|
|
5372
|
-
gadgetName: call.gadgetName,
|
|
5373
|
-
invocationId,
|
|
5374
|
-
parameters: call.parameters ?? {},
|
|
5375
|
-
failedDependency: missingDeps[0],
|
|
5376
|
-
failedDependencyError: errorMessage
|
|
5377
|
-
};
|
|
5378
|
-
events.push(skipEvent);
|
|
5379
|
-
}
|
|
5380
|
-
this.gadgetsAwaitingDependencies.clear();
|
|
5381
|
-
}
|
|
5382
|
-
return events;
|
|
5383
|
-
}
|
|
5384
|
-
/**
|
|
5385
|
-
* Process pending gadgets, yielding events in real-time.
|
|
5386
|
-
* Generator version that yields events as gadgets complete.
|
|
5194
|
+
* Yields events in real-time as gadgets complete.
|
|
5387
5195
|
*
|
|
5388
|
-
*
|
|
5196
|
+
* Gadgets are executed in parallel for efficiency,
|
|
5389
5197
|
* but results are yielded as they become available.
|
|
5390
5198
|
*/
|
|
5391
5199
|
async *processPendingGadgetsGenerator() {
|
|
@@ -5395,12 +5203,16 @@ var init_stream_processor = __esm({
|
|
|
5395
5203
|
const readyToExecute = [];
|
|
5396
5204
|
const readyToSkip = [];
|
|
5397
5205
|
for (const [_invocationId, call] of this.gadgetsAwaitingDependencies) {
|
|
5398
|
-
const failedDep = call.dependencies.find(
|
|
5206
|
+
const failedDep = call.dependencies.find(
|
|
5207
|
+
(dep) => this.failedInvocations.has(dep) || this.priorFailedInvocations.has(dep)
|
|
5208
|
+
);
|
|
5399
5209
|
if (failedDep) {
|
|
5400
5210
|
readyToSkip.push({ call, failedDep });
|
|
5401
5211
|
continue;
|
|
5402
5212
|
}
|
|
5403
|
-
const allSatisfied = call.dependencies.every(
|
|
5213
|
+
const allSatisfied = call.dependencies.every(
|
|
5214
|
+
(dep) => this.completedResults.has(dep) || this.priorCompletedInvocations.has(dep)
|
|
5215
|
+
);
|
|
5404
5216
|
if (allSatisfied) {
|
|
5405
5217
|
readyToExecute.push(call);
|
|
5406
5218
|
}
|
|
@@ -5441,7 +5253,9 @@ var init_stream_processor = __esm({
|
|
|
5441
5253
|
if (this.gadgetsAwaitingDependencies.size > 0) {
|
|
5442
5254
|
const pendingIds = new Set(this.gadgetsAwaitingDependencies.keys());
|
|
5443
5255
|
for (const [invocationId, call] of this.gadgetsAwaitingDependencies) {
|
|
5444
|
-
const missingDeps = call.dependencies.filter(
|
|
5256
|
+
const missingDeps = call.dependencies.filter(
|
|
5257
|
+
(dep) => !this.completedResults.has(dep) && !this.priorCompletedInvocations.has(dep)
|
|
5258
|
+
);
|
|
5445
5259
|
const circularDeps = missingDeps.filter((dep) => pendingIds.has(dep));
|
|
5446
5260
|
const trulyMissingDeps = missingDeps.filter((dep) => !pendingIds.has(dep));
|
|
5447
5261
|
let errorMessage;
|
|
@@ -5495,10 +5309,27 @@ var init_stream_processor = __esm({
|
|
|
5495
5309
|
*/
|
|
5496
5310
|
async runObserversInParallel(observers) {
|
|
5497
5311
|
if (observers.length === 0) return;
|
|
5498
|
-
|
|
5312
|
+
await Promise.allSettled(
|
|
5499
5313
|
observers.map((observer) => this.safeObserve(observer))
|
|
5500
5314
|
);
|
|
5501
5315
|
}
|
|
5316
|
+
// ==========================================================================
|
|
5317
|
+
// Public accessors for cross-iteration dependency tracking
|
|
5318
|
+
// ==========================================================================
|
|
5319
|
+
/**
|
|
5320
|
+
* Get all invocation IDs that completed successfully in this iteration.
|
|
5321
|
+
* Used by Agent to accumulate completed IDs across iterations.
|
|
5322
|
+
*/
|
|
5323
|
+
getCompletedInvocationIds() {
|
|
5324
|
+
return new Set(this.completedResults.keys());
|
|
5325
|
+
}
|
|
5326
|
+
/**
|
|
5327
|
+
* Get all invocation IDs that failed in this iteration.
|
|
5328
|
+
* Used by Agent to accumulate failed IDs across iterations.
|
|
5329
|
+
*/
|
|
5330
|
+
getFailedInvocationIds() {
|
|
5331
|
+
return new Set(this.failedInvocations);
|
|
5332
|
+
}
|
|
5502
5333
|
};
|
|
5503
5334
|
}
|
|
5504
5335
|
});
|
|
@@ -5561,6 +5392,9 @@ var init_agent = __esm({
|
|
|
5561
5392
|
onSubagentEvent;
|
|
5562
5393
|
// Counter for generating synthetic invocation IDs for wrapped text content
|
|
5563
5394
|
syntheticInvocationCounter = 0;
|
|
5395
|
+
// Cross-iteration dependency tracking - allows gadgets to depend on results from prior iterations
|
|
5396
|
+
completedInvocationIds = /* @__PURE__ */ new Set();
|
|
5397
|
+
failedInvocationIds = /* @__PURE__ */ new Set();
|
|
5564
5398
|
// Execution Tree - first-class model for nested subagent support
|
|
5565
5399
|
tree;
|
|
5566
5400
|
parentNodeId;
|
|
@@ -5870,96 +5704,22 @@ var init_agent = __esm({
|
|
|
5870
5704
|
maxIterations: this.maxIterations
|
|
5871
5705
|
});
|
|
5872
5706
|
while (currentIteration < this.maxIterations) {
|
|
5873
|
-
if (this.
|
|
5874
|
-
this.logger.info("Agent loop terminated by abort signal", {
|
|
5875
|
-
iteration: currentIteration,
|
|
5876
|
-
reason: this.signal.reason
|
|
5877
|
-
});
|
|
5878
|
-
await this.safeObserve(async () => {
|
|
5879
|
-
if (this.hooks.observers?.onAbort) {
|
|
5880
|
-
const context = {
|
|
5881
|
-
iteration: currentIteration,
|
|
5882
|
-
reason: this.signal?.reason,
|
|
5883
|
-
logger: this.logger
|
|
5884
|
-
};
|
|
5885
|
-
await this.hooks.observers.onAbort(context);
|
|
5886
|
-
}
|
|
5887
|
-
});
|
|
5707
|
+
if (await this.checkAbortAndNotify(currentIteration)) {
|
|
5888
5708
|
return;
|
|
5889
5709
|
}
|
|
5890
5710
|
this.logger.debug("Starting iteration", { iteration: currentIteration });
|
|
5891
5711
|
try {
|
|
5892
|
-
|
|
5893
|
-
|
|
5894
|
-
|
|
5895
|
-
currentIteration
|
|
5896
|
-
);
|
|
5897
|
-
if (compactionEvent) {
|
|
5898
|
-
this.logger.info("Context compacted", {
|
|
5899
|
-
strategy: compactionEvent.strategy,
|
|
5900
|
-
tokensBefore: compactionEvent.tokensBefore,
|
|
5901
|
-
tokensAfter: compactionEvent.tokensAfter
|
|
5902
|
-
});
|
|
5903
|
-
yield { type: "compaction", event: compactionEvent };
|
|
5904
|
-
await this.safeObserve(async () => {
|
|
5905
|
-
if (this.hooks.observers?.onCompaction) {
|
|
5906
|
-
await this.hooks.observers.onCompaction({
|
|
5907
|
-
iteration: currentIteration,
|
|
5908
|
-
event: compactionEvent,
|
|
5909
|
-
// biome-ignore lint/style/noNonNullAssertion: compactionManager exists if compactionEvent is truthy
|
|
5910
|
-
stats: this.compactionManager.getStats(),
|
|
5911
|
-
logger: this.logger
|
|
5912
|
-
});
|
|
5913
|
-
}
|
|
5914
|
-
});
|
|
5915
|
-
}
|
|
5712
|
+
const compactionEvent = await this.checkAndPerformCompaction(currentIteration);
|
|
5713
|
+
if (compactionEvent) {
|
|
5714
|
+
yield compactionEvent;
|
|
5916
5715
|
}
|
|
5917
|
-
|
|
5918
|
-
|
|
5919
|
-
|
|
5920
|
-
|
|
5921
|
-
|
|
5922
|
-
|
|
5923
|
-
};
|
|
5924
|
-
await this.safeObserve(async () => {
|
|
5925
|
-
if (this.hooks.observers?.onLLMCallStart) {
|
|
5926
|
-
const context = {
|
|
5927
|
-
iteration: currentIteration,
|
|
5928
|
-
options: llmOptions,
|
|
5929
|
-
logger: this.logger
|
|
5930
|
-
};
|
|
5931
|
-
await this.hooks.observers.onLLMCallStart(context);
|
|
5932
|
-
}
|
|
5933
|
-
});
|
|
5934
|
-
if (this.hooks.controllers?.beforeLLMCall) {
|
|
5935
|
-
const context = {
|
|
5936
|
-
iteration: currentIteration,
|
|
5937
|
-
maxIterations: this.maxIterations,
|
|
5938
|
-
options: llmOptions,
|
|
5939
|
-
logger: this.logger
|
|
5940
|
-
};
|
|
5941
|
-
const action = await this.hooks.controllers.beforeLLMCall(context);
|
|
5942
|
-
validateBeforeLLMCallAction(action);
|
|
5943
|
-
if (action.action === "skip") {
|
|
5944
|
-
this.logger.info("Controller skipped LLM call, using synthetic response");
|
|
5945
|
-
this.conversation.addAssistantMessage(action.syntheticResponse);
|
|
5946
|
-
yield { type: "text", content: action.syntheticResponse };
|
|
5947
|
-
break;
|
|
5948
|
-
} else if (action.action === "proceed" && action.modifiedOptions) {
|
|
5949
|
-
llmOptions = { ...llmOptions, ...action.modifiedOptions };
|
|
5950
|
-
}
|
|
5716
|
+
const prepared = await this.prepareLLMCall(currentIteration);
|
|
5717
|
+
const llmOptions = prepared.options;
|
|
5718
|
+
if (prepared.skipWithSynthetic !== void 0) {
|
|
5719
|
+
this.conversation.addAssistantMessage(prepared.skipWithSynthetic);
|
|
5720
|
+
yield { type: "text", content: prepared.skipWithSynthetic };
|
|
5721
|
+
break;
|
|
5951
5722
|
}
|
|
5952
|
-
await this.safeObserve(async () => {
|
|
5953
|
-
if (this.hooks.observers?.onLLMCallReady) {
|
|
5954
|
-
const context = {
|
|
5955
|
-
iteration: currentIteration,
|
|
5956
|
-
maxIterations: this.maxIterations,
|
|
5957
|
-
options: llmOptions,
|
|
5958
|
-
logger: this.logger
|
|
5959
|
-
};
|
|
5960
|
-
await this.hooks.observers.onLLMCallReady(context);
|
|
5961
|
-
}
|
|
5962
|
-
});
|
|
5963
5723
|
this.logger.info("Calling LLM", { model: this.model });
|
|
5964
5724
|
this.logger.silly("LLM request details", {
|
|
5965
5725
|
model: llmOptions.model,
|
|
@@ -5995,7 +5755,10 @@ var init_agent = __esm({
|
|
|
5995
5755
|
tree: this.tree,
|
|
5996
5756
|
parentNodeId: currentLLMNodeId,
|
|
5997
5757
|
// Gadgets are children of this LLM call
|
|
5998
|
-
baseDepth: this.baseDepth
|
|
5758
|
+
baseDepth: this.baseDepth,
|
|
5759
|
+
// Cross-iteration dependency tracking
|
|
5760
|
+
priorCompletedInvocations: this.completedInvocationIds,
|
|
5761
|
+
priorFailedInvocations: this.failedInvocationIds
|
|
5999
5762
|
});
|
|
6000
5763
|
let streamMetadata = null;
|
|
6001
5764
|
let gadgetCallCount = 0;
|
|
@@ -6018,6 +5781,12 @@ var init_agent = __esm({
|
|
|
6018
5781
|
if (!streamMetadata) {
|
|
6019
5782
|
throw new Error("Stream processing completed without metadata event");
|
|
6020
5783
|
}
|
|
5784
|
+
for (const id of processor.getCompletedInvocationIds()) {
|
|
5785
|
+
this.completedInvocationIds.add(id);
|
|
5786
|
+
}
|
|
5787
|
+
for (const id of processor.getFailedInvocationIds()) {
|
|
5788
|
+
this.failedInvocationIds.add(id);
|
|
5789
|
+
}
|
|
6021
5790
|
const result = streamMetadata;
|
|
6022
5791
|
this.logger.info("LLM response completed", {
|
|
6023
5792
|
finishReason: result.finishReason,
|
|
@@ -6041,81 +5810,21 @@ var init_agent = __esm({
|
|
|
6041
5810
|
await this.hooks.observers.onLLMCallComplete(context);
|
|
6042
5811
|
}
|
|
6043
5812
|
});
|
|
6044
|
-
this.
|
|
6045
|
-
|
|
6046
|
-
|
|
6047
|
-
|
|
6048
|
-
|
|
6049
|
-
|
|
6050
|
-
|
|
6051
|
-
|
|
6052
|
-
|
|
6053
|
-
|
|
6054
|
-
|
|
6055
|
-
|
|
6056
|
-
|
|
6057
|
-
|
|
6058
|
-
|
|
6059
|
-
logger: this.logger
|
|
6060
|
-
};
|
|
6061
|
-
const action = await this.hooks.controllers.afterLLMCall(context);
|
|
6062
|
-
validateAfterLLMCallAction(action);
|
|
6063
|
-
if (action.action === "modify_and_continue" || action.action === "append_and_modify") {
|
|
6064
|
-
finalMessage = action.modifiedMessage;
|
|
6065
|
-
}
|
|
6066
|
-
if (action.action === "append_messages" || action.action === "append_and_modify") {
|
|
6067
|
-
for (const msg of action.messages) {
|
|
6068
|
-
if (msg.role === "user") {
|
|
6069
|
-
this.conversation.addUserMessage(msg.content);
|
|
6070
|
-
} else if (msg.role === "assistant") {
|
|
6071
|
-
this.conversation.addAssistantMessage(extractMessageText(msg.content));
|
|
6072
|
-
} else if (msg.role === "system") {
|
|
6073
|
-
this.conversation.addUserMessage(`[System] ${extractMessageText(msg.content)}`);
|
|
6074
|
-
}
|
|
6075
|
-
}
|
|
6076
|
-
}
|
|
6077
|
-
}
|
|
6078
|
-
if (result.didExecuteGadgets) {
|
|
6079
|
-
if (this.textWithGadgetsHandler) {
|
|
6080
|
-
const textContent = textOutputs.join("");
|
|
6081
|
-
if (textContent.trim()) {
|
|
6082
|
-
const { gadgetName, parameterMapping, resultMapping } = this.textWithGadgetsHandler;
|
|
6083
|
-
const syntheticId = `gc_text_${++this.syntheticInvocationCounter}`;
|
|
6084
|
-
this.conversation.addGadgetCallResult(
|
|
6085
|
-
gadgetName,
|
|
6086
|
-
parameterMapping(textContent),
|
|
6087
|
-
resultMapping ? resultMapping(textContent) : textContent,
|
|
6088
|
-
syntheticId
|
|
6089
|
-
);
|
|
6090
|
-
}
|
|
6091
|
-
}
|
|
6092
|
-
for (const output of gadgetResults) {
|
|
6093
|
-
if (output.type === "gadget_result") {
|
|
6094
|
-
const gadgetResult = output.result;
|
|
6095
|
-
this.conversation.addGadgetCallResult(
|
|
6096
|
-
gadgetResult.gadgetName,
|
|
6097
|
-
gadgetResult.parameters,
|
|
6098
|
-
gadgetResult.error ?? gadgetResult.result ?? "",
|
|
6099
|
-
gadgetResult.invocationId,
|
|
6100
|
-
gadgetResult.media,
|
|
6101
|
-
gadgetResult.mediaIds
|
|
6102
|
-
);
|
|
6103
|
-
}
|
|
6104
|
-
}
|
|
6105
|
-
} else {
|
|
6106
|
-
if (finalMessage.trim()) {
|
|
6107
|
-
const syntheticId = `gc_tell_${++this.syntheticInvocationCounter}`;
|
|
6108
|
-
this.conversation.addGadgetCallResult(
|
|
6109
|
-
"TellUser",
|
|
6110
|
-
{ message: finalMessage, done: false, type: "info" },
|
|
6111
|
-
`\u2139\uFE0F ${finalMessage}`,
|
|
6112
|
-
syntheticId
|
|
6113
|
-
);
|
|
6114
|
-
}
|
|
6115
|
-
const shouldBreak = await this.handleTextOnlyResponse(finalMessage);
|
|
6116
|
-
if (shouldBreak) {
|
|
6117
|
-
break;
|
|
6118
|
-
}
|
|
5813
|
+
this.completeLLMCallInTree(currentLLMNodeId, result);
|
|
5814
|
+
const finalMessage = await this.processAfterLLMCallController(
|
|
5815
|
+
currentIteration,
|
|
5816
|
+
llmOptions,
|
|
5817
|
+
result,
|
|
5818
|
+
gadgetCallCount
|
|
5819
|
+
);
|
|
5820
|
+
const shouldBreakFromTextOnly = await this.updateConversationWithResults(
|
|
5821
|
+
result.didExecuteGadgets,
|
|
5822
|
+
textOutputs,
|
|
5823
|
+
gadgetResults,
|
|
5824
|
+
finalMessage
|
|
5825
|
+
);
|
|
5826
|
+
if (shouldBreakFromTextOnly) {
|
|
5827
|
+
break;
|
|
6119
5828
|
}
|
|
6120
5829
|
if (result.shouldBreakLoop) {
|
|
6121
5830
|
this.logger.info("Loop terminated by gadget or processor");
|
|
@@ -6268,6 +5977,210 @@ var init_agent = __esm({
|
|
|
6268
5977
|
}
|
|
6269
5978
|
};
|
|
6270
5979
|
}
|
|
5980
|
+
// ==========================================================================
|
|
5981
|
+
// Agent Loop Helper Methods (extracted from run() for readability)
|
|
5982
|
+
// ==========================================================================
|
|
5983
|
+
/**
|
|
5984
|
+
* Check abort signal and notify observers if aborted.
|
|
5985
|
+
* @returns true if agent should terminate
|
|
5986
|
+
*/
|
|
5987
|
+
async checkAbortAndNotify(iteration) {
|
|
5988
|
+
if (!this.signal?.aborted) return false;
|
|
5989
|
+
this.logger.info("Agent loop terminated by abort signal", {
|
|
5990
|
+
iteration,
|
|
5991
|
+
reason: this.signal.reason
|
|
5992
|
+
});
|
|
5993
|
+
await this.safeObserve(async () => {
|
|
5994
|
+
if (this.hooks.observers?.onAbort) {
|
|
5995
|
+
const context = {
|
|
5996
|
+
iteration,
|
|
5997
|
+
reason: this.signal?.reason,
|
|
5998
|
+
logger: this.logger
|
|
5999
|
+
};
|
|
6000
|
+
await this.hooks.observers.onAbort(context);
|
|
6001
|
+
}
|
|
6002
|
+
});
|
|
6003
|
+
return true;
|
|
6004
|
+
}
|
|
6005
|
+
/**
|
|
6006
|
+
* Check and perform context compaction if needed.
|
|
6007
|
+
* @returns compaction stream event if compaction occurred, null otherwise
|
|
6008
|
+
*/
|
|
6009
|
+
async checkAndPerformCompaction(iteration) {
|
|
6010
|
+
if (!this.compactionManager) return null;
|
|
6011
|
+
const compactionEvent = await this.compactionManager.checkAndCompact(
|
|
6012
|
+
this.conversation,
|
|
6013
|
+
iteration
|
|
6014
|
+
);
|
|
6015
|
+
if (!compactionEvent) return null;
|
|
6016
|
+
this.logger.info("Context compacted", {
|
|
6017
|
+
strategy: compactionEvent.strategy,
|
|
6018
|
+
tokensBefore: compactionEvent.tokensBefore,
|
|
6019
|
+
tokensAfter: compactionEvent.tokensAfter
|
|
6020
|
+
});
|
|
6021
|
+
await this.safeObserve(async () => {
|
|
6022
|
+
if (this.hooks.observers?.onCompaction) {
|
|
6023
|
+
await this.hooks.observers.onCompaction({
|
|
6024
|
+
iteration,
|
|
6025
|
+
event: compactionEvent,
|
|
6026
|
+
// biome-ignore lint/style/noNonNullAssertion: compactionManager exists if compactionEvent is truthy
|
|
6027
|
+
stats: this.compactionManager.getStats(),
|
|
6028
|
+
logger: this.logger
|
|
6029
|
+
});
|
|
6030
|
+
}
|
|
6031
|
+
});
|
|
6032
|
+
return { type: "compaction", event: compactionEvent };
|
|
6033
|
+
}
|
|
6034
|
+
/**
|
|
6035
|
+
* Prepare LLM call options and process beforeLLMCall controller.
|
|
6036
|
+
* @returns options and optional skipWithSynthetic response if controller wants to skip
|
|
6037
|
+
*/
|
|
6038
|
+
async prepareLLMCall(iteration) {
|
|
6039
|
+
let llmOptions = {
|
|
6040
|
+
model: this.model,
|
|
6041
|
+
messages: this.conversation.getMessages(),
|
|
6042
|
+
temperature: this.temperature,
|
|
6043
|
+
maxTokens: this.defaultMaxTokens,
|
|
6044
|
+
signal: this.signal
|
|
6045
|
+
};
|
|
6046
|
+
await this.safeObserve(async () => {
|
|
6047
|
+
if (this.hooks.observers?.onLLMCallStart) {
|
|
6048
|
+
const context = {
|
|
6049
|
+
iteration,
|
|
6050
|
+
options: llmOptions,
|
|
6051
|
+
logger: this.logger
|
|
6052
|
+
};
|
|
6053
|
+
await this.hooks.observers.onLLMCallStart(context);
|
|
6054
|
+
}
|
|
6055
|
+
});
|
|
6056
|
+
if (this.hooks.controllers?.beforeLLMCall) {
|
|
6057
|
+
const context = {
|
|
6058
|
+
iteration,
|
|
6059
|
+
maxIterations: this.maxIterations,
|
|
6060
|
+
options: llmOptions,
|
|
6061
|
+
logger: this.logger
|
|
6062
|
+
};
|
|
6063
|
+
const action = await this.hooks.controllers.beforeLLMCall(context);
|
|
6064
|
+
validateBeforeLLMCallAction(action);
|
|
6065
|
+
if (action.action === "skip") {
|
|
6066
|
+
this.logger.info("Controller skipped LLM call, using synthetic response");
|
|
6067
|
+
return { options: llmOptions, skipWithSynthetic: action.syntheticResponse };
|
|
6068
|
+
} else if (action.action === "proceed" && action.modifiedOptions) {
|
|
6069
|
+
llmOptions = { ...llmOptions, ...action.modifiedOptions };
|
|
6070
|
+
}
|
|
6071
|
+
}
|
|
6072
|
+
await this.safeObserve(async () => {
|
|
6073
|
+
if (this.hooks.observers?.onLLMCallReady) {
|
|
6074
|
+
const context = {
|
|
6075
|
+
iteration,
|
|
6076
|
+
maxIterations: this.maxIterations,
|
|
6077
|
+
options: llmOptions,
|
|
6078
|
+
logger: this.logger
|
|
6079
|
+
};
|
|
6080
|
+
await this.hooks.observers.onLLMCallReady(context);
|
|
6081
|
+
}
|
|
6082
|
+
});
|
|
6083
|
+
return { options: llmOptions };
|
|
6084
|
+
}
|
|
6085
|
+
/**
|
|
6086
|
+
* Calculate cost and complete LLM call in execution tree.
|
|
6087
|
+
*/
|
|
6088
|
+
completeLLMCallInTree(nodeId, result) {
|
|
6089
|
+
const llmCost = this.client.modelRegistry?.estimateCost?.(
|
|
6090
|
+
this.model,
|
|
6091
|
+
result.usage?.inputTokens ?? 0,
|
|
6092
|
+
result.usage?.outputTokens ?? 0,
|
|
6093
|
+
result.usage?.cachedInputTokens ?? 0,
|
|
6094
|
+
result.usage?.cacheCreationInputTokens ?? 0
|
|
6095
|
+
)?.totalCost;
|
|
6096
|
+
this.tree.completeLLMCall(nodeId, {
|
|
6097
|
+
response: result.rawResponse,
|
|
6098
|
+
usage: result.usage,
|
|
6099
|
+
finishReason: result.finishReason,
|
|
6100
|
+
cost: llmCost
|
|
6101
|
+
});
|
|
6102
|
+
}
|
|
6103
|
+
/**
|
|
6104
|
+
* Process afterLLMCall controller and return modified final message.
|
|
6105
|
+
*/
|
|
6106
|
+
async processAfterLLMCallController(iteration, llmOptions, result, gadgetCallCount) {
|
|
6107
|
+
let finalMessage = result.finalMessage;
|
|
6108
|
+
if (!this.hooks.controllers?.afterLLMCall) {
|
|
6109
|
+
return finalMessage;
|
|
6110
|
+
}
|
|
6111
|
+
const context = {
|
|
6112
|
+
iteration,
|
|
6113
|
+
maxIterations: this.maxIterations,
|
|
6114
|
+
options: llmOptions,
|
|
6115
|
+
finishReason: result.finishReason,
|
|
6116
|
+
usage: result.usage,
|
|
6117
|
+
finalMessage: result.finalMessage,
|
|
6118
|
+
gadgetCallCount,
|
|
6119
|
+
logger: this.logger
|
|
6120
|
+
};
|
|
6121
|
+
const action = await this.hooks.controllers.afterLLMCall(context);
|
|
6122
|
+
validateAfterLLMCallAction(action);
|
|
6123
|
+
if (action.action === "modify_and_continue" || action.action === "append_and_modify") {
|
|
6124
|
+
finalMessage = action.modifiedMessage;
|
|
6125
|
+
}
|
|
6126
|
+
if (action.action === "append_messages" || action.action === "append_and_modify") {
|
|
6127
|
+
for (const msg of action.messages) {
|
|
6128
|
+
if (msg.role === "user") {
|
|
6129
|
+
this.conversation.addUserMessage(msg.content);
|
|
6130
|
+
} else if (msg.role === "assistant") {
|
|
6131
|
+
this.conversation.addAssistantMessage(extractMessageText(msg.content));
|
|
6132
|
+
} else if (msg.role === "system") {
|
|
6133
|
+
this.conversation.addUserMessage(`[System] ${extractMessageText(msg.content)}`);
|
|
6134
|
+
}
|
|
6135
|
+
}
|
|
6136
|
+
}
|
|
6137
|
+
return finalMessage;
|
|
6138
|
+
}
|
|
6139
|
+
/**
|
|
6140
|
+
* Update conversation history with gadget results or text-only response.
|
|
6141
|
+
* @returns true if loop should break (text-only handler requested termination)
|
|
6142
|
+
*/
|
|
6143
|
+
async updateConversationWithResults(didExecuteGadgets, textOutputs, gadgetResults, finalMessage) {
|
|
6144
|
+
if (didExecuteGadgets) {
|
|
6145
|
+
if (this.textWithGadgetsHandler) {
|
|
6146
|
+
const textContent = textOutputs.join("");
|
|
6147
|
+
if (textContent.trim()) {
|
|
6148
|
+
const { gadgetName, parameterMapping, resultMapping } = this.textWithGadgetsHandler;
|
|
6149
|
+
const syntheticId = `gc_text_${++this.syntheticInvocationCounter}`;
|
|
6150
|
+
this.conversation.addGadgetCallResult(
|
|
6151
|
+
gadgetName,
|
|
6152
|
+
parameterMapping(textContent),
|
|
6153
|
+
resultMapping ? resultMapping(textContent) : textContent,
|
|
6154
|
+
syntheticId
|
|
6155
|
+
);
|
|
6156
|
+
}
|
|
6157
|
+
}
|
|
6158
|
+
for (const output of gadgetResults) {
|
|
6159
|
+
if (output.type === "gadget_result") {
|
|
6160
|
+
const gadgetResult = output.result;
|
|
6161
|
+
this.conversation.addGadgetCallResult(
|
|
6162
|
+
gadgetResult.gadgetName,
|
|
6163
|
+
gadgetResult.parameters,
|
|
6164
|
+
gadgetResult.error ?? gadgetResult.result ?? "",
|
|
6165
|
+
gadgetResult.invocationId,
|
|
6166
|
+
gadgetResult.media,
|
|
6167
|
+
gadgetResult.mediaIds
|
|
6168
|
+
);
|
|
6169
|
+
}
|
|
6170
|
+
}
|
|
6171
|
+
return false;
|
|
6172
|
+
}
|
|
6173
|
+
if (finalMessage.trim()) {
|
|
6174
|
+
const syntheticId = `gc_tell_${++this.syntheticInvocationCounter}`;
|
|
6175
|
+
this.conversation.addGadgetCallResult(
|
|
6176
|
+
"TellUser",
|
|
6177
|
+
{ message: finalMessage, done: false, type: "info" },
|
|
6178
|
+
`\u2139\uFE0F ${finalMessage}`,
|
|
6179
|
+
syntheticId
|
|
6180
|
+
);
|
|
6181
|
+
}
|
|
6182
|
+
return await this.handleTextOnlyResponse(finalMessage);
|
|
6183
|
+
}
|
|
6271
6184
|
/**
|
|
6272
6185
|
* Run agent with named event handlers (syntactic sugar).
|
|
6273
6186
|
*
|
|
@@ -9800,11 +9713,13 @@ var init_model_registry = __esm({
|
|
|
9800
9713
|
}
|
|
9801
9714
|
/**
|
|
9802
9715
|
* Get model specification by model ID
|
|
9803
|
-
* @param modelId - Full model identifier
|
|
9716
|
+
* @param modelId - Full model identifier, optionally with provider prefix
|
|
9717
|
+
* (e.g., 'gpt-5', 'claude-sonnet-4-5-20250929', 'anthropic:claude-sonnet-4-5')
|
|
9804
9718
|
* @returns ModelSpec if found, undefined otherwise
|
|
9805
9719
|
*/
|
|
9806
9720
|
getModelSpec(modelId) {
|
|
9807
|
-
|
|
9721
|
+
const normalizedId = modelId.includes(":") ? modelId.split(":")[1] : modelId;
|
|
9722
|
+
return this.modelSpecs.find((model) => model.modelId === normalizedId);
|
|
9808
9723
|
}
|
|
9809
9724
|
/**
|
|
9810
9725
|
* List all models, optionally filtered by provider
|
|
@@ -12222,12 +12137,6 @@ export {
|
|
|
12222
12137
|
init_event_handlers,
|
|
12223
12138
|
GadgetOutputStore,
|
|
12224
12139
|
init_gadget_output_store,
|
|
12225
|
-
GadgetCallParser,
|
|
12226
|
-
init_parser,
|
|
12227
|
-
GadgetExecutor,
|
|
12228
|
-
init_executor,
|
|
12229
|
-
StreamProcessor,
|
|
12230
|
-
init_stream_processor,
|
|
12231
12140
|
FALLBACK_CHARS_PER_TOKEN,
|
|
12232
12141
|
init_constants2 as init_constants,
|
|
12233
12142
|
AnthropicMessagesProvider,
|
|
@@ -12250,6 +12159,14 @@ export {
|
|
|
12250
12159
|
init_options,
|
|
12251
12160
|
LLMist,
|
|
12252
12161
|
init_client,
|
|
12162
|
+
GadgetCallParser,
|
|
12163
|
+
init_parser,
|
|
12164
|
+
Gadget,
|
|
12165
|
+
init_typed_gadget,
|
|
12166
|
+
GadgetExecutor,
|
|
12167
|
+
init_executor,
|
|
12168
|
+
StreamProcessor,
|
|
12169
|
+
init_stream_processor,
|
|
12253
12170
|
AgentBuilder,
|
|
12254
12171
|
init_builder,
|
|
12255
12172
|
validateAndApplyDefaults,
|
|
@@ -12294,4 +12211,4 @@ export {
|
|
|
12294
12211
|
createEmptyStream,
|
|
12295
12212
|
createErrorStream
|
|
12296
12213
|
};
|
|
12297
|
-
//# sourceMappingURL=chunk-
|
|
12214
|
+
//# sourceMappingURL=chunk-SFZIL2VR.js.map
|