llmist 6.2.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-EJEP5MHQ.js → chunk-5KEZ7SQX.js} +2 -2
- package/dist/chunk-5KEZ7SQX.js.map +1 -0
- package/dist/{chunk-36YSBSGB.js → chunk-SFZIL2VR.js} +354 -486
- package/dist/chunk-SFZIL2VR.js.map +1 -0
- package/dist/cli.cjs +5920 -6275
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +5528 -5751
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +354 -486
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +34 -32
- package/dist/index.d.ts +34 -32
- package/dist/index.js +2 -2
- package/dist/{mock-stream-DG4wF-NH.d.cts → mock-stream-r5vjy2Iq.d.cts} +53 -1
- package/dist/{mock-stream-DG4wF-NH.d.ts → mock-stream-r5vjy2Iq.d.ts} +53 -1
- package/dist/testing/index.cjs +354 -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-36YSBSGB.js.map +0 -1
- package/dist/chunk-EJEP5MHQ.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,
|
|
@@ -4171,6 +4204,7 @@ var init_typed_gadget = __esm({
|
|
|
4171
4204
|
});
|
|
4172
4205
|
|
|
4173
4206
|
// src/gadgets/executor.ts
|
|
4207
|
+
import equal from "fast-deep-equal";
|
|
4174
4208
|
import { z as z4 } from "zod";
|
|
4175
4209
|
function getHostExportsInternal() {
|
|
4176
4210
|
if (!cachedHostExports) {
|
|
@@ -4298,7 +4332,7 @@ var init_executor = __esm({
|
|
|
4298
4332
|
try {
|
|
4299
4333
|
const cleanedRaw = stripMarkdownFences(call.parametersRaw);
|
|
4300
4334
|
const initialParse = parseBlockParams(cleanedRaw, { argPrefix: this.argPrefix });
|
|
4301
|
-
const parametersWereModified = !
|
|
4335
|
+
const parametersWereModified = !equal(rawParameters, initialParse);
|
|
4302
4336
|
if (parametersWereModified) {
|
|
4303
4337
|
this.logger.debug("Parameters modified by interceptor, skipping re-parse", {
|
|
4304
4338
|
gadgetName: call.gadgetName
|
|
@@ -4375,7 +4409,9 @@ var init_executor = __esm({
|
|
|
4375
4409
|
nodeId: gadgetNodeId,
|
|
4376
4410
|
depth: gadgetDepth,
|
|
4377
4411
|
// Host exports for external gadgets to use host's llmist classes
|
|
4378
|
-
hostExports: getHostExportsInternal()
|
|
4412
|
+
hostExports: getHostExportsInternal(),
|
|
4413
|
+
// Logger for structured logging (respects CLI's log level/file config)
|
|
4414
|
+
logger: this.logger
|
|
4379
4415
|
};
|
|
4380
4416
|
let rawResult;
|
|
4381
4417
|
if (timeoutMs && timeoutMs > 0) {
|
|
@@ -4545,27 +4581,6 @@ var init_executor = __esm({
|
|
|
4545
4581
|
async executeAll(calls) {
|
|
4546
4582
|
return Promise.all(calls.map((call) => this.execute(call)));
|
|
4547
4583
|
}
|
|
4548
|
-
/**
|
|
4549
|
-
* Deep equality check for objects/arrays.
|
|
4550
|
-
* Used to detect if parameters were modified by an interceptor.
|
|
4551
|
-
*/
|
|
4552
|
-
deepEquals(a, b) {
|
|
4553
|
-
if (a === b) return true;
|
|
4554
|
-
if (a === null || b === null) return a === b;
|
|
4555
|
-
if (typeof a !== typeof b) return false;
|
|
4556
|
-
if (typeof a !== "object") return a === b;
|
|
4557
|
-
if (Array.isArray(a) !== Array.isArray(b)) return false;
|
|
4558
|
-
if (Array.isArray(a) && Array.isArray(b)) {
|
|
4559
|
-
if (a.length !== b.length) return false;
|
|
4560
|
-
return a.every((val, i) => this.deepEquals(val, b[i]));
|
|
4561
|
-
}
|
|
4562
|
-
const aObj = a;
|
|
4563
|
-
const bObj = b;
|
|
4564
|
-
const aKeys = Object.keys(aObj);
|
|
4565
|
-
const bKeys = Object.keys(bObj);
|
|
4566
|
-
if (aKeys.length !== bKeys.length) return false;
|
|
4567
|
-
return aKeys.every((key) => this.deepEquals(aObj[key], bObj[key]));
|
|
4568
|
-
}
|
|
4569
4584
|
};
|
|
4570
4585
|
}
|
|
4571
4586
|
});
|
|
@@ -4603,6 +4618,11 @@ var init_stream_processor = __esm({
|
|
|
4603
4618
|
inFlightExecutions = /* @__PURE__ */ new Map();
|
|
4604
4619
|
/** Queue of completed gadget results ready to be yielded (for real-time streaming) */
|
|
4605
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;
|
|
4606
4626
|
constructor(options) {
|
|
4607
4627
|
this.iteration = options.iteration;
|
|
4608
4628
|
this.registry = options.registry;
|
|
@@ -4611,6 +4631,8 @@ var init_stream_processor = __esm({
|
|
|
4611
4631
|
this.tree = options.tree;
|
|
4612
4632
|
this.parentNodeId = options.parentNodeId ?? null;
|
|
4613
4633
|
this.baseDepth = options.baseDepth ?? 0;
|
|
4634
|
+
this.priorCompletedInvocations = options.priorCompletedInvocations ?? /* @__PURE__ */ new Set();
|
|
4635
|
+
this.priorFailedInvocations = options.priorFailedInvocations ?? /* @__PURE__ */ new Set();
|
|
4614
4636
|
this.parser = new GadgetCallParser({
|
|
4615
4637
|
startPrefix: options.gadgetStartPrefix,
|
|
4616
4638
|
endPrefix: options.gadgetEndPrefix,
|
|
@@ -4809,62 +4831,11 @@ var init_stream_processor = __esm({
|
|
|
4809
4831
|
}
|
|
4810
4832
|
return [{ type: "text", content }];
|
|
4811
4833
|
}
|
|
4812
|
-
/**
|
|
4813
|
-
* Process a gadget call through the full lifecycle, handling dependencies.
|
|
4814
|
-
*
|
|
4815
|
-
* Gadgets without dependencies (or with all dependencies satisfied) execute immediately.
|
|
4816
|
-
* Gadgets with unsatisfied dependencies are queued for later execution.
|
|
4817
|
-
* After each execution, pending gadgets are checked to see if they can now run.
|
|
4818
|
-
*/
|
|
4819
|
-
async processGadgetCall(call) {
|
|
4820
|
-
const events = [];
|
|
4821
|
-
events.push({ type: "gadget_call", call });
|
|
4822
|
-
if (call.dependencies.length > 0) {
|
|
4823
|
-
if (call.dependencies.includes(call.invocationId)) {
|
|
4824
|
-
this.logger.warn("Gadget has self-referential dependency (depends on itself)", {
|
|
4825
|
-
gadgetName: call.gadgetName,
|
|
4826
|
-
invocationId: call.invocationId
|
|
4827
|
-
});
|
|
4828
|
-
this.failedInvocations.add(call.invocationId);
|
|
4829
|
-
const skipEvent = {
|
|
4830
|
-
type: "gadget_skipped",
|
|
4831
|
-
gadgetName: call.gadgetName,
|
|
4832
|
-
invocationId: call.invocationId,
|
|
4833
|
-
parameters: call.parameters ?? {},
|
|
4834
|
-
failedDependency: call.invocationId,
|
|
4835
|
-
failedDependencyError: `Gadget "${call.invocationId}" cannot depend on itself (self-referential dependency)`
|
|
4836
|
-
};
|
|
4837
|
-
events.push(skipEvent);
|
|
4838
|
-
return events;
|
|
4839
|
-
}
|
|
4840
|
-
const failedDep = call.dependencies.find((dep) => this.failedInvocations.has(dep));
|
|
4841
|
-
if (failedDep) {
|
|
4842
|
-
const skipEvents = await this.handleFailedDependency(call, failedDep);
|
|
4843
|
-
events.push(...skipEvents);
|
|
4844
|
-
return events;
|
|
4845
|
-
}
|
|
4846
|
-
const unsatisfied = call.dependencies.filter((dep) => !this.completedResults.has(dep));
|
|
4847
|
-
if (unsatisfied.length > 0) {
|
|
4848
|
-
this.logger.debug("Queueing gadget for later - waiting on dependencies", {
|
|
4849
|
-
gadgetName: call.gadgetName,
|
|
4850
|
-
invocationId: call.invocationId,
|
|
4851
|
-
waitingOn: unsatisfied
|
|
4852
|
-
});
|
|
4853
|
-
this.gadgetsAwaitingDependencies.set(call.invocationId, call);
|
|
4854
|
-
return events;
|
|
4855
|
-
}
|
|
4856
|
-
}
|
|
4857
|
-
const executeEvents = await this.executeGadgetWithHooks(call);
|
|
4858
|
-
events.push(...executeEvents);
|
|
4859
|
-
const triggeredEvents = await this.processPendingGadgets();
|
|
4860
|
-
events.push(...triggeredEvents);
|
|
4861
|
-
return events;
|
|
4862
|
-
}
|
|
4863
4834
|
/**
|
|
4864
4835
|
* Process a gadget call, yielding events in real-time.
|
|
4865
4836
|
*
|
|
4866
|
-
*
|
|
4867
|
-
*
|
|
4837
|
+
* Yields gadget_call event IMMEDIATELY when parsed (before execution),
|
|
4838
|
+
* enabling real-time UI feedback.
|
|
4868
4839
|
*/
|
|
4869
4840
|
async *processGadgetCallGenerator(call) {
|
|
4870
4841
|
yield { type: "gadget_call", call };
|
|
@@ -4895,7 +4866,9 @@ var init_stream_processor = __esm({
|
|
|
4895
4866
|
yield skipEvent;
|
|
4896
4867
|
return;
|
|
4897
4868
|
}
|
|
4898
|
-
const failedDep = call.dependencies.find(
|
|
4869
|
+
const failedDep = call.dependencies.find(
|
|
4870
|
+
(dep) => this.failedInvocations.has(dep) || this.priorFailedInvocations.has(dep)
|
|
4871
|
+
);
|
|
4899
4872
|
if (failedDep) {
|
|
4900
4873
|
const skipEvents = await this.handleFailedDependency(call, failedDep);
|
|
4901
4874
|
for (const evt of skipEvents) {
|
|
@@ -4903,7 +4876,9 @@ var init_stream_processor = __esm({
|
|
|
4903
4876
|
}
|
|
4904
4877
|
return;
|
|
4905
4878
|
}
|
|
4906
|
-
const unsatisfied = call.dependencies.filter(
|
|
4879
|
+
const unsatisfied = call.dependencies.filter(
|
|
4880
|
+
(dep) => !this.completedResults.has(dep) && !this.priorCompletedInvocations.has(dep)
|
|
4881
|
+
);
|
|
4907
4882
|
if (unsatisfied.length > 0) {
|
|
4908
4883
|
this.logger.debug("Queueing gadget for later - waiting on dependencies", {
|
|
4909
4884
|
gadgetName: call.gadgetName,
|
|
@@ -4925,143 +4900,9 @@ var init_stream_processor = __esm({
|
|
|
4925
4900
|
this.inFlightExecutions.set(call.invocationId, executionPromise);
|
|
4926
4901
|
}
|
|
4927
4902
|
/**
|
|
4928
|
-
* Execute a gadget through the full hook lifecycle.
|
|
4929
|
-
*
|
|
4930
|
-
*
|
|
4931
|
-
*/
|
|
4932
|
-
async executeGadgetWithHooks(call) {
|
|
4933
|
-
const events = [];
|
|
4934
|
-
if (call.parseError) {
|
|
4935
|
-
this.logger.warn("Gadget has parse error", {
|
|
4936
|
-
gadgetName: call.gadgetName,
|
|
4937
|
-
error: call.parseError,
|
|
4938
|
-
rawParameters: call.parametersRaw
|
|
4939
|
-
});
|
|
4940
|
-
}
|
|
4941
|
-
let parameters = call.parameters ?? {};
|
|
4942
|
-
if (this.hooks.interceptors?.interceptGadgetParameters) {
|
|
4943
|
-
const context = {
|
|
4944
|
-
iteration: this.iteration,
|
|
4945
|
-
gadgetName: call.gadgetName,
|
|
4946
|
-
invocationId: call.invocationId,
|
|
4947
|
-
logger: this.logger
|
|
4948
|
-
};
|
|
4949
|
-
parameters = this.hooks.interceptors.interceptGadgetParameters(parameters, context);
|
|
4950
|
-
}
|
|
4951
|
-
call.parameters = parameters;
|
|
4952
|
-
let shouldSkip = false;
|
|
4953
|
-
let syntheticResult;
|
|
4954
|
-
if (this.hooks.controllers?.beforeGadgetExecution) {
|
|
4955
|
-
const context = {
|
|
4956
|
-
iteration: this.iteration,
|
|
4957
|
-
gadgetName: call.gadgetName,
|
|
4958
|
-
invocationId: call.invocationId,
|
|
4959
|
-
parameters,
|
|
4960
|
-
logger: this.logger
|
|
4961
|
-
};
|
|
4962
|
-
const action = await this.hooks.controllers.beforeGadgetExecution(context);
|
|
4963
|
-
validateBeforeGadgetExecutionAction(action);
|
|
4964
|
-
if (action.action === "skip") {
|
|
4965
|
-
shouldSkip = true;
|
|
4966
|
-
syntheticResult = action.syntheticResult;
|
|
4967
|
-
this.logger.info("Controller skipped gadget execution", {
|
|
4968
|
-
gadgetName: call.gadgetName
|
|
4969
|
-
});
|
|
4970
|
-
}
|
|
4971
|
-
}
|
|
4972
|
-
const startObservers = [];
|
|
4973
|
-
if (this.hooks.observers?.onGadgetExecutionStart) {
|
|
4974
|
-
startObservers.push(async () => {
|
|
4975
|
-
const context = {
|
|
4976
|
-
iteration: this.iteration,
|
|
4977
|
-
gadgetName: call.gadgetName,
|
|
4978
|
-
invocationId: call.invocationId,
|
|
4979
|
-
parameters,
|
|
4980
|
-
logger: this.logger
|
|
4981
|
-
};
|
|
4982
|
-
await this.hooks.observers?.onGadgetExecutionStart?.(context);
|
|
4983
|
-
});
|
|
4984
|
-
}
|
|
4985
|
-
await this.runObserversInParallel(startObservers);
|
|
4986
|
-
let result;
|
|
4987
|
-
if (shouldSkip) {
|
|
4988
|
-
result = {
|
|
4989
|
-
gadgetName: call.gadgetName,
|
|
4990
|
-
invocationId: call.invocationId,
|
|
4991
|
-
parameters,
|
|
4992
|
-
result: syntheticResult ?? "Execution skipped",
|
|
4993
|
-
executionTimeMs: 0
|
|
4994
|
-
};
|
|
4995
|
-
} else {
|
|
4996
|
-
result = await this.executor.execute(call);
|
|
4997
|
-
}
|
|
4998
|
-
const originalResult = result.result;
|
|
4999
|
-
if (result.result && this.hooks.interceptors?.interceptGadgetResult) {
|
|
5000
|
-
const context = {
|
|
5001
|
-
iteration: this.iteration,
|
|
5002
|
-
gadgetName: result.gadgetName,
|
|
5003
|
-
invocationId: result.invocationId,
|
|
5004
|
-
parameters,
|
|
5005
|
-
executionTimeMs: result.executionTimeMs,
|
|
5006
|
-
logger: this.logger
|
|
5007
|
-
};
|
|
5008
|
-
result.result = this.hooks.interceptors.interceptGadgetResult(result.result, context);
|
|
5009
|
-
}
|
|
5010
|
-
if (this.hooks.controllers?.afterGadgetExecution) {
|
|
5011
|
-
const context = {
|
|
5012
|
-
iteration: this.iteration,
|
|
5013
|
-
gadgetName: result.gadgetName,
|
|
5014
|
-
invocationId: result.invocationId,
|
|
5015
|
-
parameters,
|
|
5016
|
-
result: result.result,
|
|
5017
|
-
error: result.error,
|
|
5018
|
-
executionTimeMs: result.executionTimeMs,
|
|
5019
|
-
logger: this.logger
|
|
5020
|
-
};
|
|
5021
|
-
const action = await this.hooks.controllers.afterGadgetExecution(context);
|
|
5022
|
-
validateAfterGadgetExecutionAction(action);
|
|
5023
|
-
if (action.action === "recover" && result.error) {
|
|
5024
|
-
this.logger.info("Controller recovered from gadget error", {
|
|
5025
|
-
gadgetName: result.gadgetName,
|
|
5026
|
-
originalError: result.error
|
|
5027
|
-
});
|
|
5028
|
-
result = {
|
|
5029
|
-
...result,
|
|
5030
|
-
error: void 0,
|
|
5031
|
-
result: action.fallbackResult
|
|
5032
|
-
};
|
|
5033
|
-
}
|
|
5034
|
-
}
|
|
5035
|
-
const completeObservers = [];
|
|
5036
|
-
if (this.hooks.observers?.onGadgetExecutionComplete) {
|
|
5037
|
-
completeObservers.push(async () => {
|
|
5038
|
-
const context = {
|
|
5039
|
-
iteration: this.iteration,
|
|
5040
|
-
gadgetName: result.gadgetName,
|
|
5041
|
-
invocationId: result.invocationId,
|
|
5042
|
-
parameters,
|
|
5043
|
-
originalResult,
|
|
5044
|
-
finalResult: result.result,
|
|
5045
|
-
error: result.error,
|
|
5046
|
-
executionTimeMs: result.executionTimeMs,
|
|
5047
|
-
breaksLoop: result.breaksLoop,
|
|
5048
|
-
cost: result.cost,
|
|
5049
|
-
logger: this.logger
|
|
5050
|
-
};
|
|
5051
|
-
await this.hooks.observers?.onGadgetExecutionComplete?.(context);
|
|
5052
|
-
});
|
|
5053
|
-
}
|
|
5054
|
-
await this.runObserversInParallel(completeObservers);
|
|
5055
|
-
this.completedResults.set(result.invocationId, result);
|
|
5056
|
-
if (result.error) {
|
|
5057
|
-
this.failedInvocations.add(result.invocationId);
|
|
5058
|
-
}
|
|
5059
|
-
events.push({ type: "gadget_result", result });
|
|
5060
|
-
return events;
|
|
5061
|
-
}
|
|
5062
|
-
/**
|
|
5063
|
-
* Execute a gadget and yield the result event.
|
|
5064
|
-
* 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.
|
|
5065
4906
|
*/
|
|
5066
4907
|
async *executeGadgetGenerator(call) {
|
|
5067
4908
|
if (call.parseError) {
|
|
@@ -5327,8 +5168,9 @@ var init_stream_processor = __esm({
|
|
|
5327
5168
|
invocationId: call.invocationId,
|
|
5328
5169
|
failedDependency: failedDep
|
|
5329
5170
|
});
|
|
5330
|
-
const
|
|
5331
|
-
|
|
5171
|
+
for await (const evt of this.executeGadgetGenerator(call)) {
|
|
5172
|
+
events.push(evt);
|
|
5173
|
+
}
|
|
5332
5174
|
} else if (action.action === "use_fallback") {
|
|
5333
5175
|
const fallbackResult = {
|
|
5334
5176
|
gadgetName: call.gadgetName,
|
|
@@ -5349,90 +5191,9 @@ var init_stream_processor = __esm({
|
|
|
5349
5191
|
}
|
|
5350
5192
|
/**
|
|
5351
5193
|
* Process pending gadgets whose dependencies are now satisfied.
|
|
5352
|
-
*
|
|
5353
|
-
*/
|
|
5354
|
-
async processPendingGadgets() {
|
|
5355
|
-
const events = [];
|
|
5356
|
-
let progress = true;
|
|
5357
|
-
while (progress && this.gadgetsAwaitingDependencies.size > 0) {
|
|
5358
|
-
progress = false;
|
|
5359
|
-
const readyToExecute = [];
|
|
5360
|
-
const readyToSkip = [];
|
|
5361
|
-
for (const [invocationId, call] of this.gadgetsAwaitingDependencies) {
|
|
5362
|
-
const failedDep = call.dependencies.find((dep) => this.failedInvocations.has(dep));
|
|
5363
|
-
if (failedDep) {
|
|
5364
|
-
readyToSkip.push({ call, failedDep });
|
|
5365
|
-
continue;
|
|
5366
|
-
}
|
|
5367
|
-
const allSatisfied = call.dependencies.every((dep) => this.completedResults.has(dep));
|
|
5368
|
-
if (allSatisfied) {
|
|
5369
|
-
readyToExecute.push(call);
|
|
5370
|
-
}
|
|
5371
|
-
}
|
|
5372
|
-
for (const { call, failedDep } of readyToSkip) {
|
|
5373
|
-
this.gadgetsAwaitingDependencies.delete(call.invocationId);
|
|
5374
|
-
const skipEvents = await this.handleFailedDependency(call, failedDep);
|
|
5375
|
-
events.push(...skipEvents);
|
|
5376
|
-
progress = true;
|
|
5377
|
-
}
|
|
5378
|
-
if (readyToExecute.length > 0) {
|
|
5379
|
-
this.logger.debug("Executing ready gadgets in parallel", {
|
|
5380
|
-
count: readyToExecute.length,
|
|
5381
|
-
invocationIds: readyToExecute.map((c) => c.invocationId)
|
|
5382
|
-
});
|
|
5383
|
-
for (const call of readyToExecute) {
|
|
5384
|
-
this.gadgetsAwaitingDependencies.delete(call.invocationId);
|
|
5385
|
-
}
|
|
5386
|
-
const executePromises = readyToExecute.map((call) => this.executeGadgetWithHooks(call));
|
|
5387
|
-
const results = await Promise.all(executePromises);
|
|
5388
|
-
for (const executeEvents of results) {
|
|
5389
|
-
events.push(...executeEvents);
|
|
5390
|
-
}
|
|
5391
|
-
progress = true;
|
|
5392
|
-
}
|
|
5393
|
-
}
|
|
5394
|
-
if (this.gadgetsAwaitingDependencies.size > 0) {
|
|
5395
|
-
const pendingIds = new Set(this.gadgetsAwaitingDependencies.keys());
|
|
5396
|
-
for (const [invocationId, call] of this.gadgetsAwaitingDependencies) {
|
|
5397
|
-
const missingDeps = call.dependencies.filter((dep) => !this.completedResults.has(dep));
|
|
5398
|
-
const circularDeps = missingDeps.filter((dep) => pendingIds.has(dep));
|
|
5399
|
-
const trulyMissingDeps = missingDeps.filter((dep) => !pendingIds.has(dep));
|
|
5400
|
-
let errorMessage;
|
|
5401
|
-
let logLevel = "warn";
|
|
5402
|
-
if (circularDeps.length > 0 && trulyMissingDeps.length > 0) {
|
|
5403
|
-
errorMessage = `Dependencies unresolvable: circular=[${circularDeps.join(", ")}], missing=[${trulyMissingDeps.join(", ")}]`;
|
|
5404
|
-
logLevel = "error";
|
|
5405
|
-
} else if (circularDeps.length > 0) {
|
|
5406
|
-
errorMessage = `Circular dependency detected: "${invocationId}" depends on "${circularDeps[0]}" which also depends on "${invocationId}" (directly or indirectly)`;
|
|
5407
|
-
} else {
|
|
5408
|
-
errorMessage = `Dependency "${missingDeps[0]}" was never executed - check that the invocation ID exists and is spelled correctly`;
|
|
5409
|
-
}
|
|
5410
|
-
this.logger[logLevel]("Gadget has unresolvable dependencies", {
|
|
5411
|
-
gadgetName: call.gadgetName,
|
|
5412
|
-
invocationId,
|
|
5413
|
-
circularDependencies: circularDeps,
|
|
5414
|
-
missingDependencies: trulyMissingDeps
|
|
5415
|
-
});
|
|
5416
|
-
this.failedInvocations.add(invocationId);
|
|
5417
|
-
const skipEvent = {
|
|
5418
|
-
type: "gadget_skipped",
|
|
5419
|
-
gadgetName: call.gadgetName,
|
|
5420
|
-
invocationId,
|
|
5421
|
-
parameters: call.parameters ?? {},
|
|
5422
|
-
failedDependency: missingDeps[0],
|
|
5423
|
-
failedDependencyError: errorMessage
|
|
5424
|
-
};
|
|
5425
|
-
events.push(skipEvent);
|
|
5426
|
-
}
|
|
5427
|
-
this.gadgetsAwaitingDependencies.clear();
|
|
5428
|
-
}
|
|
5429
|
-
return events;
|
|
5430
|
-
}
|
|
5431
|
-
/**
|
|
5432
|
-
* Process pending gadgets, yielding events in real-time.
|
|
5433
|
-
* Generator version that yields events as gadgets complete.
|
|
5194
|
+
* Yields events in real-time as gadgets complete.
|
|
5434
5195
|
*
|
|
5435
|
-
*
|
|
5196
|
+
* Gadgets are executed in parallel for efficiency,
|
|
5436
5197
|
* but results are yielded as they become available.
|
|
5437
5198
|
*/
|
|
5438
5199
|
async *processPendingGadgetsGenerator() {
|
|
@@ -5442,12 +5203,16 @@ var init_stream_processor = __esm({
|
|
|
5442
5203
|
const readyToExecute = [];
|
|
5443
5204
|
const readyToSkip = [];
|
|
5444
5205
|
for (const [_invocationId, call] of this.gadgetsAwaitingDependencies) {
|
|
5445
|
-
const failedDep = call.dependencies.find(
|
|
5206
|
+
const failedDep = call.dependencies.find(
|
|
5207
|
+
(dep) => this.failedInvocations.has(dep) || this.priorFailedInvocations.has(dep)
|
|
5208
|
+
);
|
|
5446
5209
|
if (failedDep) {
|
|
5447
5210
|
readyToSkip.push({ call, failedDep });
|
|
5448
5211
|
continue;
|
|
5449
5212
|
}
|
|
5450
|
-
const allSatisfied = call.dependencies.every(
|
|
5213
|
+
const allSatisfied = call.dependencies.every(
|
|
5214
|
+
(dep) => this.completedResults.has(dep) || this.priorCompletedInvocations.has(dep)
|
|
5215
|
+
);
|
|
5451
5216
|
if (allSatisfied) {
|
|
5452
5217
|
readyToExecute.push(call);
|
|
5453
5218
|
}
|
|
@@ -5488,7 +5253,9 @@ var init_stream_processor = __esm({
|
|
|
5488
5253
|
if (this.gadgetsAwaitingDependencies.size > 0) {
|
|
5489
5254
|
const pendingIds = new Set(this.gadgetsAwaitingDependencies.keys());
|
|
5490
5255
|
for (const [invocationId, call] of this.gadgetsAwaitingDependencies) {
|
|
5491
|
-
const missingDeps = call.dependencies.filter(
|
|
5256
|
+
const missingDeps = call.dependencies.filter(
|
|
5257
|
+
(dep) => !this.completedResults.has(dep) && !this.priorCompletedInvocations.has(dep)
|
|
5258
|
+
);
|
|
5492
5259
|
const circularDeps = missingDeps.filter((dep) => pendingIds.has(dep));
|
|
5493
5260
|
const trulyMissingDeps = missingDeps.filter((dep) => !pendingIds.has(dep));
|
|
5494
5261
|
let errorMessage;
|
|
@@ -5542,10 +5309,27 @@ var init_stream_processor = __esm({
|
|
|
5542
5309
|
*/
|
|
5543
5310
|
async runObserversInParallel(observers) {
|
|
5544
5311
|
if (observers.length === 0) return;
|
|
5545
|
-
|
|
5312
|
+
await Promise.allSettled(
|
|
5546
5313
|
observers.map((observer) => this.safeObserve(observer))
|
|
5547
5314
|
);
|
|
5548
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
|
+
}
|
|
5549
5333
|
};
|
|
5550
5334
|
}
|
|
5551
5335
|
});
|
|
@@ -5608,6 +5392,9 @@ var init_agent = __esm({
|
|
|
5608
5392
|
onSubagentEvent;
|
|
5609
5393
|
// Counter for generating synthetic invocation IDs for wrapped text content
|
|
5610
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();
|
|
5611
5398
|
// Execution Tree - first-class model for nested subagent support
|
|
5612
5399
|
tree;
|
|
5613
5400
|
parentNodeId;
|
|
@@ -5917,96 +5704,22 @@ var init_agent = __esm({
|
|
|
5917
5704
|
maxIterations: this.maxIterations
|
|
5918
5705
|
});
|
|
5919
5706
|
while (currentIteration < this.maxIterations) {
|
|
5920
|
-
if (this.
|
|
5921
|
-
this.logger.info("Agent loop terminated by abort signal", {
|
|
5922
|
-
iteration: currentIteration,
|
|
5923
|
-
reason: this.signal.reason
|
|
5924
|
-
});
|
|
5925
|
-
await this.safeObserve(async () => {
|
|
5926
|
-
if (this.hooks.observers?.onAbort) {
|
|
5927
|
-
const context = {
|
|
5928
|
-
iteration: currentIteration,
|
|
5929
|
-
reason: this.signal?.reason,
|
|
5930
|
-
logger: this.logger
|
|
5931
|
-
};
|
|
5932
|
-
await this.hooks.observers.onAbort(context);
|
|
5933
|
-
}
|
|
5934
|
-
});
|
|
5707
|
+
if (await this.checkAbortAndNotify(currentIteration)) {
|
|
5935
5708
|
return;
|
|
5936
5709
|
}
|
|
5937
5710
|
this.logger.debug("Starting iteration", { iteration: currentIteration });
|
|
5938
5711
|
try {
|
|
5939
|
-
|
|
5940
|
-
|
|
5941
|
-
|
|
5942
|
-
currentIteration
|
|
5943
|
-
);
|
|
5944
|
-
if (compactionEvent) {
|
|
5945
|
-
this.logger.info("Context compacted", {
|
|
5946
|
-
strategy: compactionEvent.strategy,
|
|
5947
|
-
tokensBefore: compactionEvent.tokensBefore,
|
|
5948
|
-
tokensAfter: compactionEvent.tokensAfter
|
|
5949
|
-
});
|
|
5950
|
-
yield { type: "compaction", event: compactionEvent };
|
|
5951
|
-
await this.safeObserve(async () => {
|
|
5952
|
-
if (this.hooks.observers?.onCompaction) {
|
|
5953
|
-
await this.hooks.observers.onCompaction({
|
|
5954
|
-
iteration: currentIteration,
|
|
5955
|
-
event: compactionEvent,
|
|
5956
|
-
// biome-ignore lint/style/noNonNullAssertion: compactionManager exists if compactionEvent is truthy
|
|
5957
|
-
stats: this.compactionManager.getStats(),
|
|
5958
|
-
logger: this.logger
|
|
5959
|
-
});
|
|
5960
|
-
}
|
|
5961
|
-
});
|
|
5962
|
-
}
|
|
5712
|
+
const compactionEvent = await this.checkAndPerformCompaction(currentIteration);
|
|
5713
|
+
if (compactionEvent) {
|
|
5714
|
+
yield compactionEvent;
|
|
5963
5715
|
}
|
|
5964
|
-
|
|
5965
|
-
|
|
5966
|
-
|
|
5967
|
-
|
|
5968
|
-
|
|
5969
|
-
|
|
5970
|
-
};
|
|
5971
|
-
await this.safeObserve(async () => {
|
|
5972
|
-
if (this.hooks.observers?.onLLMCallStart) {
|
|
5973
|
-
const context = {
|
|
5974
|
-
iteration: currentIteration,
|
|
5975
|
-
options: llmOptions,
|
|
5976
|
-
logger: this.logger
|
|
5977
|
-
};
|
|
5978
|
-
await this.hooks.observers.onLLMCallStart(context);
|
|
5979
|
-
}
|
|
5980
|
-
});
|
|
5981
|
-
if (this.hooks.controllers?.beforeLLMCall) {
|
|
5982
|
-
const context = {
|
|
5983
|
-
iteration: currentIteration,
|
|
5984
|
-
maxIterations: this.maxIterations,
|
|
5985
|
-
options: llmOptions,
|
|
5986
|
-
logger: this.logger
|
|
5987
|
-
};
|
|
5988
|
-
const action = await this.hooks.controllers.beforeLLMCall(context);
|
|
5989
|
-
validateBeforeLLMCallAction(action);
|
|
5990
|
-
if (action.action === "skip") {
|
|
5991
|
-
this.logger.info("Controller skipped LLM call, using synthetic response");
|
|
5992
|
-
this.conversation.addAssistantMessage(action.syntheticResponse);
|
|
5993
|
-
yield { type: "text", content: action.syntheticResponse };
|
|
5994
|
-
break;
|
|
5995
|
-
} else if (action.action === "proceed" && action.modifiedOptions) {
|
|
5996
|
-
llmOptions = { ...llmOptions, ...action.modifiedOptions };
|
|
5997
|
-
}
|
|
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;
|
|
5998
5722
|
}
|
|
5999
|
-
await this.safeObserve(async () => {
|
|
6000
|
-
if (this.hooks.observers?.onLLMCallReady) {
|
|
6001
|
-
const context = {
|
|
6002
|
-
iteration: currentIteration,
|
|
6003
|
-
maxIterations: this.maxIterations,
|
|
6004
|
-
options: llmOptions,
|
|
6005
|
-
logger: this.logger
|
|
6006
|
-
};
|
|
6007
|
-
await this.hooks.observers.onLLMCallReady(context);
|
|
6008
|
-
}
|
|
6009
|
-
});
|
|
6010
5723
|
this.logger.info("Calling LLM", { model: this.model });
|
|
6011
5724
|
this.logger.silly("LLM request details", {
|
|
6012
5725
|
model: llmOptions.model,
|
|
@@ -6042,7 +5755,10 @@ var init_agent = __esm({
|
|
|
6042
5755
|
tree: this.tree,
|
|
6043
5756
|
parentNodeId: currentLLMNodeId,
|
|
6044
5757
|
// Gadgets are children of this LLM call
|
|
6045
|
-
baseDepth: this.baseDepth
|
|
5758
|
+
baseDepth: this.baseDepth,
|
|
5759
|
+
// Cross-iteration dependency tracking
|
|
5760
|
+
priorCompletedInvocations: this.completedInvocationIds,
|
|
5761
|
+
priorFailedInvocations: this.failedInvocationIds
|
|
6046
5762
|
});
|
|
6047
5763
|
let streamMetadata = null;
|
|
6048
5764
|
let gadgetCallCount = 0;
|
|
@@ -6065,6 +5781,12 @@ var init_agent = __esm({
|
|
|
6065
5781
|
if (!streamMetadata) {
|
|
6066
5782
|
throw new Error("Stream processing completed without metadata event");
|
|
6067
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
|
+
}
|
|
6068
5790
|
const result = streamMetadata;
|
|
6069
5791
|
this.logger.info("LLM response completed", {
|
|
6070
5792
|
finishReason: result.finishReason,
|
|
@@ -6088,81 +5810,21 @@ var init_agent = __esm({
|
|
|
6088
5810
|
await this.hooks.observers.onLLMCallComplete(context);
|
|
6089
5811
|
}
|
|
6090
5812
|
});
|
|
6091
|
-
this.
|
|
6092
|
-
|
|
6093
|
-
|
|
6094
|
-
|
|
6095
|
-
|
|
6096
|
-
|
|
6097
|
-
|
|
6098
|
-
|
|
6099
|
-
|
|
6100
|
-
|
|
6101
|
-
|
|
6102
|
-
|
|
6103
|
-
|
|
6104
|
-
|
|
6105
|
-
|
|
6106
|
-
logger: this.logger
|
|
6107
|
-
};
|
|
6108
|
-
const action = await this.hooks.controllers.afterLLMCall(context);
|
|
6109
|
-
validateAfterLLMCallAction(action);
|
|
6110
|
-
if (action.action === "modify_and_continue" || action.action === "append_and_modify") {
|
|
6111
|
-
finalMessage = action.modifiedMessage;
|
|
6112
|
-
}
|
|
6113
|
-
if (action.action === "append_messages" || action.action === "append_and_modify") {
|
|
6114
|
-
for (const msg of action.messages) {
|
|
6115
|
-
if (msg.role === "user") {
|
|
6116
|
-
this.conversation.addUserMessage(msg.content);
|
|
6117
|
-
} else if (msg.role === "assistant") {
|
|
6118
|
-
this.conversation.addAssistantMessage(extractMessageText(msg.content));
|
|
6119
|
-
} else if (msg.role === "system") {
|
|
6120
|
-
this.conversation.addUserMessage(`[System] ${extractMessageText(msg.content)}`);
|
|
6121
|
-
}
|
|
6122
|
-
}
|
|
6123
|
-
}
|
|
6124
|
-
}
|
|
6125
|
-
if (result.didExecuteGadgets) {
|
|
6126
|
-
if (this.textWithGadgetsHandler) {
|
|
6127
|
-
const textContent = textOutputs.join("");
|
|
6128
|
-
if (textContent.trim()) {
|
|
6129
|
-
const { gadgetName, parameterMapping, resultMapping } = this.textWithGadgetsHandler;
|
|
6130
|
-
const syntheticId = `gc_text_${++this.syntheticInvocationCounter}`;
|
|
6131
|
-
this.conversation.addGadgetCallResult(
|
|
6132
|
-
gadgetName,
|
|
6133
|
-
parameterMapping(textContent),
|
|
6134
|
-
resultMapping ? resultMapping(textContent) : textContent,
|
|
6135
|
-
syntheticId
|
|
6136
|
-
);
|
|
6137
|
-
}
|
|
6138
|
-
}
|
|
6139
|
-
for (const output of gadgetResults) {
|
|
6140
|
-
if (output.type === "gadget_result") {
|
|
6141
|
-
const gadgetResult = output.result;
|
|
6142
|
-
this.conversation.addGadgetCallResult(
|
|
6143
|
-
gadgetResult.gadgetName,
|
|
6144
|
-
gadgetResult.parameters,
|
|
6145
|
-
gadgetResult.error ?? gadgetResult.result ?? "",
|
|
6146
|
-
gadgetResult.invocationId,
|
|
6147
|
-
gadgetResult.media,
|
|
6148
|
-
gadgetResult.mediaIds
|
|
6149
|
-
);
|
|
6150
|
-
}
|
|
6151
|
-
}
|
|
6152
|
-
} else {
|
|
6153
|
-
if (finalMessage.trim()) {
|
|
6154
|
-
const syntheticId = `gc_tell_${++this.syntheticInvocationCounter}`;
|
|
6155
|
-
this.conversation.addGadgetCallResult(
|
|
6156
|
-
"TellUser",
|
|
6157
|
-
{ message: finalMessage, done: false, type: "info" },
|
|
6158
|
-
`\u2139\uFE0F ${finalMessage}`,
|
|
6159
|
-
syntheticId
|
|
6160
|
-
);
|
|
6161
|
-
}
|
|
6162
|
-
const shouldBreak = await this.handleTextOnlyResponse(finalMessage);
|
|
6163
|
-
if (shouldBreak) {
|
|
6164
|
-
break;
|
|
6165
|
-
}
|
|
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;
|
|
6166
5828
|
}
|
|
6167
5829
|
if (result.shouldBreakLoop) {
|
|
6168
5830
|
this.logger.info("Loop terminated by gadget or processor");
|
|
@@ -6315,6 +5977,210 @@ var init_agent = __esm({
|
|
|
6315
5977
|
}
|
|
6316
5978
|
};
|
|
6317
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
|
+
}
|
|
6318
6184
|
/**
|
|
6319
6185
|
* Run agent with named event handlers (syntactic sugar).
|
|
6320
6186
|
*
|
|
@@ -9847,11 +9713,13 @@ var init_model_registry = __esm({
|
|
|
9847
9713
|
}
|
|
9848
9714
|
/**
|
|
9849
9715
|
* Get model specification by model ID
|
|
9850
|
-
* @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')
|
|
9851
9718
|
* @returns ModelSpec if found, undefined otherwise
|
|
9852
9719
|
*/
|
|
9853
9720
|
getModelSpec(modelId) {
|
|
9854
|
-
|
|
9721
|
+
const normalizedId = modelId.includes(":") ? modelId.split(":")[1] : modelId;
|
|
9722
|
+
return this.modelSpecs.find((model) => model.modelId === normalizedId);
|
|
9855
9723
|
}
|
|
9856
9724
|
/**
|
|
9857
9725
|
* List all models, optionally filtered by provider
|
|
@@ -12343,4 +12211,4 @@ export {
|
|
|
12343
12211
|
createEmptyStream,
|
|
12344
12212
|
createErrorStream
|
|
12345
12213
|
};
|
|
12346
|
-
//# sourceMappingURL=chunk-
|
|
12214
|
+
//# sourceMappingURL=chunk-SFZIL2VR.js.map
|