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.js
CHANGED
|
@@ -3965,6 +3965,23 @@ var init_hook_validators = __esm({
|
|
|
3965
3965
|
}
|
|
3966
3966
|
});
|
|
3967
3967
|
|
|
3968
|
+
// src/agent/safe-observe.ts
|
|
3969
|
+
async function safeObserve(fn, logger2, label) {
|
|
3970
|
+
try {
|
|
3971
|
+
await fn();
|
|
3972
|
+
} catch (error) {
|
|
3973
|
+
const message = label ? `Observer error in ${label}:` : "Observer threw error (ignoring)";
|
|
3974
|
+
logger2.error(message, {
|
|
3975
|
+
error: error instanceof Error ? error.message : String(error)
|
|
3976
|
+
});
|
|
3977
|
+
}
|
|
3978
|
+
}
|
|
3979
|
+
var init_safe_observe = __esm({
|
|
3980
|
+
"src/agent/safe-observe.ts"() {
|
|
3981
|
+
"use strict";
|
|
3982
|
+
}
|
|
3983
|
+
});
|
|
3984
|
+
|
|
3968
3985
|
// src/gadgets/registry.ts
|
|
3969
3986
|
var GadgetRegistry;
|
|
3970
3987
|
var init_registry = __esm({
|
|
@@ -4419,6 +4436,8 @@ var init_hook_presets = __esm({
|
|
|
4419
4436
|
/**
|
|
4420
4437
|
* Tracks cumulative token usage across all LLM calls.
|
|
4421
4438
|
*
|
|
4439
|
+
* @public
|
|
4440
|
+
*
|
|
4422
4441
|
* **Output:**
|
|
4423
4442
|
* - Per-call token count with 📊 emoji
|
|
4424
4443
|
* - Cumulative total across all calls
|
|
@@ -4681,6 +4700,8 @@ var init_hook_presets = __esm({
|
|
|
4681
4700
|
/**
|
|
4682
4701
|
* Logs detailed error information for debugging and troubleshooting.
|
|
4683
4702
|
*
|
|
4703
|
+
* @public
|
|
4704
|
+
*
|
|
4684
4705
|
* **Output:**
|
|
4685
4706
|
* - LLM errors with ❌ emoji, including model and recovery status
|
|
4686
4707
|
* - Gadget errors with full context (parameters, error message)
|
|
@@ -4759,6 +4780,8 @@ var init_hook_presets = __esm({
|
|
|
4759
4780
|
/**
|
|
4760
4781
|
* Tracks context compaction events.
|
|
4761
4782
|
*
|
|
4783
|
+
* @public
|
|
4784
|
+
*
|
|
4762
4785
|
* **Output:**
|
|
4763
4786
|
* - Compaction events with 🗜️ emoji
|
|
4764
4787
|
* - Strategy name, tokens before/after, and savings
|
|
@@ -4872,6 +4895,8 @@ var init_hook_presets = __esm({
|
|
|
4872
4895
|
/**
|
|
4873
4896
|
* Returns empty hook configuration for clean output without any logging.
|
|
4874
4897
|
*
|
|
4898
|
+
* @public
|
|
4899
|
+
*
|
|
4875
4900
|
* **Output:**
|
|
4876
4901
|
* - None. Returns {} (empty object).
|
|
4877
4902
|
*
|
|
@@ -5029,6 +5054,8 @@ var init_hook_presets = __esm({
|
|
|
5029
5054
|
/**
|
|
5030
5055
|
* Composite preset combining logging, timing, tokenTracking, and errorLogging.
|
|
5031
5056
|
*
|
|
5057
|
+
* @public
|
|
5058
|
+
*
|
|
5032
5059
|
* This is the recommended preset for development and initial production deployments,
|
|
5033
5060
|
* providing comprehensive observability with a single method call.
|
|
5034
5061
|
*
|
|
@@ -11979,7 +12006,9 @@ ${endPrefix}`
|
|
|
11979
12006
|
*/
|
|
11980
12007
|
/**
|
|
11981
12008
|
* Build AgentOptions with the given user prompt.
|
|
11982
|
-
* Centralizes options construction for ask(), askWithImage(), and
|
|
12009
|
+
* Centralizes options construction for ask(), askWithImage(), askWithContent(), and build().
|
|
12010
|
+
*
|
|
12011
|
+
* @param userPrompt - Optional user prompt (omitted for build() which has no prompt)
|
|
11983
12012
|
*/
|
|
11984
12013
|
buildAgentOptions(userPrompt) {
|
|
11985
12014
|
if (!this.client) {
|
|
@@ -12021,7 +12050,12 @@ ${endPrefix}`
|
|
|
12021
12050
|
// Tree context for shared tree model (subagents share parent's tree)
|
|
12022
12051
|
parentTree: this.parentContext?.tree,
|
|
12023
12052
|
parentNodeId: this.parentContext?.nodeId,
|
|
12024
|
-
baseDepth: this.parentContext ? (this.parentContext.depth ?? 0) + 1 : 0
|
|
12053
|
+
baseDepth: this.parentContext ? (this.parentContext.depth ?? 0) + 1 : 0,
|
|
12054
|
+
// Parent observer hooks for subagent visibility
|
|
12055
|
+
parentObservers: this.parentObservers,
|
|
12056
|
+
// Shared rate limit tracker and retry config (for coordinated limits across subagents)
|
|
12057
|
+
sharedRateLimitTracker: this.sharedRateLimitTracker,
|
|
12058
|
+
sharedRetryConfig: this.sharedRetryConfig
|
|
12025
12059
|
};
|
|
12026
12060
|
}
|
|
12027
12061
|
ask(userPrompt) {
|
|
@@ -12173,52 +12207,7 @@ ${endPrefix}`
|
|
|
12173
12207
|
* ```
|
|
12174
12208
|
*/
|
|
12175
12209
|
build() {
|
|
12176
|
-
|
|
12177
|
-
const { LLMist: LLMistClass } = (init_client(), __toCommonJS(client_exports));
|
|
12178
|
-
this.client = new LLMistClass();
|
|
12179
|
-
}
|
|
12180
|
-
const registry = GadgetRegistry.from(this.gadgets);
|
|
12181
|
-
const options = {
|
|
12182
|
-
client: this.client,
|
|
12183
|
-
model: this.model ?? "openai:gpt-5-nano",
|
|
12184
|
-
systemPrompt: this.systemPrompt,
|
|
12185
|
-
// No userPrompt - agent.run() will throw if called directly
|
|
12186
|
-
registry,
|
|
12187
|
-
maxIterations: this.maxIterations,
|
|
12188
|
-
budget: this.budget,
|
|
12189
|
-
temperature: this.temperature,
|
|
12190
|
-
logger: this.logger,
|
|
12191
|
-
hooks: this.composeHooks(),
|
|
12192
|
-
promptConfig: this.promptConfig,
|
|
12193
|
-
initialMessages: this.initialMessages,
|
|
12194
|
-
requestHumanInput: this.requestHumanInput,
|
|
12195
|
-
gadgetStartPrefix: this.gadgetStartPrefix,
|
|
12196
|
-
gadgetEndPrefix: this.gadgetEndPrefix,
|
|
12197
|
-
gadgetArgPrefix: this.gadgetArgPrefix,
|
|
12198
|
-
textOnlyHandler: this.textOnlyHandler,
|
|
12199
|
-
textWithGadgetsHandler: this.textWithGadgetsHandler,
|
|
12200
|
-
defaultGadgetTimeoutMs: this.defaultGadgetTimeoutMs,
|
|
12201
|
-
gadgetExecutionMode: this.gadgetExecutionMode,
|
|
12202
|
-
maxGadgetsPerResponse: this.maxGadgetsPerResponse,
|
|
12203
|
-
gadgetOutputLimit: this.gadgetOutputLimit,
|
|
12204
|
-
gadgetOutputLimitPercent: this.gadgetOutputLimitPercent,
|
|
12205
|
-
compactionConfig: this.compactionConfig,
|
|
12206
|
-
retryConfig: this.retryConfig,
|
|
12207
|
-
rateLimitConfig: this.rateLimitConfig,
|
|
12208
|
-
signal: this.signal,
|
|
12209
|
-
reasoning: this.reasoningConfig,
|
|
12210
|
-
caching: this.cachingConfig,
|
|
12211
|
-
subagentConfig: this.subagentConfig,
|
|
12212
|
-
// Tree context for shared tree model (subagents share parent's tree)
|
|
12213
|
-
parentTree: this.parentContext?.tree,
|
|
12214
|
-
parentNodeId: this.parentContext?.nodeId,
|
|
12215
|
-
baseDepth: this.parentContext ? (this.parentContext.depth ?? 0) + 1 : 0,
|
|
12216
|
-
// Parent observer hooks for subagent visibility
|
|
12217
|
-
parentObservers: this.parentObservers,
|
|
12218
|
-
// Shared rate limit tracker and retry config (for coordinated limits across subagents)
|
|
12219
|
-
sharedRateLimitTracker: this.sharedRateLimitTracker,
|
|
12220
|
-
sharedRetryConfig: this.sharedRetryConfig
|
|
12221
|
-
};
|
|
12210
|
+
const options = this.buildAgentOptions();
|
|
12222
12211
|
return new Agent(AGENT_INTERNAL_KEY, options);
|
|
12223
12212
|
}
|
|
12224
12213
|
};
|
|
@@ -13506,139 +13495,551 @@ var init_executor = __esm({
|
|
|
13506
13495
|
}
|
|
13507
13496
|
});
|
|
13508
13497
|
|
|
13509
|
-
// src/agent/
|
|
13510
|
-
|
|
13511
|
-
|
|
13512
|
-
|
|
13513
|
-
|
|
13514
|
-
|
|
13515
|
-
|
|
13516
|
-
|
|
13517
|
-
|
|
13518
|
-
|
|
13519
|
-
|
|
13520
|
-
|
|
13521
|
-
|
|
13522
|
-
|
|
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
|
-
if (cleanup) {
|
|
13570
|
-
newPromise.finally(() => chainMap.delete(key));
|
|
13571
|
-
}
|
|
13572
|
-
}
|
|
13573
|
-
function bridgeTreeToHooks(tree, hooks, logger2) {
|
|
13574
|
-
const gadgetPromiseChains = /* @__PURE__ */ new Map();
|
|
13575
|
-
const llmPromiseChains = /* @__PURE__ */ new Map();
|
|
13576
|
-
return tree.onAll((event) => {
|
|
13577
|
-
const subagentContext = buildSubagentContext(tree, event);
|
|
13578
|
-
switch (event.type) {
|
|
13579
|
-
// =================================================================
|
|
13580
|
-
// GADGET EVENTS - Bridged for subagent visibility
|
|
13581
|
-
// =================================================================
|
|
13582
|
-
// When a subagent executes gadgets, these events propagate through
|
|
13583
|
-
// the shared tree to the parent's hooks.
|
|
13584
|
-
// Only bridged for subagent events (depth > 0) to avoid double-calling
|
|
13585
|
-
// root agent events which are handled directly in stream-processor.ts
|
|
13586
|
-
case "gadget_start": {
|
|
13587
|
-
if (subagentContext && hooks.observers?.onGadgetExecutionStart) {
|
|
13588
|
-
const gadgetEvent = event;
|
|
13589
|
-
const gadgetNode = tree.getNode(event.nodeId);
|
|
13590
|
-
const context = {
|
|
13591
|
-
iteration: getIterationFromTree(tree, event.nodeId),
|
|
13592
|
-
gadgetName: gadgetEvent.name,
|
|
13593
|
-
invocationId: gadgetEvent.invocationId,
|
|
13594
|
-
parameters: gadgetNode?.parameters ?? {},
|
|
13595
|
-
logger: logger2,
|
|
13596
|
-
subagentContext
|
|
13597
|
-
};
|
|
13598
|
-
chainObserverCall(
|
|
13599
|
-
gadgetPromiseChains,
|
|
13600
|
-
gadgetEvent.invocationId,
|
|
13601
|
-
() => hooks.observers?.onGadgetExecutionStart?.(context),
|
|
13602
|
-
logger2,
|
|
13603
|
-
"onGadgetExecutionStart",
|
|
13604
|
-
false
|
|
13605
|
-
// Don't cleanup - wait for completion event
|
|
13606
|
-
);
|
|
13498
|
+
// src/agent/gadget-concurrency-manager.ts
|
|
13499
|
+
var GadgetConcurrencyManager;
|
|
13500
|
+
var init_gadget_concurrency_manager = __esm({
|
|
13501
|
+
"src/agent/gadget-concurrency-manager.ts"() {
|
|
13502
|
+
"use strict";
|
|
13503
|
+
init_logger();
|
|
13504
|
+
GadgetConcurrencyManager = class {
|
|
13505
|
+
registry;
|
|
13506
|
+
subagentConfig;
|
|
13507
|
+
logger;
|
|
13508
|
+
/** Track active execution count per gadget name */
|
|
13509
|
+
activeCountByGadget = /* @__PURE__ */ new Map();
|
|
13510
|
+
/** Queue of gadgets waiting for a concurrency slot (per gadget name) */
|
|
13511
|
+
concurrencyQueue = /* @__PURE__ */ new Map();
|
|
13512
|
+
/** All active gadget promises, keyed by invocationId */
|
|
13513
|
+
inFlightExecutions = /* @__PURE__ */ new Map();
|
|
13514
|
+
/** Queue of exclusive gadgets deferred until in-flight gadgets complete */
|
|
13515
|
+
exclusiveQueue = [];
|
|
13516
|
+
constructor(options) {
|
|
13517
|
+
this.registry = options.registry;
|
|
13518
|
+
this.subagentConfig = options.subagentConfig;
|
|
13519
|
+
this.logger = options.logger ?? createLogger({ name: "llmist:gadget-concurrency-manager" });
|
|
13520
|
+
}
|
|
13521
|
+
// ==========================================================================
|
|
13522
|
+
// Concurrency limit resolution
|
|
13523
|
+
// ==========================================================================
|
|
13524
|
+
/**
|
|
13525
|
+
* Get the effective concurrency limit for a gadget.
|
|
13526
|
+
* Uses "most restrictive wins" strategy: the lowest non-zero value from
|
|
13527
|
+
* external config (SubagentConfig) and gadget's intrinsic maxConcurrent.
|
|
13528
|
+
*
|
|
13529
|
+
* This ensures gadget authors can set safety floors (e.g., maxConcurrent: 1
|
|
13530
|
+
* for file writers) that cannot be weakened by external configuration.
|
|
13531
|
+
*
|
|
13532
|
+
* @returns 0 if unlimited, otherwise the effective limit
|
|
13533
|
+
*/
|
|
13534
|
+
getConcurrencyLimit(gadgetName) {
|
|
13535
|
+
const configLimit = this.subagentConfig?.[gadgetName]?.maxConcurrent;
|
|
13536
|
+
const gadget = this.registry.get(gadgetName);
|
|
13537
|
+
const gadgetLimit = gadget?.maxConcurrent;
|
|
13538
|
+
const config = configLimit || Number.POSITIVE_INFINITY;
|
|
13539
|
+
const intrinsic = gadgetLimit || Number.POSITIVE_INFINITY;
|
|
13540
|
+
const effective = Math.min(config, intrinsic);
|
|
13541
|
+
return effective === Number.POSITIVE_INFINITY ? 0 : effective;
|
|
13542
|
+
}
|
|
13543
|
+
// ==========================================================================
|
|
13544
|
+
// Concurrency checks
|
|
13545
|
+
// ==========================================================================
|
|
13546
|
+
/**
|
|
13547
|
+
* Check whether a gadget call can start immediately given current concurrency state.
|
|
13548
|
+
* Returns false if:
|
|
13549
|
+
* - The gadget is exclusive and other gadgets are in-flight
|
|
13550
|
+
* - The gadget has a concurrency limit and it is already reached
|
|
13551
|
+
*
|
|
13552
|
+
* Does NOT modify any state.
|
|
13553
|
+
*/
|
|
13554
|
+
canStart(call) {
|
|
13555
|
+
const gadget = this.registry.get(call.gadgetName);
|
|
13556
|
+
if (gadget?.exclusive && this.inFlightExecutions.size > 0) {
|
|
13557
|
+
return false;
|
|
13607
13558
|
}
|
|
13608
|
-
|
|
13559
|
+
const limit = this.getConcurrencyLimit(call.gadgetName);
|
|
13560
|
+
if (limit > 0) {
|
|
13561
|
+
const activeCount = this.activeCountByGadget.get(call.gadgetName) ?? 0;
|
|
13562
|
+
if (activeCount >= limit) {
|
|
13563
|
+
return false;
|
|
13564
|
+
}
|
|
13565
|
+
}
|
|
13566
|
+
return true;
|
|
13609
13567
|
}
|
|
13610
|
-
|
|
13611
|
-
|
|
13612
|
-
|
|
13613
|
-
|
|
13614
|
-
|
|
13615
|
-
|
|
13616
|
-
|
|
13617
|
-
|
|
13618
|
-
|
|
13619
|
-
|
|
13620
|
-
|
|
13621
|
-
|
|
13622
|
-
|
|
13623
|
-
|
|
13624
|
-
|
|
13625
|
-
|
|
13626
|
-
|
|
13627
|
-
|
|
13628
|
-
|
|
13629
|
-
|
|
13630
|
-
|
|
13631
|
-
true
|
|
13632
|
-
// Cleanup after completion
|
|
13633
|
-
);
|
|
13568
|
+
/**
|
|
13569
|
+
* Check whether a gadget is marked as exclusive.
|
|
13570
|
+
*/
|
|
13571
|
+
isExclusive(gadgetName) {
|
|
13572
|
+
const gadget = this.registry.get(gadgetName);
|
|
13573
|
+
return gadget?.exclusive === true;
|
|
13574
|
+
}
|
|
13575
|
+
/**
|
|
13576
|
+
* Get the current count of in-flight (actively executing) gadgets.
|
|
13577
|
+
*/
|
|
13578
|
+
get inFlightCount() {
|
|
13579
|
+
return this.inFlightExecutions.size;
|
|
13580
|
+
}
|
|
13581
|
+
/**
|
|
13582
|
+
* Get the total count of actively executing gadgets across all gadget types.
|
|
13583
|
+
* Used to know when all work is truly complete.
|
|
13584
|
+
*/
|
|
13585
|
+
getTotalActiveGadgetCount() {
|
|
13586
|
+
let total = 0;
|
|
13587
|
+
for (const count of this.activeCountByGadget.values()) {
|
|
13588
|
+
total += count;
|
|
13634
13589
|
}
|
|
13635
|
-
|
|
13590
|
+
return total;
|
|
13636
13591
|
}
|
|
13637
|
-
|
|
13638
|
-
|
|
13639
|
-
|
|
13640
|
-
|
|
13641
|
-
|
|
13592
|
+
/**
|
|
13593
|
+
* Check if there are any gadgets waiting in concurrency queues.
|
|
13594
|
+
*/
|
|
13595
|
+
hasQueuedGadgets() {
|
|
13596
|
+
for (const queue of this.concurrencyQueue.values()) {
|
|
13597
|
+
if (queue.length > 0) return true;
|
|
13598
|
+
}
|
|
13599
|
+
return false;
|
|
13600
|
+
}
|
|
13601
|
+
/**
|
|
13602
|
+
* Get total count of queued gadgets across all queues.
|
|
13603
|
+
*/
|
|
13604
|
+
getQueuedGadgetCount() {
|
|
13605
|
+
let count = 0;
|
|
13606
|
+
for (const queue of this.concurrencyQueue.values()) {
|
|
13607
|
+
count += queue.length;
|
|
13608
|
+
}
|
|
13609
|
+
return count;
|
|
13610
|
+
}
|
|
13611
|
+
/**
|
|
13612
|
+
* Check if there are exclusive gadgets waiting.
|
|
13613
|
+
*/
|
|
13614
|
+
get hasExclusiveQueued() {
|
|
13615
|
+
return this.exclusiveQueue.length > 0;
|
|
13616
|
+
}
|
|
13617
|
+
/**
|
|
13618
|
+
* Drain and return all exclusive gadgets from the queue.
|
|
13619
|
+
* The caller is responsible for executing them.
|
|
13620
|
+
*/
|
|
13621
|
+
drainExclusiveQueue() {
|
|
13622
|
+
const queue = this.exclusiveQueue;
|
|
13623
|
+
this.exclusiveQueue = [];
|
|
13624
|
+
return queue;
|
|
13625
|
+
}
|
|
13626
|
+
// ==========================================================================
|
|
13627
|
+
// State mutation
|
|
13628
|
+
// ==========================================================================
|
|
13629
|
+
/**
|
|
13630
|
+
* Track an in-flight gadget execution.
|
|
13631
|
+
* Increments the active count for the gadget name and registers the promise.
|
|
13632
|
+
*
|
|
13633
|
+
* @param invocationId - Unique ID for this execution
|
|
13634
|
+
* @param gadgetName - Name of the gadget being executed
|
|
13635
|
+
* @param promise - The execution promise to track
|
|
13636
|
+
*/
|
|
13637
|
+
trackExecution(invocationId, gadgetName, promise) {
|
|
13638
|
+
const currentCount = this.activeCountByGadget.get(gadgetName) ?? 0;
|
|
13639
|
+
this.activeCountByGadget.set(gadgetName, currentCount + 1);
|
|
13640
|
+
this.inFlightExecutions.set(invocationId, promise);
|
|
13641
|
+
}
|
|
13642
|
+
/**
|
|
13643
|
+
* Called when a gadget execution completes.
|
|
13644
|
+
* Decrements active count and triggers queue processing if a slot opened up.
|
|
13645
|
+
*
|
|
13646
|
+
* @param gadgetName - Name of the gadget that completed
|
|
13647
|
+
* @returns The next queued gadget call for this gadget, if one was promoted, otherwise null
|
|
13648
|
+
*/
|
|
13649
|
+
onComplete(gadgetName) {
|
|
13650
|
+
const newCount = (this.activeCountByGadget.get(gadgetName) ?? 1) - 1;
|
|
13651
|
+
this.activeCountByGadget.set(gadgetName, newCount);
|
|
13652
|
+
return this.promoteFromQueue(gadgetName);
|
|
13653
|
+
}
|
|
13654
|
+
/**
|
|
13655
|
+
* Queue a gadget for later execution due to a concurrency limit being reached.
|
|
13656
|
+
*
|
|
13657
|
+
* @param call - The gadget call to defer
|
|
13658
|
+
*/
|
|
13659
|
+
queueForLater(call) {
|
|
13660
|
+
this.logger.debug("Gadget queued due to concurrency limit", {
|
|
13661
|
+
gadgetName: call.gadgetName,
|
|
13662
|
+
invocationId: call.invocationId,
|
|
13663
|
+
activeCount: this.activeCountByGadget.get(call.gadgetName) ?? 0,
|
|
13664
|
+
limit: this.getConcurrencyLimit(call.gadgetName)
|
|
13665
|
+
});
|
|
13666
|
+
const queue = this.concurrencyQueue.get(call.gadgetName) ?? [];
|
|
13667
|
+
queue.push(call);
|
|
13668
|
+
this.concurrencyQueue.set(call.gadgetName, queue);
|
|
13669
|
+
}
|
|
13670
|
+
/**
|
|
13671
|
+
* Queue a gadget for exclusive execution (after all in-flight complete).
|
|
13672
|
+
*
|
|
13673
|
+
* @param call - The exclusive gadget call to defer
|
|
13674
|
+
*/
|
|
13675
|
+
queueExclusive(call) {
|
|
13676
|
+
this.logger.debug("Deferring exclusive gadget until in-flight gadgets complete", {
|
|
13677
|
+
gadgetName: call.gadgetName,
|
|
13678
|
+
invocationId: call.invocationId,
|
|
13679
|
+
inFlightCount: this.inFlightExecutions.size
|
|
13680
|
+
});
|
|
13681
|
+
this.exclusiveQueue.push(call);
|
|
13682
|
+
}
|
|
13683
|
+
/**
|
|
13684
|
+
* Clear the inFlightExecutions map after all promises have completed.
|
|
13685
|
+
* Called after waitForAll resolves.
|
|
13686
|
+
*/
|
|
13687
|
+
clearInFlight() {
|
|
13688
|
+
this.inFlightExecutions.clear();
|
|
13689
|
+
}
|
|
13690
|
+
// ==========================================================================
|
|
13691
|
+
// Waiting
|
|
13692
|
+
// ==========================================================================
|
|
13693
|
+
/**
|
|
13694
|
+
* Wait for all currently in-flight gadget executions to complete.
|
|
13695
|
+
* Resolves when the Promise.all of all tracked promises resolves.
|
|
13696
|
+
*
|
|
13697
|
+
* Note: new executions may be started during waiting (from the queue).
|
|
13698
|
+
* Callers should loop until inFlightCount === 0 AND hasQueuedGadgets() === false.
|
|
13699
|
+
*/
|
|
13700
|
+
async waitForAll() {
|
|
13701
|
+
if (this.inFlightExecutions.size === 0) return;
|
|
13702
|
+
await Promise.all(this.inFlightExecutions.values());
|
|
13703
|
+
}
|
|
13704
|
+
/**
|
|
13705
|
+
* Get a promise that resolves when all current in-flight executions complete.
|
|
13706
|
+
* Returns a resolved promise if no executions are in-flight.
|
|
13707
|
+
*/
|
|
13708
|
+
getAllDonePromise() {
|
|
13709
|
+
if (this.inFlightExecutions.size === 0) {
|
|
13710
|
+
return Promise.resolve("done");
|
|
13711
|
+
}
|
|
13712
|
+
return Promise.all(this.inFlightExecutions.values()).then(() => "done");
|
|
13713
|
+
}
|
|
13714
|
+
// ==========================================================================
|
|
13715
|
+
// Private helpers
|
|
13716
|
+
// ==========================================================================
|
|
13717
|
+
/**
|
|
13718
|
+
* Check the concurrency queue for a gadget name and promote the next queued
|
|
13719
|
+
* call if a slot is available.
|
|
13720
|
+
*
|
|
13721
|
+
* @param gadgetName - The gadget name to check
|
|
13722
|
+
* @returns The promoted call, or null if queue is empty or limit still reached
|
|
13723
|
+
*/
|
|
13724
|
+
promoteFromQueue(gadgetName) {
|
|
13725
|
+
const queue = this.concurrencyQueue.get(gadgetName);
|
|
13726
|
+
if (!queue || queue.length === 0) return null;
|
|
13727
|
+
const limit = this.getConcurrencyLimit(gadgetName);
|
|
13728
|
+
const activeCount = this.activeCountByGadget.get(gadgetName) ?? 0;
|
|
13729
|
+
if (limit === 0 || activeCount < limit) {
|
|
13730
|
+
const nextCall = queue.shift();
|
|
13731
|
+
this.logger.debug("Processing queued gadget", {
|
|
13732
|
+
gadgetName,
|
|
13733
|
+
invocationId: nextCall.invocationId,
|
|
13734
|
+
remainingInQueue: queue.length
|
|
13735
|
+
});
|
|
13736
|
+
return nextCall;
|
|
13737
|
+
}
|
|
13738
|
+
return null;
|
|
13739
|
+
}
|
|
13740
|
+
};
|
|
13741
|
+
}
|
|
13742
|
+
});
|
|
13743
|
+
|
|
13744
|
+
// src/agent/gadget-dependency-resolver.ts
|
|
13745
|
+
var GadgetDependencyResolver;
|
|
13746
|
+
var init_gadget_dependency_resolver = __esm({
|
|
13747
|
+
"src/agent/gadget-dependency-resolver.ts"() {
|
|
13748
|
+
"use strict";
|
|
13749
|
+
GadgetDependencyResolver = class {
|
|
13750
|
+
/** Gadgets waiting for their dependencies to complete */
|
|
13751
|
+
gadgetsAwaitingDependencies = /* @__PURE__ */ new Map();
|
|
13752
|
+
/** Completed gadget results, keyed by invocation ID */
|
|
13753
|
+
completedResults = /* @__PURE__ */ new Map();
|
|
13754
|
+
/** Invocation IDs of gadgets that have failed (error or skipped due to dependency) */
|
|
13755
|
+
failedInvocations = /* @__PURE__ */ new Set();
|
|
13756
|
+
/** Invocation IDs completed in previous iterations (read-only) */
|
|
13757
|
+
priorCompletedInvocations;
|
|
13758
|
+
/** Invocation IDs that failed in previous iterations (read-only) */
|
|
13759
|
+
priorFailedInvocations;
|
|
13760
|
+
constructor(options = {}) {
|
|
13761
|
+
this.priorCompletedInvocations = options.priorCompletedInvocations ?? /* @__PURE__ */ new Set();
|
|
13762
|
+
this.priorFailedInvocations = options.priorFailedInvocations ?? /* @__PURE__ */ new Set();
|
|
13763
|
+
}
|
|
13764
|
+
// ==========================================================================
|
|
13765
|
+
// State mutation
|
|
13766
|
+
// ==========================================================================
|
|
13767
|
+
/**
|
|
13768
|
+
* Queue a gadget call that is waiting for one or more dependencies to complete.
|
|
13769
|
+
* Call this when a gadget's dependencies are not yet all satisfied.
|
|
13770
|
+
*
|
|
13771
|
+
* @param call - The parsed gadget call to defer
|
|
13772
|
+
*/
|
|
13773
|
+
addPending(call) {
|
|
13774
|
+
this.gadgetsAwaitingDependencies.set(call.invocationId, call);
|
|
13775
|
+
}
|
|
13776
|
+
/**
|
|
13777
|
+
* Record that a gadget completed successfully.
|
|
13778
|
+
* This may unblock other gadgets that depend on this invocation.
|
|
13779
|
+
*
|
|
13780
|
+
* Also marks as failed if the result contains an error.
|
|
13781
|
+
*
|
|
13782
|
+
* @param result - The completed gadget execution result
|
|
13783
|
+
*/
|
|
13784
|
+
markComplete(result) {
|
|
13785
|
+
this.completedResults.set(result.invocationId, result);
|
|
13786
|
+
if (result.error) {
|
|
13787
|
+
this.failedInvocations.add(result.invocationId);
|
|
13788
|
+
}
|
|
13789
|
+
}
|
|
13790
|
+
/**
|
|
13791
|
+
* Mark an invocation ID as failed without recording a full result.
|
|
13792
|
+
* Use this for gadgets that are skipped before execution (e.g., limit exceeded,
|
|
13793
|
+
* self-referential dependency, dependency skip).
|
|
13794
|
+
*
|
|
13795
|
+
* @param invocationId - The invocation ID to mark as failed
|
|
13796
|
+
*/
|
|
13797
|
+
markFailed(invocationId) {
|
|
13798
|
+
this.failedInvocations.add(invocationId);
|
|
13799
|
+
}
|
|
13800
|
+
/**
|
|
13801
|
+
* Remove a gadget from the pending queue (called just before execution).
|
|
13802
|
+
*
|
|
13803
|
+
* @param invocationId - The invocation ID to remove from pending
|
|
13804
|
+
*/
|
|
13805
|
+
removePending(invocationId) {
|
|
13806
|
+
this.gadgetsAwaitingDependencies.delete(invocationId);
|
|
13807
|
+
}
|
|
13808
|
+
/**
|
|
13809
|
+
* Clear all remaining pending gadgets (e.g., after handling unresolvable deps).
|
|
13810
|
+
*/
|
|
13811
|
+
clearPending() {
|
|
13812
|
+
this.gadgetsAwaitingDependencies.clear();
|
|
13813
|
+
}
|
|
13814
|
+
// ==========================================================================
|
|
13815
|
+
// Queries
|
|
13816
|
+
// ==========================================================================
|
|
13817
|
+
/**
|
|
13818
|
+
* Get all gadget calls currently waiting for dependencies.
|
|
13819
|
+
* Returns entries as [invocationId, call] pairs.
|
|
13820
|
+
*/
|
|
13821
|
+
getPendingEntries() {
|
|
13822
|
+
return Array.from(this.gadgetsAwaitingDependencies.entries());
|
|
13823
|
+
}
|
|
13824
|
+
/**
|
|
13825
|
+
* Get the number of gadgets currently waiting for dependencies.
|
|
13826
|
+
*/
|
|
13827
|
+
get pendingCount() {
|
|
13828
|
+
return this.gadgetsAwaitingDependencies.size;
|
|
13829
|
+
}
|
|
13830
|
+
/**
|
|
13831
|
+
* Check whether a given invocation ID has been completed successfully
|
|
13832
|
+
* (either in this iteration or a prior one).
|
|
13833
|
+
*/
|
|
13834
|
+
isCompleted(invocationId) {
|
|
13835
|
+
return this.completedResults.has(invocationId) || this.priorCompletedInvocations.has(invocationId);
|
|
13836
|
+
}
|
|
13837
|
+
/**
|
|
13838
|
+
* Check whether a given invocation ID has failed
|
|
13839
|
+
* (either in this iteration or a prior one).
|
|
13840
|
+
*/
|
|
13841
|
+
isFailed(invocationId) {
|
|
13842
|
+
return this.failedInvocations.has(invocationId) || this.priorFailedInvocations.has(invocationId);
|
|
13843
|
+
}
|
|
13844
|
+
/**
|
|
13845
|
+
* Get the execution result for a completed invocation, if available.
|
|
13846
|
+
* Only returns results from the current iteration; prior iterations
|
|
13847
|
+
* are tracked by ID only.
|
|
13848
|
+
*/
|
|
13849
|
+
getCompletedResult(invocationId) {
|
|
13850
|
+
return this.completedResults.get(invocationId);
|
|
13851
|
+
}
|
|
13852
|
+
/**
|
|
13853
|
+
* Check if all dependencies for a gadget call are satisfied.
|
|
13854
|
+
* A dependency is satisfied if it completed in this or a prior iteration.
|
|
13855
|
+
*
|
|
13856
|
+
* @param call - The gadget call whose dependencies to check
|
|
13857
|
+
* @returns true if all deps are satisfied, false if any are still pending
|
|
13858
|
+
*/
|
|
13859
|
+
isAllSatisfied(call) {
|
|
13860
|
+
return call.dependencies.every((dep) => this.isCompleted(dep));
|
|
13861
|
+
}
|
|
13862
|
+
/**
|
|
13863
|
+
* Find the first failed dependency for a gadget call, if any.
|
|
13864
|
+
* A dependency is considered failed if it failed in this or a prior iteration.
|
|
13865
|
+
*
|
|
13866
|
+
* @param call - The gadget call to check
|
|
13867
|
+
* @returns The invocation ID of the failed dependency, or undefined if none
|
|
13868
|
+
*/
|
|
13869
|
+
getFailedDependency(call) {
|
|
13870
|
+
return call.dependencies.find((dep) => this.isFailed(dep));
|
|
13871
|
+
}
|
|
13872
|
+
/**
|
|
13873
|
+
* Separate the pending gadgets into two groups:
|
|
13874
|
+
* - Those ready to execute (all deps satisfied)
|
|
13875
|
+
* - Those ready to skip (at least one dep has failed)
|
|
13876
|
+
*
|
|
13877
|
+
* Gadgets that are neither ready nor skippable remain pending.
|
|
13878
|
+
*
|
|
13879
|
+
* @returns Object with `readyToExecute` and `readyToSkip` arrays
|
|
13880
|
+
*/
|
|
13881
|
+
getReadyCalls() {
|
|
13882
|
+
const readyToExecute = [];
|
|
13883
|
+
const readyToSkip = [];
|
|
13884
|
+
for (const [_invocationId, call] of this.gadgetsAwaitingDependencies) {
|
|
13885
|
+
const failedDep = this.getFailedDependency(call);
|
|
13886
|
+
if (failedDep) {
|
|
13887
|
+
readyToSkip.push({ call, failedDep });
|
|
13888
|
+
continue;
|
|
13889
|
+
}
|
|
13890
|
+
if (this.isAllSatisfied(call)) {
|
|
13891
|
+
readyToExecute.push(call);
|
|
13892
|
+
}
|
|
13893
|
+
}
|
|
13894
|
+
return { readyToExecute, readyToSkip };
|
|
13895
|
+
}
|
|
13896
|
+
// ==========================================================================
|
|
13897
|
+
// Cross-iteration accessors (for Agent to accumulate state)
|
|
13898
|
+
// ==========================================================================
|
|
13899
|
+
/**
|
|
13900
|
+
* Get all invocation IDs that completed successfully in this iteration.
|
|
13901
|
+
* Used by Agent to accumulate completed IDs across iterations.
|
|
13902
|
+
*/
|
|
13903
|
+
getCompletedInvocationIds() {
|
|
13904
|
+
return new Set(this.completedResults.keys());
|
|
13905
|
+
}
|
|
13906
|
+
/**
|
|
13907
|
+
* Get all invocation IDs that failed in this iteration.
|
|
13908
|
+
* Used by Agent to accumulate failed IDs across iterations.
|
|
13909
|
+
*/
|
|
13910
|
+
getFailedInvocationIds() {
|
|
13911
|
+
return new Set(this.failedInvocations);
|
|
13912
|
+
}
|
|
13913
|
+
};
|
|
13914
|
+
}
|
|
13915
|
+
});
|
|
13916
|
+
|
|
13917
|
+
// src/agent/tree-hook-bridge.ts
|
|
13918
|
+
function findParentGadgetInvocationId(tree, nodeId) {
|
|
13919
|
+
let currentId = nodeId;
|
|
13920
|
+
while (currentId) {
|
|
13921
|
+
const node = tree.getNode(currentId);
|
|
13922
|
+
if (!node) break;
|
|
13923
|
+
currentId = node.parentId;
|
|
13924
|
+
if (!currentId) break;
|
|
13925
|
+
const parentNode = tree.getNode(currentId);
|
|
13926
|
+
if (parentNode?.type === "gadget") {
|
|
13927
|
+
return parentNode.invocationId;
|
|
13928
|
+
}
|
|
13929
|
+
}
|
|
13930
|
+
return void 0;
|
|
13931
|
+
}
|
|
13932
|
+
function getIterationFromTree(tree, nodeId) {
|
|
13933
|
+
let currentId = nodeId;
|
|
13934
|
+
while (currentId) {
|
|
13935
|
+
const node = tree.getNode(currentId);
|
|
13936
|
+
if (!node) break;
|
|
13937
|
+
if (node.type === "llm_call") {
|
|
13938
|
+
return node.iteration;
|
|
13939
|
+
}
|
|
13940
|
+
currentId = node.parentId;
|
|
13941
|
+
}
|
|
13942
|
+
return 0;
|
|
13943
|
+
}
|
|
13944
|
+
function buildSubagentContext(tree, event) {
|
|
13945
|
+
const parentGadgetInvocationId = findParentGadgetInvocationId(tree, event.nodeId);
|
|
13946
|
+
if (!parentGadgetInvocationId) {
|
|
13947
|
+
return void 0;
|
|
13948
|
+
}
|
|
13949
|
+
return {
|
|
13950
|
+
parentGadgetInvocationId,
|
|
13951
|
+
depth: event.depth
|
|
13952
|
+
};
|
|
13953
|
+
}
|
|
13954
|
+
function getSubagentContextForNode(tree, nodeId) {
|
|
13955
|
+
const node = tree.getNode(nodeId);
|
|
13956
|
+
if (!node) return void 0;
|
|
13957
|
+
const parentGadgetInvocationId = findParentGadgetInvocationId(tree, nodeId);
|
|
13958
|
+
if (!parentGadgetInvocationId) {
|
|
13959
|
+
return void 0;
|
|
13960
|
+
}
|
|
13961
|
+
return {
|
|
13962
|
+
parentGadgetInvocationId,
|
|
13963
|
+
depth: node.depth
|
|
13964
|
+
};
|
|
13965
|
+
}
|
|
13966
|
+
function chainObserverCall(chainMap, key, fn, logger2, eventType, cleanup = false) {
|
|
13967
|
+
const previousPromise = chainMap.get(key) ?? Promise.resolve();
|
|
13968
|
+
const newPromise = previousPromise.then(() => safeObserve(fn, logger2, eventType));
|
|
13969
|
+
chainMap.set(key, newPromise);
|
|
13970
|
+
if (cleanup) {
|
|
13971
|
+
newPromise.finally(() => chainMap.delete(key));
|
|
13972
|
+
}
|
|
13973
|
+
}
|
|
13974
|
+
function bridgeTreeToHooks(tree, hooks, logger2) {
|
|
13975
|
+
const gadgetPromiseChains = /* @__PURE__ */ new Map();
|
|
13976
|
+
const llmPromiseChains = /* @__PURE__ */ new Map();
|
|
13977
|
+
return tree.onAll((event) => {
|
|
13978
|
+
const subagentContext = buildSubagentContext(tree, event);
|
|
13979
|
+
switch (event.type) {
|
|
13980
|
+
// =================================================================
|
|
13981
|
+
// GADGET EVENTS - Bridged for subagent visibility
|
|
13982
|
+
// =================================================================
|
|
13983
|
+
// When a subagent executes gadgets, these events propagate through
|
|
13984
|
+
// the shared tree to the parent's hooks.
|
|
13985
|
+
// Only bridged for subagent events (depth > 0) to avoid double-calling
|
|
13986
|
+
// root agent events which are handled directly in stream-processor.ts
|
|
13987
|
+
case "gadget_start": {
|
|
13988
|
+
if (subagentContext && hooks.observers?.onGadgetExecutionStart) {
|
|
13989
|
+
const gadgetEvent = event;
|
|
13990
|
+
const gadgetNode = tree.getNode(event.nodeId);
|
|
13991
|
+
const context = {
|
|
13992
|
+
iteration: getIterationFromTree(tree, event.nodeId),
|
|
13993
|
+
gadgetName: gadgetEvent.name,
|
|
13994
|
+
invocationId: gadgetEvent.invocationId,
|
|
13995
|
+
parameters: gadgetNode?.parameters ?? {},
|
|
13996
|
+
logger: logger2,
|
|
13997
|
+
subagentContext
|
|
13998
|
+
};
|
|
13999
|
+
chainObserverCall(
|
|
14000
|
+
gadgetPromiseChains,
|
|
14001
|
+
gadgetEvent.invocationId,
|
|
14002
|
+
() => hooks.observers?.onGadgetExecutionStart?.(context),
|
|
14003
|
+
logger2,
|
|
14004
|
+
"onGadgetExecutionStart",
|
|
14005
|
+
false
|
|
14006
|
+
// Don't cleanup - wait for completion event
|
|
14007
|
+
);
|
|
14008
|
+
}
|
|
14009
|
+
break;
|
|
14010
|
+
}
|
|
14011
|
+
case "gadget_complete": {
|
|
14012
|
+
if (subagentContext && hooks.observers?.onGadgetExecutionComplete) {
|
|
14013
|
+
const gadgetEvent = event;
|
|
14014
|
+
const gadgetNode = tree.getNode(event.nodeId);
|
|
14015
|
+
const context = {
|
|
14016
|
+
iteration: getIterationFromTree(tree, event.nodeId),
|
|
14017
|
+
gadgetName: gadgetEvent.name,
|
|
14018
|
+
invocationId: gadgetEvent.invocationId,
|
|
14019
|
+
parameters: gadgetNode?.parameters ?? {},
|
|
14020
|
+
finalResult: gadgetEvent.result,
|
|
14021
|
+
executionTimeMs: gadgetEvent.executionTimeMs,
|
|
14022
|
+
cost: gadgetEvent.cost,
|
|
14023
|
+
logger: logger2,
|
|
14024
|
+
subagentContext
|
|
14025
|
+
};
|
|
14026
|
+
chainObserverCall(
|
|
14027
|
+
gadgetPromiseChains,
|
|
14028
|
+
gadgetEvent.invocationId,
|
|
14029
|
+
() => hooks.observers?.onGadgetExecutionComplete?.(context),
|
|
14030
|
+
logger2,
|
|
14031
|
+
"onGadgetExecutionComplete",
|
|
14032
|
+
true
|
|
14033
|
+
// Cleanup after completion
|
|
14034
|
+
);
|
|
14035
|
+
}
|
|
14036
|
+
break;
|
|
14037
|
+
}
|
|
14038
|
+
case "gadget_error": {
|
|
14039
|
+
if (subagentContext && hooks.observers?.onGadgetExecutionComplete) {
|
|
14040
|
+
const gadgetEvent = event;
|
|
14041
|
+
const gadgetNode = tree.getNode(event.nodeId);
|
|
14042
|
+
const context = {
|
|
13642
14043
|
iteration: getIterationFromTree(tree, event.nodeId),
|
|
13643
14044
|
gadgetName: gadgetEvent.name,
|
|
13644
14045
|
invocationId: gadgetEvent.invocationId,
|
|
@@ -13785,6 +14186,82 @@ function bridgeTreeToHooks(tree, hooks, logger2) {
|
|
|
13785
14186
|
var init_tree_hook_bridge = __esm({
|
|
13786
14187
|
"src/agent/tree-hook-bridge.ts"() {
|
|
13787
14188
|
"use strict";
|
|
14189
|
+
init_safe_observe();
|
|
14190
|
+
}
|
|
14191
|
+
});
|
|
14192
|
+
|
|
14193
|
+
// src/agent/observer-notifier.ts
|
|
14194
|
+
async function notifyGadgetSkipped(ctx) {
|
|
14195
|
+
const gadgetNode = ctx.tree?.getNodeByInvocationId(ctx.invocationId);
|
|
14196
|
+
const subagentContext = ctx.tree && gadgetNode ? getSubagentContextForNode(ctx.tree, gadgetNode.id) : void 0;
|
|
14197
|
+
const context = {
|
|
14198
|
+
iteration: ctx.iteration,
|
|
14199
|
+
gadgetName: ctx.gadgetName,
|
|
14200
|
+
invocationId: ctx.invocationId,
|
|
14201
|
+
parameters: ctx.parameters,
|
|
14202
|
+
failedDependency: ctx.failedDependency,
|
|
14203
|
+
failedDependencyError: ctx.failedDependencyError,
|
|
14204
|
+
logger: ctx.logger,
|
|
14205
|
+
subagentContext
|
|
14206
|
+
};
|
|
14207
|
+
if (ctx.hooks?.onGadgetSkipped) {
|
|
14208
|
+
const hookFn = ctx.hooks.onGadgetSkipped;
|
|
14209
|
+
await safeObserve(() => hookFn(context), ctx.logger);
|
|
14210
|
+
}
|
|
14211
|
+
if (ctx.parentObservers?.onGadgetSkipped) {
|
|
14212
|
+
const hookFn = ctx.parentObservers.onGadgetSkipped;
|
|
14213
|
+
await safeObserve(() => hookFn(context), ctx.logger);
|
|
14214
|
+
}
|
|
14215
|
+
}
|
|
14216
|
+
async function notifyGadgetStart(ctx) {
|
|
14217
|
+
const gadgetNode = ctx.tree?.getNodeByInvocationId(ctx.invocationId);
|
|
14218
|
+
const subagentContext = ctx.tree && gadgetNode ? getSubagentContextForNode(ctx.tree, gadgetNode.id) : void 0;
|
|
14219
|
+
const context = {
|
|
14220
|
+
iteration: ctx.iteration,
|
|
14221
|
+
gadgetName: ctx.gadgetName,
|
|
14222
|
+
invocationId: ctx.invocationId,
|
|
14223
|
+
parameters: ctx.parameters,
|
|
14224
|
+
logger: ctx.logger,
|
|
14225
|
+
subagentContext
|
|
14226
|
+
};
|
|
14227
|
+
if (ctx.hooks?.onGadgetExecutionStart) {
|
|
14228
|
+
const hookFn = ctx.hooks.onGadgetExecutionStart;
|
|
14229
|
+
await safeObserve(() => hookFn(context), ctx.logger);
|
|
14230
|
+
}
|
|
14231
|
+
if (ctx.parentObservers?.onGadgetExecutionStart) {
|
|
14232
|
+
const hookFn = ctx.parentObservers.onGadgetExecutionStart;
|
|
14233
|
+
await safeObserve(() => hookFn(context), ctx.logger);
|
|
14234
|
+
}
|
|
14235
|
+
}
|
|
14236
|
+
async function notifyGadgetComplete(ctx) {
|
|
14237
|
+
const gadgetNode = ctx.tree?.getNodeByInvocationId(ctx.invocationId);
|
|
14238
|
+
const subagentContext = ctx.tree && gadgetNode ? getSubagentContextForNode(ctx.tree, gadgetNode.id) : void 0;
|
|
14239
|
+
const context = {
|
|
14240
|
+
iteration: ctx.iteration,
|
|
14241
|
+
gadgetName: ctx.gadgetName,
|
|
14242
|
+
invocationId: ctx.invocationId,
|
|
14243
|
+
parameters: ctx.parameters,
|
|
14244
|
+
finalResult: ctx.finalResult,
|
|
14245
|
+
error: ctx.error,
|
|
14246
|
+
executionTimeMs: ctx.executionTimeMs,
|
|
14247
|
+
cost: ctx.cost,
|
|
14248
|
+
logger: ctx.logger,
|
|
14249
|
+
subagentContext
|
|
14250
|
+
};
|
|
14251
|
+
if (ctx.hooks?.onGadgetExecutionComplete) {
|
|
14252
|
+
const hookFn = ctx.hooks.onGadgetExecutionComplete;
|
|
14253
|
+
await safeObserve(() => hookFn(context), ctx.logger);
|
|
14254
|
+
}
|
|
14255
|
+
if (ctx.parentObservers?.onGadgetExecutionComplete) {
|
|
14256
|
+
const hookFn = ctx.parentObservers.onGadgetExecutionComplete;
|
|
14257
|
+
await safeObserve(() => hookFn(context), ctx.logger);
|
|
14258
|
+
}
|
|
14259
|
+
}
|
|
14260
|
+
var init_observer_notifier = __esm({
|
|
14261
|
+
"src/agent/observer-notifier.ts"() {
|
|
14262
|
+
"use strict";
|
|
14263
|
+
init_safe_observe();
|
|
14264
|
+
init_tree_hook_bridge();
|
|
13788
14265
|
}
|
|
13789
14266
|
});
|
|
13790
14267
|
|
|
@@ -13796,11 +14273,13 @@ var init_stream_processor = __esm({
|
|
|
13796
14273
|
init_executor();
|
|
13797
14274
|
init_parser();
|
|
13798
14275
|
init_logger();
|
|
14276
|
+
init_gadget_concurrency_manager();
|
|
14277
|
+
init_gadget_dependency_resolver();
|
|
13799
14278
|
init_hook_validators();
|
|
13800
|
-
|
|
14279
|
+
init_observer_notifier();
|
|
14280
|
+
init_safe_observe();
|
|
13801
14281
|
StreamProcessor = class {
|
|
13802
14282
|
iteration;
|
|
13803
|
-
registry;
|
|
13804
14283
|
hooks;
|
|
13805
14284
|
logger;
|
|
13806
14285
|
parser;
|
|
@@ -13812,33 +14291,12 @@ var init_stream_processor = __esm({
|
|
|
13812
14291
|
// Gadget execution mode
|
|
13813
14292
|
gadgetExecutionMode;
|
|
13814
14293
|
responseText = "";
|
|
13815
|
-
|
|
13816
|
-
|
|
13817
|
-
|
|
13818
|
-
|
|
13819
|
-
/** Completed gadget results, keyed by invocation ID */
|
|
13820
|
-
completedResults = /* @__PURE__ */ new Map();
|
|
13821
|
-
/** Invocation IDs of gadgets that have failed (error or skipped due to dependency) */
|
|
13822
|
-
failedInvocations = /* @__PURE__ */ new Set();
|
|
13823
|
-
/** Promises for independent gadgets currently executing (fire-and-forget) */
|
|
13824
|
-
inFlightExecutions = /* @__PURE__ */ new Map();
|
|
14294
|
+
// Dependency resolution is delegated to GadgetDependencyResolver
|
|
14295
|
+
dependencyResolver;
|
|
14296
|
+
// Concurrency management is delegated to GadgetConcurrencyManager
|
|
14297
|
+
concurrencyManager;
|
|
13825
14298
|
/** Queue of completed gadget results ready to be yielded (for real-time streaming) */
|
|
13826
14299
|
completedResultsQueue = [];
|
|
13827
|
-
// Concurrency limiting
|
|
13828
|
-
/** Subagent configuration map for checking maxConcurrent limits */
|
|
13829
|
-
subagentConfig;
|
|
13830
|
-
/** Track active execution count per gadget name */
|
|
13831
|
-
activeCountByGadget = /* @__PURE__ */ new Map();
|
|
13832
|
-
/** Queue of gadgets waiting for a concurrency slot (per gadget name) */
|
|
13833
|
-
concurrencyQueue = /* @__PURE__ */ new Map();
|
|
13834
|
-
// Exclusive gadget support
|
|
13835
|
-
/** Queue of exclusive gadgets deferred until in-flight gadgets complete */
|
|
13836
|
-
exclusiveQueue = [];
|
|
13837
|
-
// Cross-iteration dependency tracking
|
|
13838
|
-
/** Invocation IDs completed in previous iterations (read-only reference from Agent) */
|
|
13839
|
-
priorCompletedInvocations;
|
|
13840
|
-
/** Invocation IDs that failed in previous iterations (read-only reference from Agent) */
|
|
13841
|
-
priorFailedInvocations;
|
|
13842
14300
|
// Parent observer hooks for subagent visibility
|
|
13843
14301
|
parentObservers;
|
|
13844
14302
|
// Gadget limiting per response
|
|
@@ -13847,16 +14305,21 @@ var init_stream_processor = __esm({
|
|
|
13847
14305
|
limitExceeded = false;
|
|
13848
14306
|
constructor(options) {
|
|
13849
14307
|
this.iteration = options.iteration;
|
|
13850
|
-
this.registry = options.registry;
|
|
13851
14308
|
this.hooks = options.hooks ?? {};
|
|
13852
14309
|
this.logger = options.logger ?? createLogger({ name: "llmist:stream-processor" });
|
|
13853
14310
|
this.tree = options.tree;
|
|
13854
14311
|
this.parentNodeId = options.parentNodeId ?? null;
|
|
13855
14312
|
this.baseDepth = options.baseDepth ?? 0;
|
|
13856
14313
|
this.gadgetExecutionMode = options.gadgetExecutionMode ?? "parallel";
|
|
13857
|
-
this.
|
|
13858
|
-
|
|
13859
|
-
|
|
14314
|
+
this.dependencyResolver = new GadgetDependencyResolver({
|
|
14315
|
+
priorCompletedInvocations: options.priorCompletedInvocations,
|
|
14316
|
+
priorFailedInvocations: options.priorFailedInvocations
|
|
14317
|
+
});
|
|
14318
|
+
this.concurrencyManager = new GadgetConcurrencyManager({
|
|
14319
|
+
registry: options.registry,
|
|
14320
|
+
subagentConfig: options.subagentConfig,
|
|
14321
|
+
logger: this.logger.getSubLogger({ name: "concurrency" })
|
|
14322
|
+
});
|
|
13860
14323
|
this.parentObservers = options.parentObservers;
|
|
13861
14324
|
this.maxGadgetsPerResponse = options.maxGadgetsPerResponse ?? 0;
|
|
13862
14325
|
this.parser = new GadgetCallParser({
|
|
@@ -14098,7 +14561,7 @@ var init_stream_processor = __esm({
|
|
|
14098
14561
|
gadgetName: call.gadgetName,
|
|
14099
14562
|
invocationId: call.invocationId
|
|
14100
14563
|
});
|
|
14101
|
-
this.
|
|
14564
|
+
this.dependencyResolver.markFailed(call.invocationId);
|
|
14102
14565
|
const errorMessage = `Gadget "${call.invocationId}" cannot depend on itself (self-referential dependency)`;
|
|
14103
14566
|
const skipEvent = {
|
|
14104
14567
|
type: "gadget_skipped",
|
|
@@ -14109,39 +14572,21 @@ var init_stream_processor = __esm({
|
|
|
14109
14572
|
failedDependencyError: errorMessage
|
|
14110
14573
|
};
|
|
14111
14574
|
yield skipEvent;
|
|
14112
|
-
|
|
14113
|
-
|
|
14114
|
-
|
|
14115
|
-
|
|
14116
|
-
|
|
14117
|
-
|
|
14118
|
-
|
|
14119
|
-
|
|
14120
|
-
|
|
14121
|
-
|
|
14122
|
-
|
|
14123
|
-
|
|
14124
|
-
};
|
|
14125
|
-
await this.safeObserve(() => this.hooks.observers.onGadgetSkipped(context));
|
|
14126
|
-
}
|
|
14127
|
-
if (this.parentObservers?.onGadgetSkipped) {
|
|
14128
|
-
const context = {
|
|
14129
|
-
iteration: this.iteration,
|
|
14130
|
-
gadgetName: call.gadgetName,
|
|
14131
|
-
invocationId: call.invocationId,
|
|
14132
|
-
parameters: call.parameters ?? {},
|
|
14133
|
-
failedDependency: call.invocationId,
|
|
14134
|
-
failedDependencyError: errorMessage,
|
|
14135
|
-
logger: this.logger,
|
|
14136
|
-
subagentContext: skippedSubagentContext
|
|
14137
|
-
};
|
|
14138
|
-
await this.safeObserve(() => this.parentObservers.onGadgetSkipped(context));
|
|
14139
|
-
}
|
|
14575
|
+
await notifyGadgetSkipped({
|
|
14576
|
+
tree: this.tree,
|
|
14577
|
+
hooks: this.hooks.observers,
|
|
14578
|
+
parentObservers: this.parentObservers,
|
|
14579
|
+
logger: this.logger,
|
|
14580
|
+
iteration: this.iteration,
|
|
14581
|
+
gadgetName: call.gadgetName,
|
|
14582
|
+
invocationId: call.invocationId,
|
|
14583
|
+
parameters: call.parameters ?? {},
|
|
14584
|
+
failedDependency: call.invocationId,
|
|
14585
|
+
failedDependencyError: errorMessage
|
|
14586
|
+
});
|
|
14140
14587
|
return;
|
|
14141
14588
|
}
|
|
14142
|
-
const failedDep =
|
|
14143
|
-
(dep) => this.failedInvocations.has(dep) || this.priorFailedInvocations.has(dep)
|
|
14144
|
-
);
|
|
14589
|
+
const failedDep = this.dependencyResolver.getFailedDependency(call);
|
|
14145
14590
|
if (failedDep) {
|
|
14146
14591
|
const skipEvents = await this.handleFailedDependency(call, failedDep);
|
|
14147
14592
|
for (const evt of skipEvents) {
|
|
@@ -14149,16 +14594,16 @@ var init_stream_processor = __esm({
|
|
|
14149
14594
|
}
|
|
14150
14595
|
return;
|
|
14151
14596
|
}
|
|
14152
|
-
|
|
14153
|
-
|
|
14154
|
-
|
|
14155
|
-
|
|
14597
|
+
if (!this.dependencyResolver.isAllSatisfied(call)) {
|
|
14598
|
+
const unsatisfied = call.dependencies.filter(
|
|
14599
|
+
(dep) => !this.dependencyResolver.isCompleted(dep)
|
|
14600
|
+
);
|
|
14156
14601
|
this.logger.debug("Queueing gadget for later - waiting on dependencies", {
|
|
14157
14602
|
gadgetName: call.gadgetName,
|
|
14158
14603
|
invocationId: call.invocationId,
|
|
14159
14604
|
waitingOn: unsatisfied
|
|
14160
14605
|
});
|
|
14161
|
-
this.
|
|
14606
|
+
this.dependencyResolver.addPending(call);
|
|
14162
14607
|
return;
|
|
14163
14608
|
}
|
|
14164
14609
|
const limitCheckGen2 = this.checkGadgetLimitExceeded(call);
|
|
@@ -14187,28 +14632,12 @@ var init_stream_processor = __esm({
|
|
|
14187
14632
|
if (limitResult.value === true) {
|
|
14188
14633
|
return;
|
|
14189
14634
|
}
|
|
14190
|
-
|
|
14191
|
-
|
|
14192
|
-
this.logger.debug("Deferring exclusive gadget until in-flight gadgets complete", {
|
|
14193
|
-
gadgetName: call.gadgetName,
|
|
14194
|
-
invocationId: call.invocationId,
|
|
14195
|
-
inFlightCount: this.inFlightExecutions.size
|
|
14196
|
-
});
|
|
14197
|
-
this.exclusiveQueue.push(call);
|
|
14635
|
+
if (this.concurrencyManager.isExclusive(call.gadgetName) && this.concurrencyManager.inFlightCount > 0) {
|
|
14636
|
+
this.concurrencyManager.queueExclusive(call);
|
|
14198
14637
|
return;
|
|
14199
14638
|
}
|
|
14200
|
-
|
|
14201
|
-
|
|
14202
|
-
if (limit > 0 && activeCount >= limit) {
|
|
14203
|
-
this.logger.debug("Gadget queued due to concurrency limit", {
|
|
14204
|
-
gadgetName: call.gadgetName,
|
|
14205
|
-
invocationId: call.invocationId,
|
|
14206
|
-
activeCount,
|
|
14207
|
-
limit
|
|
14208
|
-
});
|
|
14209
|
-
const queue = this.concurrencyQueue.get(call.gadgetName) ?? [];
|
|
14210
|
-
queue.push(call);
|
|
14211
|
-
this.concurrencyQueue.set(call.gadgetName, queue);
|
|
14639
|
+
if (!this.concurrencyManager.canStart(call)) {
|
|
14640
|
+
this.concurrencyManager.queueForLater(call);
|
|
14212
14641
|
return;
|
|
14213
14642
|
}
|
|
14214
14643
|
if (this.gadgetExecutionMode === "sequential") {
|
|
@@ -14219,57 +14648,19 @@ var init_stream_processor = __esm({
|
|
|
14219
14648
|
this.startGadgetWithConcurrencyTracking(call);
|
|
14220
14649
|
}
|
|
14221
14650
|
}
|
|
14222
|
-
/**
|
|
14223
|
-
* Get the effective concurrency limit for a gadget.
|
|
14224
|
-
* Uses "most restrictive wins" strategy: the lowest non-zero value from
|
|
14225
|
-
* external config (SubagentConfig) and gadget's intrinsic maxConcurrent.
|
|
14226
|
-
*
|
|
14227
|
-
* This ensures gadget authors can set safety floors (e.g., maxConcurrent: 1
|
|
14228
|
-
* for file writers) that cannot be weakened by external configuration.
|
|
14229
|
-
*
|
|
14230
|
-
* @returns 0 if unlimited, otherwise the effective limit
|
|
14231
|
-
*/
|
|
14232
|
-
getConcurrencyLimit(gadgetName) {
|
|
14233
|
-
const configLimit = this.subagentConfig?.[gadgetName]?.maxConcurrent;
|
|
14234
|
-
const gadget = this.registry.get(gadgetName);
|
|
14235
|
-
const gadgetLimit = gadget?.maxConcurrent;
|
|
14236
|
-
const config = configLimit || Number.POSITIVE_INFINITY;
|
|
14237
|
-
const intrinsic = gadgetLimit || Number.POSITIVE_INFINITY;
|
|
14238
|
-
const effective = Math.min(config, intrinsic);
|
|
14239
|
-
return effective === Number.POSITIVE_INFINITY ? 0 : effective;
|
|
14240
|
-
}
|
|
14241
14651
|
/**
|
|
14242
14652
|
* Start a gadget execution with concurrency tracking.
|
|
14243
|
-
*
|
|
14653
|
+
* Delegates tracking to GadgetConcurrencyManager; schedules queue processing on completion.
|
|
14244
14654
|
*/
|
|
14245
14655
|
startGadgetWithConcurrencyTracking(call) {
|
|
14246
14656
|
const gadgetName = call.gadgetName;
|
|
14247
|
-
const currentCount = this.activeCountByGadget.get(gadgetName) ?? 0;
|
|
14248
|
-
this.activeCountByGadget.set(gadgetName, currentCount + 1);
|
|
14249
14657
|
const executionPromise = this.executeGadgetAndCollect(call).finally(() => {
|
|
14250
|
-
const
|
|
14251
|
-
|
|
14252
|
-
|
|
14658
|
+
const nextCall = this.concurrencyManager.onComplete(gadgetName);
|
|
14659
|
+
if (nextCall) {
|
|
14660
|
+
this.startGadgetWithConcurrencyTracking(nextCall);
|
|
14661
|
+
}
|
|
14253
14662
|
});
|
|
14254
|
-
this.
|
|
14255
|
-
}
|
|
14256
|
-
/**
|
|
14257
|
-
* Process the next queued gadget for a given gadget name if a slot is available.
|
|
14258
|
-
*/
|
|
14259
|
-
processQueuedGadget(gadgetName) {
|
|
14260
|
-
const queue = this.concurrencyQueue.get(gadgetName);
|
|
14261
|
-
if (!queue || queue.length === 0) return;
|
|
14262
|
-
const limit = this.getConcurrencyLimit(gadgetName);
|
|
14263
|
-
const activeCount = this.activeCountByGadget.get(gadgetName) ?? 0;
|
|
14264
|
-
if (limit === 0 || activeCount < limit) {
|
|
14265
|
-
const nextCall = queue.shift();
|
|
14266
|
-
this.logger.debug("Processing queued gadget", {
|
|
14267
|
-
gadgetName,
|
|
14268
|
-
invocationId: nextCall.invocationId,
|
|
14269
|
-
remainingInQueue: queue.length
|
|
14270
|
-
});
|
|
14271
|
-
this.startGadgetWithConcurrencyTracking(nextCall);
|
|
14272
|
-
}
|
|
14663
|
+
this.concurrencyManager.trackExecution(call.invocationId, gadgetName, executionPromise);
|
|
14273
14664
|
}
|
|
14274
14665
|
/**
|
|
14275
14666
|
* Execute a gadget through the full hook lifecycle and yield events.
|
|
@@ -14324,30 +14715,16 @@ var init_stream_processor = __esm({
|
|
|
14324
14715
|
this.tree.startGadget(gadgetNode.id);
|
|
14325
14716
|
}
|
|
14326
14717
|
}
|
|
14327
|
-
|
|
14328
|
-
|
|
14329
|
-
|
|
14330
|
-
|
|
14331
|
-
|
|
14332
|
-
|
|
14333
|
-
|
|
14334
|
-
|
|
14335
|
-
|
|
14336
|
-
|
|
14337
|
-
};
|
|
14338
|
-
await this.safeObserve(() => this.hooks.observers.onGadgetExecutionStart(context));
|
|
14339
|
-
}
|
|
14340
|
-
if (this.parentObservers?.onGadgetExecutionStart) {
|
|
14341
|
-
const context = {
|
|
14342
|
-
iteration: this.iteration,
|
|
14343
|
-
gadgetName: call.gadgetName,
|
|
14344
|
-
invocationId: call.invocationId,
|
|
14345
|
-
parameters,
|
|
14346
|
-
logger: this.logger,
|
|
14347
|
-
subagentContext: gadgetStartSubagentContext
|
|
14348
|
-
};
|
|
14349
|
-
await this.safeObserve(() => this.parentObservers.onGadgetExecutionStart(context));
|
|
14350
|
-
}
|
|
14718
|
+
await notifyGadgetStart({
|
|
14719
|
+
tree: this.tree,
|
|
14720
|
+
hooks: this.hooks.observers,
|
|
14721
|
+
parentObservers: this.parentObservers,
|
|
14722
|
+
logger: this.logger,
|
|
14723
|
+
iteration: this.iteration,
|
|
14724
|
+
gadgetName: call.gadgetName,
|
|
14725
|
+
invocationId: call.invocationId,
|
|
14726
|
+
parameters
|
|
14727
|
+
});
|
|
14351
14728
|
let result;
|
|
14352
14729
|
if (shouldSkip) {
|
|
14353
14730
|
result = {
|
|
@@ -14360,7 +14737,7 @@ var init_stream_processor = __esm({
|
|
|
14360
14737
|
} else {
|
|
14361
14738
|
result = await this.executor.execute(call);
|
|
14362
14739
|
}
|
|
14363
|
-
if (result.result && this.hooks.interceptors?.interceptGadgetResult) {
|
|
14740
|
+
if ((result.result || result.error) && this.hooks.interceptors?.interceptGadgetResult) {
|
|
14364
14741
|
const context = {
|
|
14365
14742
|
iteration: this.iteration,
|
|
14366
14743
|
gadgetName: result.gadgetName,
|
|
@@ -14369,7 +14746,12 @@ var init_stream_processor = __esm({
|
|
|
14369
14746
|
executionTimeMs: result.executionTimeMs,
|
|
14370
14747
|
logger: this.logger
|
|
14371
14748
|
};
|
|
14372
|
-
|
|
14749
|
+
if (result.result) {
|
|
14750
|
+
result.result = this.hooks.interceptors.interceptGadgetResult(result.result, context);
|
|
14751
|
+
}
|
|
14752
|
+
if (result.error) {
|
|
14753
|
+
result.error = this.hooks.interceptors.interceptGadgetResult(result.error, context);
|
|
14754
|
+
}
|
|
14373
14755
|
}
|
|
14374
14756
|
if (this.hooks.controllers?.afterGadgetExecution) {
|
|
14375
14757
|
const context = {
|
|
@@ -14416,42 +14798,21 @@ var init_stream_processor = __esm({
|
|
|
14416
14798
|
}
|
|
14417
14799
|
}
|
|
14418
14800
|
}
|
|
14419
|
-
|
|
14420
|
-
|
|
14421
|
-
|
|
14422
|
-
|
|
14423
|
-
|
|
14424
|
-
|
|
14425
|
-
|
|
14426
|
-
|
|
14427
|
-
|
|
14428
|
-
|
|
14429
|
-
|
|
14430
|
-
|
|
14431
|
-
|
|
14432
|
-
|
|
14433
|
-
|
|
14434
|
-
await this.safeObserve(() => this.hooks.observers.onGadgetExecutionComplete(context));
|
|
14435
|
-
}
|
|
14436
|
-
if (this.parentObservers?.onGadgetExecutionComplete) {
|
|
14437
|
-
const context = {
|
|
14438
|
-
iteration: this.iteration,
|
|
14439
|
-
gadgetName: result.gadgetName,
|
|
14440
|
-
invocationId: result.invocationId,
|
|
14441
|
-
parameters,
|
|
14442
|
-
finalResult: result.result,
|
|
14443
|
-
error: result.error,
|
|
14444
|
-
executionTimeMs: result.executionTimeMs,
|
|
14445
|
-
cost: result.cost,
|
|
14446
|
-
logger: this.logger,
|
|
14447
|
-
subagentContext: gadgetCompleteSubagentContext
|
|
14448
|
-
};
|
|
14449
|
-
await this.safeObserve(() => this.parentObservers.onGadgetExecutionComplete(context));
|
|
14450
|
-
}
|
|
14451
|
-
this.completedResults.set(result.invocationId, result);
|
|
14452
|
-
if (result.error) {
|
|
14453
|
-
this.failedInvocations.add(result.invocationId);
|
|
14454
|
-
}
|
|
14801
|
+
await notifyGadgetComplete({
|
|
14802
|
+
tree: this.tree,
|
|
14803
|
+
hooks: this.hooks.observers,
|
|
14804
|
+
parentObservers: this.parentObservers,
|
|
14805
|
+
logger: this.logger,
|
|
14806
|
+
iteration: this.iteration,
|
|
14807
|
+
gadgetName: result.gadgetName,
|
|
14808
|
+
invocationId: result.invocationId,
|
|
14809
|
+
parameters,
|
|
14810
|
+
finalResult: result.result,
|
|
14811
|
+
error: result.error,
|
|
14812
|
+
executionTimeMs: result.executionTimeMs,
|
|
14813
|
+
cost: result.cost
|
|
14814
|
+
});
|
|
14815
|
+
this.dependencyResolver.markComplete(result);
|
|
14455
14816
|
yield { type: "gadget_result", result };
|
|
14456
14817
|
}
|
|
14457
14818
|
/**
|
|
@@ -14483,77 +14844,45 @@ var init_stream_processor = __esm({
|
|
|
14483
14844
|
* Clears the inFlightExecutions map after all gadgets complete.
|
|
14484
14845
|
*/
|
|
14485
14846
|
async *waitForInFlightExecutions() {
|
|
14486
|
-
if (this.
|
|
14847
|
+
if (this.concurrencyManager.inFlightCount === 0 && !this.concurrencyManager.hasQueuedGadgets() && !this.concurrencyManager.hasExclusiveQueued) {
|
|
14487
14848
|
return;
|
|
14488
14849
|
}
|
|
14489
14850
|
this.logger.debug("Waiting for in-flight gadget executions", {
|
|
14490
|
-
count: this.
|
|
14491
|
-
|
|
14492
|
-
queuedCount: this.getQueuedGadgetCount()
|
|
14851
|
+
count: this.concurrencyManager.inFlightCount,
|
|
14852
|
+
queuedCount: this.concurrencyManager.getQueuedGadgetCount()
|
|
14493
14853
|
});
|
|
14494
14854
|
const POLL_INTERVAL_MS = 100;
|
|
14495
|
-
while (this.
|
|
14496
|
-
const allDone = this.
|
|
14855
|
+
while (this.concurrencyManager.inFlightCount > 0 || this.concurrencyManager.hasQueuedGadgets()) {
|
|
14856
|
+
const allDone = this.concurrencyManager.getAllDonePromise();
|
|
14497
14857
|
const result = await Promise.race([
|
|
14498
14858
|
allDone,
|
|
14499
14859
|
new Promise((resolve2) => setTimeout(() => resolve2("poll"), POLL_INTERVAL_MS))
|
|
14500
14860
|
]);
|
|
14501
14861
|
yield* this.drainCompletedResults();
|
|
14502
|
-
if (result === "done" && this.getTotalActiveGadgetCount() === 0 && !this.hasQueuedGadgets()) {
|
|
14862
|
+
if (result === "done" && this.concurrencyManager.getTotalActiveGadgetCount() === 0 && !this.concurrencyManager.hasQueuedGadgets()) {
|
|
14503
14863
|
break;
|
|
14504
14864
|
}
|
|
14505
14865
|
}
|
|
14506
|
-
this.
|
|
14507
|
-
if (this.
|
|
14866
|
+
this.concurrencyManager.clearInFlight();
|
|
14867
|
+
if (this.concurrencyManager.hasExclusiveQueued) {
|
|
14868
|
+
const exclusiveQueue = this.concurrencyManager.drainExclusiveQueue();
|
|
14508
14869
|
this.logger.debug("Processing deferred exclusive gadgets", {
|
|
14509
|
-
count:
|
|
14870
|
+
count: exclusiveQueue.length
|
|
14510
14871
|
});
|
|
14511
|
-
const
|
|
14512
|
-
this.exclusiveQueue = [];
|
|
14513
|
-
for (const call of queue) {
|
|
14872
|
+
for (const call of exclusiveQueue) {
|
|
14514
14873
|
for await (const evt of this.executeGadgetGenerator(call)) {
|
|
14515
14874
|
yield evt;
|
|
14516
14875
|
}
|
|
14517
14876
|
}
|
|
14518
14877
|
}
|
|
14519
14878
|
}
|
|
14520
|
-
/**
|
|
14521
|
-
* Check if there are any gadgets waiting in concurrency queues.
|
|
14522
|
-
*/
|
|
14523
|
-
hasQueuedGadgets() {
|
|
14524
|
-
for (const queue of this.concurrencyQueue.values()) {
|
|
14525
|
-
if (queue.length > 0) return true;
|
|
14526
|
-
}
|
|
14527
|
-
return false;
|
|
14528
|
-
}
|
|
14529
|
-
/**
|
|
14530
|
-
* Get total count of queued gadgets across all queues.
|
|
14531
|
-
*/
|
|
14532
|
-
getQueuedGadgetCount() {
|
|
14533
|
-
let count = 0;
|
|
14534
|
-
for (const queue of this.concurrencyQueue.values()) {
|
|
14535
|
-
count += queue.length;
|
|
14536
|
-
}
|
|
14537
|
-
return count;
|
|
14538
|
-
}
|
|
14539
|
-
/**
|
|
14540
|
-
* Get total count of actively executing gadgets across all types.
|
|
14541
|
-
* Used to know when all work is truly complete (not just when allDone resolves).
|
|
14542
|
-
*/
|
|
14543
|
-
getTotalActiveGadgetCount() {
|
|
14544
|
-
let total = 0;
|
|
14545
|
-
for (const count of this.activeCountByGadget.values()) {
|
|
14546
|
-
total += count;
|
|
14547
|
-
}
|
|
14548
|
-
return total;
|
|
14549
|
-
}
|
|
14550
14879
|
/**
|
|
14551
14880
|
* Handle a gadget that cannot execute because a dependency failed.
|
|
14552
14881
|
* Calls the onDependencySkipped controller to allow customization.
|
|
14553
14882
|
*/
|
|
14554
14883
|
async handleFailedDependency(call, failedDep) {
|
|
14555
14884
|
const events = [];
|
|
14556
|
-
const depResult = this.
|
|
14885
|
+
const depResult = this.dependencyResolver.getCompletedResult(failedDep);
|
|
14557
14886
|
const depError = depResult?.error ?? "Dependency failed";
|
|
14558
14887
|
let action = { action: "skip" };
|
|
14559
14888
|
if (this.hooks.controllers?.onDependencySkipped) {
|
|
@@ -14569,7 +14898,7 @@ var init_stream_processor = __esm({
|
|
|
14569
14898
|
action = await this.hooks.controllers.onDependencySkipped(context);
|
|
14570
14899
|
}
|
|
14571
14900
|
if (action.action === "skip") {
|
|
14572
|
-
this.
|
|
14901
|
+
this.dependencyResolver.markFailed(call.invocationId);
|
|
14573
14902
|
if (this.tree) {
|
|
14574
14903
|
const gadgetNode = this.tree.getNodeByInvocationId(call.invocationId);
|
|
14575
14904
|
if (gadgetNode) {
|
|
@@ -14585,34 +14914,18 @@ var init_stream_processor = __esm({
|
|
|
14585
14914
|
failedDependencyError: depError
|
|
14586
14915
|
};
|
|
14587
14916
|
events.push(skipEvent);
|
|
14588
|
-
|
|
14589
|
-
|
|
14590
|
-
|
|
14591
|
-
|
|
14592
|
-
|
|
14593
|
-
|
|
14594
|
-
|
|
14595
|
-
|
|
14596
|
-
|
|
14597
|
-
|
|
14598
|
-
|
|
14599
|
-
|
|
14600
|
-
};
|
|
14601
|
-
await this.safeObserve(() => this.hooks.observers.onGadgetSkipped(context));
|
|
14602
|
-
}
|
|
14603
|
-
if (this.parentObservers?.onGadgetSkipped) {
|
|
14604
|
-
const context = {
|
|
14605
|
-
iteration: this.iteration,
|
|
14606
|
-
gadgetName: call.gadgetName,
|
|
14607
|
-
invocationId: call.invocationId,
|
|
14608
|
-
parameters: call.parameters ?? {},
|
|
14609
|
-
failedDependency: failedDep,
|
|
14610
|
-
failedDependencyError: depError,
|
|
14611
|
-
logger: this.logger,
|
|
14612
|
-
subagentContext: skipSubagentContext
|
|
14613
|
-
};
|
|
14614
|
-
await this.safeObserve(() => this.parentObservers.onGadgetSkipped(context));
|
|
14615
|
-
}
|
|
14917
|
+
await notifyGadgetSkipped({
|
|
14918
|
+
tree: this.tree,
|
|
14919
|
+
hooks: this.hooks.observers,
|
|
14920
|
+
parentObservers: this.parentObservers,
|
|
14921
|
+
logger: this.logger,
|
|
14922
|
+
iteration: this.iteration,
|
|
14923
|
+
gadgetName: call.gadgetName,
|
|
14924
|
+
invocationId: call.invocationId,
|
|
14925
|
+
parameters: call.parameters ?? {},
|
|
14926
|
+
failedDependency: failedDep,
|
|
14927
|
+
failedDependencyError: depError
|
|
14928
|
+
});
|
|
14616
14929
|
this.logger.info("Gadget skipped due to failed dependency", {
|
|
14617
14930
|
gadgetName: call.gadgetName,
|
|
14618
14931
|
invocationId: call.invocationId,
|
|
@@ -14635,7 +14948,7 @@ var init_stream_processor = __esm({
|
|
|
14635
14948
|
result: action.fallbackResult,
|
|
14636
14949
|
executionTimeMs: 0
|
|
14637
14950
|
};
|
|
14638
|
-
this.
|
|
14951
|
+
this.dependencyResolver.markComplete(fallbackResult);
|
|
14639
14952
|
events.push({ type: "gadget_result", result: fallbackResult });
|
|
14640
14953
|
this.logger.info("Using fallback result for gadget with failed dependency", {
|
|
14641
14954
|
gadgetName: call.gadgetName,
|
|
@@ -14666,7 +14979,7 @@ var init_stream_processor = __esm({
|
|
|
14666
14979
|
limit: this.maxGadgetsPerResponse,
|
|
14667
14980
|
currentCount: this.gadgetStartedCount
|
|
14668
14981
|
});
|
|
14669
|
-
this.
|
|
14982
|
+
this.dependencyResolver.markFailed(call.invocationId);
|
|
14670
14983
|
if (this.tree) {
|
|
14671
14984
|
const gadgetNode = this.tree.getNodeByInvocationId(call.invocationId);
|
|
14672
14985
|
if (gadgetNode) {
|
|
@@ -14687,34 +15000,18 @@ var init_stream_processor = __esm({
|
|
|
14687
15000
|
failedDependencyError: errorMessage
|
|
14688
15001
|
};
|
|
14689
15002
|
yield skipEvent;
|
|
14690
|
-
|
|
14691
|
-
|
|
14692
|
-
|
|
14693
|
-
|
|
14694
|
-
|
|
14695
|
-
|
|
14696
|
-
|
|
14697
|
-
|
|
14698
|
-
|
|
14699
|
-
|
|
14700
|
-
|
|
14701
|
-
|
|
14702
|
-
};
|
|
14703
|
-
await this.safeObserve(() => this.hooks.observers.onGadgetSkipped(context));
|
|
14704
|
-
}
|
|
14705
|
-
if (this.parentObservers?.onGadgetSkipped) {
|
|
14706
|
-
const context = {
|
|
14707
|
-
iteration: this.iteration,
|
|
14708
|
-
gadgetName: call.gadgetName,
|
|
14709
|
-
invocationId: call.invocationId,
|
|
14710
|
-
parameters: call.parameters ?? {},
|
|
14711
|
-
failedDependency: "maxGadgetsPerResponse",
|
|
14712
|
-
failedDependencyError: errorMessage,
|
|
14713
|
-
logger: this.logger,
|
|
14714
|
-
subagentContext: limitSkipSubagentContext
|
|
14715
|
-
};
|
|
14716
|
-
await this.safeObserve(() => this.parentObservers.onGadgetSkipped(context));
|
|
14717
|
-
}
|
|
15003
|
+
await notifyGadgetSkipped({
|
|
15004
|
+
tree: this.tree,
|
|
15005
|
+
hooks: this.hooks.observers,
|
|
15006
|
+
parentObservers: this.parentObservers,
|
|
15007
|
+
logger: this.logger,
|
|
15008
|
+
iteration: this.iteration,
|
|
15009
|
+
gadgetName: call.gadgetName,
|
|
15010
|
+
invocationId: call.invocationId,
|
|
15011
|
+
parameters: call.parameters ?? {},
|
|
15012
|
+
failedDependency: "maxGadgetsPerResponse",
|
|
15013
|
+
failedDependencyError: errorMessage
|
|
15014
|
+
});
|
|
14718
15015
|
return true;
|
|
14719
15016
|
}
|
|
14720
15017
|
this.gadgetStartedCount++;
|
|
@@ -14732,27 +15029,11 @@ var init_stream_processor = __esm({
|
|
|
14732
15029
|
return;
|
|
14733
15030
|
}
|
|
14734
15031
|
let progress = true;
|
|
14735
|
-
while (progress && this.
|
|
15032
|
+
while (progress && this.dependencyResolver.pendingCount > 0) {
|
|
14736
15033
|
progress = false;
|
|
14737
|
-
const readyToExecute =
|
|
14738
|
-
const readyToSkip = [];
|
|
14739
|
-
for (const [_invocationId, call] of this.gadgetsAwaitingDependencies) {
|
|
14740
|
-
const failedDep = call.dependencies.find(
|
|
14741
|
-
(dep) => this.failedInvocations.has(dep) || this.priorFailedInvocations.has(dep)
|
|
14742
|
-
);
|
|
14743
|
-
if (failedDep) {
|
|
14744
|
-
readyToSkip.push({ call, failedDep });
|
|
14745
|
-
continue;
|
|
14746
|
-
}
|
|
14747
|
-
const allSatisfied = call.dependencies.every(
|
|
14748
|
-
(dep) => this.completedResults.has(dep) || this.priorCompletedInvocations.has(dep)
|
|
14749
|
-
);
|
|
14750
|
-
if (allSatisfied) {
|
|
14751
|
-
readyToExecute.push(call);
|
|
14752
|
-
}
|
|
14753
|
-
}
|
|
15034
|
+
const { readyToExecute, readyToSkip } = this.dependencyResolver.getReadyCalls();
|
|
14754
15035
|
for (const { call, failedDep } of readyToSkip) {
|
|
14755
|
-
this.
|
|
15036
|
+
this.dependencyResolver.removePending(call.invocationId);
|
|
14756
15037
|
const skipEvents = await this.handleFailedDependency(call, failedDep);
|
|
14757
15038
|
for (const evt of skipEvents) {
|
|
14758
15039
|
yield evt;
|
|
@@ -14761,7 +15042,7 @@ var init_stream_processor = __esm({
|
|
|
14761
15042
|
}
|
|
14762
15043
|
if (readyToExecute.length > 0) {
|
|
14763
15044
|
for (const call of readyToExecute) {
|
|
14764
|
-
this.
|
|
15045
|
+
this.dependencyResolver.removePending(call.invocationId);
|
|
14765
15046
|
}
|
|
14766
15047
|
if (this.gadgetExecutionMode === "sequential") {
|
|
14767
15048
|
this.logger.debug("Executing ready gadgets sequentially", {
|
|
@@ -14814,11 +15095,12 @@ var init_stream_processor = __esm({
|
|
|
14814
15095
|
progress = true;
|
|
14815
15096
|
}
|
|
14816
15097
|
}
|
|
14817
|
-
if (this.
|
|
14818
|
-
const
|
|
14819
|
-
|
|
15098
|
+
if (this.dependencyResolver.pendingCount > 0) {
|
|
15099
|
+
const pendingEntries = this.dependencyResolver.getPendingEntries();
|
|
15100
|
+
const pendingIds = new Set(pendingEntries.map(([id]) => id));
|
|
15101
|
+
for (const [invocationId, call] of pendingEntries) {
|
|
14820
15102
|
const missingDeps = call.dependencies.filter(
|
|
14821
|
-
(dep) => !this.
|
|
15103
|
+
(dep) => !this.dependencyResolver.isCompleted(dep)
|
|
14822
15104
|
);
|
|
14823
15105
|
const circularDeps = missingDeps.filter((dep) => pendingIds.has(dep));
|
|
14824
15106
|
const trulyMissingDeps = missingDeps.filter((dep) => !pendingIds.has(dep));
|
|
@@ -14838,7 +15120,7 @@ var init_stream_processor = __esm({
|
|
|
14838
15120
|
circularDependencies: circularDeps,
|
|
14839
15121
|
missingDependencies: trulyMissingDeps
|
|
14840
15122
|
});
|
|
14841
|
-
this.
|
|
15123
|
+
this.dependencyResolver.markFailed(invocationId);
|
|
14842
15124
|
const skipEvent = {
|
|
14843
15125
|
type: "gadget_skipped",
|
|
14844
15126
|
gadgetName: call.gadgetName,
|
|
@@ -14848,51 +15130,20 @@ var init_stream_processor = __esm({
|
|
|
14848
15130
|
failedDependencyError: errorMessage
|
|
14849
15131
|
};
|
|
14850
15132
|
yield skipEvent;
|
|
14851
|
-
|
|
14852
|
-
|
|
14853
|
-
|
|
14854
|
-
|
|
14855
|
-
|
|
14856
|
-
|
|
14857
|
-
|
|
14858
|
-
|
|
14859
|
-
|
|
14860
|
-
|
|
14861
|
-
|
|
14862
|
-
|
|
14863
|
-
};
|
|
14864
|
-
await this.safeObserve(() => this.hooks.observers.onGadgetSkipped(context));
|
|
14865
|
-
}
|
|
14866
|
-
if (this.parentObservers?.onGadgetSkipped) {
|
|
14867
|
-
const context = {
|
|
14868
|
-
iteration: this.iteration,
|
|
14869
|
-
gadgetName: call.gadgetName,
|
|
14870
|
-
invocationId,
|
|
14871
|
-
parameters: call.parameters ?? {},
|
|
14872
|
-
failedDependency: missingDeps[0],
|
|
14873
|
-
failedDependencyError: errorMessage,
|
|
14874
|
-
logger: this.logger,
|
|
14875
|
-
subagentContext: timeoutSubagentContext
|
|
14876
|
-
};
|
|
14877
|
-
await this.safeObserve(() => this.parentObservers.onGadgetSkipped(context));
|
|
14878
|
-
}
|
|
15133
|
+
await notifyGadgetSkipped({
|
|
15134
|
+
tree: this.tree,
|
|
15135
|
+
hooks: this.hooks.observers,
|
|
15136
|
+
parentObservers: this.parentObservers,
|
|
15137
|
+
logger: this.logger,
|
|
15138
|
+
iteration: this.iteration,
|
|
15139
|
+
gadgetName: call.gadgetName,
|
|
15140
|
+
invocationId,
|
|
15141
|
+
parameters: call.parameters ?? {},
|
|
15142
|
+
failedDependency: missingDeps[0],
|
|
15143
|
+
failedDependencyError: errorMessage
|
|
15144
|
+
});
|
|
14879
15145
|
}
|
|
14880
|
-
this.
|
|
14881
|
-
}
|
|
14882
|
-
}
|
|
14883
|
-
/**
|
|
14884
|
-
* Safely execute an observer, catching and logging any errors.
|
|
14885
|
-
* Observers are non-critical, so errors are logged but don't crash the system.
|
|
14886
|
-
*/
|
|
14887
|
-
async safeObserve(fn) {
|
|
14888
|
-
try {
|
|
14889
|
-
await fn();
|
|
14890
|
-
} catch (error) {
|
|
14891
|
-
this.observerFailureCount++;
|
|
14892
|
-
this.logger.error("Observer threw error (ignoring)", {
|
|
14893
|
-
error: error instanceof Error ? error.message : String(error),
|
|
14894
|
-
failureCount: this.observerFailureCount
|
|
14895
|
-
});
|
|
15146
|
+
this.dependencyResolver.clearPending();
|
|
14896
15147
|
}
|
|
14897
15148
|
}
|
|
14898
15149
|
/**
|
|
@@ -14901,7 +15152,7 @@ var init_stream_processor = __esm({
|
|
|
14901
15152
|
*/
|
|
14902
15153
|
async runObserversInParallel(observers) {
|
|
14903
15154
|
if (observers.length === 0) return;
|
|
14904
|
-
await Promise.allSettled(observers.map((observer) =>
|
|
15155
|
+
await Promise.allSettled(observers.map((observer) => safeObserve(observer, this.logger)));
|
|
14905
15156
|
}
|
|
14906
15157
|
// ==========================================================================
|
|
14907
15158
|
// Public accessors for cross-iteration dependency tracking
|
|
@@ -14911,14 +15162,14 @@ var init_stream_processor = __esm({
|
|
|
14911
15162
|
* Used by Agent to accumulate completed IDs across iterations.
|
|
14912
15163
|
*/
|
|
14913
15164
|
getCompletedInvocationIds() {
|
|
14914
|
-
return
|
|
15165
|
+
return this.dependencyResolver.getCompletedInvocationIds();
|
|
14915
15166
|
}
|
|
14916
15167
|
/**
|
|
14917
15168
|
* Get all invocation IDs that failed in this iteration.
|
|
14918
15169
|
* Used by Agent to accumulate failed IDs across iterations.
|
|
14919
15170
|
*/
|
|
14920
15171
|
getFailedInvocationIds() {
|
|
14921
|
-
return
|
|
15172
|
+
return this.dependencyResolver.getFailedInvocationIds();
|
|
14922
15173
|
}
|
|
14923
15174
|
};
|
|
14924
15175
|
}
|
|
@@ -14945,6 +15196,7 @@ var init_agent = __esm({
|
|
|
14945
15196
|
init_event_handlers();
|
|
14946
15197
|
init_gadget_output_store();
|
|
14947
15198
|
init_hook_validators();
|
|
15199
|
+
init_safe_observe();
|
|
14948
15200
|
init_stream_processor();
|
|
14949
15201
|
init_tree_hook_bridge();
|
|
14950
15202
|
Agent = class {
|
|
@@ -15433,7 +15685,7 @@ var init_agent = __esm({
|
|
|
15433
15685
|
}
|
|
15434
15686
|
);
|
|
15435
15687
|
this.retryConfig.onRetry?.(error, streamAttempt);
|
|
15436
|
-
await
|
|
15688
|
+
await safeObserve(async () => {
|
|
15437
15689
|
if (this.hooks.observers?.onRetryAttempt) {
|
|
15438
15690
|
const subagentContext = getSubagentContextForNode(this.tree, currentLLMNodeId);
|
|
15439
15691
|
const hookContext = {
|
|
@@ -15447,7 +15699,7 @@ var init_agent = __esm({
|
|
|
15447
15699
|
};
|
|
15448
15700
|
await this.hooks.observers.onRetryAttempt(hookContext);
|
|
15449
15701
|
}
|
|
15450
|
-
});
|
|
15702
|
+
}, this.logger);
|
|
15451
15703
|
await this.sleep(finalDelay);
|
|
15452
15704
|
streamMetadata = null;
|
|
15453
15705
|
gadgetCallCount = 0;
|
|
@@ -15477,7 +15729,7 @@ var init_agent = __esm({
|
|
|
15477
15729
|
this.logger.silly("LLM response details", {
|
|
15478
15730
|
rawResponse: result.rawResponse
|
|
15479
15731
|
});
|
|
15480
|
-
await
|
|
15732
|
+
await safeObserve(async () => {
|
|
15481
15733
|
if (this.hooks.observers?.onLLMCallComplete) {
|
|
15482
15734
|
const subagentContext = getSubagentContextForNode(this.tree, currentLLMNodeId);
|
|
15483
15735
|
const context = {
|
|
@@ -15493,7 +15745,7 @@ var init_agent = __esm({
|
|
|
15493
15745
|
};
|
|
15494
15746
|
await this.hooks.observers.onLLMCallComplete(context);
|
|
15495
15747
|
}
|
|
15496
|
-
});
|
|
15748
|
+
}, this.logger);
|
|
15497
15749
|
this.completeLLMCallInTree(currentLLMNodeId, result);
|
|
15498
15750
|
const finalMessage = await this.processAfterLLMCallController(
|
|
15499
15751
|
currentIteration,
|
|
@@ -15527,7 +15779,7 @@ var init_agent = __esm({
|
|
|
15527
15779
|
}
|
|
15528
15780
|
} catch (error) {
|
|
15529
15781
|
const errorHandled = await this.handleLLMError(error, currentIteration);
|
|
15530
|
-
await
|
|
15782
|
+
await safeObserve(async () => {
|
|
15531
15783
|
if (this.hooks.observers?.onLLMCallError) {
|
|
15532
15784
|
const options = llmOptions ?? {
|
|
15533
15785
|
model: this.model,
|
|
@@ -15546,7 +15798,7 @@ var init_agent = __esm({
|
|
|
15546
15798
|
};
|
|
15547
15799
|
await this.hooks.observers.onLLMCallError(context);
|
|
15548
15800
|
}
|
|
15549
|
-
});
|
|
15801
|
+
}, this.logger);
|
|
15550
15802
|
if (!errorHandled) {
|
|
15551
15803
|
throw error;
|
|
15552
15804
|
}
|
|
@@ -15573,7 +15825,7 @@ var init_agent = __esm({
|
|
|
15573
15825
|
if (currentLLMNodeId) {
|
|
15574
15826
|
const node = this.tree.getNode(currentLLMNodeId);
|
|
15575
15827
|
if (node && node.type === "llm_call" && !node.completedAt) {
|
|
15576
|
-
await
|
|
15828
|
+
await safeObserve(async () => {
|
|
15577
15829
|
if (this.hooks.observers?.onLLMCallComplete) {
|
|
15578
15830
|
const subagentContext = getSubagentContextForNode(this.tree, currentLLMNodeId);
|
|
15579
15831
|
const context = {
|
|
@@ -15595,7 +15847,7 @@ var init_agent = __esm({
|
|
|
15595
15847
|
};
|
|
15596
15848
|
await this.hooks.observers.onLLMCallComplete(context);
|
|
15597
15849
|
}
|
|
15598
|
-
});
|
|
15850
|
+
}, this.logger);
|
|
15599
15851
|
this.tree.completeLLMCall(currentLLMNodeId, {
|
|
15600
15852
|
finishReason: "interrupted"
|
|
15601
15853
|
});
|
|
@@ -15615,7 +15867,7 @@ var init_agent = __esm({
|
|
|
15615
15867
|
const throttleDelay = this.rateLimitTracker.getRequiredDelayMs();
|
|
15616
15868
|
if (throttleDelay > 0) {
|
|
15617
15869
|
this.logger.debug("Rate limit throttling", { delayMs: throttleDelay });
|
|
15618
|
-
await
|
|
15870
|
+
await safeObserve(async () => {
|
|
15619
15871
|
if (this.hooks.observers?.onRateLimitThrottle) {
|
|
15620
15872
|
const subagentContext = getSubagentContextForNode(this.tree, llmNodeId);
|
|
15621
15873
|
const context = {
|
|
@@ -15627,7 +15879,7 @@ var init_agent = __esm({
|
|
|
15627
15879
|
};
|
|
15628
15880
|
await this.hooks.observers.onRateLimitThrottle(context);
|
|
15629
15881
|
}
|
|
15630
|
-
});
|
|
15882
|
+
}, this.logger);
|
|
15631
15883
|
await this.sleep(throttleDelay);
|
|
15632
15884
|
}
|
|
15633
15885
|
this.rateLimitTracker.reserveRequest();
|
|
@@ -15690,18 +15942,6 @@ var init_agent = __esm({
|
|
|
15690
15942
|
}
|
|
15691
15943
|
return true;
|
|
15692
15944
|
}
|
|
15693
|
-
/**
|
|
15694
|
-
* Safely execute an observer, catching and logging any errors.
|
|
15695
|
-
*/
|
|
15696
|
-
async safeObserve(fn) {
|
|
15697
|
-
try {
|
|
15698
|
-
await fn();
|
|
15699
|
-
} catch (error) {
|
|
15700
|
-
this.logger.error("Observer threw error (ignoring)", {
|
|
15701
|
-
error: error instanceof Error ? error.message : String(error)
|
|
15702
|
-
});
|
|
15703
|
-
}
|
|
15704
|
-
}
|
|
15705
15945
|
/**
|
|
15706
15946
|
* Resolve max tokens from model catalog.
|
|
15707
15947
|
*/
|
|
@@ -15770,7 +16010,7 @@ var init_agent = __esm({
|
|
|
15770
16010
|
iteration,
|
|
15771
16011
|
reason: this.signal.reason
|
|
15772
16012
|
});
|
|
15773
|
-
await
|
|
16013
|
+
await safeObserve(async () => {
|
|
15774
16014
|
if (this.hooks.observers?.onAbort) {
|
|
15775
16015
|
const context = {
|
|
15776
16016
|
iteration,
|
|
@@ -15779,7 +16019,7 @@ var init_agent = __esm({
|
|
|
15779
16019
|
};
|
|
15780
16020
|
await this.hooks.observers.onAbort(context);
|
|
15781
16021
|
}
|
|
15782
|
-
});
|
|
16022
|
+
}, this.logger);
|
|
15783
16023
|
return true;
|
|
15784
16024
|
}
|
|
15785
16025
|
/**
|
|
@@ -15798,7 +16038,7 @@ var init_agent = __esm({
|
|
|
15798
16038
|
tokensBefore: compactionEvent.tokensBefore,
|
|
15799
16039
|
tokensAfter: compactionEvent.tokensAfter
|
|
15800
16040
|
});
|
|
15801
|
-
await
|
|
16041
|
+
await safeObserve(async () => {
|
|
15802
16042
|
if (this.hooks.observers?.onCompaction) {
|
|
15803
16043
|
await this.hooks.observers.onCompaction({
|
|
15804
16044
|
iteration,
|
|
@@ -15808,7 +16048,7 @@ var init_agent = __esm({
|
|
|
15808
16048
|
logger: this.logger
|
|
15809
16049
|
});
|
|
15810
16050
|
}
|
|
15811
|
-
});
|
|
16051
|
+
}, this.logger);
|
|
15812
16052
|
return { type: "compaction", event: compactionEvent };
|
|
15813
16053
|
}
|
|
15814
16054
|
/**
|
|
@@ -15861,7 +16101,7 @@ var init_agent = __esm({
|
|
|
15861
16101
|
parentId: this.parentNodeId,
|
|
15862
16102
|
request: llmOptions.messages
|
|
15863
16103
|
});
|
|
15864
|
-
await
|
|
16104
|
+
await safeObserve(async () => {
|
|
15865
16105
|
if (this.hooks.observers?.onLLMCallStart) {
|
|
15866
16106
|
const subagentContext = getSubagentContextForNode(this.tree, llmNode.id);
|
|
15867
16107
|
const context = {
|
|
@@ -15872,7 +16112,7 @@ var init_agent = __esm({
|
|
|
15872
16112
|
};
|
|
15873
16113
|
await this.hooks.observers.onLLMCallStart(context);
|
|
15874
16114
|
}
|
|
15875
|
-
});
|
|
16115
|
+
}, this.logger);
|
|
15876
16116
|
if (this.hooks.controllers?.beforeLLMCall) {
|
|
15877
16117
|
const context = {
|
|
15878
16118
|
iteration,
|
|
@@ -15895,7 +16135,7 @@ var init_agent = __esm({
|
|
|
15895
16135
|
llmOptions = { ...llmOptions, ...action.modifiedOptions };
|
|
15896
16136
|
}
|
|
15897
16137
|
}
|
|
15898
|
-
await
|
|
16138
|
+
await safeObserve(async () => {
|
|
15899
16139
|
if (this.hooks.observers?.onLLMCallReady) {
|
|
15900
16140
|
const subagentContext = getSubagentContextForNode(this.tree, llmNode.id);
|
|
15901
16141
|
const context = {
|
|
@@ -15909,7 +16149,7 @@ var init_agent = __esm({
|
|
|
15909
16149
|
};
|
|
15910
16150
|
await this.hooks.observers.onLLMCallReady(context);
|
|
15911
16151
|
}
|
|
15912
|
-
});
|
|
16152
|
+
}, this.logger);
|
|
15913
16153
|
return { options: llmOptions, llmNodeId: llmNode.id };
|
|
15914
16154
|
}
|
|
15915
16155
|
/**
|