llmist 5.1.0 → 6.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-F5QK5YVI.js → chunk-7BJX376V.js} +97 -2
- package/dist/chunk-7BJX376V.js.map +1 -0
- package/dist/{chunk-YJKUWFIC.js → chunk-VAJLPRJ6.js} +939 -363
- package/dist/chunk-VAJLPRJ6.js.map +1 -0
- package/dist/cli.cjs +4131 -1597
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +3040 -1081
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +1040 -359
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +152 -39
- package/dist/index.d.ts +152 -39
- package/dist/index.js +26 -2
- package/dist/{mock-stream-CAY53Q6u.d.cts → mock-stream-Cq1Sxezz.d.cts} +854 -166
- package/dist/{mock-stream-CAY53Q6u.d.ts → mock-stream-Cq1Sxezz.d.ts} +854 -166
- package/dist/testing/index.cjs +936 -362
- package/dist/testing/index.cjs.map +1 -1
- package/dist/testing/index.d.cts +3 -3
- package/dist/testing/index.d.ts +3 -3
- package/dist/testing/index.js +1 -1
- package/package.json +2 -1
- package/dist/chunk-F5QK5YVI.js.map +0 -1
- package/dist/chunk-YJKUWFIC.js.map +0 -1
package/dist/index.cjs
CHANGED
|
@@ -513,6 +513,611 @@ var init_registry = __esm({
|
|
|
513
513
|
}
|
|
514
514
|
});
|
|
515
515
|
|
|
516
|
+
// src/core/execution-tree.ts
|
|
517
|
+
var ExecutionTree;
|
|
518
|
+
var init_execution_tree = __esm({
|
|
519
|
+
"src/core/execution-tree.ts"() {
|
|
520
|
+
"use strict";
|
|
521
|
+
ExecutionTree = class {
|
|
522
|
+
nodes = /* @__PURE__ */ new Map();
|
|
523
|
+
rootIds = [];
|
|
524
|
+
eventListeners = /* @__PURE__ */ new Map();
|
|
525
|
+
eventIdCounter = 0;
|
|
526
|
+
invocationIdToNodeId = /* @__PURE__ */ new Map();
|
|
527
|
+
// For async event streaming
|
|
528
|
+
eventQueue = [];
|
|
529
|
+
eventWaiters = [];
|
|
530
|
+
isCompleted = false;
|
|
531
|
+
/**
|
|
532
|
+
* Base depth for all nodes in this tree.
|
|
533
|
+
* Used when this tree is a subagent's view into a parent tree.
|
|
534
|
+
*/
|
|
535
|
+
baseDepth;
|
|
536
|
+
/**
|
|
537
|
+
* Parent node ID for subagent trees.
|
|
538
|
+
* All root nodes in this tree will have this as their parentId.
|
|
539
|
+
*/
|
|
540
|
+
parentNodeId;
|
|
541
|
+
constructor(options) {
|
|
542
|
+
this.baseDepth = options?.baseDepth ?? 0;
|
|
543
|
+
this.parentNodeId = options?.parentNodeId ?? null;
|
|
544
|
+
}
|
|
545
|
+
// ===========================================================================
|
|
546
|
+
// Node ID Generation
|
|
547
|
+
// ===========================================================================
|
|
548
|
+
generateLLMCallId(iteration, parentId) {
|
|
549
|
+
if (parentId) {
|
|
550
|
+
return `llm_${parentId}_${iteration}`;
|
|
551
|
+
}
|
|
552
|
+
return `llm_${iteration}`;
|
|
553
|
+
}
|
|
554
|
+
gadgetIdCounter = 0;
|
|
555
|
+
generateGadgetId(invocationId) {
|
|
556
|
+
return `gadget_${invocationId}_${++this.gadgetIdCounter}`;
|
|
557
|
+
}
|
|
558
|
+
// ===========================================================================
|
|
559
|
+
// Event Emission
|
|
560
|
+
// ===========================================================================
|
|
561
|
+
emit(event) {
|
|
562
|
+
const listeners = this.eventListeners.get(event.type);
|
|
563
|
+
if (listeners) {
|
|
564
|
+
for (const listener of listeners) {
|
|
565
|
+
try {
|
|
566
|
+
listener(event);
|
|
567
|
+
} catch (error) {
|
|
568
|
+
console.error(`Error in event listener for ${event.type}:`, error);
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
const allListeners = this.eventListeners.get("*");
|
|
573
|
+
if (allListeners) {
|
|
574
|
+
for (const listener of allListeners) {
|
|
575
|
+
try {
|
|
576
|
+
listener(event);
|
|
577
|
+
} catch (error) {
|
|
578
|
+
console.error("Error in wildcard event listener:", error);
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
if (this.eventWaiters.length > 0) {
|
|
583
|
+
const waiter = this.eventWaiters.shift();
|
|
584
|
+
if (waiter) waiter(event);
|
|
585
|
+
} else {
|
|
586
|
+
this.eventQueue.push(event);
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
createBaseEventProps(node) {
|
|
590
|
+
return {
|
|
591
|
+
eventId: ++this.eventIdCounter,
|
|
592
|
+
timestamp: Date.now(),
|
|
593
|
+
nodeId: node.id,
|
|
594
|
+
parentId: node.parentId,
|
|
595
|
+
depth: node.depth,
|
|
596
|
+
path: node.path
|
|
597
|
+
};
|
|
598
|
+
}
|
|
599
|
+
// ===========================================================================
|
|
600
|
+
// Node Creation
|
|
601
|
+
// ===========================================================================
|
|
602
|
+
/**
|
|
603
|
+
* Add a new LLM call node to the tree.
|
|
604
|
+
*/
|
|
605
|
+
addLLMCall(params) {
|
|
606
|
+
const parentId = params.parentId ?? this.parentNodeId;
|
|
607
|
+
const parent = parentId ? this.nodes.get(parentId) : null;
|
|
608
|
+
const depth = parent ? parent.depth + 1 : this.baseDepth;
|
|
609
|
+
const path = parent ? [...parent.path] : [];
|
|
610
|
+
const id = this.generateLLMCallId(params.iteration, parentId);
|
|
611
|
+
path.push(id);
|
|
612
|
+
const node = {
|
|
613
|
+
id,
|
|
614
|
+
type: "llm_call",
|
|
615
|
+
parentId,
|
|
616
|
+
depth,
|
|
617
|
+
path,
|
|
618
|
+
createdAt: Date.now(),
|
|
619
|
+
completedAt: null,
|
|
620
|
+
iteration: params.iteration,
|
|
621
|
+
model: params.model,
|
|
622
|
+
request: params.request,
|
|
623
|
+
response: "",
|
|
624
|
+
children: []
|
|
625
|
+
};
|
|
626
|
+
this.nodes.set(id, node);
|
|
627
|
+
if (!parentId) {
|
|
628
|
+
this.rootIds.push(id);
|
|
629
|
+
} else if (parent) {
|
|
630
|
+
parent.children.push(id);
|
|
631
|
+
}
|
|
632
|
+
this.emit({
|
|
633
|
+
type: "llm_call_start",
|
|
634
|
+
...this.createBaseEventProps(node),
|
|
635
|
+
iteration: node.iteration,
|
|
636
|
+
model: node.model,
|
|
637
|
+
request: node.request
|
|
638
|
+
});
|
|
639
|
+
return node;
|
|
640
|
+
}
|
|
641
|
+
/**
|
|
642
|
+
* Add text to an LLM call's response (for streaming).
|
|
643
|
+
*/
|
|
644
|
+
appendLLMResponse(nodeId, chunk) {
|
|
645
|
+
const node = this.nodes.get(nodeId);
|
|
646
|
+
if (!node || node.type !== "llm_call") {
|
|
647
|
+
throw new Error(`LLM call node not found: ${nodeId}`);
|
|
648
|
+
}
|
|
649
|
+
node.response += chunk;
|
|
650
|
+
this.emit({
|
|
651
|
+
type: "llm_call_stream",
|
|
652
|
+
...this.createBaseEventProps(node),
|
|
653
|
+
chunk
|
|
654
|
+
});
|
|
655
|
+
}
|
|
656
|
+
/**
|
|
657
|
+
* Complete an LLM call node.
|
|
658
|
+
*/
|
|
659
|
+
completeLLMCall(nodeId, params) {
|
|
660
|
+
const node = this.nodes.get(nodeId);
|
|
661
|
+
if (!node || node.type !== "llm_call") {
|
|
662
|
+
throw new Error(`LLM call node not found: ${nodeId}`);
|
|
663
|
+
}
|
|
664
|
+
const llmNode = node;
|
|
665
|
+
llmNode.completedAt = Date.now();
|
|
666
|
+
if (params.response !== void 0) llmNode.response = params.response;
|
|
667
|
+
if (params.usage) llmNode.usage = params.usage;
|
|
668
|
+
if (params.finishReason !== void 0) llmNode.finishReason = params.finishReason;
|
|
669
|
+
if (params.cost !== void 0) llmNode.cost = params.cost;
|
|
670
|
+
this.emit({
|
|
671
|
+
type: "llm_call_complete",
|
|
672
|
+
...this.createBaseEventProps(node),
|
|
673
|
+
response: llmNode.response,
|
|
674
|
+
usage: llmNode.usage,
|
|
675
|
+
finishReason: llmNode.finishReason,
|
|
676
|
+
cost: llmNode.cost
|
|
677
|
+
});
|
|
678
|
+
}
|
|
679
|
+
/**
|
|
680
|
+
* Mark an LLM call as failed.
|
|
681
|
+
*/
|
|
682
|
+
failLLMCall(nodeId, error, recovered) {
|
|
683
|
+
const node = this.nodes.get(nodeId);
|
|
684
|
+
if (!node || node.type !== "llm_call") {
|
|
685
|
+
throw new Error(`LLM call node not found: ${nodeId}`);
|
|
686
|
+
}
|
|
687
|
+
const llmNode = node;
|
|
688
|
+
llmNode.completedAt = Date.now();
|
|
689
|
+
this.emit({
|
|
690
|
+
type: "llm_call_error",
|
|
691
|
+
...this.createBaseEventProps(node),
|
|
692
|
+
error,
|
|
693
|
+
recovered
|
|
694
|
+
});
|
|
695
|
+
}
|
|
696
|
+
/**
|
|
697
|
+
* Add a new gadget node to the tree.
|
|
698
|
+
*/
|
|
699
|
+
addGadget(params) {
|
|
700
|
+
const parentId = params.parentId ?? this.getCurrentLLMCallId() ?? this.parentNodeId;
|
|
701
|
+
const parent = parentId ? this.nodes.get(parentId) : null;
|
|
702
|
+
const depth = parent ? parent.depth + 1 : this.baseDepth;
|
|
703
|
+
const path = parent ? [...parent.path] : [];
|
|
704
|
+
const id = this.generateGadgetId(params.invocationId);
|
|
705
|
+
path.push(id);
|
|
706
|
+
const node = {
|
|
707
|
+
id,
|
|
708
|
+
type: "gadget",
|
|
709
|
+
parentId,
|
|
710
|
+
depth,
|
|
711
|
+
path,
|
|
712
|
+
createdAt: Date.now(),
|
|
713
|
+
completedAt: null,
|
|
714
|
+
invocationId: params.invocationId,
|
|
715
|
+
name: params.name,
|
|
716
|
+
parameters: params.parameters,
|
|
717
|
+
dependencies: params.dependencies ?? [],
|
|
718
|
+
state: "pending",
|
|
719
|
+
children: [],
|
|
720
|
+
isSubagent: false
|
|
721
|
+
};
|
|
722
|
+
this.nodes.set(id, node);
|
|
723
|
+
this.invocationIdToNodeId.set(params.invocationId, id);
|
|
724
|
+
if (parent) {
|
|
725
|
+
parent.children.push(id);
|
|
726
|
+
}
|
|
727
|
+
this.emit({
|
|
728
|
+
type: "gadget_call",
|
|
729
|
+
...this.createBaseEventProps(node),
|
|
730
|
+
invocationId: node.invocationId,
|
|
731
|
+
name: node.name,
|
|
732
|
+
parameters: node.parameters,
|
|
733
|
+
dependencies: node.dependencies
|
|
734
|
+
});
|
|
735
|
+
return node;
|
|
736
|
+
}
|
|
737
|
+
/**
|
|
738
|
+
* Mark a gadget as started (running).
|
|
739
|
+
*/
|
|
740
|
+
startGadget(nodeId) {
|
|
741
|
+
const node = this.nodes.get(nodeId);
|
|
742
|
+
if (!node || node.type !== "gadget") {
|
|
743
|
+
throw new Error(`Gadget node not found: ${nodeId}`);
|
|
744
|
+
}
|
|
745
|
+
const gadgetNode = node;
|
|
746
|
+
gadgetNode.state = "running";
|
|
747
|
+
this.emit({
|
|
748
|
+
type: "gadget_start",
|
|
749
|
+
...this.createBaseEventProps(node),
|
|
750
|
+
invocationId: gadgetNode.invocationId,
|
|
751
|
+
name: gadgetNode.name
|
|
752
|
+
});
|
|
753
|
+
}
|
|
754
|
+
/**
|
|
755
|
+
* Complete a gadget node successfully.
|
|
756
|
+
*/
|
|
757
|
+
completeGadget(nodeId, params) {
|
|
758
|
+
const node = this.nodes.get(nodeId);
|
|
759
|
+
if (!node || node.type !== "gadget") {
|
|
760
|
+
throw new Error(`Gadget node not found: ${nodeId}`);
|
|
761
|
+
}
|
|
762
|
+
const gadgetNode = node;
|
|
763
|
+
gadgetNode.completedAt = Date.now();
|
|
764
|
+
gadgetNode.state = params.error ? "failed" : "completed";
|
|
765
|
+
if (params.result !== void 0) gadgetNode.result = params.result;
|
|
766
|
+
if (params.error) gadgetNode.error = params.error;
|
|
767
|
+
if (params.executionTimeMs !== void 0) gadgetNode.executionTimeMs = params.executionTimeMs;
|
|
768
|
+
if (params.cost !== void 0) gadgetNode.cost = params.cost;
|
|
769
|
+
if (params.media) gadgetNode.media = params.media;
|
|
770
|
+
gadgetNode.isSubagent = gadgetNode.children.some((childId) => {
|
|
771
|
+
const child = this.nodes.get(childId);
|
|
772
|
+
return child?.type === "llm_call";
|
|
773
|
+
});
|
|
774
|
+
if (params.error) {
|
|
775
|
+
this.emit({
|
|
776
|
+
type: "gadget_error",
|
|
777
|
+
...this.createBaseEventProps(node),
|
|
778
|
+
invocationId: gadgetNode.invocationId,
|
|
779
|
+
name: gadgetNode.name,
|
|
780
|
+
error: params.error,
|
|
781
|
+
executionTimeMs: params.executionTimeMs ?? 0
|
|
782
|
+
});
|
|
783
|
+
} else {
|
|
784
|
+
this.emit({
|
|
785
|
+
type: "gadget_complete",
|
|
786
|
+
...this.createBaseEventProps(node),
|
|
787
|
+
invocationId: gadgetNode.invocationId,
|
|
788
|
+
name: gadgetNode.name,
|
|
789
|
+
result: params.result ?? "",
|
|
790
|
+
executionTimeMs: params.executionTimeMs ?? 0,
|
|
791
|
+
cost: params.cost,
|
|
792
|
+
media: params.media
|
|
793
|
+
});
|
|
794
|
+
}
|
|
795
|
+
}
|
|
796
|
+
/**
|
|
797
|
+
* Mark a gadget as skipped due to dependency failure.
|
|
798
|
+
*/
|
|
799
|
+
skipGadget(nodeId, failedDependency, failedDependencyError, reason) {
|
|
800
|
+
const node = this.nodes.get(nodeId);
|
|
801
|
+
if (!node || node.type !== "gadget") {
|
|
802
|
+
throw new Error(`Gadget node not found: ${nodeId}`);
|
|
803
|
+
}
|
|
804
|
+
const gadgetNode = node;
|
|
805
|
+
gadgetNode.completedAt = Date.now();
|
|
806
|
+
gadgetNode.state = "skipped";
|
|
807
|
+
gadgetNode.failedDependency = failedDependency;
|
|
808
|
+
gadgetNode.error = failedDependencyError;
|
|
809
|
+
const error = reason === "controller_skip" ? "Skipped by controller" : `Dependency ${failedDependency} failed: ${failedDependencyError}`;
|
|
810
|
+
this.emit({
|
|
811
|
+
type: "gadget_skipped",
|
|
812
|
+
...this.createBaseEventProps(node),
|
|
813
|
+
invocationId: gadgetNode.invocationId,
|
|
814
|
+
name: gadgetNode.name,
|
|
815
|
+
reason,
|
|
816
|
+
error,
|
|
817
|
+
failedDependency,
|
|
818
|
+
failedDependencyError
|
|
819
|
+
});
|
|
820
|
+
}
|
|
821
|
+
// ===========================================================================
|
|
822
|
+
// Text Events (pure notifications, not tree nodes)
|
|
823
|
+
// ===========================================================================
|
|
824
|
+
/**
|
|
825
|
+
* Emit a text event (notification only, not stored in tree).
|
|
826
|
+
*/
|
|
827
|
+
emitText(content, llmCallNodeId) {
|
|
828
|
+
const node = this.nodes.get(llmCallNodeId);
|
|
829
|
+
if (!node) {
|
|
830
|
+
throw new Error(`Node not found: ${llmCallNodeId}`);
|
|
831
|
+
}
|
|
832
|
+
this.emit({
|
|
833
|
+
type: "text",
|
|
834
|
+
...this.createBaseEventProps(node),
|
|
835
|
+
content
|
|
836
|
+
});
|
|
837
|
+
}
|
|
838
|
+
// ===========================================================================
|
|
839
|
+
// Query Methods
|
|
840
|
+
// ===========================================================================
|
|
841
|
+
/**
|
|
842
|
+
* Get a node by ID.
|
|
843
|
+
*/
|
|
844
|
+
getNode(id) {
|
|
845
|
+
return this.nodes.get(id);
|
|
846
|
+
}
|
|
847
|
+
/**
|
|
848
|
+
* Get a gadget node by invocation ID.
|
|
849
|
+
*/
|
|
850
|
+
getNodeByInvocationId(invocationId) {
|
|
851
|
+
const nodeId = this.invocationIdToNodeId.get(invocationId);
|
|
852
|
+
if (!nodeId) return void 0;
|
|
853
|
+
const node = this.nodes.get(nodeId);
|
|
854
|
+
return node?.type === "gadget" ? node : void 0;
|
|
855
|
+
}
|
|
856
|
+
/**
|
|
857
|
+
* Get all root nodes (depth 0 for this tree).
|
|
858
|
+
*/
|
|
859
|
+
getRoots() {
|
|
860
|
+
return this.rootIds.map((id) => this.nodes.get(id)).filter((node) => node !== void 0);
|
|
861
|
+
}
|
|
862
|
+
/**
|
|
863
|
+
* Get children of a node.
|
|
864
|
+
*/
|
|
865
|
+
getChildren(id) {
|
|
866
|
+
const node = this.nodes.get(id);
|
|
867
|
+
if (!node) return [];
|
|
868
|
+
return node.children.map((childId) => this.nodes.get(childId)).filter((child) => child !== void 0);
|
|
869
|
+
}
|
|
870
|
+
/**
|
|
871
|
+
* Get ancestors of a node (from root to parent).
|
|
872
|
+
*/
|
|
873
|
+
getAncestors(id) {
|
|
874
|
+
const node = this.nodes.get(id);
|
|
875
|
+
if (!node) return [];
|
|
876
|
+
const ancestors = [];
|
|
877
|
+
let currentId = node.parentId;
|
|
878
|
+
while (currentId) {
|
|
879
|
+
const ancestor = this.nodes.get(currentId);
|
|
880
|
+
if (ancestor) {
|
|
881
|
+
ancestors.unshift(ancestor);
|
|
882
|
+
currentId = ancestor.parentId;
|
|
883
|
+
} else {
|
|
884
|
+
break;
|
|
885
|
+
}
|
|
886
|
+
}
|
|
887
|
+
return ancestors;
|
|
888
|
+
}
|
|
889
|
+
/**
|
|
890
|
+
* Get all descendants of a node.
|
|
891
|
+
*/
|
|
892
|
+
getDescendants(id, type) {
|
|
893
|
+
const node = this.nodes.get(id);
|
|
894
|
+
if (!node) return [];
|
|
895
|
+
const descendants = [];
|
|
896
|
+
const stack = [...node.children];
|
|
897
|
+
while (stack.length > 0) {
|
|
898
|
+
const childId = stack.pop();
|
|
899
|
+
const child = this.nodes.get(childId);
|
|
900
|
+
if (child) {
|
|
901
|
+
if (!type || child.type === type) {
|
|
902
|
+
descendants.push(child);
|
|
903
|
+
}
|
|
904
|
+
stack.push(...child.children);
|
|
905
|
+
}
|
|
906
|
+
}
|
|
907
|
+
return descendants;
|
|
908
|
+
}
|
|
909
|
+
/**
|
|
910
|
+
* Get the current (most recent incomplete) LLM call node.
|
|
911
|
+
*/
|
|
912
|
+
getCurrentLLMCallId() {
|
|
913
|
+
for (let i = this.rootIds.length - 1; i >= 0; i--) {
|
|
914
|
+
const node = this.nodes.get(this.rootIds[i]);
|
|
915
|
+
if (node?.type === "llm_call" && !node.completedAt) {
|
|
916
|
+
return node.id;
|
|
917
|
+
}
|
|
918
|
+
}
|
|
919
|
+
return void 0;
|
|
920
|
+
}
|
|
921
|
+
// ===========================================================================
|
|
922
|
+
// Aggregation Methods (for subagent support)
|
|
923
|
+
// ===========================================================================
|
|
924
|
+
/**
|
|
925
|
+
* Get total cost for entire tree.
|
|
926
|
+
*/
|
|
927
|
+
getTotalCost() {
|
|
928
|
+
let total = 0;
|
|
929
|
+
for (const node of this.nodes.values()) {
|
|
930
|
+
if (node.type === "llm_call" && node.cost) {
|
|
931
|
+
total += node.cost;
|
|
932
|
+
} else if (node.type === "gadget" && node.cost) {
|
|
933
|
+
total += node.cost;
|
|
934
|
+
}
|
|
935
|
+
}
|
|
936
|
+
return total;
|
|
937
|
+
}
|
|
938
|
+
/**
|
|
939
|
+
* Get total cost for a subtree (node and all descendants).
|
|
940
|
+
*/
|
|
941
|
+
getSubtreeCost(nodeId) {
|
|
942
|
+
const node = this.nodes.get(nodeId);
|
|
943
|
+
if (!node) return 0;
|
|
944
|
+
let total = 0;
|
|
945
|
+
if (node.type === "llm_call" && node.cost) {
|
|
946
|
+
total += node.cost;
|
|
947
|
+
} else if (node.type === "gadget" && node.cost) {
|
|
948
|
+
total += node.cost;
|
|
949
|
+
}
|
|
950
|
+
for (const descendant of this.getDescendants(nodeId)) {
|
|
951
|
+
if (descendant.type === "llm_call" && descendant.cost) {
|
|
952
|
+
total += descendant.cost;
|
|
953
|
+
} else if (descendant.type === "gadget" && descendant.cost) {
|
|
954
|
+
total += descendant.cost;
|
|
955
|
+
}
|
|
956
|
+
}
|
|
957
|
+
return total;
|
|
958
|
+
}
|
|
959
|
+
/**
|
|
960
|
+
* Get token usage for entire tree.
|
|
961
|
+
*/
|
|
962
|
+
getTotalTokens() {
|
|
963
|
+
let input = 0;
|
|
964
|
+
let output = 0;
|
|
965
|
+
let cached = 0;
|
|
966
|
+
for (const node of this.nodes.values()) {
|
|
967
|
+
if (node.type === "llm_call") {
|
|
968
|
+
const llmNode = node;
|
|
969
|
+
if (llmNode.usage) {
|
|
970
|
+
input += llmNode.usage.inputTokens;
|
|
971
|
+
output += llmNode.usage.outputTokens;
|
|
972
|
+
cached += llmNode.usage.cachedInputTokens ?? 0;
|
|
973
|
+
}
|
|
974
|
+
}
|
|
975
|
+
}
|
|
976
|
+
return { input, output, cached };
|
|
977
|
+
}
|
|
978
|
+
/**
|
|
979
|
+
* Get token usage for a subtree.
|
|
980
|
+
*/
|
|
981
|
+
getSubtreeTokens(nodeId) {
|
|
982
|
+
const node = this.nodes.get(nodeId);
|
|
983
|
+
if (!node) return { input: 0, output: 0, cached: 0 };
|
|
984
|
+
let input = 0;
|
|
985
|
+
let output = 0;
|
|
986
|
+
let cached = 0;
|
|
987
|
+
const nodesToProcess = [node, ...this.getDescendants(nodeId)];
|
|
988
|
+
for (const n of nodesToProcess) {
|
|
989
|
+
if (n.type === "llm_call") {
|
|
990
|
+
const llmNode = n;
|
|
991
|
+
if (llmNode.usage) {
|
|
992
|
+
input += llmNode.usage.inputTokens;
|
|
993
|
+
output += llmNode.usage.outputTokens;
|
|
994
|
+
cached += llmNode.usage.cachedInputTokens ?? 0;
|
|
995
|
+
}
|
|
996
|
+
}
|
|
997
|
+
}
|
|
998
|
+
return { input, output, cached };
|
|
999
|
+
}
|
|
1000
|
+
/**
|
|
1001
|
+
* Collect all media from a subtree.
|
|
1002
|
+
*/
|
|
1003
|
+
getSubtreeMedia(nodeId) {
|
|
1004
|
+
const node = this.nodes.get(nodeId);
|
|
1005
|
+
if (!node) return [];
|
|
1006
|
+
const media = [];
|
|
1007
|
+
const nodesToProcess = node.type === "gadget" ? [node] : [];
|
|
1008
|
+
nodesToProcess.push(...this.getDescendants(nodeId, "gadget"));
|
|
1009
|
+
for (const n of nodesToProcess) {
|
|
1010
|
+
if (n.type === "gadget") {
|
|
1011
|
+
const gadgetNode = n;
|
|
1012
|
+
if (gadgetNode.media) {
|
|
1013
|
+
media.push(...gadgetNode.media);
|
|
1014
|
+
}
|
|
1015
|
+
}
|
|
1016
|
+
}
|
|
1017
|
+
return media;
|
|
1018
|
+
}
|
|
1019
|
+
/**
|
|
1020
|
+
* Check if a subtree is complete (all nodes finished).
|
|
1021
|
+
*/
|
|
1022
|
+
isSubtreeComplete(nodeId) {
|
|
1023
|
+
const node = this.nodes.get(nodeId);
|
|
1024
|
+
if (!node) return true;
|
|
1025
|
+
if (!node.completedAt) return false;
|
|
1026
|
+
for (const descendant of this.getDescendants(nodeId)) {
|
|
1027
|
+
if (!descendant.completedAt) return false;
|
|
1028
|
+
}
|
|
1029
|
+
return true;
|
|
1030
|
+
}
|
|
1031
|
+
/**
|
|
1032
|
+
* Get node counts.
|
|
1033
|
+
*/
|
|
1034
|
+
getNodeCount() {
|
|
1035
|
+
let llmCalls = 0;
|
|
1036
|
+
let gadgets = 0;
|
|
1037
|
+
for (const node of this.nodes.values()) {
|
|
1038
|
+
if (node.type === "llm_call") llmCalls++;
|
|
1039
|
+
else if (node.type === "gadget") gadgets++;
|
|
1040
|
+
}
|
|
1041
|
+
return { llmCalls, gadgets };
|
|
1042
|
+
}
|
|
1043
|
+
// ===========================================================================
|
|
1044
|
+
// Event Subscription
|
|
1045
|
+
// ===========================================================================
|
|
1046
|
+
/**
|
|
1047
|
+
* Subscribe to events of a specific type.
|
|
1048
|
+
* Returns unsubscribe function.
|
|
1049
|
+
*
|
|
1050
|
+
* @param type - Event type to subscribe to (use "*" for all events)
|
|
1051
|
+
* @param listener - Callback function that receives matching events
|
|
1052
|
+
* @returns Unsubscribe function
|
|
1053
|
+
*
|
|
1054
|
+
* @example
|
|
1055
|
+
* ```typescript
|
|
1056
|
+
* const unsubscribe = tree.on("gadget_complete", (event) => {
|
|
1057
|
+
* if (event.type === "gadget_complete") {
|
|
1058
|
+
* console.log(`Gadget ${event.name} completed`);
|
|
1059
|
+
* }
|
|
1060
|
+
* });
|
|
1061
|
+
* ```
|
|
1062
|
+
*/
|
|
1063
|
+
on(type, listener) {
|
|
1064
|
+
if (!this.eventListeners.has(type)) {
|
|
1065
|
+
this.eventListeners.set(type, /* @__PURE__ */ new Set());
|
|
1066
|
+
}
|
|
1067
|
+
const listeners = this.eventListeners.get(type);
|
|
1068
|
+
listeners.add(listener);
|
|
1069
|
+
return () => {
|
|
1070
|
+
listeners.delete(listener);
|
|
1071
|
+
};
|
|
1072
|
+
}
|
|
1073
|
+
/**
|
|
1074
|
+
* Subscribe to all events.
|
|
1075
|
+
*/
|
|
1076
|
+
onAll(listener) {
|
|
1077
|
+
return this.on("*", listener);
|
|
1078
|
+
}
|
|
1079
|
+
/**
|
|
1080
|
+
* Get async iterable of all events.
|
|
1081
|
+
* Events are yielded as they occur.
|
|
1082
|
+
*/
|
|
1083
|
+
async *events() {
|
|
1084
|
+
while (!this.isCompleted) {
|
|
1085
|
+
while (this.eventQueue.length > 0) {
|
|
1086
|
+
yield this.eventQueue.shift();
|
|
1087
|
+
}
|
|
1088
|
+
if (this.isCompleted) break;
|
|
1089
|
+
const event = await new Promise((resolve) => {
|
|
1090
|
+
if (this.eventQueue.length > 0) {
|
|
1091
|
+
resolve(this.eventQueue.shift());
|
|
1092
|
+
} else {
|
|
1093
|
+
this.eventWaiters.push(resolve);
|
|
1094
|
+
}
|
|
1095
|
+
});
|
|
1096
|
+
yield event;
|
|
1097
|
+
}
|
|
1098
|
+
while (this.eventQueue.length > 0) {
|
|
1099
|
+
yield this.eventQueue.shift();
|
|
1100
|
+
}
|
|
1101
|
+
}
|
|
1102
|
+
/**
|
|
1103
|
+
* Mark the tree as complete (no more events will be emitted).
|
|
1104
|
+
*/
|
|
1105
|
+
complete() {
|
|
1106
|
+
this.isCompleted = true;
|
|
1107
|
+
for (const waiter of this.eventWaiters) {
|
|
1108
|
+
}
|
|
1109
|
+
this.eventWaiters = [];
|
|
1110
|
+
}
|
|
1111
|
+
/**
|
|
1112
|
+
* Check if the tree is complete.
|
|
1113
|
+
*/
|
|
1114
|
+
isComplete() {
|
|
1115
|
+
return this.isCompleted;
|
|
1116
|
+
}
|
|
1117
|
+
};
|
|
1118
|
+
}
|
|
1119
|
+
});
|
|
1120
|
+
|
|
516
1121
|
// src/core/prompt-config.ts
|
|
517
1122
|
function resolvePromptTemplate(template, defaultValue, context) {
|
|
518
1123
|
const resolved = template ?? defaultValue;
|
|
@@ -936,23 +1541,26 @@ Produces: { "items": ["first", "second"] }`);
|
|
|
936
1541
|
* Record a gadget execution result in the message history.
|
|
937
1542
|
* Creates an assistant message with the gadget invocation and a user message with the result.
|
|
938
1543
|
*
|
|
1544
|
+
* The invocationId is shown to the LLM so it can reference previous calls when building dependencies.
|
|
1545
|
+
*
|
|
939
1546
|
* @param gadget - Name of the gadget that was executed
|
|
940
1547
|
* @param parameters - Parameters that were passed to the gadget
|
|
941
1548
|
* @param result - Text result from the gadget execution
|
|
1549
|
+
* @param invocationId - Invocation ID (shown to LLM so it can reference for dependencies)
|
|
942
1550
|
* @param media - Optional media outputs from the gadget
|
|
943
1551
|
* @param mediaIds - Optional IDs for the media outputs
|
|
944
1552
|
*/
|
|
945
|
-
addGadgetCallResult(gadget, parameters, result, media, mediaIds) {
|
|
1553
|
+
addGadgetCallResult(gadget, parameters, result, invocationId, media, mediaIds) {
|
|
946
1554
|
const paramStr = this.formatBlockParameters(parameters, "");
|
|
947
1555
|
this.messages.push({
|
|
948
1556
|
role: "assistant",
|
|
949
|
-
content: `${this.startPrefix}${gadget}
|
|
1557
|
+
content: `${this.startPrefix}${gadget}:${invocationId}
|
|
950
1558
|
${paramStr}
|
|
951
1559
|
${this.endPrefix}`
|
|
952
1560
|
});
|
|
953
1561
|
if (media && media.length > 0 && mediaIds && mediaIds.length > 0) {
|
|
954
1562
|
const idRefs = media.map((m, i) => `[Media: ${mediaIds[i]} (${m.kind})]`).join("\n");
|
|
955
|
-
const textWithIds = `Result: ${result}
|
|
1563
|
+
const textWithIds = `Result (${invocationId}): ${result}
|
|
956
1564
|
${idRefs}`;
|
|
957
1565
|
const parts = [text(textWithIds)];
|
|
958
1566
|
for (const item of media) {
|
|
@@ -966,7 +1574,7 @@ ${idRefs}`;
|
|
|
966
1574
|
} else {
|
|
967
1575
|
this.messages.push({
|
|
968
1576
|
role: "user",
|
|
969
|
-
content: `Result: ${result}`
|
|
1577
|
+
content: `Result (${invocationId}): ${result}`
|
|
970
1578
|
});
|
|
971
1579
|
}
|
|
972
1580
|
return this;
|
|
@@ -2380,8 +2988,8 @@ var init_conversation_manager = __esm({
|
|
|
2380
2988
|
addAssistantMessage(content) {
|
|
2381
2989
|
this.historyBuilder.addAssistant(content);
|
|
2382
2990
|
}
|
|
2383
|
-
addGadgetCallResult(gadgetName, parameters, result, media, mediaIds) {
|
|
2384
|
-
this.historyBuilder.addGadgetCallResult(gadgetName, parameters, result, media, mediaIds);
|
|
2991
|
+
addGadgetCallResult(gadgetName, parameters, result, invocationId, media, mediaIds) {
|
|
2992
|
+
this.historyBuilder.addGadgetCallResult(gadgetName, parameters, result, invocationId, media, mediaIds);
|
|
2385
2993
|
}
|
|
2386
2994
|
getMessages() {
|
|
2387
2995
|
return [...this.baseMessages, ...this.initialMessages, ...this.historyBuilder.build()];
|
|
@@ -3562,7 +4170,7 @@ var init_executor = __esm({
|
|
|
3562
4170
|
init_exceptions();
|
|
3563
4171
|
init_parser();
|
|
3564
4172
|
GadgetExecutor = class {
|
|
3565
|
-
constructor(registry, requestHumanInput, logger, defaultGadgetTimeoutMs, errorFormatterOptions, client, mediaStore, agentConfig, subagentConfig, onSubagentEvent) {
|
|
4173
|
+
constructor(registry, requestHumanInput, logger, defaultGadgetTimeoutMs, errorFormatterOptions, client, mediaStore, agentConfig, subagentConfig, onSubagentEvent, tree, parentNodeId, baseDepth) {
|
|
3566
4174
|
this.registry = registry;
|
|
3567
4175
|
this.requestHumanInput = requestHumanInput;
|
|
3568
4176
|
this.defaultGadgetTimeoutMs = defaultGadgetTimeoutMs;
|
|
@@ -3571,6 +4179,9 @@ var init_executor = __esm({
|
|
|
3571
4179
|
this.agentConfig = agentConfig;
|
|
3572
4180
|
this.subagentConfig = subagentConfig;
|
|
3573
4181
|
this.onSubagentEvent = onSubagentEvent;
|
|
4182
|
+
this.tree = tree;
|
|
4183
|
+
this.parentNodeId = parentNodeId;
|
|
4184
|
+
this.baseDepth = baseDepth;
|
|
3574
4185
|
this.logger = logger ?? createLogger({ name: "llmist:executor" });
|
|
3575
4186
|
this.errorFormatter = new GadgetExecutionErrorFormatter(errorFormatterOptions);
|
|
3576
4187
|
this.argPrefix = errorFormatterOptions?.argPrefix ?? GADGET_ARG_PREFIX;
|
|
@@ -3581,15 +4192,21 @@ var init_executor = __esm({
|
|
|
3581
4192
|
/**
|
|
3582
4193
|
* Creates a promise that rejects with a TimeoutException after the specified timeout.
|
|
3583
4194
|
* Aborts the provided AbortController before rejecting, allowing gadgets to clean up.
|
|
4195
|
+
* Returns both the promise and a cancel function to clear the timeout when no longer needed.
|
|
3584
4196
|
*/
|
|
3585
4197
|
createTimeoutPromise(gadgetName, timeoutMs, abortController) {
|
|
3586
|
-
|
|
3587
|
-
|
|
4198
|
+
let timeoutId;
|
|
4199
|
+
const promise = new Promise((_, reject) => {
|
|
4200
|
+
timeoutId = setTimeout(() => {
|
|
3588
4201
|
const timeoutError = new TimeoutException(gadgetName, timeoutMs);
|
|
3589
4202
|
abortController.abort(timeoutError.message);
|
|
3590
4203
|
reject(timeoutError);
|
|
3591
4204
|
}, timeoutMs);
|
|
3592
4205
|
});
|
|
4206
|
+
return {
|
|
4207
|
+
promise,
|
|
4208
|
+
cancel: () => clearTimeout(timeoutId)
|
|
4209
|
+
};
|
|
3593
4210
|
}
|
|
3594
4211
|
/**
|
|
3595
4212
|
* Unify gadget execute result to consistent internal format.
|
|
@@ -3711,6 +4328,8 @@ var init_executor = __esm({
|
|
|
3711
4328
|
});
|
|
3712
4329
|
}
|
|
3713
4330
|
};
|
|
4331
|
+
const gadgetNodeId = this.tree?.getNodeByInvocationId(call.invocationId)?.id;
|
|
4332
|
+
const gadgetDepth = gadgetNodeId ? this.tree?.getNode(gadgetNodeId)?.depth ?? this.baseDepth : this.baseDepth;
|
|
3714
4333
|
const ctx = {
|
|
3715
4334
|
reportCost,
|
|
3716
4335
|
llmist: this.client ? new CostReportingLLMistWrapper(this.client, reportCost) : void 0,
|
|
@@ -3718,7 +4337,11 @@ var init_executor = __esm({
|
|
|
3718
4337
|
agentConfig: this.agentConfig,
|
|
3719
4338
|
subagentConfig: this.subagentConfig,
|
|
3720
4339
|
invocationId: call.invocationId,
|
|
3721
|
-
onSubagentEvent: this.onSubagentEvent
|
|
4340
|
+
onSubagentEvent: this.onSubagentEvent,
|
|
4341
|
+
// Tree context for subagent support - use gadget's own node ID
|
|
4342
|
+
tree: this.tree,
|
|
4343
|
+
nodeId: gadgetNodeId,
|
|
4344
|
+
depth: gadgetDepth
|
|
3722
4345
|
};
|
|
3723
4346
|
let rawResult;
|
|
3724
4347
|
if (timeoutMs && timeoutMs > 0) {
|
|
@@ -3726,10 +4349,15 @@ var init_executor = __esm({
|
|
|
3726
4349
|
gadgetName: call.gadgetName,
|
|
3727
4350
|
timeoutMs
|
|
3728
4351
|
});
|
|
3729
|
-
|
|
3730
|
-
|
|
3731
|
-
|
|
3732
|
-
|
|
4352
|
+
const timeout = this.createTimeoutPromise(call.gadgetName, timeoutMs, abortController);
|
|
4353
|
+
try {
|
|
4354
|
+
rawResult = await Promise.race([
|
|
4355
|
+
Promise.resolve(gadget.execute(validatedParameters, ctx)),
|
|
4356
|
+
timeout.promise
|
|
4357
|
+
]);
|
|
4358
|
+
} finally {
|
|
4359
|
+
timeout.cancel();
|
|
4360
|
+
}
|
|
3733
4361
|
} else {
|
|
3734
4362
|
rawResult = await Promise.resolve(gadget.execute(validatedParameters, ctx));
|
|
3735
4363
|
}
|
|
@@ -3924,10 +4552,11 @@ var init_stream_processor = __esm({
|
|
|
3924
4552
|
logger;
|
|
3925
4553
|
parser;
|
|
3926
4554
|
executor;
|
|
3927
|
-
|
|
3928
|
-
|
|
4555
|
+
// Execution Tree context
|
|
4556
|
+
tree;
|
|
4557
|
+
parentNodeId;
|
|
4558
|
+
baseDepth;
|
|
3929
4559
|
responseText = "";
|
|
3930
|
-
executionHalted = false;
|
|
3931
4560
|
observerFailureCount = 0;
|
|
3932
4561
|
// Dependency tracking for gadget execution DAG
|
|
3933
4562
|
/** Gadgets waiting for their dependencies to complete */
|
|
@@ -3936,18 +4565,30 @@ var init_stream_processor = __esm({
|
|
|
3936
4565
|
completedResults = /* @__PURE__ */ new Map();
|
|
3937
4566
|
/** Invocation IDs of gadgets that have failed (error or skipped due to dependency) */
|
|
3938
4567
|
failedInvocations = /* @__PURE__ */ new Set();
|
|
4568
|
+
/** Promises for independent gadgets currently executing (fire-and-forget) */
|
|
4569
|
+
inFlightExecutions = /* @__PURE__ */ new Map();
|
|
4570
|
+
/** Queue of completed gadget results ready to be yielded (for real-time streaming) */
|
|
4571
|
+
completedResultsQueue = [];
|
|
3939
4572
|
constructor(options) {
|
|
3940
4573
|
this.iteration = options.iteration;
|
|
3941
4574
|
this.registry = options.registry;
|
|
3942
4575
|
this.hooks = options.hooks ?? {};
|
|
3943
4576
|
this.logger = options.logger ?? createLogger({ name: "llmist:stream-processor" });
|
|
3944
|
-
this.
|
|
3945
|
-
this.
|
|
4577
|
+
this.tree = options.tree;
|
|
4578
|
+
this.parentNodeId = options.parentNodeId ?? null;
|
|
4579
|
+
this.baseDepth = options.baseDepth ?? 0;
|
|
3946
4580
|
this.parser = new GadgetCallParser({
|
|
3947
4581
|
startPrefix: options.gadgetStartPrefix,
|
|
3948
4582
|
endPrefix: options.gadgetEndPrefix,
|
|
3949
4583
|
argPrefix: options.gadgetArgPrefix
|
|
3950
4584
|
});
|
|
4585
|
+
const wrappedOnSubagentEvent = options.onSubagentEvent ? (event) => {
|
|
4586
|
+
this.completedResultsQueue.push({
|
|
4587
|
+
type: "subagent_event",
|
|
4588
|
+
subagentEvent: event
|
|
4589
|
+
});
|
|
4590
|
+
options.onSubagentEvent?.(event);
|
|
4591
|
+
} : void 0;
|
|
3951
4592
|
this.executor = new GadgetExecutor(
|
|
3952
4593
|
options.registry,
|
|
3953
4594
|
options.requestHumanInput,
|
|
@@ -3958,7 +4599,11 @@ var init_stream_processor = __esm({
|
|
|
3958
4599
|
options.mediaStore,
|
|
3959
4600
|
options.agentConfig,
|
|
3960
4601
|
options.subagentConfig,
|
|
3961
|
-
|
|
4602
|
+
wrappedOnSubagentEvent,
|
|
4603
|
+
// Tree context for gadget execution
|
|
4604
|
+
options.tree,
|
|
4605
|
+
options.parentNodeId,
|
|
4606
|
+
options.baseDepth
|
|
3962
4607
|
);
|
|
3963
4608
|
}
|
|
3964
4609
|
/**
|
|
@@ -4009,7 +4654,7 @@ var init_stream_processor = __esm({
|
|
|
4009
4654
|
usage,
|
|
4010
4655
|
logger: this.logger
|
|
4011
4656
|
};
|
|
4012
|
-
await this.hooks.observers
|
|
4657
|
+
await this.hooks.observers?.onStreamChunk?.(context);
|
|
4013
4658
|
});
|
|
4014
4659
|
await this.runObserversInParallel(chunkObservers);
|
|
4015
4660
|
}
|
|
@@ -4027,24 +4672,7 @@ var init_stream_processor = __esm({
|
|
|
4027
4672
|
}
|
|
4028
4673
|
}
|
|
4029
4674
|
}
|
|
4030
|
-
|
|
4031
|
-
this.logger.info("Breaking from LLM stream due to gadget error");
|
|
4032
|
-
break;
|
|
4033
|
-
}
|
|
4034
|
-
}
|
|
4035
|
-
if (!this.executionHalted) {
|
|
4036
|
-
for (const event of this.parser.finalize()) {
|
|
4037
|
-
for await (const processedEvent of this.processEventGenerator(event)) {
|
|
4038
|
-
yield processedEvent;
|
|
4039
|
-
if (processedEvent.type === "gadget_result") {
|
|
4040
|
-
didExecuteGadgets = true;
|
|
4041
|
-
if (processedEvent.result.breaksLoop) {
|
|
4042
|
-
shouldBreakLoop = true;
|
|
4043
|
-
}
|
|
4044
|
-
}
|
|
4045
|
-
}
|
|
4046
|
-
}
|
|
4047
|
-
for await (const evt of this.processPendingGadgetsGenerator()) {
|
|
4675
|
+
for (const evt of this.drainCompletedResults()) {
|
|
4048
4676
|
yield evt;
|
|
4049
4677
|
if (evt.type === "gadget_result") {
|
|
4050
4678
|
didExecuteGadgets = true;
|
|
@@ -4054,6 +4682,44 @@ var init_stream_processor = __esm({
|
|
|
4054
4682
|
}
|
|
4055
4683
|
}
|
|
4056
4684
|
}
|
|
4685
|
+
for (const event of this.parser.finalize()) {
|
|
4686
|
+
for await (const processedEvent of this.processEventGenerator(event)) {
|
|
4687
|
+
yield processedEvent;
|
|
4688
|
+
if (processedEvent.type === "gadget_result") {
|
|
4689
|
+
didExecuteGadgets = true;
|
|
4690
|
+
if (processedEvent.result.breaksLoop) {
|
|
4691
|
+
shouldBreakLoop = true;
|
|
4692
|
+
}
|
|
4693
|
+
}
|
|
4694
|
+
}
|
|
4695
|
+
}
|
|
4696
|
+
for await (const evt of this.waitForInFlightExecutions()) {
|
|
4697
|
+
yield evt;
|
|
4698
|
+
if (evt.type === "gadget_result") {
|
|
4699
|
+
didExecuteGadgets = true;
|
|
4700
|
+
if (evt.result.breaksLoop) {
|
|
4701
|
+
shouldBreakLoop = true;
|
|
4702
|
+
}
|
|
4703
|
+
}
|
|
4704
|
+
}
|
|
4705
|
+
for (const evt of this.drainCompletedResults()) {
|
|
4706
|
+
yield evt;
|
|
4707
|
+
if (evt.type === "gadget_result") {
|
|
4708
|
+
didExecuteGadgets = true;
|
|
4709
|
+
if (evt.result.breaksLoop) {
|
|
4710
|
+
shouldBreakLoop = true;
|
|
4711
|
+
}
|
|
4712
|
+
}
|
|
4713
|
+
}
|
|
4714
|
+
for await (const evt of this.processPendingGadgetsGenerator()) {
|
|
4715
|
+
yield evt;
|
|
4716
|
+
if (evt.type === "gadget_result") {
|
|
4717
|
+
didExecuteGadgets = true;
|
|
4718
|
+
if (evt.result.breaksLoop) {
|
|
4719
|
+
shouldBreakLoop = true;
|
|
4720
|
+
}
|
|
4721
|
+
}
|
|
4722
|
+
}
|
|
4057
4723
|
let finalMessage = this.responseText;
|
|
4058
4724
|
if (this.hooks.interceptors?.interceptAssistantMessage) {
|
|
4059
4725
|
const context = {
|
|
@@ -4074,21 +4740,8 @@ var init_stream_processor = __esm({
|
|
|
4074
4740
|
};
|
|
4075
4741
|
yield completionEvent;
|
|
4076
4742
|
}
|
|
4077
|
-
/**
|
|
4078
|
-
* Process a single parsed event (text or gadget call).
|
|
4079
|
-
* @deprecated Use processEventGenerator for real-time streaming
|
|
4080
|
-
*/
|
|
4081
|
-
async processEvent(event) {
|
|
4082
|
-
if (event.type === "text") {
|
|
4083
|
-
return this.processTextEvent(event);
|
|
4084
|
-
} else if (event.type === "gadget_call") {
|
|
4085
|
-
return this.processGadgetCall(event.call);
|
|
4086
|
-
}
|
|
4087
|
-
return [event];
|
|
4088
|
-
}
|
|
4089
4743
|
/**
|
|
4090
4744
|
* Process a single parsed event, yielding events in real-time.
|
|
4091
|
-
* Generator version of processEvent for streaming support.
|
|
4092
4745
|
*/
|
|
4093
4746
|
async *processEventGenerator(event) {
|
|
4094
4747
|
if (event.type === "text") {
|
|
@@ -4130,12 +4783,6 @@ var init_stream_processor = __esm({
|
|
|
4130
4783
|
* After each execution, pending gadgets are checked to see if they can now run.
|
|
4131
4784
|
*/
|
|
4132
4785
|
async processGadgetCall(call) {
|
|
4133
|
-
if (this.executionHalted) {
|
|
4134
|
-
this.logger.debug("Skipping gadget execution due to previous error", {
|
|
4135
|
-
gadgetName: call.gadgetName
|
|
4136
|
-
});
|
|
4137
|
-
return [];
|
|
4138
|
-
}
|
|
4139
4786
|
const events = [];
|
|
4140
4787
|
events.push({ type: "gadget_call", call });
|
|
4141
4788
|
if (call.dependencies.length > 0) {
|
|
@@ -4186,13 +4833,16 @@ var init_stream_processor = __esm({
|
|
|
4186
4833
|
* when parsed (before execution), enabling real-time UI feedback.
|
|
4187
4834
|
*/
|
|
4188
4835
|
async *processGadgetCallGenerator(call) {
|
|
4189
|
-
|
|
4190
|
-
|
|
4191
|
-
|
|
4836
|
+
yield { type: "gadget_call", call };
|
|
4837
|
+
if (this.tree) {
|
|
4838
|
+
this.tree.addGadget({
|
|
4839
|
+
invocationId: call.invocationId,
|
|
4840
|
+
name: call.gadgetName,
|
|
4841
|
+
parameters: call.parameters ?? {},
|
|
4842
|
+
dependencies: call.dependencies,
|
|
4843
|
+
parentId: this.parentNodeId
|
|
4192
4844
|
});
|
|
4193
|
-
return;
|
|
4194
4845
|
}
|
|
4195
|
-
yield { type: "gadget_call", call };
|
|
4196
4846
|
if (call.dependencies.length > 0) {
|
|
4197
4847
|
if (call.dependencies.includes(call.invocationId)) {
|
|
4198
4848
|
this.logger.warn("Gadget has self-referential dependency (depends on itself)", {
|
|
@@ -4229,13 +4879,16 @@ var init_stream_processor = __esm({
|
|
|
4229
4879
|
this.gadgetsAwaitingDependencies.set(call.invocationId, call);
|
|
4230
4880
|
return;
|
|
4231
4881
|
}
|
|
4882
|
+
for await (const evt of this.executeGadgetGenerator(call)) {
|
|
4883
|
+
yield evt;
|
|
4884
|
+
}
|
|
4885
|
+
for await (const evt of this.processPendingGadgetsGenerator()) {
|
|
4886
|
+
yield evt;
|
|
4887
|
+
}
|
|
4888
|
+
return;
|
|
4232
4889
|
}
|
|
4233
|
-
|
|
4234
|
-
|
|
4235
|
-
}
|
|
4236
|
-
for await (const evt of this.processPendingGadgetsGenerator()) {
|
|
4237
|
-
yield evt;
|
|
4238
|
-
}
|
|
4890
|
+
const executionPromise = this.executeGadgetAndCollect(call);
|
|
4891
|
+
this.inFlightExecutions.set(call.invocationId, executionPromise);
|
|
4239
4892
|
}
|
|
4240
4893
|
/**
|
|
4241
4894
|
* Execute a gadget through the full hook lifecycle.
|
|
@@ -4250,15 +4903,6 @@ var init_stream_processor = __esm({
|
|
|
4250
4903
|
error: call.parseError,
|
|
4251
4904
|
rawParameters: call.parametersRaw
|
|
4252
4905
|
});
|
|
4253
|
-
const shouldContinue = await this.checkCanRecoverFromError(
|
|
4254
|
-
call.parseError,
|
|
4255
|
-
call.gadgetName,
|
|
4256
|
-
"parse",
|
|
4257
|
-
call.parameters
|
|
4258
|
-
);
|
|
4259
|
-
if (!shouldContinue) {
|
|
4260
|
-
this.executionHalted = true;
|
|
4261
|
-
}
|
|
4262
4906
|
}
|
|
4263
4907
|
let parameters = call.parameters ?? {};
|
|
4264
4908
|
if (this.hooks.interceptors?.interceptGadgetParameters) {
|
|
@@ -4301,7 +4945,7 @@ var init_stream_processor = __esm({
|
|
|
4301
4945
|
parameters,
|
|
4302
4946
|
logger: this.logger
|
|
4303
4947
|
};
|
|
4304
|
-
await this.hooks.observers
|
|
4948
|
+
await this.hooks.observers?.onGadgetExecutionStart?.(context);
|
|
4305
4949
|
});
|
|
4306
4950
|
}
|
|
4307
4951
|
await this.runObserversInParallel(startObservers);
|
|
@@ -4370,7 +5014,7 @@ var init_stream_processor = __esm({
|
|
|
4370
5014
|
cost: result.cost,
|
|
4371
5015
|
logger: this.logger
|
|
4372
5016
|
};
|
|
4373
|
-
await this.hooks.observers
|
|
5017
|
+
await this.hooks.observers?.onGadgetExecutionComplete?.(context);
|
|
4374
5018
|
});
|
|
4375
5019
|
}
|
|
4376
5020
|
await this.runObserversInParallel(completeObservers);
|
|
@@ -4379,18 +5023,6 @@ var init_stream_processor = __esm({
|
|
|
4379
5023
|
this.failedInvocations.add(result.invocationId);
|
|
4380
5024
|
}
|
|
4381
5025
|
events.push({ type: "gadget_result", result });
|
|
4382
|
-
if (result.error) {
|
|
4383
|
-
const errorType = this.determineErrorType(call, result);
|
|
4384
|
-
const shouldContinue = await this.checkCanRecoverFromError(
|
|
4385
|
-
result.error,
|
|
4386
|
-
result.gadgetName,
|
|
4387
|
-
errorType,
|
|
4388
|
-
result.parameters
|
|
4389
|
-
);
|
|
4390
|
-
if (!shouldContinue) {
|
|
4391
|
-
this.executionHalted = true;
|
|
4392
|
-
}
|
|
4393
|
-
}
|
|
4394
5026
|
return events;
|
|
4395
5027
|
}
|
|
4396
5028
|
/**
|
|
@@ -4404,15 +5036,6 @@ var init_stream_processor = __esm({
|
|
|
4404
5036
|
error: call.parseError,
|
|
4405
5037
|
rawParameters: call.parametersRaw
|
|
4406
5038
|
});
|
|
4407
|
-
const shouldContinue = await this.checkCanRecoverFromError(
|
|
4408
|
-
call.parseError,
|
|
4409
|
-
call.gadgetName,
|
|
4410
|
-
"parse",
|
|
4411
|
-
call.parameters
|
|
4412
|
-
);
|
|
4413
|
-
if (!shouldContinue) {
|
|
4414
|
-
this.executionHalted = true;
|
|
4415
|
-
}
|
|
4416
5039
|
}
|
|
4417
5040
|
let parameters = call.parameters ?? {};
|
|
4418
5041
|
if (this.hooks.interceptors?.interceptGadgetParameters) {
|
|
@@ -4455,10 +5078,16 @@ var init_stream_processor = __esm({
|
|
|
4455
5078
|
parameters,
|
|
4456
5079
|
logger: this.logger
|
|
4457
5080
|
};
|
|
4458
|
-
await this.hooks.observers
|
|
5081
|
+
await this.hooks.observers?.onGadgetExecutionStart?.(context);
|
|
4459
5082
|
});
|
|
4460
5083
|
}
|
|
4461
5084
|
await this.runObserversInParallel(startObservers);
|
|
5085
|
+
if (this.tree) {
|
|
5086
|
+
const gadgetNode = this.tree.getNodeByInvocationId(call.invocationId);
|
|
5087
|
+
if (gadgetNode) {
|
|
5088
|
+
this.tree.startGadget(gadgetNode.id);
|
|
5089
|
+
}
|
|
5090
|
+
}
|
|
4462
5091
|
let result;
|
|
4463
5092
|
if (shouldSkip) {
|
|
4464
5093
|
result = {
|
|
@@ -4524,27 +5153,84 @@ var init_stream_processor = __esm({
|
|
|
4524
5153
|
cost: result.cost,
|
|
4525
5154
|
logger: this.logger
|
|
4526
5155
|
};
|
|
4527
|
-
await this.hooks.observers
|
|
5156
|
+
await this.hooks.observers?.onGadgetExecutionComplete?.(context);
|
|
4528
5157
|
});
|
|
4529
5158
|
}
|
|
4530
5159
|
await this.runObserversInParallel(completeObservers);
|
|
5160
|
+
if (this.tree) {
|
|
5161
|
+
const gadgetNode = this.tree.getNodeByInvocationId(result.invocationId);
|
|
5162
|
+
if (gadgetNode) {
|
|
5163
|
+
if (result.error) {
|
|
5164
|
+
this.tree.completeGadget(gadgetNode.id, {
|
|
5165
|
+
error: result.error,
|
|
5166
|
+
executionTimeMs: result.executionTimeMs,
|
|
5167
|
+
cost: result.cost
|
|
5168
|
+
});
|
|
5169
|
+
} else {
|
|
5170
|
+
this.tree.completeGadget(gadgetNode.id, {
|
|
5171
|
+
result: result.result,
|
|
5172
|
+
executionTimeMs: result.executionTimeMs,
|
|
5173
|
+
cost: result.cost,
|
|
5174
|
+
media: result.media
|
|
5175
|
+
});
|
|
5176
|
+
}
|
|
5177
|
+
}
|
|
5178
|
+
}
|
|
4531
5179
|
this.completedResults.set(result.invocationId, result);
|
|
4532
5180
|
if (result.error) {
|
|
4533
5181
|
this.failedInvocations.add(result.invocationId);
|
|
4534
5182
|
}
|
|
4535
5183
|
yield { type: "gadget_result", result };
|
|
4536
|
-
|
|
4537
|
-
|
|
4538
|
-
|
|
4539
|
-
|
|
4540
|
-
|
|
4541
|
-
|
|
4542
|
-
|
|
4543
|
-
|
|
4544
|
-
|
|
4545
|
-
|
|
5184
|
+
}
|
|
5185
|
+
/**
|
|
5186
|
+
* Execute a gadget and push events to the completed results queue (non-blocking).
|
|
5187
|
+
* Used for fire-and-forget parallel execution of independent gadgets.
|
|
5188
|
+
* Results are pushed to completedResultsQueue for real-time streaming to the caller.
|
|
5189
|
+
*/
|
|
5190
|
+
async executeGadgetAndCollect(call) {
|
|
5191
|
+
for await (const evt of this.executeGadgetGenerator(call)) {
|
|
5192
|
+
this.completedResultsQueue.push(evt);
|
|
5193
|
+
}
|
|
5194
|
+
}
|
|
5195
|
+
/**
|
|
5196
|
+
* Drain all completed results from the queue.
|
|
5197
|
+
* Used to yield results as they complete during stream processing.
|
|
5198
|
+
* @returns Generator that yields all events currently in the queue
|
|
5199
|
+
*/
|
|
5200
|
+
*drainCompletedResults() {
|
|
5201
|
+
while (this.completedResultsQueue.length > 0) {
|
|
5202
|
+
yield this.completedResultsQueue.shift();
|
|
5203
|
+
}
|
|
5204
|
+
}
|
|
5205
|
+
/**
|
|
5206
|
+
* Wait for all in-flight gadget executions to complete, yielding events in real-time.
|
|
5207
|
+
* Called at stream end to ensure all parallel executions finish.
|
|
5208
|
+
* Results and subagent events are pushed to completedResultsQueue during execution.
|
|
5209
|
+
* This generator yields queued events while polling, enabling real-time display
|
|
5210
|
+
* of subagent activity (LLM calls, nested gadgets) during long-running gadgets.
|
|
5211
|
+
* Clears the inFlightExecutions map after all gadgets complete.
|
|
5212
|
+
*/
|
|
5213
|
+
async *waitForInFlightExecutions() {
|
|
5214
|
+
if (this.inFlightExecutions.size === 0) {
|
|
5215
|
+
return;
|
|
5216
|
+
}
|
|
5217
|
+
this.logger.debug("Waiting for in-flight gadget executions", {
|
|
5218
|
+
count: this.inFlightExecutions.size,
|
|
5219
|
+
invocationIds: Array.from(this.inFlightExecutions.keys())
|
|
5220
|
+
});
|
|
5221
|
+
const allDone = Promise.all(this.inFlightExecutions.values()).then(() => "done");
|
|
5222
|
+
const POLL_INTERVAL_MS = 100;
|
|
5223
|
+
while (true) {
|
|
5224
|
+
const result = await Promise.race([
|
|
5225
|
+
allDone,
|
|
5226
|
+
new Promise((resolve) => setTimeout(() => resolve("poll"), POLL_INTERVAL_MS))
|
|
5227
|
+
]);
|
|
5228
|
+
yield* this.drainCompletedResults();
|
|
5229
|
+
if (result === "done") {
|
|
5230
|
+
break;
|
|
4546
5231
|
}
|
|
4547
5232
|
}
|
|
5233
|
+
this.inFlightExecutions.clear();
|
|
4548
5234
|
}
|
|
4549
5235
|
/**
|
|
4550
5236
|
* Handle a gadget that cannot execute because a dependency failed.
|
|
@@ -4569,6 +5255,12 @@ var init_stream_processor = __esm({
|
|
|
4569
5255
|
}
|
|
4570
5256
|
if (action.action === "skip") {
|
|
4571
5257
|
this.failedInvocations.add(call.invocationId);
|
|
5258
|
+
if (this.tree) {
|
|
5259
|
+
const gadgetNode = this.tree.getNodeByInvocationId(call.invocationId);
|
|
5260
|
+
if (gadgetNode) {
|
|
5261
|
+
this.tree.skipGadget(gadgetNode.id, failedDep, depError, "dependency_failed");
|
|
5262
|
+
}
|
|
5263
|
+
}
|
|
4572
5264
|
const skipEvent = {
|
|
4573
5265
|
type: "gadget_skipped",
|
|
4574
5266
|
gadgetName: call.gadgetName,
|
|
@@ -4588,7 +5280,7 @@ var init_stream_processor = __esm({
|
|
|
4588
5280
|
failedDependencyError: depError,
|
|
4589
5281
|
logger: this.logger
|
|
4590
5282
|
};
|
|
4591
|
-
await this.safeObserve(() => this.hooks.observers
|
|
5283
|
+
await this.safeObserve(() => this.hooks.observers?.onGadgetSkipped?.(observeContext));
|
|
4592
5284
|
}
|
|
4593
5285
|
this.logger.info("Gadget skipped due to failed dependency", {
|
|
4594
5286
|
gadgetName: call.gadgetName,
|
|
@@ -4820,48 +5512,6 @@ var init_stream_processor = __esm({
|
|
|
4820
5512
|
observers.map((observer) => this.safeObserve(observer))
|
|
4821
5513
|
);
|
|
4822
5514
|
}
|
|
4823
|
-
/**
|
|
4824
|
-
* Check if execution can recover from an error.
|
|
4825
|
-
*
|
|
4826
|
-
* Returns true if we should continue processing subsequent gadgets, false if we should stop.
|
|
4827
|
-
*
|
|
4828
|
-
* Logic:
|
|
4829
|
-
* - If custom canRecoverFromGadgetError is provided, use it
|
|
4830
|
-
* - Otherwise, use stopOnGadgetError config:
|
|
4831
|
-
* - stopOnGadgetError=true → return false (stop execution)
|
|
4832
|
-
* - stopOnGadgetError=false → return true (continue execution)
|
|
4833
|
-
*/
|
|
4834
|
-
async checkCanRecoverFromError(error, gadgetName, errorType, parameters) {
|
|
4835
|
-
if (this.canRecoverFromGadgetError) {
|
|
4836
|
-
return await this.canRecoverFromGadgetError({
|
|
4837
|
-
error,
|
|
4838
|
-
gadgetName,
|
|
4839
|
-
errorType,
|
|
4840
|
-
parameters
|
|
4841
|
-
});
|
|
4842
|
-
}
|
|
4843
|
-
const shouldContinue = !this.stopOnGadgetError;
|
|
4844
|
-
this.logger.debug("Checking if should continue after error", {
|
|
4845
|
-
error,
|
|
4846
|
-
gadgetName,
|
|
4847
|
-
errorType,
|
|
4848
|
-
stopOnGadgetError: this.stopOnGadgetError,
|
|
4849
|
-
shouldContinue
|
|
4850
|
-
});
|
|
4851
|
-
return shouldContinue;
|
|
4852
|
-
}
|
|
4853
|
-
/**
|
|
4854
|
-
* Determine the type of error from a gadget execution.
|
|
4855
|
-
*/
|
|
4856
|
-
determineErrorType(call, result) {
|
|
4857
|
-
if (call.parseError) {
|
|
4858
|
-
return "parse";
|
|
4859
|
-
}
|
|
4860
|
-
if (result.error?.includes("Invalid parameters:")) {
|
|
4861
|
-
return "validation";
|
|
4862
|
-
}
|
|
4863
|
-
return "execution";
|
|
4864
|
-
}
|
|
4865
5515
|
};
|
|
4866
5516
|
}
|
|
4867
5517
|
});
|
|
@@ -4872,6 +5522,7 @@ var init_agent = __esm({
|
|
|
4872
5522
|
"src/agent/agent.ts"() {
|
|
4873
5523
|
"use strict";
|
|
4874
5524
|
init_constants();
|
|
5525
|
+
init_execution_tree();
|
|
4875
5526
|
init_messages();
|
|
4876
5527
|
init_model_shortcuts();
|
|
4877
5528
|
init_media_store();
|
|
@@ -4899,8 +5550,6 @@ var init_agent = __esm({
|
|
|
4899
5550
|
requestHumanInput;
|
|
4900
5551
|
textOnlyHandler;
|
|
4901
5552
|
textWithGadgetsHandler;
|
|
4902
|
-
stopOnGadgetError;
|
|
4903
|
-
canRecoverFromGadgetError;
|
|
4904
5553
|
defaultGadgetTimeoutMs;
|
|
4905
5554
|
defaultMaxTokens;
|
|
4906
5555
|
hasUserPrompt;
|
|
@@ -4923,6 +5572,12 @@ var init_agent = __esm({
|
|
|
4923
5572
|
pendingSubagentEvents = [];
|
|
4924
5573
|
// Combined callback that queues events AND calls user callback
|
|
4925
5574
|
onSubagentEvent;
|
|
5575
|
+
// Counter for generating synthetic invocation IDs for wrapped text content
|
|
5576
|
+
syntheticInvocationCounter = 0;
|
|
5577
|
+
// Execution Tree - first-class model for nested subagent support
|
|
5578
|
+
tree;
|
|
5579
|
+
parentNodeId;
|
|
5580
|
+
baseDepth;
|
|
4926
5581
|
/**
|
|
4927
5582
|
* Creates a new Agent instance.
|
|
4928
5583
|
* @internal This constructor is private. Use LLMist.createAgent() or AgentBuilder instead.
|
|
@@ -4945,8 +5600,6 @@ var init_agent = __esm({
|
|
|
4945
5600
|
this.requestHumanInput = options.requestHumanInput;
|
|
4946
5601
|
this.textOnlyHandler = options.textOnlyHandler ?? "terminate";
|
|
4947
5602
|
this.textWithGadgetsHandler = options.textWithGadgetsHandler;
|
|
4948
|
-
this.stopOnGadgetError = options.stopOnGadgetError ?? true;
|
|
4949
|
-
this.canRecoverFromGadgetError = options.canRecoverFromGadgetError;
|
|
4950
5603
|
this.defaultGadgetTimeoutMs = options.defaultGadgetTimeoutMs;
|
|
4951
5604
|
this.defaultMaxTokens = this.resolveMaxTokensFromCatalog(options.model);
|
|
4952
5605
|
this.outputLimitEnabled = options.gadgetOutputLimit ?? DEFAULT_GADGET_OUTPUT_LIMIT;
|
|
@@ -5000,6 +5653,9 @@ var init_agent = __esm({
|
|
|
5000
5653
|
temperature: this.temperature
|
|
5001
5654
|
};
|
|
5002
5655
|
this.subagentConfig = options.subagentConfig;
|
|
5656
|
+
this.tree = options.parentTree ?? new ExecutionTree();
|
|
5657
|
+
this.parentNodeId = options.parentNodeId ?? null;
|
|
5658
|
+
this.baseDepth = options.baseDepth ?? 0;
|
|
5003
5659
|
this.userSubagentEventCallback = options.onSubagentEvent;
|
|
5004
5660
|
this.onSubagentEvent = (event) => {
|
|
5005
5661
|
this.pendingSubagentEvents.push(event);
|
|
@@ -5064,7 +5720,9 @@ var init_agent = __esm({
|
|
|
5064
5720
|
*flushPendingSubagentEvents() {
|
|
5065
5721
|
while (this.pendingSubagentEvents.length > 0) {
|
|
5066
5722
|
const event = this.pendingSubagentEvents.shift();
|
|
5067
|
-
|
|
5723
|
+
if (event) {
|
|
5724
|
+
yield { type: "subagent_event", subagentEvent: event };
|
|
5725
|
+
}
|
|
5068
5726
|
}
|
|
5069
5727
|
}
|
|
5070
5728
|
/**
|
|
@@ -5118,6 +5776,48 @@ var init_agent = __esm({
|
|
|
5118
5776
|
getMediaStore() {
|
|
5119
5777
|
return this.mediaStore;
|
|
5120
5778
|
}
|
|
5779
|
+
/**
|
|
5780
|
+
* Get the execution tree for this agent.
|
|
5781
|
+
*
|
|
5782
|
+
* The execution tree provides a first-class model of all LLM calls and gadget executions,
|
|
5783
|
+
* including nested subagent activity. Use this to:
|
|
5784
|
+
* - Query execution state: `tree.getNode(id)`
|
|
5785
|
+
* - Get total cost: `tree.getTotalCost()`
|
|
5786
|
+
* - Get subtree cost/media/tokens: `tree.getSubtreeCost(nodeId)`
|
|
5787
|
+
* - Subscribe to events: `tree.on("llm_call_complete", handler)`
|
|
5788
|
+
* - Stream all events: `for await (const event of tree.events())`
|
|
5789
|
+
*
|
|
5790
|
+
* For subagents (created with `withParentContext`), the tree is shared with the parent,
|
|
5791
|
+
* enabling unified tracking and real-time visibility across all nesting levels.
|
|
5792
|
+
*
|
|
5793
|
+
* @returns The ExecutionTree instance
|
|
5794
|
+
*
|
|
5795
|
+
* @example
|
|
5796
|
+
* ```typescript
|
|
5797
|
+
* const agent = LLMist.createAgent()
|
|
5798
|
+
* .withModel("sonnet")
|
|
5799
|
+
* .withGadgets(BrowseWeb)
|
|
5800
|
+
* .ask("Research topic X");
|
|
5801
|
+
*
|
|
5802
|
+
* for await (const event of agent.run()) {
|
|
5803
|
+
* // Process events...
|
|
5804
|
+
* }
|
|
5805
|
+
*
|
|
5806
|
+
* // After execution, query the tree
|
|
5807
|
+
* const tree = agent.getTree();
|
|
5808
|
+
* console.log(`Total cost: $${tree.getTotalCost().toFixed(4)}`);
|
|
5809
|
+
*
|
|
5810
|
+
* // Inspect all LLM calls
|
|
5811
|
+
* for (const node of tree.getAllNodes()) {
|
|
5812
|
+
* if (node.type === "llm_call") {
|
|
5813
|
+
* console.log(`LLM #${node.iteration}: ${node.model}`);
|
|
5814
|
+
* }
|
|
5815
|
+
* }
|
|
5816
|
+
* ```
|
|
5817
|
+
*/
|
|
5818
|
+
getTree() {
|
|
5819
|
+
return this.tree;
|
|
5820
|
+
}
|
|
5121
5821
|
/**
|
|
5122
5822
|
* Manually trigger context compaction.
|
|
5123
5823
|
*
|
|
@@ -5219,6 +5919,7 @@ var init_agent = __esm({
|
|
|
5219
5919
|
await this.hooks.observers.onCompaction({
|
|
5220
5920
|
iteration: currentIteration,
|
|
5221
5921
|
event: compactionEvent,
|
|
5922
|
+
// biome-ignore lint/style/noNonNullAssertion: compactionManager exists if compactionEvent is truthy
|
|
5222
5923
|
stats: this.compactionManager.getStats(),
|
|
5223
5924
|
logger: this.logger
|
|
5224
5925
|
});
|
|
@@ -5280,6 +5981,13 @@ var init_agent = __esm({
|
|
|
5280
5981
|
messageCount: llmOptions.messages.length,
|
|
5281
5982
|
messages: llmOptions.messages
|
|
5282
5983
|
});
|
|
5984
|
+
const llmNode = this.tree.addLLMCall({
|
|
5985
|
+
iteration: currentIteration,
|
|
5986
|
+
model: llmOptions.model,
|
|
5987
|
+
parentId: this.parentNodeId,
|
|
5988
|
+
request: llmOptions.messages
|
|
5989
|
+
});
|
|
5990
|
+
const currentLLMNodeId = llmNode.id;
|
|
5283
5991
|
const stream2 = this.client.stream(llmOptions);
|
|
5284
5992
|
const processor = new StreamProcessor({
|
|
5285
5993
|
iteration: currentIteration,
|
|
@@ -5290,14 +5998,17 @@ var init_agent = __esm({
|
|
|
5290
5998
|
hooks: this.hooks,
|
|
5291
5999
|
logger: this.logger.getSubLogger({ name: "stream-processor" }),
|
|
5292
6000
|
requestHumanInput: this.requestHumanInput,
|
|
5293
|
-
stopOnGadgetError: this.stopOnGadgetError,
|
|
5294
|
-
canRecoverFromGadgetError: this.canRecoverFromGadgetError,
|
|
5295
6001
|
defaultGadgetTimeoutMs: this.defaultGadgetTimeoutMs,
|
|
5296
6002
|
client: this.client,
|
|
5297
6003
|
mediaStore: this.mediaStore,
|
|
5298
6004
|
agentConfig: this.agentContextConfig,
|
|
5299
6005
|
subagentConfig: this.subagentConfig,
|
|
5300
|
-
onSubagentEvent: this.onSubagentEvent
|
|
6006
|
+
onSubagentEvent: this.onSubagentEvent,
|
|
6007
|
+
// Tree context for execution tracking
|
|
6008
|
+
tree: this.tree,
|
|
6009
|
+
parentNodeId: currentLLMNodeId,
|
|
6010
|
+
// Gadgets are children of this LLM call
|
|
6011
|
+
baseDepth: this.baseDepth
|
|
5301
6012
|
});
|
|
5302
6013
|
let streamMetadata = null;
|
|
5303
6014
|
let gadgetCallCount = 0;
|
|
@@ -5343,6 +6054,11 @@ var init_agent = __esm({
|
|
|
5343
6054
|
await this.hooks.observers.onLLMCallComplete(context);
|
|
5344
6055
|
}
|
|
5345
6056
|
});
|
|
6057
|
+
this.tree.completeLLMCall(currentLLMNodeId, {
|
|
6058
|
+
response: result.rawResponse,
|
|
6059
|
+
usage: result.usage,
|
|
6060
|
+
finishReason: result.finishReason
|
|
6061
|
+
});
|
|
5346
6062
|
let finalMessage = result.finalMessage;
|
|
5347
6063
|
if (this.hooks.controllers?.afterLLMCall) {
|
|
5348
6064
|
const context = {
|
|
@@ -5377,10 +6093,12 @@ var init_agent = __esm({
|
|
|
5377
6093
|
const textContent = textOutputs.join("");
|
|
5378
6094
|
if (textContent.trim()) {
|
|
5379
6095
|
const { gadgetName, parameterMapping, resultMapping } = this.textWithGadgetsHandler;
|
|
6096
|
+
const syntheticId = `gc_text_${++this.syntheticInvocationCounter}`;
|
|
5380
6097
|
this.conversation.addGadgetCallResult(
|
|
5381
6098
|
gadgetName,
|
|
5382
6099
|
parameterMapping(textContent),
|
|
5383
|
-
resultMapping ? resultMapping(textContent) : textContent
|
|
6100
|
+
resultMapping ? resultMapping(textContent) : textContent,
|
|
6101
|
+
syntheticId
|
|
5384
6102
|
);
|
|
5385
6103
|
}
|
|
5386
6104
|
}
|
|
@@ -5391,6 +6109,7 @@ var init_agent = __esm({
|
|
|
5391
6109
|
gadgetResult.gadgetName,
|
|
5392
6110
|
gadgetResult.parameters,
|
|
5393
6111
|
gadgetResult.error ?? gadgetResult.result ?? "",
|
|
6112
|
+
gadgetResult.invocationId,
|
|
5394
6113
|
gadgetResult.media,
|
|
5395
6114
|
gadgetResult.mediaIds
|
|
5396
6115
|
);
|
|
@@ -5398,10 +6117,12 @@ var init_agent = __esm({
|
|
|
5398
6117
|
}
|
|
5399
6118
|
} else {
|
|
5400
6119
|
if (finalMessage.trim()) {
|
|
6120
|
+
const syntheticId = `gc_tell_${++this.syntheticInvocationCounter}`;
|
|
5401
6121
|
this.conversation.addGadgetCallResult(
|
|
5402
6122
|
"TellUser",
|
|
5403
6123
|
{ message: finalMessage, done: false, type: "info" },
|
|
5404
|
-
`\u2139\uFE0F ${finalMessage}
|
|
6124
|
+
`\u2139\uFE0F ${finalMessage}`,
|
|
6125
|
+
syntheticId
|
|
5405
6126
|
);
|
|
5406
6127
|
}
|
|
5407
6128
|
const shouldBreak = await this.handleTextOnlyResponse(finalMessage);
|
|
@@ -8886,8 +9607,6 @@ var init_builder = __esm({
|
|
|
8886
9607
|
gadgetArgPrefix;
|
|
8887
9608
|
textOnlyHandler;
|
|
8888
9609
|
textWithGadgetsHandler;
|
|
8889
|
-
stopOnGadgetError;
|
|
8890
|
-
canRecoverFromGadgetError;
|
|
8891
9610
|
defaultGadgetTimeoutMs;
|
|
8892
9611
|
gadgetOutputLimit;
|
|
8893
9612
|
gadgetOutputLimitPercent;
|
|
@@ -8896,6 +9615,8 @@ var init_builder = __esm({
|
|
|
8896
9615
|
trailingMessage;
|
|
8897
9616
|
subagentConfig;
|
|
8898
9617
|
subagentEventCallback;
|
|
9618
|
+
// Tree context for subagent support - enables shared tree model
|
|
9619
|
+
// When a gadget calls withParentContext(ctx), it shares the parent's tree
|
|
8899
9620
|
parentContext;
|
|
8900
9621
|
constructor(client) {
|
|
8901
9622
|
this.client = client;
|
|
@@ -9178,62 +9899,6 @@ var init_builder = __esm({
|
|
|
9178
9899
|
this.textWithGadgetsHandler = handler;
|
|
9179
9900
|
return this;
|
|
9180
9901
|
}
|
|
9181
|
-
/**
|
|
9182
|
-
* Set whether to stop gadget execution on first error.
|
|
9183
|
-
*
|
|
9184
|
-
* When true (default), if a gadget fails:
|
|
9185
|
-
* - Subsequent gadgets in the same response are skipped
|
|
9186
|
-
* - LLM stream is cancelled to save costs
|
|
9187
|
-
* - Agent loop continues with error in context
|
|
9188
|
-
*
|
|
9189
|
-
* When false:
|
|
9190
|
-
* - All gadgets in the response still execute
|
|
9191
|
-
* - LLM stream continues to completion
|
|
9192
|
-
*
|
|
9193
|
-
* @param stop - Whether to stop on gadget error
|
|
9194
|
-
* @returns This builder for chaining
|
|
9195
|
-
*
|
|
9196
|
-
* @example
|
|
9197
|
-
* ```typescript
|
|
9198
|
-
* .withStopOnGadgetError(false)
|
|
9199
|
-
* ```
|
|
9200
|
-
*/
|
|
9201
|
-
withStopOnGadgetError(stop) {
|
|
9202
|
-
this.stopOnGadgetError = stop;
|
|
9203
|
-
return this;
|
|
9204
|
-
}
|
|
9205
|
-
/**
|
|
9206
|
-
* Set custom error handling logic.
|
|
9207
|
-
*
|
|
9208
|
-
* Provides fine-grained control over whether to continue after different types of errors.
|
|
9209
|
-
* Overrides `stopOnGadgetError` when provided.
|
|
9210
|
-
*
|
|
9211
|
-
* **Note:** This builder method configures the underlying `canRecoverFromGadgetError` option
|
|
9212
|
-
* in `AgentOptions`. The method is named `withErrorHandler` for better developer experience,
|
|
9213
|
-
* but maps to the `canRecoverFromGadgetError` property internally.
|
|
9214
|
-
*
|
|
9215
|
-
* @param handler - Function that decides whether to continue after an error.
|
|
9216
|
-
* Return `true` to continue execution, `false` to stop.
|
|
9217
|
-
* @returns This builder for chaining
|
|
9218
|
-
*
|
|
9219
|
-
* @example
|
|
9220
|
-
* ```typescript
|
|
9221
|
-
* .withErrorHandler((context) => {
|
|
9222
|
-
* // Stop on parse errors, continue on validation/execution errors
|
|
9223
|
-
* if (context.errorType === "parse") {
|
|
9224
|
-
* return false;
|
|
9225
|
-
* }
|
|
9226
|
-
* if (context.error.includes("CRITICAL")) {
|
|
9227
|
-
* return false;
|
|
9228
|
-
* }
|
|
9229
|
-
* return true;
|
|
9230
|
-
* })
|
|
9231
|
-
* ```
|
|
9232
|
-
*/
|
|
9233
|
-
withErrorHandler(handler) {
|
|
9234
|
-
this.canRecoverFromGadgetError = handler;
|
|
9235
|
-
return this;
|
|
9236
|
-
}
|
|
9237
9902
|
/**
|
|
9238
9903
|
* Set default timeout for gadget execution.
|
|
9239
9904
|
*
|
|
@@ -9428,6 +10093,15 @@ var init_builder = __esm({
|
|
|
9428
10093
|
* The method extracts `invocationId` and `onSubagentEvent` from the execution
|
|
9429
10094
|
* context and sets up automatic forwarding via hooks and event wrapping.
|
|
9430
10095
|
*
|
|
10096
|
+
* **NEW: Shared Tree Model** - When the parent provides an ExecutionTree via context,
|
|
10097
|
+
* the subagent shares that tree instead of creating its own. This enables:
|
|
10098
|
+
* - Unified cost tracking across all nesting levels
|
|
10099
|
+
* - Automatic media aggregation via `tree.getSubtreeMedia(nodeId)`
|
|
10100
|
+
* - Real-time visibility of nested execution in the parent
|
|
10101
|
+
*
|
|
10102
|
+
* **Signal Forwarding** - When parent context includes a signal, it's automatically
|
|
10103
|
+
* forwarded to the subagent for proper cancellation propagation.
|
|
10104
|
+
*
|
|
9431
10105
|
* @param ctx - ExecutionContext passed to the gadget's execute() method
|
|
9432
10106
|
* @param depth - Nesting depth (default: 1 for direct child)
|
|
9433
10107
|
* @returns This builder for chaining
|
|
@@ -9448,17 +10122,25 @@ var init_builder = __esm({
|
|
|
9448
10122
|
* result = event.content;
|
|
9449
10123
|
* }
|
|
9450
10124
|
* }
|
|
10125
|
+
*
|
|
10126
|
+
* // After subagent completes, costs are automatically aggregated
|
|
10127
|
+
* // No manual tracking needed - use tree methods:
|
|
10128
|
+
* const totalCost = ctx.tree?.getSubtreeCost(ctx.nodeId!);
|
|
10129
|
+
* const allMedia = ctx.tree?.getSubtreeMedia(ctx.nodeId!);
|
|
9451
10130
|
* }
|
|
9452
10131
|
* ```
|
|
9453
10132
|
*/
|
|
9454
10133
|
withParentContext(ctx, depth = 1) {
|
|
9455
|
-
if (ctx.
|
|
10134
|
+
if (ctx.tree) {
|
|
9456
10135
|
this.parentContext = {
|
|
9457
|
-
|
|
9458
|
-
|
|
10136
|
+
tree: ctx.tree,
|
|
10137
|
+
nodeId: ctx.nodeId,
|
|
9459
10138
|
depth
|
|
9460
10139
|
};
|
|
9461
10140
|
}
|
|
10141
|
+
if (ctx.signal && !this.signal) {
|
|
10142
|
+
this.signal = ctx.signal;
|
|
10143
|
+
}
|
|
9462
10144
|
return this;
|
|
9463
10145
|
}
|
|
9464
10146
|
/**
|
|
@@ -9491,11 +10173,13 @@ var init_builder = __esm({
|
|
|
9491
10173
|
*
|
|
9492
10174
|
* This is useful for in-context learning - showing the LLM what "past self"
|
|
9493
10175
|
* did correctly so it mimics the pattern. The call is formatted with proper
|
|
9494
|
-
* markers and parameter format
|
|
10176
|
+
* markers and parameter format, including the invocation ID so the LLM can
|
|
10177
|
+
* reference previous calls when building dependencies.
|
|
9495
10178
|
*
|
|
9496
10179
|
* @param gadgetName - Name of the gadget
|
|
9497
10180
|
* @param parameters - Parameters passed to the gadget
|
|
9498
10181
|
* @param result - Result returned by the gadget
|
|
10182
|
+
* @param invocationId - Invocation ID (shown to LLM so it can reference for dependencies)
|
|
9499
10183
|
* @returns This builder for chaining
|
|
9500
10184
|
*
|
|
9501
10185
|
* @example
|
|
@@ -9507,124 +10191,36 @@ var init_builder = __esm({
|
|
|
9507
10191
|
* done: false,
|
|
9508
10192
|
* type: 'info'
|
|
9509
10193
|
* },
|
|
9510
|
-
* 'ℹ️ 👋 Hello!\n\nHere\'s what I can do:\n- Analyze code\n- Run commands'
|
|
10194
|
+
* 'ℹ️ 👋 Hello!\n\nHere\'s what I can do:\n- Analyze code\n- Run commands',
|
|
10195
|
+
* 'gc_1'
|
|
9511
10196
|
* )
|
|
9512
10197
|
* ```
|
|
9513
10198
|
*/
|
|
9514
|
-
withSyntheticGadgetCall(gadgetName, parameters, result) {
|
|
10199
|
+
withSyntheticGadgetCall(gadgetName, parameters, result, invocationId) {
|
|
9515
10200
|
const startPrefix = this.gadgetStartPrefix ?? GADGET_START_PREFIX;
|
|
9516
10201
|
const endPrefix = this.gadgetEndPrefix ?? GADGET_END_PREFIX;
|
|
9517
10202
|
const paramStr = this.formatBlockParameters(parameters, "");
|
|
9518
10203
|
this.initialMessages.push({
|
|
9519
10204
|
role: "assistant",
|
|
9520
|
-
content: `${startPrefix}${gadgetName}
|
|
10205
|
+
content: `${startPrefix}${gadgetName}:${invocationId}
|
|
9521
10206
|
${paramStr}
|
|
9522
10207
|
${endPrefix}`
|
|
9523
10208
|
});
|
|
9524
10209
|
this.initialMessages.push({
|
|
9525
10210
|
role: "user",
|
|
9526
|
-
content: `Result: ${result}`
|
|
10211
|
+
content: `Result (${invocationId}): ${result}`
|
|
9527
10212
|
});
|
|
9528
10213
|
return this;
|
|
9529
10214
|
}
|
|
9530
10215
|
/**
|
|
9531
|
-
* Compose the final hooks, including
|
|
9532
|
-
*
|
|
9533
|
-
*
|
|
10216
|
+
* Compose the final hooks, including trailing message injection if configured.
|
|
10217
|
+
*
|
|
10218
|
+
* Note: Subagent event visibility is now handled entirely by the ExecutionTree.
|
|
10219
|
+
* When a subagent uses withParentContext(ctx), it shares the parent's tree,
|
|
10220
|
+
* and all events are automatically visible to tree subscribers (like the TUI).
|
|
9534
10221
|
*/
|
|
9535
10222
|
composeHooks() {
|
|
9536
|
-
|
|
9537
|
-
if (this.parentContext) {
|
|
9538
|
-
const { invocationId, onSubagentEvent, depth } = this.parentContext;
|
|
9539
|
-
const existingOnLLMCallStart = hooks?.observers?.onLLMCallStart;
|
|
9540
|
-
const existingOnLLMCallComplete = hooks?.observers?.onLLMCallComplete;
|
|
9541
|
-
const existingOnGadgetExecutionStart = hooks?.observers?.onGadgetExecutionStart;
|
|
9542
|
-
const existingOnGadgetExecutionComplete = hooks?.observers?.onGadgetExecutionComplete;
|
|
9543
|
-
hooks = {
|
|
9544
|
-
...hooks,
|
|
9545
|
-
observers: {
|
|
9546
|
-
...hooks?.observers,
|
|
9547
|
-
onLLMCallStart: async (context) => {
|
|
9548
|
-
let inputTokens;
|
|
9549
|
-
try {
|
|
9550
|
-
if (this.client) {
|
|
9551
|
-
inputTokens = await this.client.countTokens(
|
|
9552
|
-
context.options.model,
|
|
9553
|
-
context.options.messages
|
|
9554
|
-
);
|
|
9555
|
-
}
|
|
9556
|
-
} catch {
|
|
9557
|
-
}
|
|
9558
|
-
onSubagentEvent({
|
|
9559
|
-
type: "llm_call_start",
|
|
9560
|
-
gadgetInvocationId: invocationId,
|
|
9561
|
-
depth,
|
|
9562
|
-
event: {
|
|
9563
|
-
iteration: context.iteration,
|
|
9564
|
-
model: context.options.model,
|
|
9565
|
-
inputTokens
|
|
9566
|
-
}
|
|
9567
|
-
});
|
|
9568
|
-
if (existingOnLLMCallStart) {
|
|
9569
|
-
await existingOnLLMCallStart(context);
|
|
9570
|
-
}
|
|
9571
|
-
},
|
|
9572
|
-
onLLMCallComplete: async (context) => {
|
|
9573
|
-
onSubagentEvent({
|
|
9574
|
-
type: "llm_call_end",
|
|
9575
|
-
gadgetInvocationId: invocationId,
|
|
9576
|
-
depth,
|
|
9577
|
-
event: {
|
|
9578
|
-
iteration: context.iteration,
|
|
9579
|
-
model: context.options.model,
|
|
9580
|
-
// Backward compat fields
|
|
9581
|
-
inputTokens: context.usage?.inputTokens,
|
|
9582
|
-
outputTokens: context.usage?.outputTokens,
|
|
9583
|
-
finishReason: context.finishReason ?? void 0,
|
|
9584
|
-
// Full usage object with cache details (for first-class display)
|
|
9585
|
-
usage: context.usage
|
|
9586
|
-
// Cost will be calculated by parent if it has model registry
|
|
9587
|
-
}
|
|
9588
|
-
});
|
|
9589
|
-
if (existingOnLLMCallComplete) {
|
|
9590
|
-
await existingOnLLMCallComplete(context);
|
|
9591
|
-
}
|
|
9592
|
-
},
|
|
9593
|
-
onGadgetExecutionStart: async (context) => {
|
|
9594
|
-
onSubagentEvent({
|
|
9595
|
-
type: "gadget_call",
|
|
9596
|
-
gadgetInvocationId: invocationId,
|
|
9597
|
-
depth,
|
|
9598
|
-
event: {
|
|
9599
|
-
call: {
|
|
9600
|
-
invocationId: context.invocationId,
|
|
9601
|
-
gadgetName: context.gadgetName,
|
|
9602
|
-
parameters: context.parameters
|
|
9603
|
-
}
|
|
9604
|
-
}
|
|
9605
|
-
});
|
|
9606
|
-
if (existingOnGadgetExecutionStart) {
|
|
9607
|
-
await existingOnGadgetExecutionStart(context);
|
|
9608
|
-
}
|
|
9609
|
-
},
|
|
9610
|
-
onGadgetExecutionComplete: async (context) => {
|
|
9611
|
-
onSubagentEvent({
|
|
9612
|
-
type: "gadget_result",
|
|
9613
|
-
gadgetInvocationId: invocationId,
|
|
9614
|
-
depth,
|
|
9615
|
-
event: {
|
|
9616
|
-
result: {
|
|
9617
|
-
invocationId: context.invocationId
|
|
9618
|
-
}
|
|
9619
|
-
}
|
|
9620
|
-
});
|
|
9621
|
-
if (existingOnGadgetExecutionComplete) {
|
|
9622
|
-
await existingOnGadgetExecutionComplete(context);
|
|
9623
|
-
}
|
|
9624
|
-
}
|
|
9625
|
-
}
|
|
9626
|
-
};
|
|
9627
|
-
}
|
|
10223
|
+
const hooks = this.hooks;
|
|
9628
10224
|
if (!this.trailingMessage) {
|
|
9629
10225
|
return hooks;
|
|
9630
10226
|
}
|
|
@@ -9707,19 +10303,6 @@ ${endPrefix}`
|
|
|
9707
10303
|
this.client = new LLMistClass();
|
|
9708
10304
|
}
|
|
9709
10305
|
const registry = GadgetRegistry.from(this.gadgets);
|
|
9710
|
-
let onSubagentEvent = this.subagentEventCallback;
|
|
9711
|
-
if (this.parentContext) {
|
|
9712
|
-
const { invocationId, onSubagentEvent: parentCallback, depth } = this.parentContext;
|
|
9713
|
-
const existingCallback = this.subagentEventCallback;
|
|
9714
|
-
onSubagentEvent = (event) => {
|
|
9715
|
-
parentCallback({
|
|
9716
|
-
...event,
|
|
9717
|
-
gadgetInvocationId: invocationId,
|
|
9718
|
-
depth: event.depth + depth
|
|
9719
|
-
});
|
|
9720
|
-
existingCallback?.(event);
|
|
9721
|
-
};
|
|
9722
|
-
}
|
|
9723
10306
|
return {
|
|
9724
10307
|
client: this.client,
|
|
9725
10308
|
model: this.model ?? "openai:gpt-5-nano",
|
|
@@ -9738,15 +10321,17 @@ ${endPrefix}`
|
|
|
9738
10321
|
gadgetArgPrefix: this.gadgetArgPrefix,
|
|
9739
10322
|
textOnlyHandler: this.textOnlyHandler,
|
|
9740
10323
|
textWithGadgetsHandler: this.textWithGadgetsHandler,
|
|
9741
|
-
stopOnGadgetError: this.stopOnGadgetError,
|
|
9742
|
-
canRecoverFromGadgetError: this.canRecoverFromGadgetError,
|
|
9743
10324
|
defaultGadgetTimeoutMs: this.defaultGadgetTimeoutMs,
|
|
9744
10325
|
gadgetOutputLimit: this.gadgetOutputLimit,
|
|
9745
10326
|
gadgetOutputLimitPercent: this.gadgetOutputLimitPercent,
|
|
9746
10327
|
compactionConfig: this.compactionConfig,
|
|
9747
10328
|
signal: this.signal,
|
|
9748
10329
|
subagentConfig: this.subagentConfig,
|
|
9749
|
-
onSubagentEvent
|
|
10330
|
+
onSubagentEvent: this.subagentEventCallback,
|
|
10331
|
+
// Tree context for shared tree model (subagents share parent's tree)
|
|
10332
|
+
parentTree: this.parentContext?.tree,
|
|
10333
|
+
parentNodeId: this.parentContext?.nodeId,
|
|
10334
|
+
baseDepth: this.parentContext ? (this.parentContext.depth ?? 0) + 1 : 0
|
|
9750
10335
|
};
|
|
9751
10336
|
}
|
|
9752
10337
|
ask(userPrompt) {
|
|
@@ -9903,19 +10488,6 @@ ${endPrefix}`
|
|
|
9903
10488
|
this.client = new LLMistClass();
|
|
9904
10489
|
}
|
|
9905
10490
|
const registry = GadgetRegistry.from(this.gadgets);
|
|
9906
|
-
let onSubagentEvent = this.subagentEventCallback;
|
|
9907
|
-
if (this.parentContext) {
|
|
9908
|
-
const { invocationId, onSubagentEvent: parentCallback, depth } = this.parentContext;
|
|
9909
|
-
const existingCallback = this.subagentEventCallback;
|
|
9910
|
-
onSubagentEvent = (event) => {
|
|
9911
|
-
parentCallback({
|
|
9912
|
-
...event,
|
|
9913
|
-
gadgetInvocationId: invocationId,
|
|
9914
|
-
depth: event.depth + depth
|
|
9915
|
-
});
|
|
9916
|
-
existingCallback?.(event);
|
|
9917
|
-
};
|
|
9918
|
-
}
|
|
9919
10491
|
const options = {
|
|
9920
10492
|
client: this.client,
|
|
9921
10493
|
model: this.model ?? "openai:gpt-5-nano",
|
|
@@ -9934,15 +10506,17 @@ ${endPrefix}`
|
|
|
9934
10506
|
gadgetArgPrefix: this.gadgetArgPrefix,
|
|
9935
10507
|
textOnlyHandler: this.textOnlyHandler,
|
|
9936
10508
|
textWithGadgetsHandler: this.textWithGadgetsHandler,
|
|
9937
|
-
stopOnGadgetError: this.stopOnGadgetError,
|
|
9938
|
-
canRecoverFromGadgetError: this.canRecoverFromGadgetError,
|
|
9939
10509
|
defaultGadgetTimeoutMs: this.defaultGadgetTimeoutMs,
|
|
9940
10510
|
gadgetOutputLimit: this.gadgetOutputLimit,
|
|
9941
10511
|
gadgetOutputLimitPercent: this.gadgetOutputLimitPercent,
|
|
9942
10512
|
compactionConfig: this.compactionConfig,
|
|
9943
10513
|
signal: this.signal,
|
|
9944
10514
|
subagentConfig: this.subagentConfig,
|
|
9945
|
-
onSubagentEvent
|
|
10515
|
+
onSubagentEvent: this.subagentEventCallback,
|
|
10516
|
+
// Tree context for shared tree model (subagents share parent's tree)
|
|
10517
|
+
parentTree: this.parentContext?.tree,
|
|
10518
|
+
parentNodeId: this.parentContext?.nodeId,
|
|
10519
|
+
baseDepth: this.parentContext ? (this.parentContext.depth ?? 0) + 1 : 0
|
|
9946
10520
|
};
|
|
9947
10521
|
return new Agent(AGENT_INTERNAL_KEY, options);
|
|
9948
10522
|
}
|
|
@@ -9963,6 +10537,7 @@ __export(index_exports, {
|
|
|
9963
10537
|
DEFAULT_HINTS: () => DEFAULT_HINTS,
|
|
9964
10538
|
DEFAULT_PROMPTS: () => DEFAULT_PROMPTS,
|
|
9965
10539
|
DEFAULT_SUMMARIZATION_PROMPT: () => DEFAULT_SUMMARIZATION_PROMPT,
|
|
10540
|
+
ExecutionTree: () => ExecutionTree,
|
|
9966
10541
|
Gadget: () => Gadget,
|
|
9967
10542
|
GadgetCallParser: () => GadgetCallParser,
|
|
9968
10543
|
GadgetExecutor: () => GadgetExecutor,
|
|
@@ -10009,26 +10584,37 @@ __export(index_exports, {
|
|
|
10009
10584
|
detectImageMimeType: () => detectImageMimeType,
|
|
10010
10585
|
discoverProviderAdapters: () => discoverProviderAdapters,
|
|
10011
10586
|
extractMessageText: () => extractMessageText,
|
|
10587
|
+
filterByDepth: () => filterByDepth,
|
|
10588
|
+
filterByParent: () => filterByParent,
|
|
10589
|
+
filterRootEvents: () => filterRootEvents,
|
|
10012
10590
|
getMockManager: () => getMockManager,
|
|
10013
10591
|
getModelId: () => getModelId,
|
|
10014
10592
|
getProvider: () => getProvider,
|
|
10593
|
+
groupByParent: () => groupByParent,
|
|
10015
10594
|
hasProviderPrefix: () => hasProviderPrefix,
|
|
10016
10595
|
imageFromBase64: () => imageFromBase64,
|
|
10017
10596
|
imageFromBuffer: () => imageFromBuffer,
|
|
10018
10597
|
imageFromUrl: () => imageFromUrl,
|
|
10019
10598
|
isAudioPart: () => isAudioPart,
|
|
10020
10599
|
isDataUrl: () => isDataUrl,
|
|
10600
|
+
isGadgetEvent: () => isGadgetEvent,
|
|
10021
10601
|
isImagePart: () => isImagePart,
|
|
10602
|
+
isLLMEvent: () => isLLMEvent,
|
|
10603
|
+
isRootEvent: () => isRootEvent,
|
|
10604
|
+
isSubagentEvent: () => isSubagentEvent,
|
|
10022
10605
|
isTextPart: () => isTextPart,
|
|
10023
10606
|
iterationProgressHint: () => iterationProgressHint,
|
|
10024
10607
|
mockLLM: () => mockLLM,
|
|
10025
10608
|
normalizeMessageContent: () => normalizeMessageContent,
|
|
10026
10609
|
parallelGadgetHint: () => parallelGadgetHint,
|
|
10027
10610
|
parseDataUrl: () => parseDataUrl,
|
|
10611
|
+
resolveConfig: () => resolveConfig,
|
|
10028
10612
|
resolveHintTemplate: () => resolveHintTemplate,
|
|
10029
10613
|
resolveModel: () => resolveModel,
|
|
10030
10614
|
resolvePromptTemplate: () => resolvePromptTemplate,
|
|
10031
10615
|
resolveRulesTemplate: () => resolveRulesTemplate,
|
|
10616
|
+
resolveSubagentModel: () => resolveSubagentModel,
|
|
10617
|
+
resolveValue: () => resolveValue,
|
|
10032
10618
|
resultWithAudio: () => resultWithAudio,
|
|
10033
10619
|
resultWithFile: () => resultWithFile,
|
|
10034
10620
|
resultWithImage: () => resultWithImage,
|
|
@@ -10934,6 +11520,43 @@ init_stream_processor();
|
|
|
10934
11520
|
|
|
10935
11521
|
// src/index.ts
|
|
10936
11522
|
init_client();
|
|
11523
|
+
init_execution_tree();
|
|
11524
|
+
|
|
11525
|
+
// src/core/execution-events.ts
|
|
11526
|
+
function isLLMEvent(event) {
|
|
11527
|
+
return event.type.startsWith("llm_call_");
|
|
11528
|
+
}
|
|
11529
|
+
function isGadgetEvent(event) {
|
|
11530
|
+
return event.type.startsWith("gadget_");
|
|
11531
|
+
}
|
|
11532
|
+
function isSubagentEvent(event) {
|
|
11533
|
+
return event.depth > 0;
|
|
11534
|
+
}
|
|
11535
|
+
function isRootEvent(event) {
|
|
11536
|
+
return event.depth === 0;
|
|
11537
|
+
}
|
|
11538
|
+
function filterByDepth(events, depth) {
|
|
11539
|
+
return events.filter((e) => e.depth === depth);
|
|
11540
|
+
}
|
|
11541
|
+
function filterByParent(events, parentId) {
|
|
11542
|
+
return events.filter((e) => e.parentId === parentId);
|
|
11543
|
+
}
|
|
11544
|
+
function filterRootEvents(events) {
|
|
11545
|
+
return filterByDepth(events, 0);
|
|
11546
|
+
}
|
|
11547
|
+
function groupByParent(events) {
|
|
11548
|
+
const groups = /* @__PURE__ */ new Map();
|
|
11549
|
+
for (const event of events) {
|
|
11550
|
+
const parentId = event.parentId;
|
|
11551
|
+
if (!groups.has(parentId)) {
|
|
11552
|
+
groups.set(parentId, []);
|
|
11553
|
+
}
|
|
11554
|
+
groups.get(parentId)?.push(event);
|
|
11555
|
+
}
|
|
11556
|
+
return groups;
|
|
11557
|
+
}
|
|
11558
|
+
|
|
11559
|
+
// src/index.ts
|
|
10937
11560
|
init_input_content();
|
|
10938
11561
|
init_messages();
|
|
10939
11562
|
init_model_registry();
|
|
@@ -11115,6 +11738,52 @@ function validateGadgetParams(gadget, params) {
|
|
|
11115
11738
|
|
|
11116
11739
|
// src/index.ts
|
|
11117
11740
|
init_logger();
|
|
11741
|
+
|
|
11742
|
+
// src/utils/config-resolver.ts
|
|
11743
|
+
function resolveValue(ctx, gadgetName, options) {
|
|
11744
|
+
const { runtime, subagentKey, parentKey, defaultValue, handleInherit } = options;
|
|
11745
|
+
if (runtime !== void 0) {
|
|
11746
|
+
if (handleInherit && runtime === "inherit") {
|
|
11747
|
+
} else {
|
|
11748
|
+
return runtime;
|
|
11749
|
+
}
|
|
11750
|
+
}
|
|
11751
|
+
if (subagentKey && ctx.subagentConfig) {
|
|
11752
|
+
const subagentCfg = ctx.subagentConfig[gadgetName];
|
|
11753
|
+
if (subagentCfg && subagentKey in subagentCfg) {
|
|
11754
|
+
const value = subagentCfg[subagentKey];
|
|
11755
|
+
if (handleInherit && value === "inherit") {
|
|
11756
|
+
} else if (value !== void 0) {
|
|
11757
|
+
return value;
|
|
11758
|
+
}
|
|
11759
|
+
}
|
|
11760
|
+
}
|
|
11761
|
+
if (parentKey && ctx.agentConfig && parentKey in ctx.agentConfig) {
|
|
11762
|
+
const parentValue = ctx.agentConfig[parentKey];
|
|
11763
|
+
if (parentValue !== void 0) {
|
|
11764
|
+
return parentValue;
|
|
11765
|
+
}
|
|
11766
|
+
}
|
|
11767
|
+
return defaultValue;
|
|
11768
|
+
}
|
|
11769
|
+
function resolveConfig(ctx, gadgetName, config) {
|
|
11770
|
+
const result = {};
|
|
11771
|
+
for (const [key, options] of Object.entries(config)) {
|
|
11772
|
+
result[key] = resolveValue(ctx, gadgetName, options);
|
|
11773
|
+
}
|
|
11774
|
+
return result;
|
|
11775
|
+
}
|
|
11776
|
+
function resolveSubagentModel(ctx, gadgetName, runtimeModel, defaultModel) {
|
|
11777
|
+
return resolveValue(ctx, gadgetName, {
|
|
11778
|
+
runtime: runtimeModel,
|
|
11779
|
+
subagentKey: "model",
|
|
11780
|
+
parentKey: "model",
|
|
11781
|
+
defaultValue: defaultModel,
|
|
11782
|
+
handleInherit: true
|
|
11783
|
+
});
|
|
11784
|
+
}
|
|
11785
|
+
|
|
11786
|
+
// src/index.ts
|
|
11118
11787
|
init_anthropic();
|
|
11119
11788
|
init_discovery();
|
|
11120
11789
|
init_gemini();
|
|
@@ -12129,6 +12798,7 @@ init_gadget();
|
|
|
12129
12798
|
DEFAULT_HINTS,
|
|
12130
12799
|
DEFAULT_PROMPTS,
|
|
12131
12800
|
DEFAULT_SUMMARIZATION_PROMPT,
|
|
12801
|
+
ExecutionTree,
|
|
12132
12802
|
Gadget,
|
|
12133
12803
|
GadgetCallParser,
|
|
12134
12804
|
GadgetExecutor,
|
|
@@ -12175,26 +12845,37 @@ init_gadget();
|
|
|
12175
12845
|
detectImageMimeType,
|
|
12176
12846
|
discoverProviderAdapters,
|
|
12177
12847
|
extractMessageText,
|
|
12848
|
+
filterByDepth,
|
|
12849
|
+
filterByParent,
|
|
12850
|
+
filterRootEvents,
|
|
12178
12851
|
getMockManager,
|
|
12179
12852
|
getModelId,
|
|
12180
12853
|
getProvider,
|
|
12854
|
+
groupByParent,
|
|
12181
12855
|
hasProviderPrefix,
|
|
12182
12856
|
imageFromBase64,
|
|
12183
12857
|
imageFromBuffer,
|
|
12184
12858
|
imageFromUrl,
|
|
12185
12859
|
isAudioPart,
|
|
12186
12860
|
isDataUrl,
|
|
12861
|
+
isGadgetEvent,
|
|
12187
12862
|
isImagePart,
|
|
12863
|
+
isLLMEvent,
|
|
12864
|
+
isRootEvent,
|
|
12865
|
+
isSubagentEvent,
|
|
12188
12866
|
isTextPart,
|
|
12189
12867
|
iterationProgressHint,
|
|
12190
12868
|
mockLLM,
|
|
12191
12869
|
normalizeMessageContent,
|
|
12192
12870
|
parallelGadgetHint,
|
|
12193
12871
|
parseDataUrl,
|
|
12872
|
+
resolveConfig,
|
|
12194
12873
|
resolveHintTemplate,
|
|
12195
12874
|
resolveModel,
|
|
12196
12875
|
resolvePromptTemplate,
|
|
12197
12876
|
resolveRulesTemplate,
|
|
12877
|
+
resolveSubagentModel,
|
|
12878
|
+
resolveValue,
|
|
12198
12879
|
resultWithAudio,
|
|
12199
12880
|
resultWithFile,
|
|
12200
12881
|
resultWithImage,
|