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/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
- return new Promise((_, reject) => {
3587
- setTimeout(() => {
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
- rawResult = await Promise.race([
3730
- Promise.resolve(gadget.execute(validatedParameters, ctx)),
3731
- this.createTimeoutPromise(call.gadgetName, timeoutMs, abortController)
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
- stopOnGadgetError;
3928
- canRecoverFromGadgetError;
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.stopOnGadgetError = options.stopOnGadgetError ?? true;
3945
- this.canRecoverFromGadgetError = options.canRecoverFromGadgetError;
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
- options.onSubagentEvent
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.onStreamChunk(context);
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
- if (this.executionHalted) {
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
- if (this.executionHalted) {
4190
- this.logger.debug("Skipping gadget execution due to previous error", {
4191
- gadgetName: call.gadgetName
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
- for await (const evt of this.executeGadgetGenerator(call)) {
4234
- yield evt;
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.onGadgetExecutionStart(context);
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.onGadgetExecutionComplete(context);
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.onGadgetExecutionStart(context);
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.onGadgetExecutionComplete(context);
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
- if (result.error) {
4537
- const errorType = this.determineErrorType(call, result);
4538
- const shouldContinue = await this.checkCanRecoverFromError(
4539
- result.error,
4540
- result.gadgetName,
4541
- errorType,
4542
- result.parameters
4543
- );
4544
- if (!shouldContinue) {
4545
- this.executionHalted = true;
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.onGadgetSkipped(observeContext));
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
- yield { type: "subagent_event", subagentEvent: event };
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.onSubagentEvent && ctx.invocationId) {
10134
+ if (ctx.tree) {
9456
10135
  this.parentContext = {
9457
- invocationId: ctx.invocationId,
9458
- onSubagentEvent: ctx.onSubagentEvent,
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
- * - Trailing message injection (if configured)
9533
- * - Subagent event forwarding for LLM calls (if parentContext is set)
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
- let hooks = this.hooks;
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,