llmist 16.0.3 → 16.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +816 -576
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +20 -64
- package/dist/index.d.ts +20 -64
- package/dist/index.js +816 -576
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -3978,6 +3978,23 @@ var init_hook_validators = __esm({
|
|
|
3978
3978
|
}
|
|
3979
3979
|
});
|
|
3980
3980
|
|
|
3981
|
+
// src/agent/safe-observe.ts
|
|
3982
|
+
async function safeObserve(fn, logger2, label) {
|
|
3983
|
+
try {
|
|
3984
|
+
await fn();
|
|
3985
|
+
} catch (error) {
|
|
3986
|
+
const message = label ? `Observer error in ${label}:` : "Observer threw error (ignoring)";
|
|
3987
|
+
logger2.error(message, {
|
|
3988
|
+
error: error instanceof Error ? error.message : String(error)
|
|
3989
|
+
});
|
|
3990
|
+
}
|
|
3991
|
+
}
|
|
3992
|
+
var init_safe_observe = __esm({
|
|
3993
|
+
"src/agent/safe-observe.ts"() {
|
|
3994
|
+
"use strict";
|
|
3995
|
+
}
|
|
3996
|
+
});
|
|
3997
|
+
|
|
3981
3998
|
// src/gadgets/registry.ts
|
|
3982
3999
|
var GadgetRegistry;
|
|
3983
4000
|
var init_registry = __esm({
|
|
@@ -4432,6 +4449,8 @@ var init_hook_presets = __esm({
|
|
|
4432
4449
|
/**
|
|
4433
4450
|
* Tracks cumulative token usage across all LLM calls.
|
|
4434
4451
|
*
|
|
4452
|
+
* @public
|
|
4453
|
+
*
|
|
4435
4454
|
* **Output:**
|
|
4436
4455
|
* - Per-call token count with 📊 emoji
|
|
4437
4456
|
* - Cumulative total across all calls
|
|
@@ -4694,6 +4713,8 @@ var init_hook_presets = __esm({
|
|
|
4694
4713
|
/**
|
|
4695
4714
|
* Logs detailed error information for debugging and troubleshooting.
|
|
4696
4715
|
*
|
|
4716
|
+
* @public
|
|
4717
|
+
*
|
|
4697
4718
|
* **Output:**
|
|
4698
4719
|
* - LLM errors with ❌ emoji, including model and recovery status
|
|
4699
4720
|
* - Gadget errors with full context (parameters, error message)
|
|
@@ -4772,6 +4793,8 @@ var init_hook_presets = __esm({
|
|
|
4772
4793
|
/**
|
|
4773
4794
|
* Tracks context compaction events.
|
|
4774
4795
|
*
|
|
4796
|
+
* @public
|
|
4797
|
+
*
|
|
4775
4798
|
* **Output:**
|
|
4776
4799
|
* - Compaction events with 🗜️ emoji
|
|
4777
4800
|
* - Strategy name, tokens before/after, and savings
|
|
@@ -4885,6 +4908,8 @@ var init_hook_presets = __esm({
|
|
|
4885
4908
|
/**
|
|
4886
4909
|
* Returns empty hook configuration for clean output without any logging.
|
|
4887
4910
|
*
|
|
4911
|
+
* @public
|
|
4912
|
+
*
|
|
4888
4913
|
* **Output:**
|
|
4889
4914
|
* - None. Returns {} (empty object).
|
|
4890
4915
|
*
|
|
@@ -5042,6 +5067,8 @@ var init_hook_presets = __esm({
|
|
|
5042
5067
|
/**
|
|
5043
5068
|
* Composite preset combining logging, timing, tokenTracking, and errorLogging.
|
|
5044
5069
|
*
|
|
5070
|
+
* @public
|
|
5071
|
+
*
|
|
5045
5072
|
* This is the recommended preset for development and initial production deployments,
|
|
5046
5073
|
* providing comprehensive observability with a single method call.
|
|
5047
5074
|
*
|
|
@@ -11992,7 +12019,9 @@ ${endPrefix}`
|
|
|
11992
12019
|
*/
|
|
11993
12020
|
/**
|
|
11994
12021
|
* Build AgentOptions with the given user prompt.
|
|
11995
|
-
* Centralizes options construction for ask(), askWithImage(), and
|
|
12022
|
+
* Centralizes options construction for ask(), askWithImage(), askWithContent(), and build().
|
|
12023
|
+
*
|
|
12024
|
+
* @param userPrompt - Optional user prompt (omitted for build() which has no prompt)
|
|
11996
12025
|
*/
|
|
11997
12026
|
buildAgentOptions(userPrompt) {
|
|
11998
12027
|
if (!this.client) {
|
|
@@ -12034,7 +12063,12 @@ ${endPrefix}`
|
|
|
12034
12063
|
// Tree context for shared tree model (subagents share parent's tree)
|
|
12035
12064
|
parentTree: this.parentContext?.tree,
|
|
12036
12065
|
parentNodeId: this.parentContext?.nodeId,
|
|
12037
|
-
baseDepth: this.parentContext ? (this.parentContext.depth ?? 0) + 1 : 0
|
|
12066
|
+
baseDepth: this.parentContext ? (this.parentContext.depth ?? 0) + 1 : 0,
|
|
12067
|
+
// Parent observer hooks for subagent visibility
|
|
12068
|
+
parentObservers: this.parentObservers,
|
|
12069
|
+
// Shared rate limit tracker and retry config (for coordinated limits across subagents)
|
|
12070
|
+
sharedRateLimitTracker: this.sharedRateLimitTracker,
|
|
12071
|
+
sharedRetryConfig: this.sharedRetryConfig
|
|
12038
12072
|
};
|
|
12039
12073
|
}
|
|
12040
12074
|
ask(userPrompt) {
|
|
@@ -12186,52 +12220,7 @@ ${endPrefix}`
|
|
|
12186
12220
|
* ```
|
|
12187
12221
|
*/
|
|
12188
12222
|
build() {
|
|
12189
|
-
|
|
12190
|
-
const { LLMist: LLMistClass } = (init_client(), __toCommonJS(client_exports));
|
|
12191
|
-
this.client = new LLMistClass();
|
|
12192
|
-
}
|
|
12193
|
-
const registry = GadgetRegistry.from(this.gadgets);
|
|
12194
|
-
const options = {
|
|
12195
|
-
client: this.client,
|
|
12196
|
-
model: this.model ?? "openai:gpt-5-nano",
|
|
12197
|
-
systemPrompt: this.systemPrompt,
|
|
12198
|
-
// No userPrompt - agent.run() will throw if called directly
|
|
12199
|
-
registry,
|
|
12200
|
-
maxIterations: this.maxIterations,
|
|
12201
|
-
budget: this.budget,
|
|
12202
|
-
temperature: this.temperature,
|
|
12203
|
-
logger: this.logger,
|
|
12204
|
-
hooks: this.composeHooks(),
|
|
12205
|
-
promptConfig: this.promptConfig,
|
|
12206
|
-
initialMessages: this.initialMessages,
|
|
12207
|
-
requestHumanInput: this.requestHumanInput,
|
|
12208
|
-
gadgetStartPrefix: this.gadgetStartPrefix,
|
|
12209
|
-
gadgetEndPrefix: this.gadgetEndPrefix,
|
|
12210
|
-
gadgetArgPrefix: this.gadgetArgPrefix,
|
|
12211
|
-
textOnlyHandler: this.textOnlyHandler,
|
|
12212
|
-
textWithGadgetsHandler: this.textWithGadgetsHandler,
|
|
12213
|
-
defaultGadgetTimeoutMs: this.defaultGadgetTimeoutMs,
|
|
12214
|
-
gadgetExecutionMode: this.gadgetExecutionMode,
|
|
12215
|
-
maxGadgetsPerResponse: this.maxGadgetsPerResponse,
|
|
12216
|
-
gadgetOutputLimit: this.gadgetOutputLimit,
|
|
12217
|
-
gadgetOutputLimitPercent: this.gadgetOutputLimitPercent,
|
|
12218
|
-
compactionConfig: this.compactionConfig,
|
|
12219
|
-
retryConfig: this.retryConfig,
|
|
12220
|
-
rateLimitConfig: this.rateLimitConfig,
|
|
12221
|
-
signal: this.signal,
|
|
12222
|
-
reasoning: this.reasoningConfig,
|
|
12223
|
-
caching: this.cachingConfig,
|
|
12224
|
-
subagentConfig: this.subagentConfig,
|
|
12225
|
-
// Tree context for shared tree model (subagents share parent's tree)
|
|
12226
|
-
parentTree: this.parentContext?.tree,
|
|
12227
|
-
parentNodeId: this.parentContext?.nodeId,
|
|
12228
|
-
baseDepth: this.parentContext ? (this.parentContext.depth ?? 0) + 1 : 0,
|
|
12229
|
-
// Parent observer hooks for subagent visibility
|
|
12230
|
-
parentObservers: this.parentObservers,
|
|
12231
|
-
// Shared rate limit tracker and retry config (for coordinated limits across subagents)
|
|
12232
|
-
sharedRateLimitTracker: this.sharedRateLimitTracker,
|
|
12233
|
-
sharedRetryConfig: this.sharedRetryConfig
|
|
12234
|
-
};
|
|
12223
|
+
const options = this.buildAgentOptions();
|
|
12235
12224
|
return new Agent(AGENT_INTERNAL_KEY, options);
|
|
12236
12225
|
}
|
|
12237
12226
|
};
|
|
@@ -13519,139 +13508,551 @@ var init_executor = __esm({
|
|
|
13519
13508
|
}
|
|
13520
13509
|
});
|
|
13521
13510
|
|
|
13522
|
-
// src/agent/
|
|
13523
|
-
|
|
13524
|
-
|
|
13525
|
-
|
|
13526
|
-
|
|
13527
|
-
|
|
13528
|
-
|
|
13529
|
-
|
|
13530
|
-
|
|
13531
|
-
|
|
13532
|
-
|
|
13533
|
-
|
|
13534
|
-
|
|
13535
|
-
|
|
13536
|
-
|
|
13537
|
-
|
|
13538
|
-
|
|
13539
|
-
|
|
13540
|
-
|
|
13541
|
-
|
|
13542
|
-
|
|
13543
|
-
|
|
13544
|
-
|
|
13545
|
-
|
|
13546
|
-
|
|
13547
|
-
|
|
13548
|
-
|
|
13549
|
-
|
|
13550
|
-
|
|
13551
|
-
|
|
13552
|
-
|
|
13553
|
-
|
|
13554
|
-
|
|
13555
|
-
|
|
13556
|
-
|
|
13557
|
-
|
|
13558
|
-
|
|
13559
|
-
|
|
13560
|
-
|
|
13561
|
-
|
|
13562
|
-
|
|
13563
|
-
|
|
13564
|
-
|
|
13565
|
-
|
|
13566
|
-
|
|
13567
|
-
|
|
13568
|
-
|
|
13569
|
-
|
|
13570
|
-
|
|
13571
|
-
|
|
13572
|
-
|
|
13573
|
-
|
|
13574
|
-
|
|
13575
|
-
|
|
13576
|
-
|
|
13577
|
-
|
|
13578
|
-
|
|
13579
|
-
|
|
13580
|
-
|
|
13581
|
-
|
|
13582
|
-
if (cleanup) {
|
|
13583
|
-
newPromise.finally(() => chainMap.delete(key));
|
|
13584
|
-
}
|
|
13585
|
-
}
|
|
13586
|
-
function bridgeTreeToHooks(tree, hooks, logger2) {
|
|
13587
|
-
const gadgetPromiseChains = /* @__PURE__ */ new Map();
|
|
13588
|
-
const llmPromiseChains = /* @__PURE__ */ new Map();
|
|
13589
|
-
return tree.onAll((event) => {
|
|
13590
|
-
const subagentContext = buildSubagentContext(tree, event);
|
|
13591
|
-
switch (event.type) {
|
|
13592
|
-
// =================================================================
|
|
13593
|
-
// GADGET EVENTS - Bridged for subagent visibility
|
|
13594
|
-
// =================================================================
|
|
13595
|
-
// When a subagent executes gadgets, these events propagate through
|
|
13596
|
-
// the shared tree to the parent's hooks.
|
|
13597
|
-
// Only bridged for subagent events (depth > 0) to avoid double-calling
|
|
13598
|
-
// root agent events which are handled directly in stream-processor.ts
|
|
13599
|
-
case "gadget_start": {
|
|
13600
|
-
if (subagentContext && hooks.observers?.onGadgetExecutionStart) {
|
|
13601
|
-
const gadgetEvent = event;
|
|
13602
|
-
const gadgetNode = tree.getNode(event.nodeId);
|
|
13603
|
-
const context = {
|
|
13604
|
-
iteration: getIterationFromTree(tree, event.nodeId),
|
|
13605
|
-
gadgetName: gadgetEvent.name,
|
|
13606
|
-
invocationId: gadgetEvent.invocationId,
|
|
13607
|
-
parameters: gadgetNode?.parameters ?? {},
|
|
13608
|
-
logger: logger2,
|
|
13609
|
-
subagentContext
|
|
13610
|
-
};
|
|
13611
|
-
chainObserverCall(
|
|
13612
|
-
gadgetPromiseChains,
|
|
13613
|
-
gadgetEvent.invocationId,
|
|
13614
|
-
() => hooks.observers?.onGadgetExecutionStart?.(context),
|
|
13615
|
-
logger2,
|
|
13616
|
-
"onGadgetExecutionStart",
|
|
13617
|
-
false
|
|
13618
|
-
// Don't cleanup - wait for completion event
|
|
13619
|
-
);
|
|
13511
|
+
// src/agent/gadget-concurrency-manager.ts
|
|
13512
|
+
var GadgetConcurrencyManager;
|
|
13513
|
+
var init_gadget_concurrency_manager = __esm({
|
|
13514
|
+
"src/agent/gadget-concurrency-manager.ts"() {
|
|
13515
|
+
"use strict";
|
|
13516
|
+
init_logger();
|
|
13517
|
+
GadgetConcurrencyManager = class {
|
|
13518
|
+
registry;
|
|
13519
|
+
subagentConfig;
|
|
13520
|
+
logger;
|
|
13521
|
+
/** Track active execution count per gadget name */
|
|
13522
|
+
activeCountByGadget = /* @__PURE__ */ new Map();
|
|
13523
|
+
/** Queue of gadgets waiting for a concurrency slot (per gadget name) */
|
|
13524
|
+
concurrencyQueue = /* @__PURE__ */ new Map();
|
|
13525
|
+
/** All active gadget promises, keyed by invocationId */
|
|
13526
|
+
inFlightExecutions = /* @__PURE__ */ new Map();
|
|
13527
|
+
/** Queue of exclusive gadgets deferred until in-flight gadgets complete */
|
|
13528
|
+
exclusiveQueue = [];
|
|
13529
|
+
constructor(options) {
|
|
13530
|
+
this.registry = options.registry;
|
|
13531
|
+
this.subagentConfig = options.subagentConfig;
|
|
13532
|
+
this.logger = options.logger ?? createLogger({ name: "llmist:gadget-concurrency-manager" });
|
|
13533
|
+
}
|
|
13534
|
+
// ==========================================================================
|
|
13535
|
+
// Concurrency limit resolution
|
|
13536
|
+
// ==========================================================================
|
|
13537
|
+
/**
|
|
13538
|
+
* Get the effective concurrency limit for a gadget.
|
|
13539
|
+
* Uses "most restrictive wins" strategy: the lowest non-zero value from
|
|
13540
|
+
* external config (SubagentConfig) and gadget's intrinsic maxConcurrent.
|
|
13541
|
+
*
|
|
13542
|
+
* This ensures gadget authors can set safety floors (e.g., maxConcurrent: 1
|
|
13543
|
+
* for file writers) that cannot be weakened by external configuration.
|
|
13544
|
+
*
|
|
13545
|
+
* @returns 0 if unlimited, otherwise the effective limit
|
|
13546
|
+
*/
|
|
13547
|
+
getConcurrencyLimit(gadgetName) {
|
|
13548
|
+
const configLimit = this.subagentConfig?.[gadgetName]?.maxConcurrent;
|
|
13549
|
+
const gadget = this.registry.get(gadgetName);
|
|
13550
|
+
const gadgetLimit = gadget?.maxConcurrent;
|
|
13551
|
+
const config = configLimit || Number.POSITIVE_INFINITY;
|
|
13552
|
+
const intrinsic = gadgetLimit || Number.POSITIVE_INFINITY;
|
|
13553
|
+
const effective = Math.min(config, intrinsic);
|
|
13554
|
+
return effective === Number.POSITIVE_INFINITY ? 0 : effective;
|
|
13555
|
+
}
|
|
13556
|
+
// ==========================================================================
|
|
13557
|
+
// Concurrency checks
|
|
13558
|
+
// ==========================================================================
|
|
13559
|
+
/**
|
|
13560
|
+
* Check whether a gadget call can start immediately given current concurrency state.
|
|
13561
|
+
* Returns false if:
|
|
13562
|
+
* - The gadget is exclusive and other gadgets are in-flight
|
|
13563
|
+
* - The gadget has a concurrency limit and it is already reached
|
|
13564
|
+
*
|
|
13565
|
+
* Does NOT modify any state.
|
|
13566
|
+
*/
|
|
13567
|
+
canStart(call) {
|
|
13568
|
+
const gadget = this.registry.get(call.gadgetName);
|
|
13569
|
+
if (gadget?.exclusive && this.inFlightExecutions.size > 0) {
|
|
13570
|
+
return false;
|
|
13620
13571
|
}
|
|
13621
|
-
|
|
13572
|
+
const limit = this.getConcurrencyLimit(call.gadgetName);
|
|
13573
|
+
if (limit > 0) {
|
|
13574
|
+
const activeCount = this.activeCountByGadget.get(call.gadgetName) ?? 0;
|
|
13575
|
+
if (activeCount >= limit) {
|
|
13576
|
+
return false;
|
|
13577
|
+
}
|
|
13578
|
+
}
|
|
13579
|
+
return true;
|
|
13622
13580
|
}
|
|
13623
|
-
|
|
13624
|
-
|
|
13625
|
-
|
|
13626
|
-
|
|
13627
|
-
|
|
13628
|
-
|
|
13629
|
-
|
|
13630
|
-
|
|
13631
|
-
|
|
13632
|
-
|
|
13633
|
-
|
|
13634
|
-
|
|
13635
|
-
|
|
13636
|
-
|
|
13637
|
-
|
|
13638
|
-
|
|
13639
|
-
|
|
13640
|
-
|
|
13641
|
-
|
|
13642
|
-
|
|
13643
|
-
|
|
13644
|
-
true
|
|
13645
|
-
// Cleanup after completion
|
|
13646
|
-
);
|
|
13581
|
+
/**
|
|
13582
|
+
* Check whether a gadget is marked as exclusive.
|
|
13583
|
+
*/
|
|
13584
|
+
isExclusive(gadgetName) {
|
|
13585
|
+
const gadget = this.registry.get(gadgetName);
|
|
13586
|
+
return gadget?.exclusive === true;
|
|
13587
|
+
}
|
|
13588
|
+
/**
|
|
13589
|
+
* Get the current count of in-flight (actively executing) gadgets.
|
|
13590
|
+
*/
|
|
13591
|
+
get inFlightCount() {
|
|
13592
|
+
return this.inFlightExecutions.size;
|
|
13593
|
+
}
|
|
13594
|
+
/**
|
|
13595
|
+
* Get the total count of actively executing gadgets across all gadget types.
|
|
13596
|
+
* Used to know when all work is truly complete.
|
|
13597
|
+
*/
|
|
13598
|
+
getTotalActiveGadgetCount() {
|
|
13599
|
+
let total = 0;
|
|
13600
|
+
for (const count of this.activeCountByGadget.values()) {
|
|
13601
|
+
total += count;
|
|
13647
13602
|
}
|
|
13648
|
-
|
|
13603
|
+
return total;
|
|
13649
13604
|
}
|
|
13650
|
-
|
|
13651
|
-
|
|
13652
|
-
|
|
13653
|
-
|
|
13654
|
-
|
|
13605
|
+
/**
|
|
13606
|
+
* Check if there are any gadgets waiting in concurrency queues.
|
|
13607
|
+
*/
|
|
13608
|
+
hasQueuedGadgets() {
|
|
13609
|
+
for (const queue of this.concurrencyQueue.values()) {
|
|
13610
|
+
if (queue.length > 0) return true;
|
|
13611
|
+
}
|
|
13612
|
+
return false;
|
|
13613
|
+
}
|
|
13614
|
+
/**
|
|
13615
|
+
* Get total count of queued gadgets across all queues.
|
|
13616
|
+
*/
|
|
13617
|
+
getQueuedGadgetCount() {
|
|
13618
|
+
let count = 0;
|
|
13619
|
+
for (const queue of this.concurrencyQueue.values()) {
|
|
13620
|
+
count += queue.length;
|
|
13621
|
+
}
|
|
13622
|
+
return count;
|
|
13623
|
+
}
|
|
13624
|
+
/**
|
|
13625
|
+
* Check if there are exclusive gadgets waiting.
|
|
13626
|
+
*/
|
|
13627
|
+
get hasExclusiveQueued() {
|
|
13628
|
+
return this.exclusiveQueue.length > 0;
|
|
13629
|
+
}
|
|
13630
|
+
/**
|
|
13631
|
+
* Drain and return all exclusive gadgets from the queue.
|
|
13632
|
+
* The caller is responsible for executing them.
|
|
13633
|
+
*/
|
|
13634
|
+
drainExclusiveQueue() {
|
|
13635
|
+
const queue = this.exclusiveQueue;
|
|
13636
|
+
this.exclusiveQueue = [];
|
|
13637
|
+
return queue;
|
|
13638
|
+
}
|
|
13639
|
+
// ==========================================================================
|
|
13640
|
+
// State mutation
|
|
13641
|
+
// ==========================================================================
|
|
13642
|
+
/**
|
|
13643
|
+
* Track an in-flight gadget execution.
|
|
13644
|
+
* Increments the active count for the gadget name and registers the promise.
|
|
13645
|
+
*
|
|
13646
|
+
* @param invocationId - Unique ID for this execution
|
|
13647
|
+
* @param gadgetName - Name of the gadget being executed
|
|
13648
|
+
* @param promise - The execution promise to track
|
|
13649
|
+
*/
|
|
13650
|
+
trackExecution(invocationId, gadgetName, promise) {
|
|
13651
|
+
const currentCount = this.activeCountByGadget.get(gadgetName) ?? 0;
|
|
13652
|
+
this.activeCountByGadget.set(gadgetName, currentCount + 1);
|
|
13653
|
+
this.inFlightExecutions.set(invocationId, promise);
|
|
13654
|
+
}
|
|
13655
|
+
/**
|
|
13656
|
+
* Called when a gadget execution completes.
|
|
13657
|
+
* Decrements active count and triggers queue processing if a slot opened up.
|
|
13658
|
+
*
|
|
13659
|
+
* @param gadgetName - Name of the gadget that completed
|
|
13660
|
+
* @returns The next queued gadget call for this gadget, if one was promoted, otherwise null
|
|
13661
|
+
*/
|
|
13662
|
+
onComplete(gadgetName) {
|
|
13663
|
+
const newCount = (this.activeCountByGadget.get(gadgetName) ?? 1) - 1;
|
|
13664
|
+
this.activeCountByGadget.set(gadgetName, newCount);
|
|
13665
|
+
return this.promoteFromQueue(gadgetName);
|
|
13666
|
+
}
|
|
13667
|
+
/**
|
|
13668
|
+
* Queue a gadget for later execution due to a concurrency limit being reached.
|
|
13669
|
+
*
|
|
13670
|
+
* @param call - The gadget call to defer
|
|
13671
|
+
*/
|
|
13672
|
+
queueForLater(call) {
|
|
13673
|
+
this.logger.debug("Gadget queued due to concurrency limit", {
|
|
13674
|
+
gadgetName: call.gadgetName,
|
|
13675
|
+
invocationId: call.invocationId,
|
|
13676
|
+
activeCount: this.activeCountByGadget.get(call.gadgetName) ?? 0,
|
|
13677
|
+
limit: this.getConcurrencyLimit(call.gadgetName)
|
|
13678
|
+
});
|
|
13679
|
+
const queue = this.concurrencyQueue.get(call.gadgetName) ?? [];
|
|
13680
|
+
queue.push(call);
|
|
13681
|
+
this.concurrencyQueue.set(call.gadgetName, queue);
|
|
13682
|
+
}
|
|
13683
|
+
/**
|
|
13684
|
+
* Queue a gadget for exclusive execution (after all in-flight complete).
|
|
13685
|
+
*
|
|
13686
|
+
* @param call - The exclusive gadget call to defer
|
|
13687
|
+
*/
|
|
13688
|
+
queueExclusive(call) {
|
|
13689
|
+
this.logger.debug("Deferring exclusive gadget until in-flight gadgets complete", {
|
|
13690
|
+
gadgetName: call.gadgetName,
|
|
13691
|
+
invocationId: call.invocationId,
|
|
13692
|
+
inFlightCount: this.inFlightExecutions.size
|
|
13693
|
+
});
|
|
13694
|
+
this.exclusiveQueue.push(call);
|
|
13695
|
+
}
|
|
13696
|
+
/**
|
|
13697
|
+
* Clear the inFlightExecutions map after all promises have completed.
|
|
13698
|
+
* Called after waitForAll resolves.
|
|
13699
|
+
*/
|
|
13700
|
+
clearInFlight() {
|
|
13701
|
+
this.inFlightExecutions.clear();
|
|
13702
|
+
}
|
|
13703
|
+
// ==========================================================================
|
|
13704
|
+
// Waiting
|
|
13705
|
+
// ==========================================================================
|
|
13706
|
+
/**
|
|
13707
|
+
* Wait for all currently in-flight gadget executions to complete.
|
|
13708
|
+
* Resolves when the Promise.all of all tracked promises resolves.
|
|
13709
|
+
*
|
|
13710
|
+
* Note: new executions may be started during waiting (from the queue).
|
|
13711
|
+
* Callers should loop until inFlightCount === 0 AND hasQueuedGadgets() === false.
|
|
13712
|
+
*/
|
|
13713
|
+
async waitForAll() {
|
|
13714
|
+
if (this.inFlightExecutions.size === 0) return;
|
|
13715
|
+
await Promise.all(this.inFlightExecutions.values());
|
|
13716
|
+
}
|
|
13717
|
+
/**
|
|
13718
|
+
* Get a promise that resolves when all current in-flight executions complete.
|
|
13719
|
+
* Returns a resolved promise if no executions are in-flight.
|
|
13720
|
+
*/
|
|
13721
|
+
getAllDonePromise() {
|
|
13722
|
+
if (this.inFlightExecutions.size === 0) {
|
|
13723
|
+
return Promise.resolve("done");
|
|
13724
|
+
}
|
|
13725
|
+
return Promise.all(this.inFlightExecutions.values()).then(() => "done");
|
|
13726
|
+
}
|
|
13727
|
+
// ==========================================================================
|
|
13728
|
+
// Private helpers
|
|
13729
|
+
// ==========================================================================
|
|
13730
|
+
/**
|
|
13731
|
+
* Check the concurrency queue for a gadget name and promote the next queued
|
|
13732
|
+
* call if a slot is available.
|
|
13733
|
+
*
|
|
13734
|
+
* @param gadgetName - The gadget name to check
|
|
13735
|
+
* @returns The promoted call, or null if queue is empty or limit still reached
|
|
13736
|
+
*/
|
|
13737
|
+
promoteFromQueue(gadgetName) {
|
|
13738
|
+
const queue = this.concurrencyQueue.get(gadgetName);
|
|
13739
|
+
if (!queue || queue.length === 0) return null;
|
|
13740
|
+
const limit = this.getConcurrencyLimit(gadgetName);
|
|
13741
|
+
const activeCount = this.activeCountByGadget.get(gadgetName) ?? 0;
|
|
13742
|
+
if (limit === 0 || activeCount < limit) {
|
|
13743
|
+
const nextCall = queue.shift();
|
|
13744
|
+
this.logger.debug("Processing queued gadget", {
|
|
13745
|
+
gadgetName,
|
|
13746
|
+
invocationId: nextCall.invocationId,
|
|
13747
|
+
remainingInQueue: queue.length
|
|
13748
|
+
});
|
|
13749
|
+
return nextCall;
|
|
13750
|
+
}
|
|
13751
|
+
return null;
|
|
13752
|
+
}
|
|
13753
|
+
};
|
|
13754
|
+
}
|
|
13755
|
+
});
|
|
13756
|
+
|
|
13757
|
+
// src/agent/gadget-dependency-resolver.ts
|
|
13758
|
+
var GadgetDependencyResolver;
|
|
13759
|
+
var init_gadget_dependency_resolver = __esm({
|
|
13760
|
+
"src/agent/gadget-dependency-resolver.ts"() {
|
|
13761
|
+
"use strict";
|
|
13762
|
+
GadgetDependencyResolver = class {
|
|
13763
|
+
/** Gadgets waiting for their dependencies to complete */
|
|
13764
|
+
gadgetsAwaitingDependencies = /* @__PURE__ */ new Map();
|
|
13765
|
+
/** Completed gadget results, keyed by invocation ID */
|
|
13766
|
+
completedResults = /* @__PURE__ */ new Map();
|
|
13767
|
+
/** Invocation IDs of gadgets that have failed (error or skipped due to dependency) */
|
|
13768
|
+
failedInvocations = /* @__PURE__ */ new Set();
|
|
13769
|
+
/** Invocation IDs completed in previous iterations (read-only) */
|
|
13770
|
+
priorCompletedInvocations;
|
|
13771
|
+
/** Invocation IDs that failed in previous iterations (read-only) */
|
|
13772
|
+
priorFailedInvocations;
|
|
13773
|
+
constructor(options = {}) {
|
|
13774
|
+
this.priorCompletedInvocations = options.priorCompletedInvocations ?? /* @__PURE__ */ new Set();
|
|
13775
|
+
this.priorFailedInvocations = options.priorFailedInvocations ?? /* @__PURE__ */ new Set();
|
|
13776
|
+
}
|
|
13777
|
+
// ==========================================================================
|
|
13778
|
+
// State mutation
|
|
13779
|
+
// ==========================================================================
|
|
13780
|
+
/**
|
|
13781
|
+
* Queue a gadget call that is waiting for one or more dependencies to complete.
|
|
13782
|
+
* Call this when a gadget's dependencies are not yet all satisfied.
|
|
13783
|
+
*
|
|
13784
|
+
* @param call - The parsed gadget call to defer
|
|
13785
|
+
*/
|
|
13786
|
+
addPending(call) {
|
|
13787
|
+
this.gadgetsAwaitingDependencies.set(call.invocationId, call);
|
|
13788
|
+
}
|
|
13789
|
+
/**
|
|
13790
|
+
* Record that a gadget completed successfully.
|
|
13791
|
+
* This may unblock other gadgets that depend on this invocation.
|
|
13792
|
+
*
|
|
13793
|
+
* Also marks as failed if the result contains an error.
|
|
13794
|
+
*
|
|
13795
|
+
* @param result - The completed gadget execution result
|
|
13796
|
+
*/
|
|
13797
|
+
markComplete(result) {
|
|
13798
|
+
this.completedResults.set(result.invocationId, result);
|
|
13799
|
+
if (result.error) {
|
|
13800
|
+
this.failedInvocations.add(result.invocationId);
|
|
13801
|
+
}
|
|
13802
|
+
}
|
|
13803
|
+
/**
|
|
13804
|
+
* Mark an invocation ID as failed without recording a full result.
|
|
13805
|
+
* Use this for gadgets that are skipped before execution (e.g., limit exceeded,
|
|
13806
|
+
* self-referential dependency, dependency skip).
|
|
13807
|
+
*
|
|
13808
|
+
* @param invocationId - The invocation ID to mark as failed
|
|
13809
|
+
*/
|
|
13810
|
+
markFailed(invocationId) {
|
|
13811
|
+
this.failedInvocations.add(invocationId);
|
|
13812
|
+
}
|
|
13813
|
+
/**
|
|
13814
|
+
* Remove a gadget from the pending queue (called just before execution).
|
|
13815
|
+
*
|
|
13816
|
+
* @param invocationId - The invocation ID to remove from pending
|
|
13817
|
+
*/
|
|
13818
|
+
removePending(invocationId) {
|
|
13819
|
+
this.gadgetsAwaitingDependencies.delete(invocationId);
|
|
13820
|
+
}
|
|
13821
|
+
/**
|
|
13822
|
+
* Clear all remaining pending gadgets (e.g., after handling unresolvable deps).
|
|
13823
|
+
*/
|
|
13824
|
+
clearPending() {
|
|
13825
|
+
this.gadgetsAwaitingDependencies.clear();
|
|
13826
|
+
}
|
|
13827
|
+
// ==========================================================================
|
|
13828
|
+
// Queries
|
|
13829
|
+
// ==========================================================================
|
|
13830
|
+
/**
|
|
13831
|
+
* Get all gadget calls currently waiting for dependencies.
|
|
13832
|
+
* Returns entries as [invocationId, call] pairs.
|
|
13833
|
+
*/
|
|
13834
|
+
getPendingEntries() {
|
|
13835
|
+
return Array.from(this.gadgetsAwaitingDependencies.entries());
|
|
13836
|
+
}
|
|
13837
|
+
/**
|
|
13838
|
+
* Get the number of gadgets currently waiting for dependencies.
|
|
13839
|
+
*/
|
|
13840
|
+
get pendingCount() {
|
|
13841
|
+
return this.gadgetsAwaitingDependencies.size;
|
|
13842
|
+
}
|
|
13843
|
+
/**
|
|
13844
|
+
* Check whether a given invocation ID has been completed successfully
|
|
13845
|
+
* (either in this iteration or a prior one).
|
|
13846
|
+
*/
|
|
13847
|
+
isCompleted(invocationId) {
|
|
13848
|
+
return this.completedResults.has(invocationId) || this.priorCompletedInvocations.has(invocationId);
|
|
13849
|
+
}
|
|
13850
|
+
/**
|
|
13851
|
+
* Check whether a given invocation ID has failed
|
|
13852
|
+
* (either in this iteration or a prior one).
|
|
13853
|
+
*/
|
|
13854
|
+
isFailed(invocationId) {
|
|
13855
|
+
return this.failedInvocations.has(invocationId) || this.priorFailedInvocations.has(invocationId);
|
|
13856
|
+
}
|
|
13857
|
+
/**
|
|
13858
|
+
* Get the execution result for a completed invocation, if available.
|
|
13859
|
+
* Only returns results from the current iteration; prior iterations
|
|
13860
|
+
* are tracked by ID only.
|
|
13861
|
+
*/
|
|
13862
|
+
getCompletedResult(invocationId) {
|
|
13863
|
+
return this.completedResults.get(invocationId);
|
|
13864
|
+
}
|
|
13865
|
+
/**
|
|
13866
|
+
* Check if all dependencies for a gadget call are satisfied.
|
|
13867
|
+
* A dependency is satisfied if it completed in this or a prior iteration.
|
|
13868
|
+
*
|
|
13869
|
+
* @param call - The gadget call whose dependencies to check
|
|
13870
|
+
* @returns true if all deps are satisfied, false if any are still pending
|
|
13871
|
+
*/
|
|
13872
|
+
isAllSatisfied(call) {
|
|
13873
|
+
return call.dependencies.every((dep) => this.isCompleted(dep));
|
|
13874
|
+
}
|
|
13875
|
+
/**
|
|
13876
|
+
* Find the first failed dependency for a gadget call, if any.
|
|
13877
|
+
* A dependency is considered failed if it failed in this or a prior iteration.
|
|
13878
|
+
*
|
|
13879
|
+
* @param call - The gadget call to check
|
|
13880
|
+
* @returns The invocation ID of the failed dependency, or undefined if none
|
|
13881
|
+
*/
|
|
13882
|
+
getFailedDependency(call) {
|
|
13883
|
+
return call.dependencies.find((dep) => this.isFailed(dep));
|
|
13884
|
+
}
|
|
13885
|
+
/**
|
|
13886
|
+
* Separate the pending gadgets into two groups:
|
|
13887
|
+
* - Those ready to execute (all deps satisfied)
|
|
13888
|
+
* - Those ready to skip (at least one dep has failed)
|
|
13889
|
+
*
|
|
13890
|
+
* Gadgets that are neither ready nor skippable remain pending.
|
|
13891
|
+
*
|
|
13892
|
+
* @returns Object with `readyToExecute` and `readyToSkip` arrays
|
|
13893
|
+
*/
|
|
13894
|
+
getReadyCalls() {
|
|
13895
|
+
const readyToExecute = [];
|
|
13896
|
+
const readyToSkip = [];
|
|
13897
|
+
for (const [_invocationId, call] of this.gadgetsAwaitingDependencies) {
|
|
13898
|
+
const failedDep = this.getFailedDependency(call);
|
|
13899
|
+
if (failedDep) {
|
|
13900
|
+
readyToSkip.push({ call, failedDep });
|
|
13901
|
+
continue;
|
|
13902
|
+
}
|
|
13903
|
+
if (this.isAllSatisfied(call)) {
|
|
13904
|
+
readyToExecute.push(call);
|
|
13905
|
+
}
|
|
13906
|
+
}
|
|
13907
|
+
return { readyToExecute, readyToSkip };
|
|
13908
|
+
}
|
|
13909
|
+
// ==========================================================================
|
|
13910
|
+
// Cross-iteration accessors (for Agent to accumulate state)
|
|
13911
|
+
// ==========================================================================
|
|
13912
|
+
/**
|
|
13913
|
+
* Get all invocation IDs that completed successfully in this iteration.
|
|
13914
|
+
* Used by Agent to accumulate completed IDs across iterations.
|
|
13915
|
+
*/
|
|
13916
|
+
getCompletedInvocationIds() {
|
|
13917
|
+
return new Set(this.completedResults.keys());
|
|
13918
|
+
}
|
|
13919
|
+
/**
|
|
13920
|
+
* Get all invocation IDs that failed in this iteration.
|
|
13921
|
+
* Used by Agent to accumulate failed IDs across iterations.
|
|
13922
|
+
*/
|
|
13923
|
+
getFailedInvocationIds() {
|
|
13924
|
+
return new Set(this.failedInvocations);
|
|
13925
|
+
}
|
|
13926
|
+
};
|
|
13927
|
+
}
|
|
13928
|
+
});
|
|
13929
|
+
|
|
13930
|
+
// src/agent/tree-hook-bridge.ts
|
|
13931
|
+
function findParentGadgetInvocationId(tree, nodeId) {
|
|
13932
|
+
let currentId = nodeId;
|
|
13933
|
+
while (currentId) {
|
|
13934
|
+
const node = tree.getNode(currentId);
|
|
13935
|
+
if (!node) break;
|
|
13936
|
+
currentId = node.parentId;
|
|
13937
|
+
if (!currentId) break;
|
|
13938
|
+
const parentNode = tree.getNode(currentId);
|
|
13939
|
+
if (parentNode?.type === "gadget") {
|
|
13940
|
+
return parentNode.invocationId;
|
|
13941
|
+
}
|
|
13942
|
+
}
|
|
13943
|
+
return void 0;
|
|
13944
|
+
}
|
|
13945
|
+
function getIterationFromTree(tree, nodeId) {
|
|
13946
|
+
let currentId = nodeId;
|
|
13947
|
+
while (currentId) {
|
|
13948
|
+
const node = tree.getNode(currentId);
|
|
13949
|
+
if (!node) break;
|
|
13950
|
+
if (node.type === "llm_call") {
|
|
13951
|
+
return node.iteration;
|
|
13952
|
+
}
|
|
13953
|
+
currentId = node.parentId;
|
|
13954
|
+
}
|
|
13955
|
+
return 0;
|
|
13956
|
+
}
|
|
13957
|
+
function buildSubagentContext(tree, event) {
|
|
13958
|
+
const parentGadgetInvocationId = findParentGadgetInvocationId(tree, event.nodeId);
|
|
13959
|
+
if (!parentGadgetInvocationId) {
|
|
13960
|
+
return void 0;
|
|
13961
|
+
}
|
|
13962
|
+
return {
|
|
13963
|
+
parentGadgetInvocationId,
|
|
13964
|
+
depth: event.depth
|
|
13965
|
+
};
|
|
13966
|
+
}
|
|
13967
|
+
function getSubagentContextForNode(tree, nodeId) {
|
|
13968
|
+
const node = tree.getNode(nodeId);
|
|
13969
|
+
if (!node) return void 0;
|
|
13970
|
+
const parentGadgetInvocationId = findParentGadgetInvocationId(tree, nodeId);
|
|
13971
|
+
if (!parentGadgetInvocationId) {
|
|
13972
|
+
return void 0;
|
|
13973
|
+
}
|
|
13974
|
+
return {
|
|
13975
|
+
parentGadgetInvocationId,
|
|
13976
|
+
depth: node.depth
|
|
13977
|
+
};
|
|
13978
|
+
}
|
|
13979
|
+
function chainObserverCall(chainMap, key, fn, logger2, eventType, cleanup = false) {
|
|
13980
|
+
const previousPromise = chainMap.get(key) ?? Promise.resolve();
|
|
13981
|
+
const newPromise = previousPromise.then(() => safeObserve(fn, logger2, eventType));
|
|
13982
|
+
chainMap.set(key, newPromise);
|
|
13983
|
+
if (cleanup) {
|
|
13984
|
+
newPromise.finally(() => chainMap.delete(key));
|
|
13985
|
+
}
|
|
13986
|
+
}
|
|
13987
|
+
function bridgeTreeToHooks(tree, hooks, logger2) {
|
|
13988
|
+
const gadgetPromiseChains = /* @__PURE__ */ new Map();
|
|
13989
|
+
const llmPromiseChains = /* @__PURE__ */ new Map();
|
|
13990
|
+
return tree.onAll((event) => {
|
|
13991
|
+
const subagentContext = buildSubagentContext(tree, event);
|
|
13992
|
+
switch (event.type) {
|
|
13993
|
+
// =================================================================
|
|
13994
|
+
// GADGET EVENTS - Bridged for subagent visibility
|
|
13995
|
+
// =================================================================
|
|
13996
|
+
// When a subagent executes gadgets, these events propagate through
|
|
13997
|
+
// the shared tree to the parent's hooks.
|
|
13998
|
+
// Only bridged for subagent events (depth > 0) to avoid double-calling
|
|
13999
|
+
// root agent events which are handled directly in stream-processor.ts
|
|
14000
|
+
case "gadget_start": {
|
|
14001
|
+
if (subagentContext && hooks.observers?.onGadgetExecutionStart) {
|
|
14002
|
+
const gadgetEvent = event;
|
|
14003
|
+
const gadgetNode = tree.getNode(event.nodeId);
|
|
14004
|
+
const context = {
|
|
14005
|
+
iteration: getIterationFromTree(tree, event.nodeId),
|
|
14006
|
+
gadgetName: gadgetEvent.name,
|
|
14007
|
+
invocationId: gadgetEvent.invocationId,
|
|
14008
|
+
parameters: gadgetNode?.parameters ?? {},
|
|
14009
|
+
logger: logger2,
|
|
14010
|
+
subagentContext
|
|
14011
|
+
};
|
|
14012
|
+
chainObserverCall(
|
|
14013
|
+
gadgetPromiseChains,
|
|
14014
|
+
gadgetEvent.invocationId,
|
|
14015
|
+
() => hooks.observers?.onGadgetExecutionStart?.(context),
|
|
14016
|
+
logger2,
|
|
14017
|
+
"onGadgetExecutionStart",
|
|
14018
|
+
false
|
|
14019
|
+
// Don't cleanup - wait for completion event
|
|
14020
|
+
);
|
|
14021
|
+
}
|
|
14022
|
+
break;
|
|
14023
|
+
}
|
|
14024
|
+
case "gadget_complete": {
|
|
14025
|
+
if (subagentContext && hooks.observers?.onGadgetExecutionComplete) {
|
|
14026
|
+
const gadgetEvent = event;
|
|
14027
|
+
const gadgetNode = tree.getNode(event.nodeId);
|
|
14028
|
+
const context = {
|
|
14029
|
+
iteration: getIterationFromTree(tree, event.nodeId),
|
|
14030
|
+
gadgetName: gadgetEvent.name,
|
|
14031
|
+
invocationId: gadgetEvent.invocationId,
|
|
14032
|
+
parameters: gadgetNode?.parameters ?? {},
|
|
14033
|
+
finalResult: gadgetEvent.result,
|
|
14034
|
+
executionTimeMs: gadgetEvent.executionTimeMs,
|
|
14035
|
+
cost: gadgetEvent.cost,
|
|
14036
|
+
logger: logger2,
|
|
14037
|
+
subagentContext
|
|
14038
|
+
};
|
|
14039
|
+
chainObserverCall(
|
|
14040
|
+
gadgetPromiseChains,
|
|
14041
|
+
gadgetEvent.invocationId,
|
|
14042
|
+
() => hooks.observers?.onGadgetExecutionComplete?.(context),
|
|
14043
|
+
logger2,
|
|
14044
|
+
"onGadgetExecutionComplete",
|
|
14045
|
+
true
|
|
14046
|
+
// Cleanup after completion
|
|
14047
|
+
);
|
|
14048
|
+
}
|
|
14049
|
+
break;
|
|
14050
|
+
}
|
|
14051
|
+
case "gadget_error": {
|
|
14052
|
+
if (subagentContext && hooks.observers?.onGadgetExecutionComplete) {
|
|
14053
|
+
const gadgetEvent = event;
|
|
14054
|
+
const gadgetNode = tree.getNode(event.nodeId);
|
|
14055
|
+
const context = {
|
|
13655
14056
|
iteration: getIterationFromTree(tree, event.nodeId),
|
|
13656
14057
|
gadgetName: gadgetEvent.name,
|
|
13657
14058
|
invocationId: gadgetEvent.invocationId,
|
|
@@ -13798,6 +14199,82 @@ function bridgeTreeToHooks(tree, hooks, logger2) {
|
|
|
13798
14199
|
var init_tree_hook_bridge = __esm({
|
|
13799
14200
|
"src/agent/tree-hook-bridge.ts"() {
|
|
13800
14201
|
"use strict";
|
|
14202
|
+
init_safe_observe();
|
|
14203
|
+
}
|
|
14204
|
+
});
|
|
14205
|
+
|
|
14206
|
+
// src/agent/observer-notifier.ts
|
|
14207
|
+
async function notifyGadgetSkipped(ctx) {
|
|
14208
|
+
const gadgetNode = ctx.tree?.getNodeByInvocationId(ctx.invocationId);
|
|
14209
|
+
const subagentContext = ctx.tree && gadgetNode ? getSubagentContextForNode(ctx.tree, gadgetNode.id) : void 0;
|
|
14210
|
+
const context = {
|
|
14211
|
+
iteration: ctx.iteration,
|
|
14212
|
+
gadgetName: ctx.gadgetName,
|
|
14213
|
+
invocationId: ctx.invocationId,
|
|
14214
|
+
parameters: ctx.parameters,
|
|
14215
|
+
failedDependency: ctx.failedDependency,
|
|
14216
|
+
failedDependencyError: ctx.failedDependencyError,
|
|
14217
|
+
logger: ctx.logger,
|
|
14218
|
+
subagentContext
|
|
14219
|
+
};
|
|
14220
|
+
if (ctx.hooks?.onGadgetSkipped) {
|
|
14221
|
+
const hookFn = ctx.hooks.onGadgetSkipped;
|
|
14222
|
+
await safeObserve(() => hookFn(context), ctx.logger);
|
|
14223
|
+
}
|
|
14224
|
+
if (ctx.parentObservers?.onGadgetSkipped) {
|
|
14225
|
+
const hookFn = ctx.parentObservers.onGadgetSkipped;
|
|
14226
|
+
await safeObserve(() => hookFn(context), ctx.logger);
|
|
14227
|
+
}
|
|
14228
|
+
}
|
|
14229
|
+
async function notifyGadgetStart(ctx) {
|
|
14230
|
+
const gadgetNode = ctx.tree?.getNodeByInvocationId(ctx.invocationId);
|
|
14231
|
+
const subagentContext = ctx.tree && gadgetNode ? getSubagentContextForNode(ctx.tree, gadgetNode.id) : void 0;
|
|
14232
|
+
const context = {
|
|
14233
|
+
iteration: ctx.iteration,
|
|
14234
|
+
gadgetName: ctx.gadgetName,
|
|
14235
|
+
invocationId: ctx.invocationId,
|
|
14236
|
+
parameters: ctx.parameters,
|
|
14237
|
+
logger: ctx.logger,
|
|
14238
|
+
subagentContext
|
|
14239
|
+
};
|
|
14240
|
+
if (ctx.hooks?.onGadgetExecutionStart) {
|
|
14241
|
+
const hookFn = ctx.hooks.onGadgetExecutionStart;
|
|
14242
|
+
await safeObserve(() => hookFn(context), ctx.logger);
|
|
14243
|
+
}
|
|
14244
|
+
if (ctx.parentObservers?.onGadgetExecutionStart) {
|
|
14245
|
+
const hookFn = ctx.parentObservers.onGadgetExecutionStart;
|
|
14246
|
+
await safeObserve(() => hookFn(context), ctx.logger);
|
|
14247
|
+
}
|
|
14248
|
+
}
|
|
14249
|
+
async function notifyGadgetComplete(ctx) {
|
|
14250
|
+
const gadgetNode = ctx.tree?.getNodeByInvocationId(ctx.invocationId);
|
|
14251
|
+
const subagentContext = ctx.tree && gadgetNode ? getSubagentContextForNode(ctx.tree, gadgetNode.id) : void 0;
|
|
14252
|
+
const context = {
|
|
14253
|
+
iteration: ctx.iteration,
|
|
14254
|
+
gadgetName: ctx.gadgetName,
|
|
14255
|
+
invocationId: ctx.invocationId,
|
|
14256
|
+
parameters: ctx.parameters,
|
|
14257
|
+
finalResult: ctx.finalResult,
|
|
14258
|
+
error: ctx.error,
|
|
14259
|
+
executionTimeMs: ctx.executionTimeMs,
|
|
14260
|
+
cost: ctx.cost,
|
|
14261
|
+
logger: ctx.logger,
|
|
14262
|
+
subagentContext
|
|
14263
|
+
};
|
|
14264
|
+
if (ctx.hooks?.onGadgetExecutionComplete) {
|
|
14265
|
+
const hookFn = ctx.hooks.onGadgetExecutionComplete;
|
|
14266
|
+
await safeObserve(() => hookFn(context), ctx.logger);
|
|
14267
|
+
}
|
|
14268
|
+
if (ctx.parentObservers?.onGadgetExecutionComplete) {
|
|
14269
|
+
const hookFn = ctx.parentObservers.onGadgetExecutionComplete;
|
|
14270
|
+
await safeObserve(() => hookFn(context), ctx.logger);
|
|
14271
|
+
}
|
|
14272
|
+
}
|
|
14273
|
+
var init_observer_notifier = __esm({
|
|
14274
|
+
"src/agent/observer-notifier.ts"() {
|
|
14275
|
+
"use strict";
|
|
14276
|
+
init_safe_observe();
|
|
14277
|
+
init_tree_hook_bridge();
|
|
13801
14278
|
}
|
|
13802
14279
|
});
|
|
13803
14280
|
|
|
@@ -13809,11 +14286,13 @@ var init_stream_processor = __esm({
|
|
|
13809
14286
|
init_executor();
|
|
13810
14287
|
init_parser();
|
|
13811
14288
|
init_logger();
|
|
14289
|
+
init_gadget_concurrency_manager();
|
|
14290
|
+
init_gadget_dependency_resolver();
|
|
13812
14291
|
init_hook_validators();
|
|
13813
|
-
|
|
14292
|
+
init_observer_notifier();
|
|
14293
|
+
init_safe_observe();
|
|
13814
14294
|
StreamProcessor = class {
|
|
13815
14295
|
iteration;
|
|
13816
|
-
registry;
|
|
13817
14296
|
hooks;
|
|
13818
14297
|
logger;
|
|
13819
14298
|
parser;
|
|
@@ -13825,33 +14304,12 @@ var init_stream_processor = __esm({
|
|
|
13825
14304
|
// Gadget execution mode
|
|
13826
14305
|
gadgetExecutionMode;
|
|
13827
14306
|
responseText = "";
|
|
13828
|
-
|
|
13829
|
-
|
|
13830
|
-
|
|
13831
|
-
|
|
13832
|
-
/** Completed gadget results, keyed by invocation ID */
|
|
13833
|
-
completedResults = /* @__PURE__ */ new Map();
|
|
13834
|
-
/** Invocation IDs of gadgets that have failed (error or skipped due to dependency) */
|
|
13835
|
-
failedInvocations = /* @__PURE__ */ new Set();
|
|
13836
|
-
/** Promises for independent gadgets currently executing (fire-and-forget) */
|
|
13837
|
-
inFlightExecutions = /* @__PURE__ */ new Map();
|
|
14307
|
+
// Dependency resolution is delegated to GadgetDependencyResolver
|
|
14308
|
+
dependencyResolver;
|
|
14309
|
+
// Concurrency management is delegated to GadgetConcurrencyManager
|
|
14310
|
+
concurrencyManager;
|
|
13838
14311
|
/** Queue of completed gadget results ready to be yielded (for real-time streaming) */
|
|
13839
14312
|
completedResultsQueue = [];
|
|
13840
|
-
// Concurrency limiting
|
|
13841
|
-
/** Subagent configuration map for checking maxConcurrent limits */
|
|
13842
|
-
subagentConfig;
|
|
13843
|
-
/** Track active execution count per gadget name */
|
|
13844
|
-
activeCountByGadget = /* @__PURE__ */ new Map();
|
|
13845
|
-
/** Queue of gadgets waiting for a concurrency slot (per gadget name) */
|
|
13846
|
-
concurrencyQueue = /* @__PURE__ */ new Map();
|
|
13847
|
-
// Exclusive gadget support
|
|
13848
|
-
/** Queue of exclusive gadgets deferred until in-flight gadgets complete */
|
|
13849
|
-
exclusiveQueue = [];
|
|
13850
|
-
// Cross-iteration dependency tracking
|
|
13851
|
-
/** Invocation IDs completed in previous iterations (read-only reference from Agent) */
|
|
13852
|
-
priorCompletedInvocations;
|
|
13853
|
-
/** Invocation IDs that failed in previous iterations (read-only reference from Agent) */
|
|
13854
|
-
priorFailedInvocations;
|
|
13855
14313
|
// Parent observer hooks for subagent visibility
|
|
13856
14314
|
parentObservers;
|
|
13857
14315
|
// Gadget limiting per response
|
|
@@ -13860,16 +14318,21 @@ var init_stream_processor = __esm({
|
|
|
13860
14318
|
limitExceeded = false;
|
|
13861
14319
|
constructor(options) {
|
|
13862
14320
|
this.iteration = options.iteration;
|
|
13863
|
-
this.registry = options.registry;
|
|
13864
14321
|
this.hooks = options.hooks ?? {};
|
|
13865
14322
|
this.logger = options.logger ?? createLogger({ name: "llmist:stream-processor" });
|
|
13866
14323
|
this.tree = options.tree;
|
|
13867
14324
|
this.parentNodeId = options.parentNodeId ?? null;
|
|
13868
14325
|
this.baseDepth = options.baseDepth ?? 0;
|
|
13869
14326
|
this.gadgetExecutionMode = options.gadgetExecutionMode ?? "parallel";
|
|
13870
|
-
this.
|
|
13871
|
-
|
|
13872
|
-
|
|
14327
|
+
this.dependencyResolver = new GadgetDependencyResolver({
|
|
14328
|
+
priorCompletedInvocations: options.priorCompletedInvocations,
|
|
14329
|
+
priorFailedInvocations: options.priorFailedInvocations
|
|
14330
|
+
});
|
|
14331
|
+
this.concurrencyManager = new GadgetConcurrencyManager({
|
|
14332
|
+
registry: options.registry,
|
|
14333
|
+
subagentConfig: options.subagentConfig,
|
|
14334
|
+
logger: this.logger.getSubLogger({ name: "concurrency" })
|
|
14335
|
+
});
|
|
13873
14336
|
this.parentObservers = options.parentObservers;
|
|
13874
14337
|
this.maxGadgetsPerResponse = options.maxGadgetsPerResponse ?? 0;
|
|
13875
14338
|
this.parser = new GadgetCallParser({
|
|
@@ -14111,7 +14574,7 @@ var init_stream_processor = __esm({
|
|
|
14111
14574
|
gadgetName: call.gadgetName,
|
|
14112
14575
|
invocationId: call.invocationId
|
|
14113
14576
|
});
|
|
14114
|
-
this.
|
|
14577
|
+
this.dependencyResolver.markFailed(call.invocationId);
|
|
14115
14578
|
const errorMessage = `Gadget "${call.invocationId}" cannot depend on itself (self-referential dependency)`;
|
|
14116
14579
|
const skipEvent = {
|
|
14117
14580
|
type: "gadget_skipped",
|
|
@@ -14122,39 +14585,21 @@ var init_stream_processor = __esm({
|
|
|
14122
14585
|
failedDependencyError: errorMessage
|
|
14123
14586
|
};
|
|
14124
14587
|
yield skipEvent;
|
|
14125
|
-
|
|
14126
|
-
|
|
14127
|
-
|
|
14128
|
-
|
|
14129
|
-
|
|
14130
|
-
|
|
14131
|
-
|
|
14132
|
-
|
|
14133
|
-
|
|
14134
|
-
|
|
14135
|
-
|
|
14136
|
-
|
|
14137
|
-
};
|
|
14138
|
-
await this.safeObserve(() => this.hooks.observers.onGadgetSkipped(context));
|
|
14139
|
-
}
|
|
14140
|
-
if (this.parentObservers?.onGadgetSkipped) {
|
|
14141
|
-
const context = {
|
|
14142
|
-
iteration: this.iteration,
|
|
14143
|
-
gadgetName: call.gadgetName,
|
|
14144
|
-
invocationId: call.invocationId,
|
|
14145
|
-
parameters: call.parameters ?? {},
|
|
14146
|
-
failedDependency: call.invocationId,
|
|
14147
|
-
failedDependencyError: errorMessage,
|
|
14148
|
-
logger: this.logger,
|
|
14149
|
-
subagentContext: skippedSubagentContext
|
|
14150
|
-
};
|
|
14151
|
-
await this.safeObserve(() => this.parentObservers.onGadgetSkipped(context));
|
|
14152
|
-
}
|
|
14588
|
+
await notifyGadgetSkipped({
|
|
14589
|
+
tree: this.tree,
|
|
14590
|
+
hooks: this.hooks.observers,
|
|
14591
|
+
parentObservers: this.parentObservers,
|
|
14592
|
+
logger: this.logger,
|
|
14593
|
+
iteration: this.iteration,
|
|
14594
|
+
gadgetName: call.gadgetName,
|
|
14595
|
+
invocationId: call.invocationId,
|
|
14596
|
+
parameters: call.parameters ?? {},
|
|
14597
|
+
failedDependency: call.invocationId,
|
|
14598
|
+
failedDependencyError: errorMessage
|
|
14599
|
+
});
|
|
14153
14600
|
return;
|
|
14154
14601
|
}
|
|
14155
|
-
const failedDep =
|
|
14156
|
-
(dep) => this.failedInvocations.has(dep) || this.priorFailedInvocations.has(dep)
|
|
14157
|
-
);
|
|
14602
|
+
const failedDep = this.dependencyResolver.getFailedDependency(call);
|
|
14158
14603
|
if (failedDep) {
|
|
14159
14604
|
const skipEvents = await this.handleFailedDependency(call, failedDep);
|
|
14160
14605
|
for (const evt of skipEvents) {
|
|
@@ -14162,16 +14607,16 @@ var init_stream_processor = __esm({
|
|
|
14162
14607
|
}
|
|
14163
14608
|
return;
|
|
14164
14609
|
}
|
|
14165
|
-
|
|
14166
|
-
|
|
14167
|
-
|
|
14168
|
-
|
|
14610
|
+
if (!this.dependencyResolver.isAllSatisfied(call)) {
|
|
14611
|
+
const unsatisfied = call.dependencies.filter(
|
|
14612
|
+
(dep) => !this.dependencyResolver.isCompleted(dep)
|
|
14613
|
+
);
|
|
14169
14614
|
this.logger.debug("Queueing gadget for later - waiting on dependencies", {
|
|
14170
14615
|
gadgetName: call.gadgetName,
|
|
14171
14616
|
invocationId: call.invocationId,
|
|
14172
14617
|
waitingOn: unsatisfied
|
|
14173
14618
|
});
|
|
14174
|
-
this.
|
|
14619
|
+
this.dependencyResolver.addPending(call);
|
|
14175
14620
|
return;
|
|
14176
14621
|
}
|
|
14177
14622
|
const limitCheckGen2 = this.checkGadgetLimitExceeded(call);
|
|
@@ -14200,28 +14645,12 @@ var init_stream_processor = __esm({
|
|
|
14200
14645
|
if (limitResult.value === true) {
|
|
14201
14646
|
return;
|
|
14202
14647
|
}
|
|
14203
|
-
|
|
14204
|
-
|
|
14205
|
-
this.logger.debug("Deferring exclusive gadget until in-flight gadgets complete", {
|
|
14206
|
-
gadgetName: call.gadgetName,
|
|
14207
|
-
invocationId: call.invocationId,
|
|
14208
|
-
inFlightCount: this.inFlightExecutions.size
|
|
14209
|
-
});
|
|
14210
|
-
this.exclusiveQueue.push(call);
|
|
14648
|
+
if (this.concurrencyManager.isExclusive(call.gadgetName) && this.concurrencyManager.inFlightCount > 0) {
|
|
14649
|
+
this.concurrencyManager.queueExclusive(call);
|
|
14211
14650
|
return;
|
|
14212
14651
|
}
|
|
14213
|
-
|
|
14214
|
-
|
|
14215
|
-
if (limit > 0 && activeCount >= limit) {
|
|
14216
|
-
this.logger.debug("Gadget queued due to concurrency limit", {
|
|
14217
|
-
gadgetName: call.gadgetName,
|
|
14218
|
-
invocationId: call.invocationId,
|
|
14219
|
-
activeCount,
|
|
14220
|
-
limit
|
|
14221
|
-
});
|
|
14222
|
-
const queue = this.concurrencyQueue.get(call.gadgetName) ?? [];
|
|
14223
|
-
queue.push(call);
|
|
14224
|
-
this.concurrencyQueue.set(call.gadgetName, queue);
|
|
14652
|
+
if (!this.concurrencyManager.canStart(call)) {
|
|
14653
|
+
this.concurrencyManager.queueForLater(call);
|
|
14225
14654
|
return;
|
|
14226
14655
|
}
|
|
14227
14656
|
if (this.gadgetExecutionMode === "sequential") {
|
|
@@ -14232,57 +14661,19 @@ var init_stream_processor = __esm({
|
|
|
14232
14661
|
this.startGadgetWithConcurrencyTracking(call);
|
|
14233
14662
|
}
|
|
14234
14663
|
}
|
|
14235
|
-
/**
|
|
14236
|
-
* Get the effective concurrency limit for a gadget.
|
|
14237
|
-
* Uses "most restrictive wins" strategy: the lowest non-zero value from
|
|
14238
|
-
* external config (SubagentConfig) and gadget's intrinsic maxConcurrent.
|
|
14239
|
-
*
|
|
14240
|
-
* This ensures gadget authors can set safety floors (e.g., maxConcurrent: 1
|
|
14241
|
-
* for file writers) that cannot be weakened by external configuration.
|
|
14242
|
-
*
|
|
14243
|
-
* @returns 0 if unlimited, otherwise the effective limit
|
|
14244
|
-
*/
|
|
14245
|
-
getConcurrencyLimit(gadgetName) {
|
|
14246
|
-
const configLimit = this.subagentConfig?.[gadgetName]?.maxConcurrent;
|
|
14247
|
-
const gadget = this.registry.get(gadgetName);
|
|
14248
|
-
const gadgetLimit = gadget?.maxConcurrent;
|
|
14249
|
-
const config = configLimit || Number.POSITIVE_INFINITY;
|
|
14250
|
-
const intrinsic = gadgetLimit || Number.POSITIVE_INFINITY;
|
|
14251
|
-
const effective = Math.min(config, intrinsic);
|
|
14252
|
-
return effective === Number.POSITIVE_INFINITY ? 0 : effective;
|
|
14253
|
-
}
|
|
14254
14664
|
/**
|
|
14255
14665
|
* Start a gadget execution with concurrency tracking.
|
|
14256
|
-
*
|
|
14666
|
+
* Delegates tracking to GadgetConcurrencyManager; schedules queue processing on completion.
|
|
14257
14667
|
*/
|
|
14258
14668
|
startGadgetWithConcurrencyTracking(call) {
|
|
14259
14669
|
const gadgetName = call.gadgetName;
|
|
14260
|
-
const currentCount = this.activeCountByGadget.get(gadgetName) ?? 0;
|
|
14261
|
-
this.activeCountByGadget.set(gadgetName, currentCount + 1);
|
|
14262
14670
|
const executionPromise = this.executeGadgetAndCollect(call).finally(() => {
|
|
14263
|
-
const
|
|
14264
|
-
|
|
14265
|
-
|
|
14671
|
+
const nextCall = this.concurrencyManager.onComplete(gadgetName);
|
|
14672
|
+
if (nextCall) {
|
|
14673
|
+
this.startGadgetWithConcurrencyTracking(nextCall);
|
|
14674
|
+
}
|
|
14266
14675
|
});
|
|
14267
|
-
this.
|
|
14268
|
-
}
|
|
14269
|
-
/**
|
|
14270
|
-
* Process the next queued gadget for a given gadget name if a slot is available.
|
|
14271
|
-
*/
|
|
14272
|
-
processQueuedGadget(gadgetName) {
|
|
14273
|
-
const queue = this.concurrencyQueue.get(gadgetName);
|
|
14274
|
-
if (!queue || queue.length === 0) return;
|
|
14275
|
-
const limit = this.getConcurrencyLimit(gadgetName);
|
|
14276
|
-
const activeCount = this.activeCountByGadget.get(gadgetName) ?? 0;
|
|
14277
|
-
if (limit === 0 || activeCount < limit) {
|
|
14278
|
-
const nextCall = queue.shift();
|
|
14279
|
-
this.logger.debug("Processing queued gadget", {
|
|
14280
|
-
gadgetName,
|
|
14281
|
-
invocationId: nextCall.invocationId,
|
|
14282
|
-
remainingInQueue: queue.length
|
|
14283
|
-
});
|
|
14284
|
-
this.startGadgetWithConcurrencyTracking(nextCall);
|
|
14285
|
-
}
|
|
14676
|
+
this.concurrencyManager.trackExecution(call.invocationId, gadgetName, executionPromise);
|
|
14286
14677
|
}
|
|
14287
14678
|
/**
|
|
14288
14679
|
* Execute a gadget through the full hook lifecycle and yield events.
|
|
@@ -14337,30 +14728,16 @@ var init_stream_processor = __esm({
|
|
|
14337
14728
|
this.tree.startGadget(gadgetNode.id);
|
|
14338
14729
|
}
|
|
14339
14730
|
}
|
|
14340
|
-
|
|
14341
|
-
|
|
14342
|
-
|
|
14343
|
-
|
|
14344
|
-
|
|
14345
|
-
|
|
14346
|
-
|
|
14347
|
-
|
|
14348
|
-
|
|
14349
|
-
|
|
14350
|
-
};
|
|
14351
|
-
await this.safeObserve(() => this.hooks.observers.onGadgetExecutionStart(context));
|
|
14352
|
-
}
|
|
14353
|
-
if (this.parentObservers?.onGadgetExecutionStart) {
|
|
14354
|
-
const context = {
|
|
14355
|
-
iteration: this.iteration,
|
|
14356
|
-
gadgetName: call.gadgetName,
|
|
14357
|
-
invocationId: call.invocationId,
|
|
14358
|
-
parameters,
|
|
14359
|
-
logger: this.logger,
|
|
14360
|
-
subagentContext: gadgetStartSubagentContext
|
|
14361
|
-
};
|
|
14362
|
-
await this.safeObserve(() => this.parentObservers.onGadgetExecutionStart(context));
|
|
14363
|
-
}
|
|
14731
|
+
await notifyGadgetStart({
|
|
14732
|
+
tree: this.tree,
|
|
14733
|
+
hooks: this.hooks.observers,
|
|
14734
|
+
parentObservers: this.parentObservers,
|
|
14735
|
+
logger: this.logger,
|
|
14736
|
+
iteration: this.iteration,
|
|
14737
|
+
gadgetName: call.gadgetName,
|
|
14738
|
+
invocationId: call.invocationId,
|
|
14739
|
+
parameters
|
|
14740
|
+
});
|
|
14364
14741
|
let result;
|
|
14365
14742
|
if (shouldSkip) {
|
|
14366
14743
|
result = {
|
|
@@ -14373,7 +14750,7 @@ var init_stream_processor = __esm({
|
|
|
14373
14750
|
} else {
|
|
14374
14751
|
result = await this.executor.execute(call);
|
|
14375
14752
|
}
|
|
14376
|
-
if (result.result && this.hooks.interceptors?.interceptGadgetResult) {
|
|
14753
|
+
if ((result.result || result.error) && this.hooks.interceptors?.interceptGadgetResult) {
|
|
14377
14754
|
const context = {
|
|
14378
14755
|
iteration: this.iteration,
|
|
14379
14756
|
gadgetName: result.gadgetName,
|
|
@@ -14382,7 +14759,12 @@ var init_stream_processor = __esm({
|
|
|
14382
14759
|
executionTimeMs: result.executionTimeMs,
|
|
14383
14760
|
logger: this.logger
|
|
14384
14761
|
};
|
|
14385
|
-
|
|
14762
|
+
if (result.result) {
|
|
14763
|
+
result.result = this.hooks.interceptors.interceptGadgetResult(result.result, context);
|
|
14764
|
+
}
|
|
14765
|
+
if (result.error) {
|
|
14766
|
+
result.error = this.hooks.interceptors.interceptGadgetResult(result.error, context);
|
|
14767
|
+
}
|
|
14386
14768
|
}
|
|
14387
14769
|
if (this.hooks.controllers?.afterGadgetExecution) {
|
|
14388
14770
|
const context = {
|
|
@@ -14429,42 +14811,21 @@ var init_stream_processor = __esm({
|
|
|
14429
14811
|
}
|
|
14430
14812
|
}
|
|
14431
14813
|
}
|
|
14432
|
-
|
|
14433
|
-
|
|
14434
|
-
|
|
14435
|
-
|
|
14436
|
-
|
|
14437
|
-
|
|
14438
|
-
|
|
14439
|
-
|
|
14440
|
-
|
|
14441
|
-
|
|
14442
|
-
|
|
14443
|
-
|
|
14444
|
-
|
|
14445
|
-
|
|
14446
|
-
|
|
14447
|
-
await this.safeObserve(() => this.hooks.observers.onGadgetExecutionComplete(context));
|
|
14448
|
-
}
|
|
14449
|
-
if (this.parentObservers?.onGadgetExecutionComplete) {
|
|
14450
|
-
const context = {
|
|
14451
|
-
iteration: this.iteration,
|
|
14452
|
-
gadgetName: result.gadgetName,
|
|
14453
|
-
invocationId: result.invocationId,
|
|
14454
|
-
parameters,
|
|
14455
|
-
finalResult: result.result,
|
|
14456
|
-
error: result.error,
|
|
14457
|
-
executionTimeMs: result.executionTimeMs,
|
|
14458
|
-
cost: result.cost,
|
|
14459
|
-
logger: this.logger,
|
|
14460
|
-
subagentContext: gadgetCompleteSubagentContext
|
|
14461
|
-
};
|
|
14462
|
-
await this.safeObserve(() => this.parentObservers.onGadgetExecutionComplete(context));
|
|
14463
|
-
}
|
|
14464
|
-
this.completedResults.set(result.invocationId, result);
|
|
14465
|
-
if (result.error) {
|
|
14466
|
-
this.failedInvocations.add(result.invocationId);
|
|
14467
|
-
}
|
|
14814
|
+
await notifyGadgetComplete({
|
|
14815
|
+
tree: this.tree,
|
|
14816
|
+
hooks: this.hooks.observers,
|
|
14817
|
+
parentObservers: this.parentObservers,
|
|
14818
|
+
logger: this.logger,
|
|
14819
|
+
iteration: this.iteration,
|
|
14820
|
+
gadgetName: result.gadgetName,
|
|
14821
|
+
invocationId: result.invocationId,
|
|
14822
|
+
parameters,
|
|
14823
|
+
finalResult: result.result,
|
|
14824
|
+
error: result.error,
|
|
14825
|
+
executionTimeMs: result.executionTimeMs,
|
|
14826
|
+
cost: result.cost
|
|
14827
|
+
});
|
|
14828
|
+
this.dependencyResolver.markComplete(result);
|
|
14468
14829
|
yield { type: "gadget_result", result };
|
|
14469
14830
|
}
|
|
14470
14831
|
/**
|
|
@@ -14496,77 +14857,45 @@ var init_stream_processor = __esm({
|
|
|
14496
14857
|
* Clears the inFlightExecutions map after all gadgets complete.
|
|
14497
14858
|
*/
|
|
14498
14859
|
async *waitForInFlightExecutions() {
|
|
14499
|
-
if (this.
|
|
14860
|
+
if (this.concurrencyManager.inFlightCount === 0 && !this.concurrencyManager.hasQueuedGadgets() && !this.concurrencyManager.hasExclusiveQueued) {
|
|
14500
14861
|
return;
|
|
14501
14862
|
}
|
|
14502
14863
|
this.logger.debug("Waiting for in-flight gadget executions", {
|
|
14503
|
-
count: this.
|
|
14504
|
-
|
|
14505
|
-
queuedCount: this.getQueuedGadgetCount()
|
|
14864
|
+
count: this.concurrencyManager.inFlightCount,
|
|
14865
|
+
queuedCount: this.concurrencyManager.getQueuedGadgetCount()
|
|
14506
14866
|
});
|
|
14507
14867
|
const POLL_INTERVAL_MS = 100;
|
|
14508
|
-
while (this.
|
|
14509
|
-
const allDone = this.
|
|
14868
|
+
while (this.concurrencyManager.inFlightCount > 0 || this.concurrencyManager.hasQueuedGadgets()) {
|
|
14869
|
+
const allDone = this.concurrencyManager.getAllDonePromise();
|
|
14510
14870
|
const result = await Promise.race([
|
|
14511
14871
|
allDone,
|
|
14512
14872
|
new Promise((resolve2) => setTimeout(() => resolve2("poll"), POLL_INTERVAL_MS))
|
|
14513
14873
|
]);
|
|
14514
14874
|
yield* this.drainCompletedResults();
|
|
14515
|
-
if (result === "done" && this.getTotalActiveGadgetCount() === 0 && !this.hasQueuedGadgets()) {
|
|
14875
|
+
if (result === "done" && this.concurrencyManager.getTotalActiveGadgetCount() === 0 && !this.concurrencyManager.hasQueuedGadgets()) {
|
|
14516
14876
|
break;
|
|
14517
14877
|
}
|
|
14518
14878
|
}
|
|
14519
|
-
this.
|
|
14520
|
-
if (this.
|
|
14879
|
+
this.concurrencyManager.clearInFlight();
|
|
14880
|
+
if (this.concurrencyManager.hasExclusiveQueued) {
|
|
14881
|
+
const exclusiveQueue = this.concurrencyManager.drainExclusiveQueue();
|
|
14521
14882
|
this.logger.debug("Processing deferred exclusive gadgets", {
|
|
14522
|
-
count:
|
|
14883
|
+
count: exclusiveQueue.length
|
|
14523
14884
|
});
|
|
14524
|
-
const
|
|
14525
|
-
this.exclusiveQueue = [];
|
|
14526
|
-
for (const call of queue) {
|
|
14885
|
+
for (const call of exclusiveQueue) {
|
|
14527
14886
|
for await (const evt of this.executeGadgetGenerator(call)) {
|
|
14528
14887
|
yield evt;
|
|
14529
14888
|
}
|
|
14530
14889
|
}
|
|
14531
14890
|
}
|
|
14532
14891
|
}
|
|
14533
|
-
/**
|
|
14534
|
-
* Check if there are any gadgets waiting in concurrency queues.
|
|
14535
|
-
*/
|
|
14536
|
-
hasQueuedGadgets() {
|
|
14537
|
-
for (const queue of this.concurrencyQueue.values()) {
|
|
14538
|
-
if (queue.length > 0) return true;
|
|
14539
|
-
}
|
|
14540
|
-
return false;
|
|
14541
|
-
}
|
|
14542
|
-
/**
|
|
14543
|
-
* Get total count of queued gadgets across all queues.
|
|
14544
|
-
*/
|
|
14545
|
-
getQueuedGadgetCount() {
|
|
14546
|
-
let count = 0;
|
|
14547
|
-
for (const queue of this.concurrencyQueue.values()) {
|
|
14548
|
-
count += queue.length;
|
|
14549
|
-
}
|
|
14550
|
-
return count;
|
|
14551
|
-
}
|
|
14552
|
-
/**
|
|
14553
|
-
* Get total count of actively executing gadgets across all types.
|
|
14554
|
-
* Used to know when all work is truly complete (not just when allDone resolves).
|
|
14555
|
-
*/
|
|
14556
|
-
getTotalActiveGadgetCount() {
|
|
14557
|
-
let total = 0;
|
|
14558
|
-
for (const count of this.activeCountByGadget.values()) {
|
|
14559
|
-
total += count;
|
|
14560
|
-
}
|
|
14561
|
-
return total;
|
|
14562
|
-
}
|
|
14563
14892
|
/**
|
|
14564
14893
|
* Handle a gadget that cannot execute because a dependency failed.
|
|
14565
14894
|
* Calls the onDependencySkipped controller to allow customization.
|
|
14566
14895
|
*/
|
|
14567
14896
|
async handleFailedDependency(call, failedDep) {
|
|
14568
14897
|
const events = [];
|
|
14569
|
-
const depResult = this.
|
|
14898
|
+
const depResult = this.dependencyResolver.getCompletedResult(failedDep);
|
|
14570
14899
|
const depError = depResult?.error ?? "Dependency failed";
|
|
14571
14900
|
let action = { action: "skip" };
|
|
14572
14901
|
if (this.hooks.controllers?.onDependencySkipped) {
|
|
@@ -14582,7 +14911,7 @@ var init_stream_processor = __esm({
|
|
|
14582
14911
|
action = await this.hooks.controllers.onDependencySkipped(context);
|
|
14583
14912
|
}
|
|
14584
14913
|
if (action.action === "skip") {
|
|
14585
|
-
this.
|
|
14914
|
+
this.dependencyResolver.markFailed(call.invocationId);
|
|
14586
14915
|
if (this.tree) {
|
|
14587
14916
|
const gadgetNode = this.tree.getNodeByInvocationId(call.invocationId);
|
|
14588
14917
|
if (gadgetNode) {
|
|
@@ -14598,34 +14927,18 @@ var init_stream_processor = __esm({
|
|
|
14598
14927
|
failedDependencyError: depError
|
|
14599
14928
|
};
|
|
14600
14929
|
events.push(skipEvent);
|
|
14601
|
-
|
|
14602
|
-
|
|
14603
|
-
|
|
14604
|
-
|
|
14605
|
-
|
|
14606
|
-
|
|
14607
|
-
|
|
14608
|
-
|
|
14609
|
-
|
|
14610
|
-
|
|
14611
|
-
|
|
14612
|
-
|
|
14613
|
-
};
|
|
14614
|
-
await this.safeObserve(() => this.hooks.observers.onGadgetSkipped(context));
|
|
14615
|
-
}
|
|
14616
|
-
if (this.parentObservers?.onGadgetSkipped) {
|
|
14617
|
-
const context = {
|
|
14618
|
-
iteration: this.iteration,
|
|
14619
|
-
gadgetName: call.gadgetName,
|
|
14620
|
-
invocationId: call.invocationId,
|
|
14621
|
-
parameters: call.parameters ?? {},
|
|
14622
|
-
failedDependency: failedDep,
|
|
14623
|
-
failedDependencyError: depError,
|
|
14624
|
-
logger: this.logger,
|
|
14625
|
-
subagentContext: skipSubagentContext
|
|
14626
|
-
};
|
|
14627
|
-
await this.safeObserve(() => this.parentObservers.onGadgetSkipped(context));
|
|
14628
|
-
}
|
|
14930
|
+
await notifyGadgetSkipped({
|
|
14931
|
+
tree: this.tree,
|
|
14932
|
+
hooks: this.hooks.observers,
|
|
14933
|
+
parentObservers: this.parentObservers,
|
|
14934
|
+
logger: this.logger,
|
|
14935
|
+
iteration: this.iteration,
|
|
14936
|
+
gadgetName: call.gadgetName,
|
|
14937
|
+
invocationId: call.invocationId,
|
|
14938
|
+
parameters: call.parameters ?? {},
|
|
14939
|
+
failedDependency: failedDep,
|
|
14940
|
+
failedDependencyError: depError
|
|
14941
|
+
});
|
|
14629
14942
|
this.logger.info("Gadget skipped due to failed dependency", {
|
|
14630
14943
|
gadgetName: call.gadgetName,
|
|
14631
14944
|
invocationId: call.invocationId,
|
|
@@ -14648,7 +14961,7 @@ var init_stream_processor = __esm({
|
|
|
14648
14961
|
result: action.fallbackResult,
|
|
14649
14962
|
executionTimeMs: 0
|
|
14650
14963
|
};
|
|
14651
|
-
this.
|
|
14964
|
+
this.dependencyResolver.markComplete(fallbackResult);
|
|
14652
14965
|
events.push({ type: "gadget_result", result: fallbackResult });
|
|
14653
14966
|
this.logger.info("Using fallback result for gadget with failed dependency", {
|
|
14654
14967
|
gadgetName: call.gadgetName,
|
|
@@ -14679,7 +14992,7 @@ var init_stream_processor = __esm({
|
|
|
14679
14992
|
limit: this.maxGadgetsPerResponse,
|
|
14680
14993
|
currentCount: this.gadgetStartedCount
|
|
14681
14994
|
});
|
|
14682
|
-
this.
|
|
14995
|
+
this.dependencyResolver.markFailed(call.invocationId);
|
|
14683
14996
|
if (this.tree) {
|
|
14684
14997
|
const gadgetNode = this.tree.getNodeByInvocationId(call.invocationId);
|
|
14685
14998
|
if (gadgetNode) {
|
|
@@ -14700,34 +15013,18 @@ var init_stream_processor = __esm({
|
|
|
14700
15013
|
failedDependencyError: errorMessage
|
|
14701
15014
|
};
|
|
14702
15015
|
yield skipEvent;
|
|
14703
|
-
|
|
14704
|
-
|
|
14705
|
-
|
|
14706
|
-
|
|
14707
|
-
|
|
14708
|
-
|
|
14709
|
-
|
|
14710
|
-
|
|
14711
|
-
|
|
14712
|
-
|
|
14713
|
-
|
|
14714
|
-
|
|
14715
|
-
};
|
|
14716
|
-
await this.safeObserve(() => this.hooks.observers.onGadgetSkipped(context));
|
|
14717
|
-
}
|
|
14718
|
-
if (this.parentObservers?.onGadgetSkipped) {
|
|
14719
|
-
const context = {
|
|
14720
|
-
iteration: this.iteration,
|
|
14721
|
-
gadgetName: call.gadgetName,
|
|
14722
|
-
invocationId: call.invocationId,
|
|
14723
|
-
parameters: call.parameters ?? {},
|
|
14724
|
-
failedDependency: "maxGadgetsPerResponse",
|
|
14725
|
-
failedDependencyError: errorMessage,
|
|
14726
|
-
logger: this.logger,
|
|
14727
|
-
subagentContext: limitSkipSubagentContext
|
|
14728
|
-
};
|
|
14729
|
-
await this.safeObserve(() => this.parentObservers.onGadgetSkipped(context));
|
|
14730
|
-
}
|
|
15016
|
+
await notifyGadgetSkipped({
|
|
15017
|
+
tree: this.tree,
|
|
15018
|
+
hooks: this.hooks.observers,
|
|
15019
|
+
parentObservers: this.parentObservers,
|
|
15020
|
+
logger: this.logger,
|
|
15021
|
+
iteration: this.iteration,
|
|
15022
|
+
gadgetName: call.gadgetName,
|
|
15023
|
+
invocationId: call.invocationId,
|
|
15024
|
+
parameters: call.parameters ?? {},
|
|
15025
|
+
failedDependency: "maxGadgetsPerResponse",
|
|
15026
|
+
failedDependencyError: errorMessage
|
|
15027
|
+
});
|
|
14731
15028
|
return true;
|
|
14732
15029
|
}
|
|
14733
15030
|
this.gadgetStartedCount++;
|
|
@@ -14745,27 +15042,11 @@ var init_stream_processor = __esm({
|
|
|
14745
15042
|
return;
|
|
14746
15043
|
}
|
|
14747
15044
|
let progress = true;
|
|
14748
|
-
while (progress && this.
|
|
15045
|
+
while (progress && this.dependencyResolver.pendingCount > 0) {
|
|
14749
15046
|
progress = false;
|
|
14750
|
-
const readyToExecute =
|
|
14751
|
-
const readyToSkip = [];
|
|
14752
|
-
for (const [_invocationId, call] of this.gadgetsAwaitingDependencies) {
|
|
14753
|
-
const failedDep = call.dependencies.find(
|
|
14754
|
-
(dep) => this.failedInvocations.has(dep) || this.priorFailedInvocations.has(dep)
|
|
14755
|
-
);
|
|
14756
|
-
if (failedDep) {
|
|
14757
|
-
readyToSkip.push({ call, failedDep });
|
|
14758
|
-
continue;
|
|
14759
|
-
}
|
|
14760
|
-
const allSatisfied = call.dependencies.every(
|
|
14761
|
-
(dep) => this.completedResults.has(dep) || this.priorCompletedInvocations.has(dep)
|
|
14762
|
-
);
|
|
14763
|
-
if (allSatisfied) {
|
|
14764
|
-
readyToExecute.push(call);
|
|
14765
|
-
}
|
|
14766
|
-
}
|
|
15047
|
+
const { readyToExecute, readyToSkip } = this.dependencyResolver.getReadyCalls();
|
|
14767
15048
|
for (const { call, failedDep } of readyToSkip) {
|
|
14768
|
-
this.
|
|
15049
|
+
this.dependencyResolver.removePending(call.invocationId);
|
|
14769
15050
|
const skipEvents = await this.handleFailedDependency(call, failedDep);
|
|
14770
15051
|
for (const evt of skipEvents) {
|
|
14771
15052
|
yield evt;
|
|
@@ -14774,7 +15055,7 @@ var init_stream_processor = __esm({
|
|
|
14774
15055
|
}
|
|
14775
15056
|
if (readyToExecute.length > 0) {
|
|
14776
15057
|
for (const call of readyToExecute) {
|
|
14777
|
-
this.
|
|
15058
|
+
this.dependencyResolver.removePending(call.invocationId);
|
|
14778
15059
|
}
|
|
14779
15060
|
if (this.gadgetExecutionMode === "sequential") {
|
|
14780
15061
|
this.logger.debug("Executing ready gadgets sequentially", {
|
|
@@ -14827,11 +15108,12 @@ var init_stream_processor = __esm({
|
|
|
14827
15108
|
progress = true;
|
|
14828
15109
|
}
|
|
14829
15110
|
}
|
|
14830
|
-
if (this.
|
|
14831
|
-
const
|
|
14832
|
-
|
|
15111
|
+
if (this.dependencyResolver.pendingCount > 0) {
|
|
15112
|
+
const pendingEntries = this.dependencyResolver.getPendingEntries();
|
|
15113
|
+
const pendingIds = new Set(pendingEntries.map(([id]) => id));
|
|
15114
|
+
for (const [invocationId, call] of pendingEntries) {
|
|
14833
15115
|
const missingDeps = call.dependencies.filter(
|
|
14834
|
-
(dep) => !this.
|
|
15116
|
+
(dep) => !this.dependencyResolver.isCompleted(dep)
|
|
14835
15117
|
);
|
|
14836
15118
|
const circularDeps = missingDeps.filter((dep) => pendingIds.has(dep));
|
|
14837
15119
|
const trulyMissingDeps = missingDeps.filter((dep) => !pendingIds.has(dep));
|
|
@@ -14851,7 +15133,7 @@ var init_stream_processor = __esm({
|
|
|
14851
15133
|
circularDependencies: circularDeps,
|
|
14852
15134
|
missingDependencies: trulyMissingDeps
|
|
14853
15135
|
});
|
|
14854
|
-
this.
|
|
15136
|
+
this.dependencyResolver.markFailed(invocationId);
|
|
14855
15137
|
const skipEvent = {
|
|
14856
15138
|
type: "gadget_skipped",
|
|
14857
15139
|
gadgetName: call.gadgetName,
|
|
@@ -14861,51 +15143,20 @@ var init_stream_processor = __esm({
|
|
|
14861
15143
|
failedDependencyError: errorMessage
|
|
14862
15144
|
};
|
|
14863
15145
|
yield skipEvent;
|
|
14864
|
-
|
|
14865
|
-
|
|
14866
|
-
|
|
14867
|
-
|
|
14868
|
-
|
|
14869
|
-
|
|
14870
|
-
|
|
14871
|
-
|
|
14872
|
-
|
|
14873
|
-
|
|
14874
|
-
|
|
14875
|
-
|
|
14876
|
-
};
|
|
14877
|
-
await this.safeObserve(() => this.hooks.observers.onGadgetSkipped(context));
|
|
14878
|
-
}
|
|
14879
|
-
if (this.parentObservers?.onGadgetSkipped) {
|
|
14880
|
-
const context = {
|
|
14881
|
-
iteration: this.iteration,
|
|
14882
|
-
gadgetName: call.gadgetName,
|
|
14883
|
-
invocationId,
|
|
14884
|
-
parameters: call.parameters ?? {},
|
|
14885
|
-
failedDependency: missingDeps[0],
|
|
14886
|
-
failedDependencyError: errorMessage,
|
|
14887
|
-
logger: this.logger,
|
|
14888
|
-
subagentContext: timeoutSubagentContext
|
|
14889
|
-
};
|
|
14890
|
-
await this.safeObserve(() => this.parentObservers.onGadgetSkipped(context));
|
|
14891
|
-
}
|
|
15146
|
+
await notifyGadgetSkipped({
|
|
15147
|
+
tree: this.tree,
|
|
15148
|
+
hooks: this.hooks.observers,
|
|
15149
|
+
parentObservers: this.parentObservers,
|
|
15150
|
+
logger: this.logger,
|
|
15151
|
+
iteration: this.iteration,
|
|
15152
|
+
gadgetName: call.gadgetName,
|
|
15153
|
+
invocationId,
|
|
15154
|
+
parameters: call.parameters ?? {},
|
|
15155
|
+
failedDependency: missingDeps[0],
|
|
15156
|
+
failedDependencyError: errorMessage
|
|
15157
|
+
});
|
|
14892
15158
|
}
|
|
14893
|
-
this.
|
|
14894
|
-
}
|
|
14895
|
-
}
|
|
14896
|
-
/**
|
|
14897
|
-
* Safely execute an observer, catching and logging any errors.
|
|
14898
|
-
* Observers are non-critical, so errors are logged but don't crash the system.
|
|
14899
|
-
*/
|
|
14900
|
-
async safeObserve(fn) {
|
|
14901
|
-
try {
|
|
14902
|
-
await fn();
|
|
14903
|
-
} catch (error) {
|
|
14904
|
-
this.observerFailureCount++;
|
|
14905
|
-
this.logger.error("Observer threw error (ignoring)", {
|
|
14906
|
-
error: error instanceof Error ? error.message : String(error),
|
|
14907
|
-
failureCount: this.observerFailureCount
|
|
14908
|
-
});
|
|
15159
|
+
this.dependencyResolver.clearPending();
|
|
14909
15160
|
}
|
|
14910
15161
|
}
|
|
14911
15162
|
/**
|
|
@@ -14914,7 +15165,7 @@ var init_stream_processor = __esm({
|
|
|
14914
15165
|
*/
|
|
14915
15166
|
async runObserversInParallel(observers) {
|
|
14916
15167
|
if (observers.length === 0) return;
|
|
14917
|
-
await Promise.allSettled(observers.map((observer) =>
|
|
15168
|
+
await Promise.allSettled(observers.map((observer) => safeObserve(observer, this.logger)));
|
|
14918
15169
|
}
|
|
14919
15170
|
// ==========================================================================
|
|
14920
15171
|
// Public accessors for cross-iteration dependency tracking
|
|
@@ -14924,14 +15175,14 @@ var init_stream_processor = __esm({
|
|
|
14924
15175
|
* Used by Agent to accumulate completed IDs across iterations.
|
|
14925
15176
|
*/
|
|
14926
15177
|
getCompletedInvocationIds() {
|
|
14927
|
-
return
|
|
15178
|
+
return this.dependencyResolver.getCompletedInvocationIds();
|
|
14928
15179
|
}
|
|
14929
15180
|
/**
|
|
14930
15181
|
* Get all invocation IDs that failed in this iteration.
|
|
14931
15182
|
* Used by Agent to accumulate failed IDs across iterations.
|
|
14932
15183
|
*/
|
|
14933
15184
|
getFailedInvocationIds() {
|
|
14934
|
-
return
|
|
15185
|
+
return this.dependencyResolver.getFailedInvocationIds();
|
|
14935
15186
|
}
|
|
14936
15187
|
};
|
|
14937
15188
|
}
|
|
@@ -14958,6 +15209,7 @@ var init_agent = __esm({
|
|
|
14958
15209
|
init_event_handlers();
|
|
14959
15210
|
init_gadget_output_store();
|
|
14960
15211
|
init_hook_validators();
|
|
15212
|
+
init_safe_observe();
|
|
14961
15213
|
init_stream_processor();
|
|
14962
15214
|
init_tree_hook_bridge();
|
|
14963
15215
|
Agent = class {
|
|
@@ -15446,7 +15698,7 @@ var init_agent = __esm({
|
|
|
15446
15698
|
}
|
|
15447
15699
|
);
|
|
15448
15700
|
this.retryConfig.onRetry?.(error, streamAttempt);
|
|
15449
|
-
await
|
|
15701
|
+
await safeObserve(async () => {
|
|
15450
15702
|
if (this.hooks.observers?.onRetryAttempt) {
|
|
15451
15703
|
const subagentContext = getSubagentContextForNode(this.tree, currentLLMNodeId);
|
|
15452
15704
|
const hookContext = {
|
|
@@ -15460,7 +15712,7 @@ var init_agent = __esm({
|
|
|
15460
15712
|
};
|
|
15461
15713
|
await this.hooks.observers.onRetryAttempt(hookContext);
|
|
15462
15714
|
}
|
|
15463
|
-
});
|
|
15715
|
+
}, this.logger);
|
|
15464
15716
|
await this.sleep(finalDelay);
|
|
15465
15717
|
streamMetadata = null;
|
|
15466
15718
|
gadgetCallCount = 0;
|
|
@@ -15490,7 +15742,7 @@ var init_agent = __esm({
|
|
|
15490
15742
|
this.logger.silly("LLM response details", {
|
|
15491
15743
|
rawResponse: result.rawResponse
|
|
15492
15744
|
});
|
|
15493
|
-
await
|
|
15745
|
+
await safeObserve(async () => {
|
|
15494
15746
|
if (this.hooks.observers?.onLLMCallComplete) {
|
|
15495
15747
|
const subagentContext = getSubagentContextForNode(this.tree, currentLLMNodeId);
|
|
15496
15748
|
const context = {
|
|
@@ -15506,7 +15758,7 @@ var init_agent = __esm({
|
|
|
15506
15758
|
};
|
|
15507
15759
|
await this.hooks.observers.onLLMCallComplete(context);
|
|
15508
15760
|
}
|
|
15509
|
-
});
|
|
15761
|
+
}, this.logger);
|
|
15510
15762
|
this.completeLLMCallInTree(currentLLMNodeId, result);
|
|
15511
15763
|
const finalMessage = await this.processAfterLLMCallController(
|
|
15512
15764
|
currentIteration,
|
|
@@ -15540,7 +15792,7 @@ var init_agent = __esm({
|
|
|
15540
15792
|
}
|
|
15541
15793
|
} catch (error) {
|
|
15542
15794
|
const errorHandled = await this.handleLLMError(error, currentIteration);
|
|
15543
|
-
await
|
|
15795
|
+
await safeObserve(async () => {
|
|
15544
15796
|
if (this.hooks.observers?.onLLMCallError) {
|
|
15545
15797
|
const options = llmOptions ?? {
|
|
15546
15798
|
model: this.model,
|
|
@@ -15559,7 +15811,7 @@ var init_agent = __esm({
|
|
|
15559
15811
|
};
|
|
15560
15812
|
await this.hooks.observers.onLLMCallError(context);
|
|
15561
15813
|
}
|
|
15562
|
-
});
|
|
15814
|
+
}, this.logger);
|
|
15563
15815
|
if (!errorHandled) {
|
|
15564
15816
|
throw error;
|
|
15565
15817
|
}
|
|
@@ -15586,7 +15838,7 @@ var init_agent = __esm({
|
|
|
15586
15838
|
if (currentLLMNodeId) {
|
|
15587
15839
|
const node = this.tree.getNode(currentLLMNodeId);
|
|
15588
15840
|
if (node && node.type === "llm_call" && !node.completedAt) {
|
|
15589
|
-
await
|
|
15841
|
+
await safeObserve(async () => {
|
|
15590
15842
|
if (this.hooks.observers?.onLLMCallComplete) {
|
|
15591
15843
|
const subagentContext = getSubagentContextForNode(this.tree, currentLLMNodeId);
|
|
15592
15844
|
const context = {
|
|
@@ -15608,7 +15860,7 @@ var init_agent = __esm({
|
|
|
15608
15860
|
};
|
|
15609
15861
|
await this.hooks.observers.onLLMCallComplete(context);
|
|
15610
15862
|
}
|
|
15611
|
-
});
|
|
15863
|
+
}, this.logger);
|
|
15612
15864
|
this.tree.completeLLMCall(currentLLMNodeId, {
|
|
15613
15865
|
finishReason: "interrupted"
|
|
15614
15866
|
});
|
|
@@ -15628,7 +15880,7 @@ var init_agent = __esm({
|
|
|
15628
15880
|
const throttleDelay = this.rateLimitTracker.getRequiredDelayMs();
|
|
15629
15881
|
if (throttleDelay > 0) {
|
|
15630
15882
|
this.logger.debug("Rate limit throttling", { delayMs: throttleDelay });
|
|
15631
|
-
await
|
|
15883
|
+
await safeObserve(async () => {
|
|
15632
15884
|
if (this.hooks.observers?.onRateLimitThrottle) {
|
|
15633
15885
|
const subagentContext = getSubagentContextForNode(this.tree, llmNodeId);
|
|
15634
15886
|
const context = {
|
|
@@ -15640,7 +15892,7 @@ var init_agent = __esm({
|
|
|
15640
15892
|
};
|
|
15641
15893
|
await this.hooks.observers.onRateLimitThrottle(context);
|
|
15642
15894
|
}
|
|
15643
|
-
});
|
|
15895
|
+
}, this.logger);
|
|
15644
15896
|
await this.sleep(throttleDelay);
|
|
15645
15897
|
}
|
|
15646
15898
|
this.rateLimitTracker.reserveRequest();
|
|
@@ -15703,18 +15955,6 @@ var init_agent = __esm({
|
|
|
15703
15955
|
}
|
|
15704
15956
|
return true;
|
|
15705
15957
|
}
|
|
15706
|
-
/**
|
|
15707
|
-
* Safely execute an observer, catching and logging any errors.
|
|
15708
|
-
*/
|
|
15709
|
-
async safeObserve(fn) {
|
|
15710
|
-
try {
|
|
15711
|
-
await fn();
|
|
15712
|
-
} catch (error) {
|
|
15713
|
-
this.logger.error("Observer threw error (ignoring)", {
|
|
15714
|
-
error: error instanceof Error ? error.message : String(error)
|
|
15715
|
-
});
|
|
15716
|
-
}
|
|
15717
|
-
}
|
|
15718
15958
|
/**
|
|
15719
15959
|
* Resolve max tokens from model catalog.
|
|
15720
15960
|
*/
|
|
@@ -15783,7 +16023,7 @@ var init_agent = __esm({
|
|
|
15783
16023
|
iteration,
|
|
15784
16024
|
reason: this.signal.reason
|
|
15785
16025
|
});
|
|
15786
|
-
await
|
|
16026
|
+
await safeObserve(async () => {
|
|
15787
16027
|
if (this.hooks.observers?.onAbort) {
|
|
15788
16028
|
const context = {
|
|
15789
16029
|
iteration,
|
|
@@ -15792,7 +16032,7 @@ var init_agent = __esm({
|
|
|
15792
16032
|
};
|
|
15793
16033
|
await this.hooks.observers.onAbort(context);
|
|
15794
16034
|
}
|
|
15795
|
-
});
|
|
16035
|
+
}, this.logger);
|
|
15796
16036
|
return true;
|
|
15797
16037
|
}
|
|
15798
16038
|
/**
|
|
@@ -15811,7 +16051,7 @@ var init_agent = __esm({
|
|
|
15811
16051
|
tokensBefore: compactionEvent.tokensBefore,
|
|
15812
16052
|
tokensAfter: compactionEvent.tokensAfter
|
|
15813
16053
|
});
|
|
15814
|
-
await
|
|
16054
|
+
await safeObserve(async () => {
|
|
15815
16055
|
if (this.hooks.observers?.onCompaction) {
|
|
15816
16056
|
await this.hooks.observers.onCompaction({
|
|
15817
16057
|
iteration,
|
|
@@ -15821,7 +16061,7 @@ var init_agent = __esm({
|
|
|
15821
16061
|
logger: this.logger
|
|
15822
16062
|
});
|
|
15823
16063
|
}
|
|
15824
|
-
});
|
|
16064
|
+
}, this.logger);
|
|
15825
16065
|
return { type: "compaction", event: compactionEvent };
|
|
15826
16066
|
}
|
|
15827
16067
|
/**
|
|
@@ -15874,7 +16114,7 @@ var init_agent = __esm({
|
|
|
15874
16114
|
parentId: this.parentNodeId,
|
|
15875
16115
|
request: llmOptions.messages
|
|
15876
16116
|
});
|
|
15877
|
-
await
|
|
16117
|
+
await safeObserve(async () => {
|
|
15878
16118
|
if (this.hooks.observers?.onLLMCallStart) {
|
|
15879
16119
|
const subagentContext = getSubagentContextForNode(this.tree, llmNode.id);
|
|
15880
16120
|
const context = {
|
|
@@ -15885,7 +16125,7 @@ var init_agent = __esm({
|
|
|
15885
16125
|
};
|
|
15886
16126
|
await this.hooks.observers.onLLMCallStart(context);
|
|
15887
16127
|
}
|
|
15888
|
-
});
|
|
16128
|
+
}, this.logger);
|
|
15889
16129
|
if (this.hooks.controllers?.beforeLLMCall) {
|
|
15890
16130
|
const context = {
|
|
15891
16131
|
iteration,
|
|
@@ -15908,7 +16148,7 @@ var init_agent = __esm({
|
|
|
15908
16148
|
llmOptions = { ...llmOptions, ...action.modifiedOptions };
|
|
15909
16149
|
}
|
|
15910
16150
|
}
|
|
15911
|
-
await
|
|
16151
|
+
await safeObserve(async () => {
|
|
15912
16152
|
if (this.hooks.observers?.onLLMCallReady) {
|
|
15913
16153
|
const subagentContext = getSubagentContextForNode(this.tree, llmNode.id);
|
|
15914
16154
|
const context = {
|
|
@@ -15922,7 +16162,7 @@ var init_agent = __esm({
|
|
|
15922
16162
|
};
|
|
15923
16163
|
await this.hooks.observers.onLLMCallReady(context);
|
|
15924
16164
|
}
|
|
15925
|
-
});
|
|
16165
|
+
}, this.logger);
|
|
15926
16166
|
return { options: llmOptions, llmNodeId: llmNode.id };
|
|
15927
16167
|
}
|
|
15928
16168
|
/**
|