llmist 6.0.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-F62X5W2G.js → chunk-7BJX376V.js} +97 -2
- package/dist/chunk-7BJX376V.js.map +1 -0
- package/dist/{chunk-EIE5VRSI.js → chunk-VAJLPRJ6.js} +911 -389
- package/dist/chunk-VAJLPRJ6.js.map +1 -0
- package/dist/cli.cjs +4073 -1645
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +3008 -1101
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +1012 -385
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +144 -45
- package/dist/index.d.ts +144 -45
- 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 +908 -388
- 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-EIE5VRSI.js.map +0 -1
- package/dist/chunk-F62X5W2G.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 */
|
|
@@ -3938,18 +4567,28 @@ var init_stream_processor = __esm({
|
|
|
3938
4567
|
failedInvocations = /* @__PURE__ */ new Set();
|
|
3939
4568
|
/** Promises for independent gadgets currently executing (fire-and-forget) */
|
|
3940
4569
|
inFlightExecutions = /* @__PURE__ */ new Map();
|
|
4570
|
+
/** Queue of completed gadget results ready to be yielded (for real-time streaming) */
|
|
4571
|
+
completedResultsQueue = [];
|
|
3941
4572
|
constructor(options) {
|
|
3942
4573
|
this.iteration = options.iteration;
|
|
3943
4574
|
this.registry = options.registry;
|
|
3944
4575
|
this.hooks = options.hooks ?? {};
|
|
3945
4576
|
this.logger = options.logger ?? createLogger({ name: "llmist:stream-processor" });
|
|
3946
|
-
this.
|
|
3947
|
-
this.
|
|
4577
|
+
this.tree = options.tree;
|
|
4578
|
+
this.parentNodeId = options.parentNodeId ?? null;
|
|
4579
|
+
this.baseDepth = options.baseDepth ?? 0;
|
|
3948
4580
|
this.parser = new GadgetCallParser({
|
|
3949
4581
|
startPrefix: options.gadgetStartPrefix,
|
|
3950
4582
|
endPrefix: options.gadgetEndPrefix,
|
|
3951
4583
|
argPrefix: options.gadgetArgPrefix
|
|
3952
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;
|
|
3953
4592
|
this.executor = new GadgetExecutor(
|
|
3954
4593
|
options.registry,
|
|
3955
4594
|
options.requestHumanInput,
|
|
@@ -3960,7 +4599,11 @@ var init_stream_processor = __esm({
|
|
|
3960
4599
|
options.mediaStore,
|
|
3961
4600
|
options.agentConfig,
|
|
3962
4601
|
options.subagentConfig,
|
|
3963
|
-
|
|
4602
|
+
wrappedOnSubagentEvent,
|
|
4603
|
+
// Tree context for gadget execution
|
|
4604
|
+
options.tree,
|
|
4605
|
+
options.parentNodeId,
|
|
4606
|
+
options.baseDepth
|
|
3964
4607
|
);
|
|
3965
4608
|
}
|
|
3966
4609
|
/**
|
|
@@ -4011,7 +4654,7 @@ var init_stream_processor = __esm({
|
|
|
4011
4654
|
usage,
|
|
4012
4655
|
logger: this.logger
|
|
4013
4656
|
};
|
|
4014
|
-
await this.hooks.observers
|
|
4657
|
+
await this.hooks.observers?.onStreamChunk?.(context);
|
|
4015
4658
|
});
|
|
4016
4659
|
await this.runObserversInParallel(chunkObservers);
|
|
4017
4660
|
}
|
|
@@ -4029,25 +4672,7 @@ var init_stream_processor = __esm({
|
|
|
4029
4672
|
}
|
|
4030
4673
|
}
|
|
4031
4674
|
}
|
|
4032
|
-
|
|
4033
|
-
this.logger.info("Breaking from LLM stream due to gadget error");
|
|
4034
|
-
break;
|
|
4035
|
-
}
|
|
4036
|
-
}
|
|
4037
|
-
if (!this.executionHalted) {
|
|
4038
|
-
for (const event of this.parser.finalize()) {
|
|
4039
|
-
for await (const processedEvent of this.processEventGenerator(event)) {
|
|
4040
|
-
yield processedEvent;
|
|
4041
|
-
if (processedEvent.type === "gadget_result") {
|
|
4042
|
-
didExecuteGadgets = true;
|
|
4043
|
-
if (processedEvent.result.breaksLoop) {
|
|
4044
|
-
shouldBreakLoop = true;
|
|
4045
|
-
}
|
|
4046
|
-
}
|
|
4047
|
-
}
|
|
4048
|
-
}
|
|
4049
|
-
const inFlightResults = await this.collectInFlightResults();
|
|
4050
|
-
for (const evt of inFlightResults) {
|
|
4675
|
+
for (const evt of this.drainCompletedResults()) {
|
|
4051
4676
|
yield evt;
|
|
4052
4677
|
if (evt.type === "gadget_result") {
|
|
4053
4678
|
didExecuteGadgets = true;
|
|
@@ -4056,16 +4681,45 @@ var init_stream_processor = __esm({
|
|
|
4056
4681
|
}
|
|
4057
4682
|
}
|
|
4058
4683
|
}
|
|
4059
|
-
|
|
4060
|
-
|
|
4061
|
-
|
|
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") {
|
|
4062
4689
|
didExecuteGadgets = true;
|
|
4063
|
-
if (
|
|
4690
|
+
if (processedEvent.result.breaksLoop) {
|
|
4064
4691
|
shouldBreakLoop = true;
|
|
4065
4692
|
}
|
|
4066
4693
|
}
|
|
4067
4694
|
}
|
|
4068
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
|
+
}
|
|
4069
4723
|
let finalMessage = this.responseText;
|
|
4070
4724
|
if (this.hooks.interceptors?.interceptAssistantMessage) {
|
|
4071
4725
|
const context = {
|
|
@@ -4086,21 +4740,8 @@ var init_stream_processor = __esm({
|
|
|
4086
4740
|
};
|
|
4087
4741
|
yield completionEvent;
|
|
4088
4742
|
}
|
|
4089
|
-
/**
|
|
4090
|
-
* Process a single parsed event (text or gadget call).
|
|
4091
|
-
* @deprecated Use processEventGenerator for real-time streaming
|
|
4092
|
-
*/
|
|
4093
|
-
async processEvent(event) {
|
|
4094
|
-
if (event.type === "text") {
|
|
4095
|
-
return this.processTextEvent(event);
|
|
4096
|
-
} else if (event.type === "gadget_call") {
|
|
4097
|
-
return this.processGadgetCall(event.call);
|
|
4098
|
-
}
|
|
4099
|
-
return [event];
|
|
4100
|
-
}
|
|
4101
4743
|
/**
|
|
4102
4744
|
* Process a single parsed event, yielding events in real-time.
|
|
4103
|
-
* Generator version of processEvent for streaming support.
|
|
4104
4745
|
*/
|
|
4105
4746
|
async *processEventGenerator(event) {
|
|
4106
4747
|
if (event.type === "text") {
|
|
@@ -4142,12 +4783,6 @@ var init_stream_processor = __esm({
|
|
|
4142
4783
|
* After each execution, pending gadgets are checked to see if they can now run.
|
|
4143
4784
|
*/
|
|
4144
4785
|
async processGadgetCall(call) {
|
|
4145
|
-
if (this.executionHalted) {
|
|
4146
|
-
this.logger.debug("Skipping gadget execution due to previous error", {
|
|
4147
|
-
gadgetName: call.gadgetName
|
|
4148
|
-
});
|
|
4149
|
-
return [];
|
|
4150
|
-
}
|
|
4151
4786
|
const events = [];
|
|
4152
4787
|
events.push({ type: "gadget_call", call });
|
|
4153
4788
|
if (call.dependencies.length > 0) {
|
|
@@ -4198,13 +4833,16 @@ var init_stream_processor = __esm({
|
|
|
4198
4833
|
* when parsed (before execution), enabling real-time UI feedback.
|
|
4199
4834
|
*/
|
|
4200
4835
|
async *processGadgetCallGenerator(call) {
|
|
4201
|
-
|
|
4202
|
-
|
|
4203
|
-
|
|
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
|
|
4204
4844
|
});
|
|
4205
|
-
return;
|
|
4206
4845
|
}
|
|
4207
|
-
yield { type: "gadget_call", call };
|
|
4208
4846
|
if (call.dependencies.length > 0) {
|
|
4209
4847
|
if (call.dependencies.includes(call.invocationId)) {
|
|
4210
4848
|
this.logger.warn("Gadget has self-referential dependency (depends on itself)", {
|
|
@@ -4249,17 +4887,8 @@ var init_stream_processor = __esm({
|
|
|
4249
4887
|
}
|
|
4250
4888
|
return;
|
|
4251
4889
|
}
|
|
4252
|
-
|
|
4253
|
-
|
|
4254
|
-
yield evt;
|
|
4255
|
-
}
|
|
4256
|
-
for await (const evt of this.processPendingGadgetsGenerator()) {
|
|
4257
|
-
yield evt;
|
|
4258
|
-
}
|
|
4259
|
-
} else {
|
|
4260
|
-
const executionPromise = this.executeGadgetAndCollect(call);
|
|
4261
|
-
this.inFlightExecutions.set(call.invocationId, executionPromise);
|
|
4262
|
-
}
|
|
4890
|
+
const executionPromise = this.executeGadgetAndCollect(call);
|
|
4891
|
+
this.inFlightExecutions.set(call.invocationId, executionPromise);
|
|
4263
4892
|
}
|
|
4264
4893
|
/**
|
|
4265
4894
|
* Execute a gadget through the full hook lifecycle.
|
|
@@ -4274,15 +4903,6 @@ var init_stream_processor = __esm({
|
|
|
4274
4903
|
error: call.parseError,
|
|
4275
4904
|
rawParameters: call.parametersRaw
|
|
4276
4905
|
});
|
|
4277
|
-
const shouldContinue = await this.checkCanRecoverFromError(
|
|
4278
|
-
call.parseError,
|
|
4279
|
-
call.gadgetName,
|
|
4280
|
-
"parse",
|
|
4281
|
-
call.parameters
|
|
4282
|
-
);
|
|
4283
|
-
if (!shouldContinue) {
|
|
4284
|
-
this.executionHalted = true;
|
|
4285
|
-
}
|
|
4286
4906
|
}
|
|
4287
4907
|
let parameters = call.parameters ?? {};
|
|
4288
4908
|
if (this.hooks.interceptors?.interceptGadgetParameters) {
|
|
@@ -4325,7 +4945,7 @@ var init_stream_processor = __esm({
|
|
|
4325
4945
|
parameters,
|
|
4326
4946
|
logger: this.logger
|
|
4327
4947
|
};
|
|
4328
|
-
await this.hooks.observers
|
|
4948
|
+
await this.hooks.observers?.onGadgetExecutionStart?.(context);
|
|
4329
4949
|
});
|
|
4330
4950
|
}
|
|
4331
4951
|
await this.runObserversInParallel(startObservers);
|
|
@@ -4394,7 +5014,7 @@ var init_stream_processor = __esm({
|
|
|
4394
5014
|
cost: result.cost,
|
|
4395
5015
|
logger: this.logger
|
|
4396
5016
|
};
|
|
4397
|
-
await this.hooks.observers
|
|
5017
|
+
await this.hooks.observers?.onGadgetExecutionComplete?.(context);
|
|
4398
5018
|
});
|
|
4399
5019
|
}
|
|
4400
5020
|
await this.runObserversInParallel(completeObservers);
|
|
@@ -4403,18 +5023,6 @@ var init_stream_processor = __esm({
|
|
|
4403
5023
|
this.failedInvocations.add(result.invocationId);
|
|
4404
5024
|
}
|
|
4405
5025
|
events.push({ type: "gadget_result", result });
|
|
4406
|
-
if (result.error) {
|
|
4407
|
-
const errorType = this.determineErrorType(call, result);
|
|
4408
|
-
const shouldContinue = await this.checkCanRecoverFromError(
|
|
4409
|
-
result.error,
|
|
4410
|
-
result.gadgetName,
|
|
4411
|
-
errorType,
|
|
4412
|
-
result.parameters
|
|
4413
|
-
);
|
|
4414
|
-
if (!shouldContinue) {
|
|
4415
|
-
this.executionHalted = true;
|
|
4416
|
-
}
|
|
4417
|
-
}
|
|
4418
5026
|
return events;
|
|
4419
5027
|
}
|
|
4420
5028
|
/**
|
|
@@ -4428,15 +5036,6 @@ var init_stream_processor = __esm({
|
|
|
4428
5036
|
error: call.parseError,
|
|
4429
5037
|
rawParameters: call.parametersRaw
|
|
4430
5038
|
});
|
|
4431
|
-
const shouldContinue = await this.checkCanRecoverFromError(
|
|
4432
|
-
call.parseError,
|
|
4433
|
-
call.gadgetName,
|
|
4434
|
-
"parse",
|
|
4435
|
-
call.parameters
|
|
4436
|
-
);
|
|
4437
|
-
if (!shouldContinue) {
|
|
4438
|
-
this.executionHalted = true;
|
|
4439
|
-
}
|
|
4440
5039
|
}
|
|
4441
5040
|
let parameters = call.parameters ?? {};
|
|
4442
5041
|
if (this.hooks.interceptors?.interceptGadgetParameters) {
|
|
@@ -4479,10 +5078,16 @@ var init_stream_processor = __esm({
|
|
|
4479
5078
|
parameters,
|
|
4480
5079
|
logger: this.logger
|
|
4481
5080
|
};
|
|
4482
|
-
await this.hooks.observers
|
|
5081
|
+
await this.hooks.observers?.onGadgetExecutionStart?.(context);
|
|
4483
5082
|
});
|
|
4484
5083
|
}
|
|
4485
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
|
+
}
|
|
4486
5091
|
let result;
|
|
4487
5092
|
if (shouldSkip) {
|
|
4488
5093
|
result = {
|
|
@@ -4548,57 +5153,84 @@ var init_stream_processor = __esm({
|
|
|
4548
5153
|
cost: result.cost,
|
|
4549
5154
|
logger: this.logger
|
|
4550
5155
|
};
|
|
4551
|
-
await this.hooks.observers
|
|
5156
|
+
await this.hooks.observers?.onGadgetExecutionComplete?.(context);
|
|
4552
5157
|
});
|
|
4553
5158
|
}
|
|
4554
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
|
+
}
|
|
4555
5179
|
this.completedResults.set(result.invocationId, result);
|
|
4556
5180
|
if (result.error) {
|
|
4557
5181
|
this.failedInvocations.add(result.invocationId);
|
|
4558
5182
|
}
|
|
4559
5183
|
yield { type: "gadget_result", result };
|
|
4560
|
-
if (result.error) {
|
|
4561
|
-
const errorType = this.determineErrorType(call, result);
|
|
4562
|
-
const shouldContinue = await this.checkCanRecoverFromError(
|
|
4563
|
-
result.error,
|
|
4564
|
-
result.gadgetName,
|
|
4565
|
-
errorType,
|
|
4566
|
-
result.parameters
|
|
4567
|
-
);
|
|
4568
|
-
if (!shouldContinue) {
|
|
4569
|
-
this.executionHalted = true;
|
|
4570
|
-
}
|
|
4571
|
-
}
|
|
4572
5184
|
}
|
|
4573
5185
|
/**
|
|
4574
|
-
* Execute a gadget and
|
|
5186
|
+
* Execute a gadget and push events to the completed results queue (non-blocking).
|
|
4575
5187
|
* Used for fire-and-forget parallel execution of independent gadgets.
|
|
5188
|
+
* Results are pushed to completedResultsQueue for real-time streaming to the caller.
|
|
4576
5189
|
*/
|
|
4577
5190
|
async executeGadgetAndCollect(call) {
|
|
4578
|
-
const events = [];
|
|
4579
5191
|
for await (const evt of this.executeGadgetGenerator(call)) {
|
|
4580
|
-
|
|
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();
|
|
4581
5203
|
}
|
|
4582
|
-
return events;
|
|
4583
5204
|
}
|
|
4584
5205
|
/**
|
|
4585
|
-
*
|
|
4586
|
-
* Called at stream end to
|
|
4587
|
-
*
|
|
4588
|
-
*
|
|
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.
|
|
4589
5212
|
*/
|
|
4590
|
-
async
|
|
5213
|
+
async *waitForInFlightExecutions() {
|
|
4591
5214
|
if (this.inFlightExecutions.size === 0) {
|
|
4592
|
-
return
|
|
5215
|
+
return;
|
|
4593
5216
|
}
|
|
4594
|
-
this.logger.debug("
|
|
5217
|
+
this.logger.debug("Waiting for in-flight gadget executions", {
|
|
4595
5218
|
count: this.inFlightExecutions.size,
|
|
4596
5219
|
invocationIds: Array.from(this.inFlightExecutions.keys())
|
|
4597
5220
|
});
|
|
4598
|
-
const
|
|
4599
|
-
const
|
|
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;
|
|
5231
|
+
}
|
|
5232
|
+
}
|
|
4600
5233
|
this.inFlightExecutions.clear();
|
|
4601
|
-
return results.flat();
|
|
4602
5234
|
}
|
|
4603
5235
|
/**
|
|
4604
5236
|
* Handle a gadget that cannot execute because a dependency failed.
|
|
@@ -4623,6 +5255,12 @@ var init_stream_processor = __esm({
|
|
|
4623
5255
|
}
|
|
4624
5256
|
if (action.action === "skip") {
|
|
4625
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
|
+
}
|
|
4626
5264
|
const skipEvent = {
|
|
4627
5265
|
type: "gadget_skipped",
|
|
4628
5266
|
gadgetName: call.gadgetName,
|
|
@@ -4642,7 +5280,7 @@ var init_stream_processor = __esm({
|
|
|
4642
5280
|
failedDependencyError: depError,
|
|
4643
5281
|
logger: this.logger
|
|
4644
5282
|
};
|
|
4645
|
-
await this.safeObserve(() => this.hooks.observers
|
|
5283
|
+
await this.safeObserve(() => this.hooks.observers?.onGadgetSkipped?.(observeContext));
|
|
4646
5284
|
}
|
|
4647
5285
|
this.logger.info("Gadget skipped due to failed dependency", {
|
|
4648
5286
|
gadgetName: call.gadgetName,
|
|
@@ -4874,48 +5512,6 @@ var init_stream_processor = __esm({
|
|
|
4874
5512
|
observers.map((observer) => this.safeObserve(observer))
|
|
4875
5513
|
);
|
|
4876
5514
|
}
|
|
4877
|
-
/**
|
|
4878
|
-
* Check if execution can recover from an error.
|
|
4879
|
-
*
|
|
4880
|
-
* Returns true if we should continue processing subsequent gadgets, false if we should stop.
|
|
4881
|
-
*
|
|
4882
|
-
* Logic:
|
|
4883
|
-
* - If custom canRecoverFromGadgetError is provided, use it
|
|
4884
|
-
* - Otherwise, use stopOnGadgetError config:
|
|
4885
|
-
* - stopOnGadgetError=true → return false (stop execution)
|
|
4886
|
-
* - stopOnGadgetError=false → return true (continue execution)
|
|
4887
|
-
*/
|
|
4888
|
-
async checkCanRecoverFromError(error, gadgetName, errorType, parameters) {
|
|
4889
|
-
if (this.canRecoverFromGadgetError) {
|
|
4890
|
-
return await this.canRecoverFromGadgetError({
|
|
4891
|
-
error,
|
|
4892
|
-
gadgetName,
|
|
4893
|
-
errorType,
|
|
4894
|
-
parameters
|
|
4895
|
-
});
|
|
4896
|
-
}
|
|
4897
|
-
const shouldContinue = !this.stopOnGadgetError;
|
|
4898
|
-
this.logger.debug("Checking if should continue after error", {
|
|
4899
|
-
error,
|
|
4900
|
-
gadgetName,
|
|
4901
|
-
errorType,
|
|
4902
|
-
stopOnGadgetError: this.stopOnGadgetError,
|
|
4903
|
-
shouldContinue
|
|
4904
|
-
});
|
|
4905
|
-
return shouldContinue;
|
|
4906
|
-
}
|
|
4907
|
-
/**
|
|
4908
|
-
* Determine the type of error from a gadget execution.
|
|
4909
|
-
*/
|
|
4910
|
-
determineErrorType(call, result) {
|
|
4911
|
-
if (call.parseError) {
|
|
4912
|
-
return "parse";
|
|
4913
|
-
}
|
|
4914
|
-
if (result.error?.includes("Invalid parameters:")) {
|
|
4915
|
-
return "validation";
|
|
4916
|
-
}
|
|
4917
|
-
return "execution";
|
|
4918
|
-
}
|
|
4919
5515
|
};
|
|
4920
5516
|
}
|
|
4921
5517
|
});
|
|
@@ -4926,6 +5522,7 @@ var init_agent = __esm({
|
|
|
4926
5522
|
"src/agent/agent.ts"() {
|
|
4927
5523
|
"use strict";
|
|
4928
5524
|
init_constants();
|
|
5525
|
+
init_execution_tree();
|
|
4929
5526
|
init_messages();
|
|
4930
5527
|
init_model_shortcuts();
|
|
4931
5528
|
init_media_store();
|
|
@@ -4953,8 +5550,6 @@ var init_agent = __esm({
|
|
|
4953
5550
|
requestHumanInput;
|
|
4954
5551
|
textOnlyHandler;
|
|
4955
5552
|
textWithGadgetsHandler;
|
|
4956
|
-
stopOnGadgetError;
|
|
4957
|
-
canRecoverFromGadgetError;
|
|
4958
5553
|
defaultGadgetTimeoutMs;
|
|
4959
5554
|
defaultMaxTokens;
|
|
4960
5555
|
hasUserPrompt;
|
|
@@ -4977,6 +5572,12 @@ var init_agent = __esm({
|
|
|
4977
5572
|
pendingSubagentEvents = [];
|
|
4978
5573
|
// Combined callback that queues events AND calls user callback
|
|
4979
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;
|
|
4980
5581
|
/**
|
|
4981
5582
|
* Creates a new Agent instance.
|
|
4982
5583
|
* @internal This constructor is private. Use LLMist.createAgent() or AgentBuilder instead.
|
|
@@ -4999,8 +5600,6 @@ var init_agent = __esm({
|
|
|
4999
5600
|
this.requestHumanInput = options.requestHumanInput;
|
|
5000
5601
|
this.textOnlyHandler = options.textOnlyHandler ?? "terminate";
|
|
5001
5602
|
this.textWithGadgetsHandler = options.textWithGadgetsHandler;
|
|
5002
|
-
this.stopOnGadgetError = options.stopOnGadgetError ?? true;
|
|
5003
|
-
this.canRecoverFromGadgetError = options.canRecoverFromGadgetError;
|
|
5004
5603
|
this.defaultGadgetTimeoutMs = options.defaultGadgetTimeoutMs;
|
|
5005
5604
|
this.defaultMaxTokens = this.resolveMaxTokensFromCatalog(options.model);
|
|
5006
5605
|
this.outputLimitEnabled = options.gadgetOutputLimit ?? DEFAULT_GADGET_OUTPUT_LIMIT;
|
|
@@ -5054,6 +5653,9 @@ var init_agent = __esm({
|
|
|
5054
5653
|
temperature: this.temperature
|
|
5055
5654
|
};
|
|
5056
5655
|
this.subagentConfig = options.subagentConfig;
|
|
5656
|
+
this.tree = options.parentTree ?? new ExecutionTree();
|
|
5657
|
+
this.parentNodeId = options.parentNodeId ?? null;
|
|
5658
|
+
this.baseDepth = options.baseDepth ?? 0;
|
|
5057
5659
|
this.userSubagentEventCallback = options.onSubagentEvent;
|
|
5058
5660
|
this.onSubagentEvent = (event) => {
|
|
5059
5661
|
this.pendingSubagentEvents.push(event);
|
|
@@ -5118,7 +5720,9 @@ var init_agent = __esm({
|
|
|
5118
5720
|
*flushPendingSubagentEvents() {
|
|
5119
5721
|
while (this.pendingSubagentEvents.length > 0) {
|
|
5120
5722
|
const event = this.pendingSubagentEvents.shift();
|
|
5121
|
-
|
|
5723
|
+
if (event) {
|
|
5724
|
+
yield { type: "subagent_event", subagentEvent: event };
|
|
5725
|
+
}
|
|
5122
5726
|
}
|
|
5123
5727
|
}
|
|
5124
5728
|
/**
|
|
@@ -5172,6 +5776,48 @@ var init_agent = __esm({
|
|
|
5172
5776
|
getMediaStore() {
|
|
5173
5777
|
return this.mediaStore;
|
|
5174
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
|
+
}
|
|
5175
5821
|
/**
|
|
5176
5822
|
* Manually trigger context compaction.
|
|
5177
5823
|
*
|
|
@@ -5273,6 +5919,7 @@ var init_agent = __esm({
|
|
|
5273
5919
|
await this.hooks.observers.onCompaction({
|
|
5274
5920
|
iteration: currentIteration,
|
|
5275
5921
|
event: compactionEvent,
|
|
5922
|
+
// biome-ignore lint/style/noNonNullAssertion: compactionManager exists if compactionEvent is truthy
|
|
5276
5923
|
stats: this.compactionManager.getStats(),
|
|
5277
5924
|
logger: this.logger
|
|
5278
5925
|
});
|
|
@@ -5334,6 +5981,13 @@ var init_agent = __esm({
|
|
|
5334
5981
|
messageCount: llmOptions.messages.length,
|
|
5335
5982
|
messages: llmOptions.messages
|
|
5336
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;
|
|
5337
5991
|
const stream2 = this.client.stream(llmOptions);
|
|
5338
5992
|
const processor = new StreamProcessor({
|
|
5339
5993
|
iteration: currentIteration,
|
|
@@ -5344,14 +5998,17 @@ var init_agent = __esm({
|
|
|
5344
5998
|
hooks: this.hooks,
|
|
5345
5999
|
logger: this.logger.getSubLogger({ name: "stream-processor" }),
|
|
5346
6000
|
requestHumanInput: this.requestHumanInput,
|
|
5347
|
-
stopOnGadgetError: this.stopOnGadgetError,
|
|
5348
|
-
canRecoverFromGadgetError: this.canRecoverFromGadgetError,
|
|
5349
6001
|
defaultGadgetTimeoutMs: this.defaultGadgetTimeoutMs,
|
|
5350
6002
|
client: this.client,
|
|
5351
6003
|
mediaStore: this.mediaStore,
|
|
5352
6004
|
agentConfig: this.agentContextConfig,
|
|
5353
6005
|
subagentConfig: this.subagentConfig,
|
|
5354
|
-
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
|
|
5355
6012
|
});
|
|
5356
6013
|
let streamMetadata = null;
|
|
5357
6014
|
let gadgetCallCount = 0;
|
|
@@ -5397,6 +6054,11 @@ var init_agent = __esm({
|
|
|
5397
6054
|
await this.hooks.observers.onLLMCallComplete(context);
|
|
5398
6055
|
}
|
|
5399
6056
|
});
|
|
6057
|
+
this.tree.completeLLMCall(currentLLMNodeId, {
|
|
6058
|
+
response: result.rawResponse,
|
|
6059
|
+
usage: result.usage,
|
|
6060
|
+
finishReason: result.finishReason
|
|
6061
|
+
});
|
|
5400
6062
|
let finalMessage = result.finalMessage;
|
|
5401
6063
|
if (this.hooks.controllers?.afterLLMCall) {
|
|
5402
6064
|
const context = {
|
|
@@ -5431,10 +6093,12 @@ var init_agent = __esm({
|
|
|
5431
6093
|
const textContent = textOutputs.join("");
|
|
5432
6094
|
if (textContent.trim()) {
|
|
5433
6095
|
const { gadgetName, parameterMapping, resultMapping } = this.textWithGadgetsHandler;
|
|
6096
|
+
const syntheticId = `gc_text_${++this.syntheticInvocationCounter}`;
|
|
5434
6097
|
this.conversation.addGadgetCallResult(
|
|
5435
6098
|
gadgetName,
|
|
5436
6099
|
parameterMapping(textContent),
|
|
5437
|
-
resultMapping ? resultMapping(textContent) : textContent
|
|
6100
|
+
resultMapping ? resultMapping(textContent) : textContent,
|
|
6101
|
+
syntheticId
|
|
5438
6102
|
);
|
|
5439
6103
|
}
|
|
5440
6104
|
}
|
|
@@ -5445,6 +6109,7 @@ var init_agent = __esm({
|
|
|
5445
6109
|
gadgetResult.gadgetName,
|
|
5446
6110
|
gadgetResult.parameters,
|
|
5447
6111
|
gadgetResult.error ?? gadgetResult.result ?? "",
|
|
6112
|
+
gadgetResult.invocationId,
|
|
5448
6113
|
gadgetResult.media,
|
|
5449
6114
|
gadgetResult.mediaIds
|
|
5450
6115
|
);
|
|
@@ -5452,10 +6117,12 @@ var init_agent = __esm({
|
|
|
5452
6117
|
}
|
|
5453
6118
|
} else {
|
|
5454
6119
|
if (finalMessage.trim()) {
|
|
6120
|
+
const syntheticId = `gc_tell_${++this.syntheticInvocationCounter}`;
|
|
5455
6121
|
this.conversation.addGadgetCallResult(
|
|
5456
6122
|
"TellUser",
|
|
5457
6123
|
{ message: finalMessage, done: false, type: "info" },
|
|
5458
|
-
`\u2139\uFE0F ${finalMessage}
|
|
6124
|
+
`\u2139\uFE0F ${finalMessage}`,
|
|
6125
|
+
syntheticId
|
|
5459
6126
|
);
|
|
5460
6127
|
}
|
|
5461
6128
|
const shouldBreak = await this.handleTextOnlyResponse(finalMessage);
|
|
@@ -8940,8 +9607,6 @@ var init_builder = __esm({
|
|
|
8940
9607
|
gadgetArgPrefix;
|
|
8941
9608
|
textOnlyHandler;
|
|
8942
9609
|
textWithGadgetsHandler;
|
|
8943
|
-
stopOnGadgetError;
|
|
8944
|
-
canRecoverFromGadgetError;
|
|
8945
9610
|
defaultGadgetTimeoutMs;
|
|
8946
9611
|
gadgetOutputLimit;
|
|
8947
9612
|
gadgetOutputLimitPercent;
|
|
@@ -8950,6 +9615,8 @@ var init_builder = __esm({
|
|
|
8950
9615
|
trailingMessage;
|
|
8951
9616
|
subagentConfig;
|
|
8952
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
|
|
8953
9620
|
parentContext;
|
|
8954
9621
|
constructor(client) {
|
|
8955
9622
|
this.client = client;
|
|
@@ -9232,62 +9899,6 @@ var init_builder = __esm({
|
|
|
9232
9899
|
this.textWithGadgetsHandler = handler;
|
|
9233
9900
|
return this;
|
|
9234
9901
|
}
|
|
9235
|
-
/**
|
|
9236
|
-
* Set whether to stop gadget execution on first error.
|
|
9237
|
-
*
|
|
9238
|
-
* When true (default), if a gadget fails:
|
|
9239
|
-
* - Subsequent gadgets in the same response are skipped
|
|
9240
|
-
* - LLM stream is cancelled to save costs
|
|
9241
|
-
* - Agent loop continues with error in context
|
|
9242
|
-
*
|
|
9243
|
-
* When false:
|
|
9244
|
-
* - All gadgets in the response still execute
|
|
9245
|
-
* - LLM stream continues to completion
|
|
9246
|
-
*
|
|
9247
|
-
* @param stop - Whether to stop on gadget error
|
|
9248
|
-
* @returns This builder for chaining
|
|
9249
|
-
*
|
|
9250
|
-
* @example
|
|
9251
|
-
* ```typescript
|
|
9252
|
-
* .withStopOnGadgetError(false)
|
|
9253
|
-
* ```
|
|
9254
|
-
*/
|
|
9255
|
-
withStopOnGadgetError(stop) {
|
|
9256
|
-
this.stopOnGadgetError = stop;
|
|
9257
|
-
return this;
|
|
9258
|
-
}
|
|
9259
|
-
/**
|
|
9260
|
-
* Set custom error handling logic.
|
|
9261
|
-
*
|
|
9262
|
-
* Provides fine-grained control over whether to continue after different types of errors.
|
|
9263
|
-
* Overrides `stopOnGadgetError` when provided.
|
|
9264
|
-
*
|
|
9265
|
-
* **Note:** This builder method configures the underlying `canRecoverFromGadgetError` option
|
|
9266
|
-
* in `AgentOptions`. The method is named `withErrorHandler` for better developer experience,
|
|
9267
|
-
* but maps to the `canRecoverFromGadgetError` property internally.
|
|
9268
|
-
*
|
|
9269
|
-
* @param handler - Function that decides whether to continue after an error.
|
|
9270
|
-
* Return `true` to continue execution, `false` to stop.
|
|
9271
|
-
* @returns This builder for chaining
|
|
9272
|
-
*
|
|
9273
|
-
* @example
|
|
9274
|
-
* ```typescript
|
|
9275
|
-
* .withErrorHandler((context) => {
|
|
9276
|
-
* // Stop on parse errors, continue on validation/execution errors
|
|
9277
|
-
* if (context.errorType === "parse") {
|
|
9278
|
-
* return false;
|
|
9279
|
-
* }
|
|
9280
|
-
* if (context.error.includes("CRITICAL")) {
|
|
9281
|
-
* return false;
|
|
9282
|
-
* }
|
|
9283
|
-
* return true;
|
|
9284
|
-
* })
|
|
9285
|
-
* ```
|
|
9286
|
-
*/
|
|
9287
|
-
withErrorHandler(handler) {
|
|
9288
|
-
this.canRecoverFromGadgetError = handler;
|
|
9289
|
-
return this;
|
|
9290
|
-
}
|
|
9291
9902
|
/**
|
|
9292
9903
|
* Set default timeout for gadget execution.
|
|
9293
9904
|
*
|
|
@@ -9482,6 +10093,15 @@ var init_builder = __esm({
|
|
|
9482
10093
|
* The method extracts `invocationId` and `onSubagentEvent` from the execution
|
|
9483
10094
|
* context and sets up automatic forwarding via hooks and event wrapping.
|
|
9484
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
|
+
*
|
|
9485
10105
|
* @param ctx - ExecutionContext passed to the gadget's execute() method
|
|
9486
10106
|
* @param depth - Nesting depth (default: 1 for direct child)
|
|
9487
10107
|
* @returns This builder for chaining
|
|
@@ -9502,17 +10122,25 @@ var init_builder = __esm({
|
|
|
9502
10122
|
* result = event.content;
|
|
9503
10123
|
* }
|
|
9504
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!);
|
|
9505
10130
|
* }
|
|
9506
10131
|
* ```
|
|
9507
10132
|
*/
|
|
9508
10133
|
withParentContext(ctx, depth = 1) {
|
|
9509
|
-
if (ctx.
|
|
10134
|
+
if (ctx.tree) {
|
|
9510
10135
|
this.parentContext = {
|
|
9511
|
-
|
|
9512
|
-
|
|
10136
|
+
tree: ctx.tree,
|
|
10137
|
+
nodeId: ctx.nodeId,
|
|
9513
10138
|
depth
|
|
9514
10139
|
};
|
|
9515
10140
|
}
|
|
10141
|
+
if (ctx.signal && !this.signal) {
|
|
10142
|
+
this.signal = ctx.signal;
|
|
10143
|
+
}
|
|
9516
10144
|
return this;
|
|
9517
10145
|
}
|
|
9518
10146
|
/**
|
|
@@ -9545,11 +10173,13 @@ var init_builder = __esm({
|
|
|
9545
10173
|
*
|
|
9546
10174
|
* This is useful for in-context learning - showing the LLM what "past self"
|
|
9547
10175
|
* did correctly so it mimics the pattern. The call is formatted with proper
|
|
9548
|
-
* markers and parameter format
|
|
10176
|
+
* markers and parameter format, including the invocation ID so the LLM can
|
|
10177
|
+
* reference previous calls when building dependencies.
|
|
9549
10178
|
*
|
|
9550
10179
|
* @param gadgetName - Name of the gadget
|
|
9551
10180
|
* @param parameters - Parameters passed to the gadget
|
|
9552
10181
|
* @param result - Result returned by the gadget
|
|
10182
|
+
* @param invocationId - Invocation ID (shown to LLM so it can reference for dependencies)
|
|
9553
10183
|
* @returns This builder for chaining
|
|
9554
10184
|
*
|
|
9555
10185
|
* @example
|
|
@@ -9561,124 +10191,36 @@ var init_builder = __esm({
|
|
|
9561
10191
|
* done: false,
|
|
9562
10192
|
* type: 'info'
|
|
9563
10193
|
* },
|
|
9564
|
-
* 'ℹ️ 👋 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'
|
|
9565
10196
|
* )
|
|
9566
10197
|
* ```
|
|
9567
10198
|
*/
|
|
9568
|
-
withSyntheticGadgetCall(gadgetName, parameters, result) {
|
|
10199
|
+
withSyntheticGadgetCall(gadgetName, parameters, result, invocationId) {
|
|
9569
10200
|
const startPrefix = this.gadgetStartPrefix ?? GADGET_START_PREFIX;
|
|
9570
10201
|
const endPrefix = this.gadgetEndPrefix ?? GADGET_END_PREFIX;
|
|
9571
10202
|
const paramStr = this.formatBlockParameters(parameters, "");
|
|
9572
10203
|
this.initialMessages.push({
|
|
9573
10204
|
role: "assistant",
|
|
9574
|
-
content: `${startPrefix}${gadgetName}
|
|
10205
|
+
content: `${startPrefix}${gadgetName}:${invocationId}
|
|
9575
10206
|
${paramStr}
|
|
9576
10207
|
${endPrefix}`
|
|
9577
10208
|
});
|
|
9578
10209
|
this.initialMessages.push({
|
|
9579
10210
|
role: "user",
|
|
9580
|
-
content: `Result: ${result}`
|
|
10211
|
+
content: `Result (${invocationId}): ${result}`
|
|
9581
10212
|
});
|
|
9582
10213
|
return this;
|
|
9583
10214
|
}
|
|
9584
10215
|
/**
|
|
9585
|
-
* Compose the final hooks, including
|
|
9586
|
-
*
|
|
9587
|
-
*
|
|
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).
|
|
9588
10221
|
*/
|
|
9589
10222
|
composeHooks() {
|
|
9590
|
-
|
|
9591
|
-
if (this.parentContext) {
|
|
9592
|
-
const { invocationId, onSubagentEvent, depth } = this.parentContext;
|
|
9593
|
-
const existingOnLLMCallStart = hooks?.observers?.onLLMCallStart;
|
|
9594
|
-
const existingOnLLMCallComplete = hooks?.observers?.onLLMCallComplete;
|
|
9595
|
-
const existingOnGadgetExecutionStart = hooks?.observers?.onGadgetExecutionStart;
|
|
9596
|
-
const existingOnGadgetExecutionComplete = hooks?.observers?.onGadgetExecutionComplete;
|
|
9597
|
-
hooks = {
|
|
9598
|
-
...hooks,
|
|
9599
|
-
observers: {
|
|
9600
|
-
...hooks?.observers,
|
|
9601
|
-
onLLMCallStart: async (context) => {
|
|
9602
|
-
let inputTokens;
|
|
9603
|
-
try {
|
|
9604
|
-
if (this.client) {
|
|
9605
|
-
inputTokens = await this.client.countTokens(
|
|
9606
|
-
context.options.model,
|
|
9607
|
-
context.options.messages
|
|
9608
|
-
);
|
|
9609
|
-
}
|
|
9610
|
-
} catch {
|
|
9611
|
-
}
|
|
9612
|
-
onSubagentEvent({
|
|
9613
|
-
type: "llm_call_start",
|
|
9614
|
-
gadgetInvocationId: invocationId,
|
|
9615
|
-
depth,
|
|
9616
|
-
event: {
|
|
9617
|
-
iteration: context.iteration,
|
|
9618
|
-
model: context.options.model,
|
|
9619
|
-
inputTokens
|
|
9620
|
-
}
|
|
9621
|
-
});
|
|
9622
|
-
if (existingOnLLMCallStart) {
|
|
9623
|
-
await existingOnLLMCallStart(context);
|
|
9624
|
-
}
|
|
9625
|
-
},
|
|
9626
|
-
onLLMCallComplete: async (context) => {
|
|
9627
|
-
onSubagentEvent({
|
|
9628
|
-
type: "llm_call_end",
|
|
9629
|
-
gadgetInvocationId: invocationId,
|
|
9630
|
-
depth,
|
|
9631
|
-
event: {
|
|
9632
|
-
iteration: context.iteration,
|
|
9633
|
-
model: context.options.model,
|
|
9634
|
-
// Backward compat fields
|
|
9635
|
-
inputTokens: context.usage?.inputTokens,
|
|
9636
|
-
outputTokens: context.usage?.outputTokens,
|
|
9637
|
-
finishReason: context.finishReason ?? void 0,
|
|
9638
|
-
// Full usage object with cache details (for first-class display)
|
|
9639
|
-
usage: context.usage
|
|
9640
|
-
// Cost will be calculated by parent if it has model registry
|
|
9641
|
-
}
|
|
9642
|
-
});
|
|
9643
|
-
if (existingOnLLMCallComplete) {
|
|
9644
|
-
await existingOnLLMCallComplete(context);
|
|
9645
|
-
}
|
|
9646
|
-
},
|
|
9647
|
-
onGadgetExecutionStart: async (context) => {
|
|
9648
|
-
onSubagentEvent({
|
|
9649
|
-
type: "gadget_call",
|
|
9650
|
-
gadgetInvocationId: invocationId,
|
|
9651
|
-
depth,
|
|
9652
|
-
event: {
|
|
9653
|
-
call: {
|
|
9654
|
-
invocationId: context.invocationId,
|
|
9655
|
-
gadgetName: context.gadgetName,
|
|
9656
|
-
parameters: context.parameters
|
|
9657
|
-
}
|
|
9658
|
-
}
|
|
9659
|
-
});
|
|
9660
|
-
if (existingOnGadgetExecutionStart) {
|
|
9661
|
-
await existingOnGadgetExecutionStart(context);
|
|
9662
|
-
}
|
|
9663
|
-
},
|
|
9664
|
-
onGadgetExecutionComplete: async (context) => {
|
|
9665
|
-
onSubagentEvent({
|
|
9666
|
-
type: "gadget_result",
|
|
9667
|
-
gadgetInvocationId: invocationId,
|
|
9668
|
-
depth,
|
|
9669
|
-
event: {
|
|
9670
|
-
result: {
|
|
9671
|
-
invocationId: context.invocationId
|
|
9672
|
-
}
|
|
9673
|
-
}
|
|
9674
|
-
});
|
|
9675
|
-
if (existingOnGadgetExecutionComplete) {
|
|
9676
|
-
await existingOnGadgetExecutionComplete(context);
|
|
9677
|
-
}
|
|
9678
|
-
}
|
|
9679
|
-
}
|
|
9680
|
-
};
|
|
9681
|
-
}
|
|
10223
|
+
const hooks = this.hooks;
|
|
9682
10224
|
if (!this.trailingMessage) {
|
|
9683
10225
|
return hooks;
|
|
9684
10226
|
}
|
|
@@ -9761,19 +10303,6 @@ ${endPrefix}`
|
|
|
9761
10303
|
this.client = new LLMistClass();
|
|
9762
10304
|
}
|
|
9763
10305
|
const registry = GadgetRegistry.from(this.gadgets);
|
|
9764
|
-
let onSubagentEvent = this.subagentEventCallback;
|
|
9765
|
-
if (this.parentContext) {
|
|
9766
|
-
const { invocationId, onSubagentEvent: parentCallback, depth } = this.parentContext;
|
|
9767
|
-
const existingCallback = this.subagentEventCallback;
|
|
9768
|
-
onSubagentEvent = (event) => {
|
|
9769
|
-
parentCallback({
|
|
9770
|
-
...event,
|
|
9771
|
-
gadgetInvocationId: invocationId,
|
|
9772
|
-
depth: event.depth + depth
|
|
9773
|
-
});
|
|
9774
|
-
existingCallback?.(event);
|
|
9775
|
-
};
|
|
9776
|
-
}
|
|
9777
10306
|
return {
|
|
9778
10307
|
client: this.client,
|
|
9779
10308
|
model: this.model ?? "openai:gpt-5-nano",
|
|
@@ -9792,15 +10321,17 @@ ${endPrefix}`
|
|
|
9792
10321
|
gadgetArgPrefix: this.gadgetArgPrefix,
|
|
9793
10322
|
textOnlyHandler: this.textOnlyHandler,
|
|
9794
10323
|
textWithGadgetsHandler: this.textWithGadgetsHandler,
|
|
9795
|
-
stopOnGadgetError: this.stopOnGadgetError,
|
|
9796
|
-
canRecoverFromGadgetError: this.canRecoverFromGadgetError,
|
|
9797
10324
|
defaultGadgetTimeoutMs: this.defaultGadgetTimeoutMs,
|
|
9798
10325
|
gadgetOutputLimit: this.gadgetOutputLimit,
|
|
9799
10326
|
gadgetOutputLimitPercent: this.gadgetOutputLimitPercent,
|
|
9800
10327
|
compactionConfig: this.compactionConfig,
|
|
9801
10328
|
signal: this.signal,
|
|
9802
10329
|
subagentConfig: this.subagentConfig,
|
|
9803
|
-
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
|
|
9804
10335
|
};
|
|
9805
10336
|
}
|
|
9806
10337
|
ask(userPrompt) {
|
|
@@ -9957,19 +10488,6 @@ ${endPrefix}`
|
|
|
9957
10488
|
this.client = new LLMistClass();
|
|
9958
10489
|
}
|
|
9959
10490
|
const registry = GadgetRegistry.from(this.gadgets);
|
|
9960
|
-
let onSubagentEvent = this.subagentEventCallback;
|
|
9961
|
-
if (this.parentContext) {
|
|
9962
|
-
const { invocationId, onSubagentEvent: parentCallback, depth } = this.parentContext;
|
|
9963
|
-
const existingCallback = this.subagentEventCallback;
|
|
9964
|
-
onSubagentEvent = (event) => {
|
|
9965
|
-
parentCallback({
|
|
9966
|
-
...event,
|
|
9967
|
-
gadgetInvocationId: invocationId,
|
|
9968
|
-
depth: event.depth + depth
|
|
9969
|
-
});
|
|
9970
|
-
existingCallback?.(event);
|
|
9971
|
-
};
|
|
9972
|
-
}
|
|
9973
10491
|
const options = {
|
|
9974
10492
|
client: this.client,
|
|
9975
10493
|
model: this.model ?? "openai:gpt-5-nano",
|
|
@@ -9988,15 +10506,17 @@ ${endPrefix}`
|
|
|
9988
10506
|
gadgetArgPrefix: this.gadgetArgPrefix,
|
|
9989
10507
|
textOnlyHandler: this.textOnlyHandler,
|
|
9990
10508
|
textWithGadgetsHandler: this.textWithGadgetsHandler,
|
|
9991
|
-
stopOnGadgetError: this.stopOnGadgetError,
|
|
9992
|
-
canRecoverFromGadgetError: this.canRecoverFromGadgetError,
|
|
9993
10509
|
defaultGadgetTimeoutMs: this.defaultGadgetTimeoutMs,
|
|
9994
10510
|
gadgetOutputLimit: this.gadgetOutputLimit,
|
|
9995
10511
|
gadgetOutputLimitPercent: this.gadgetOutputLimitPercent,
|
|
9996
10512
|
compactionConfig: this.compactionConfig,
|
|
9997
10513
|
signal: this.signal,
|
|
9998
10514
|
subagentConfig: this.subagentConfig,
|
|
9999
|
-
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
|
|
10000
10520
|
};
|
|
10001
10521
|
return new Agent(AGENT_INTERNAL_KEY, options);
|
|
10002
10522
|
}
|
|
@@ -10017,6 +10537,7 @@ __export(index_exports, {
|
|
|
10017
10537
|
DEFAULT_HINTS: () => DEFAULT_HINTS,
|
|
10018
10538
|
DEFAULT_PROMPTS: () => DEFAULT_PROMPTS,
|
|
10019
10539
|
DEFAULT_SUMMARIZATION_PROMPT: () => DEFAULT_SUMMARIZATION_PROMPT,
|
|
10540
|
+
ExecutionTree: () => ExecutionTree,
|
|
10020
10541
|
Gadget: () => Gadget,
|
|
10021
10542
|
GadgetCallParser: () => GadgetCallParser,
|
|
10022
10543
|
GadgetExecutor: () => GadgetExecutor,
|
|
@@ -10063,26 +10584,37 @@ __export(index_exports, {
|
|
|
10063
10584
|
detectImageMimeType: () => detectImageMimeType,
|
|
10064
10585
|
discoverProviderAdapters: () => discoverProviderAdapters,
|
|
10065
10586
|
extractMessageText: () => extractMessageText,
|
|
10587
|
+
filterByDepth: () => filterByDepth,
|
|
10588
|
+
filterByParent: () => filterByParent,
|
|
10589
|
+
filterRootEvents: () => filterRootEvents,
|
|
10066
10590
|
getMockManager: () => getMockManager,
|
|
10067
10591
|
getModelId: () => getModelId,
|
|
10068
10592
|
getProvider: () => getProvider,
|
|
10593
|
+
groupByParent: () => groupByParent,
|
|
10069
10594
|
hasProviderPrefix: () => hasProviderPrefix,
|
|
10070
10595
|
imageFromBase64: () => imageFromBase64,
|
|
10071
10596
|
imageFromBuffer: () => imageFromBuffer,
|
|
10072
10597
|
imageFromUrl: () => imageFromUrl,
|
|
10073
10598
|
isAudioPart: () => isAudioPart,
|
|
10074
10599
|
isDataUrl: () => isDataUrl,
|
|
10600
|
+
isGadgetEvent: () => isGadgetEvent,
|
|
10075
10601
|
isImagePart: () => isImagePart,
|
|
10602
|
+
isLLMEvent: () => isLLMEvent,
|
|
10603
|
+
isRootEvent: () => isRootEvent,
|
|
10604
|
+
isSubagentEvent: () => isSubagentEvent,
|
|
10076
10605
|
isTextPart: () => isTextPart,
|
|
10077
10606
|
iterationProgressHint: () => iterationProgressHint,
|
|
10078
10607
|
mockLLM: () => mockLLM,
|
|
10079
10608
|
normalizeMessageContent: () => normalizeMessageContent,
|
|
10080
10609
|
parallelGadgetHint: () => parallelGadgetHint,
|
|
10081
10610
|
parseDataUrl: () => parseDataUrl,
|
|
10611
|
+
resolveConfig: () => resolveConfig,
|
|
10082
10612
|
resolveHintTemplate: () => resolveHintTemplate,
|
|
10083
10613
|
resolveModel: () => resolveModel,
|
|
10084
10614
|
resolvePromptTemplate: () => resolvePromptTemplate,
|
|
10085
10615
|
resolveRulesTemplate: () => resolveRulesTemplate,
|
|
10616
|
+
resolveSubagentModel: () => resolveSubagentModel,
|
|
10617
|
+
resolveValue: () => resolveValue,
|
|
10086
10618
|
resultWithAudio: () => resultWithAudio,
|
|
10087
10619
|
resultWithFile: () => resultWithFile,
|
|
10088
10620
|
resultWithImage: () => resultWithImage,
|
|
@@ -10988,6 +11520,43 @@ init_stream_processor();
|
|
|
10988
11520
|
|
|
10989
11521
|
// src/index.ts
|
|
10990
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
|
|
10991
11560
|
init_input_content();
|
|
10992
11561
|
init_messages();
|
|
10993
11562
|
init_model_registry();
|
|
@@ -11169,6 +11738,52 @@ function validateGadgetParams(gadget, params) {
|
|
|
11169
11738
|
|
|
11170
11739
|
// src/index.ts
|
|
11171
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
|
|
11172
11787
|
init_anthropic();
|
|
11173
11788
|
init_discovery();
|
|
11174
11789
|
init_gemini();
|
|
@@ -12183,6 +12798,7 @@ init_gadget();
|
|
|
12183
12798
|
DEFAULT_HINTS,
|
|
12184
12799
|
DEFAULT_PROMPTS,
|
|
12185
12800
|
DEFAULT_SUMMARIZATION_PROMPT,
|
|
12801
|
+
ExecutionTree,
|
|
12186
12802
|
Gadget,
|
|
12187
12803
|
GadgetCallParser,
|
|
12188
12804
|
GadgetExecutor,
|
|
@@ -12229,26 +12845,37 @@ init_gadget();
|
|
|
12229
12845
|
detectImageMimeType,
|
|
12230
12846
|
discoverProviderAdapters,
|
|
12231
12847
|
extractMessageText,
|
|
12848
|
+
filterByDepth,
|
|
12849
|
+
filterByParent,
|
|
12850
|
+
filterRootEvents,
|
|
12232
12851
|
getMockManager,
|
|
12233
12852
|
getModelId,
|
|
12234
12853
|
getProvider,
|
|
12854
|
+
groupByParent,
|
|
12235
12855
|
hasProviderPrefix,
|
|
12236
12856
|
imageFromBase64,
|
|
12237
12857
|
imageFromBuffer,
|
|
12238
12858
|
imageFromUrl,
|
|
12239
12859
|
isAudioPart,
|
|
12240
12860
|
isDataUrl,
|
|
12861
|
+
isGadgetEvent,
|
|
12241
12862
|
isImagePart,
|
|
12863
|
+
isLLMEvent,
|
|
12864
|
+
isRootEvent,
|
|
12865
|
+
isSubagentEvent,
|
|
12242
12866
|
isTextPart,
|
|
12243
12867
|
iterationProgressHint,
|
|
12244
12868
|
mockLLM,
|
|
12245
12869
|
normalizeMessageContent,
|
|
12246
12870
|
parallelGadgetHint,
|
|
12247
12871
|
parseDataUrl,
|
|
12872
|
+
resolveConfig,
|
|
12248
12873
|
resolveHintTemplate,
|
|
12249
12874
|
resolveModel,
|
|
12250
12875
|
resolvePromptTemplate,
|
|
12251
12876
|
resolveRulesTemplate,
|
|
12877
|
+
resolveSubagentModel,
|
|
12878
|
+
resolveValue,
|
|
12252
12879
|
resultWithAudio,
|
|
12253
12880
|
resultWithFile,
|
|
12254
12881
|
resultWithImage,
|