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/testing/index.cjs
CHANGED
|
@@ -671,23 +671,26 @@ Produces: { "items": ["first", "second"] }`);
|
|
|
671
671
|
* Record a gadget execution result in the message history.
|
|
672
672
|
* Creates an assistant message with the gadget invocation and a user message with the result.
|
|
673
673
|
*
|
|
674
|
+
* The invocationId is shown to the LLM so it can reference previous calls when building dependencies.
|
|
675
|
+
*
|
|
674
676
|
* @param gadget - Name of the gadget that was executed
|
|
675
677
|
* @param parameters - Parameters that were passed to the gadget
|
|
676
678
|
* @param result - Text result from the gadget execution
|
|
679
|
+
* @param invocationId - Invocation ID (shown to LLM so it can reference for dependencies)
|
|
677
680
|
* @param media - Optional media outputs from the gadget
|
|
678
681
|
* @param mediaIds - Optional IDs for the media outputs
|
|
679
682
|
*/
|
|
680
|
-
addGadgetCallResult(gadget, parameters, result, media, mediaIds) {
|
|
683
|
+
addGadgetCallResult(gadget, parameters, result, invocationId, media, mediaIds) {
|
|
681
684
|
const paramStr = this.formatBlockParameters(parameters, "");
|
|
682
685
|
this.messages.push({
|
|
683
686
|
role: "assistant",
|
|
684
|
-
content: `${this.startPrefix}${gadget}
|
|
687
|
+
content: `${this.startPrefix}${gadget}:${invocationId}
|
|
685
688
|
${paramStr}
|
|
686
689
|
${this.endPrefix}`
|
|
687
690
|
});
|
|
688
691
|
if (media && media.length > 0 && mediaIds && mediaIds.length > 0) {
|
|
689
692
|
const idRefs = media.map((m, i) => `[Media: ${mediaIds[i]} (${m.kind})]`).join("\n");
|
|
690
|
-
const textWithIds = `Result: ${result}
|
|
693
|
+
const textWithIds = `Result (${invocationId}): ${result}
|
|
691
694
|
${idRefs}`;
|
|
692
695
|
const parts = [text(textWithIds)];
|
|
693
696
|
for (const item of media) {
|
|
@@ -701,7 +704,7 @@ ${idRefs}`;
|
|
|
701
704
|
} else {
|
|
702
705
|
this.messages.push({
|
|
703
706
|
role: "user",
|
|
704
|
-
content: `Result: ${result}`
|
|
707
|
+
content: `Result (${invocationId}): ${result}`
|
|
705
708
|
});
|
|
706
709
|
}
|
|
707
710
|
return this;
|
|
@@ -1037,6 +1040,611 @@ var init_registry = __esm({
|
|
|
1037
1040
|
}
|
|
1038
1041
|
});
|
|
1039
1042
|
|
|
1043
|
+
// src/core/execution-tree.ts
|
|
1044
|
+
var ExecutionTree;
|
|
1045
|
+
var init_execution_tree = __esm({
|
|
1046
|
+
"src/core/execution-tree.ts"() {
|
|
1047
|
+
"use strict";
|
|
1048
|
+
ExecutionTree = class {
|
|
1049
|
+
nodes = /* @__PURE__ */ new Map();
|
|
1050
|
+
rootIds = [];
|
|
1051
|
+
eventListeners = /* @__PURE__ */ new Map();
|
|
1052
|
+
eventIdCounter = 0;
|
|
1053
|
+
invocationIdToNodeId = /* @__PURE__ */ new Map();
|
|
1054
|
+
// For async event streaming
|
|
1055
|
+
eventQueue = [];
|
|
1056
|
+
eventWaiters = [];
|
|
1057
|
+
isCompleted = false;
|
|
1058
|
+
/**
|
|
1059
|
+
* Base depth for all nodes in this tree.
|
|
1060
|
+
* Used when this tree is a subagent's view into a parent tree.
|
|
1061
|
+
*/
|
|
1062
|
+
baseDepth;
|
|
1063
|
+
/**
|
|
1064
|
+
* Parent node ID for subagent trees.
|
|
1065
|
+
* All root nodes in this tree will have this as their parentId.
|
|
1066
|
+
*/
|
|
1067
|
+
parentNodeId;
|
|
1068
|
+
constructor(options) {
|
|
1069
|
+
this.baseDepth = options?.baseDepth ?? 0;
|
|
1070
|
+
this.parentNodeId = options?.parentNodeId ?? null;
|
|
1071
|
+
}
|
|
1072
|
+
// ===========================================================================
|
|
1073
|
+
// Node ID Generation
|
|
1074
|
+
// ===========================================================================
|
|
1075
|
+
generateLLMCallId(iteration, parentId) {
|
|
1076
|
+
if (parentId) {
|
|
1077
|
+
return `llm_${parentId}_${iteration}`;
|
|
1078
|
+
}
|
|
1079
|
+
return `llm_${iteration}`;
|
|
1080
|
+
}
|
|
1081
|
+
gadgetIdCounter = 0;
|
|
1082
|
+
generateGadgetId(invocationId) {
|
|
1083
|
+
return `gadget_${invocationId}_${++this.gadgetIdCounter}`;
|
|
1084
|
+
}
|
|
1085
|
+
// ===========================================================================
|
|
1086
|
+
// Event Emission
|
|
1087
|
+
// ===========================================================================
|
|
1088
|
+
emit(event) {
|
|
1089
|
+
const listeners = this.eventListeners.get(event.type);
|
|
1090
|
+
if (listeners) {
|
|
1091
|
+
for (const listener of listeners) {
|
|
1092
|
+
try {
|
|
1093
|
+
listener(event);
|
|
1094
|
+
} catch (error) {
|
|
1095
|
+
console.error(`Error in event listener for ${event.type}:`, error);
|
|
1096
|
+
}
|
|
1097
|
+
}
|
|
1098
|
+
}
|
|
1099
|
+
const allListeners = this.eventListeners.get("*");
|
|
1100
|
+
if (allListeners) {
|
|
1101
|
+
for (const listener of allListeners) {
|
|
1102
|
+
try {
|
|
1103
|
+
listener(event);
|
|
1104
|
+
} catch (error) {
|
|
1105
|
+
console.error("Error in wildcard event listener:", error);
|
|
1106
|
+
}
|
|
1107
|
+
}
|
|
1108
|
+
}
|
|
1109
|
+
if (this.eventWaiters.length > 0) {
|
|
1110
|
+
const waiter = this.eventWaiters.shift();
|
|
1111
|
+
if (waiter) waiter(event);
|
|
1112
|
+
} else {
|
|
1113
|
+
this.eventQueue.push(event);
|
|
1114
|
+
}
|
|
1115
|
+
}
|
|
1116
|
+
createBaseEventProps(node) {
|
|
1117
|
+
return {
|
|
1118
|
+
eventId: ++this.eventIdCounter,
|
|
1119
|
+
timestamp: Date.now(),
|
|
1120
|
+
nodeId: node.id,
|
|
1121
|
+
parentId: node.parentId,
|
|
1122
|
+
depth: node.depth,
|
|
1123
|
+
path: node.path
|
|
1124
|
+
};
|
|
1125
|
+
}
|
|
1126
|
+
// ===========================================================================
|
|
1127
|
+
// Node Creation
|
|
1128
|
+
// ===========================================================================
|
|
1129
|
+
/**
|
|
1130
|
+
* Add a new LLM call node to the tree.
|
|
1131
|
+
*/
|
|
1132
|
+
addLLMCall(params) {
|
|
1133
|
+
const parentId = params.parentId ?? this.parentNodeId;
|
|
1134
|
+
const parent = parentId ? this.nodes.get(parentId) : null;
|
|
1135
|
+
const depth = parent ? parent.depth + 1 : this.baseDepth;
|
|
1136
|
+
const path = parent ? [...parent.path] : [];
|
|
1137
|
+
const id = this.generateLLMCallId(params.iteration, parentId);
|
|
1138
|
+
path.push(id);
|
|
1139
|
+
const node = {
|
|
1140
|
+
id,
|
|
1141
|
+
type: "llm_call",
|
|
1142
|
+
parentId,
|
|
1143
|
+
depth,
|
|
1144
|
+
path,
|
|
1145
|
+
createdAt: Date.now(),
|
|
1146
|
+
completedAt: null,
|
|
1147
|
+
iteration: params.iteration,
|
|
1148
|
+
model: params.model,
|
|
1149
|
+
request: params.request,
|
|
1150
|
+
response: "",
|
|
1151
|
+
children: []
|
|
1152
|
+
};
|
|
1153
|
+
this.nodes.set(id, node);
|
|
1154
|
+
if (!parentId) {
|
|
1155
|
+
this.rootIds.push(id);
|
|
1156
|
+
} else if (parent) {
|
|
1157
|
+
parent.children.push(id);
|
|
1158
|
+
}
|
|
1159
|
+
this.emit({
|
|
1160
|
+
type: "llm_call_start",
|
|
1161
|
+
...this.createBaseEventProps(node),
|
|
1162
|
+
iteration: node.iteration,
|
|
1163
|
+
model: node.model,
|
|
1164
|
+
request: node.request
|
|
1165
|
+
});
|
|
1166
|
+
return node;
|
|
1167
|
+
}
|
|
1168
|
+
/**
|
|
1169
|
+
* Add text to an LLM call's response (for streaming).
|
|
1170
|
+
*/
|
|
1171
|
+
appendLLMResponse(nodeId, chunk) {
|
|
1172
|
+
const node = this.nodes.get(nodeId);
|
|
1173
|
+
if (!node || node.type !== "llm_call") {
|
|
1174
|
+
throw new Error(`LLM call node not found: ${nodeId}`);
|
|
1175
|
+
}
|
|
1176
|
+
node.response += chunk;
|
|
1177
|
+
this.emit({
|
|
1178
|
+
type: "llm_call_stream",
|
|
1179
|
+
...this.createBaseEventProps(node),
|
|
1180
|
+
chunk
|
|
1181
|
+
});
|
|
1182
|
+
}
|
|
1183
|
+
/**
|
|
1184
|
+
* Complete an LLM call node.
|
|
1185
|
+
*/
|
|
1186
|
+
completeLLMCall(nodeId, params) {
|
|
1187
|
+
const node = this.nodes.get(nodeId);
|
|
1188
|
+
if (!node || node.type !== "llm_call") {
|
|
1189
|
+
throw new Error(`LLM call node not found: ${nodeId}`);
|
|
1190
|
+
}
|
|
1191
|
+
const llmNode = node;
|
|
1192
|
+
llmNode.completedAt = Date.now();
|
|
1193
|
+
if (params.response !== void 0) llmNode.response = params.response;
|
|
1194
|
+
if (params.usage) llmNode.usage = params.usage;
|
|
1195
|
+
if (params.finishReason !== void 0) llmNode.finishReason = params.finishReason;
|
|
1196
|
+
if (params.cost !== void 0) llmNode.cost = params.cost;
|
|
1197
|
+
this.emit({
|
|
1198
|
+
type: "llm_call_complete",
|
|
1199
|
+
...this.createBaseEventProps(node),
|
|
1200
|
+
response: llmNode.response,
|
|
1201
|
+
usage: llmNode.usage,
|
|
1202
|
+
finishReason: llmNode.finishReason,
|
|
1203
|
+
cost: llmNode.cost
|
|
1204
|
+
});
|
|
1205
|
+
}
|
|
1206
|
+
/**
|
|
1207
|
+
* Mark an LLM call as failed.
|
|
1208
|
+
*/
|
|
1209
|
+
failLLMCall(nodeId, error, recovered) {
|
|
1210
|
+
const node = this.nodes.get(nodeId);
|
|
1211
|
+
if (!node || node.type !== "llm_call") {
|
|
1212
|
+
throw new Error(`LLM call node not found: ${nodeId}`);
|
|
1213
|
+
}
|
|
1214
|
+
const llmNode = node;
|
|
1215
|
+
llmNode.completedAt = Date.now();
|
|
1216
|
+
this.emit({
|
|
1217
|
+
type: "llm_call_error",
|
|
1218
|
+
...this.createBaseEventProps(node),
|
|
1219
|
+
error,
|
|
1220
|
+
recovered
|
|
1221
|
+
});
|
|
1222
|
+
}
|
|
1223
|
+
/**
|
|
1224
|
+
* Add a new gadget node to the tree.
|
|
1225
|
+
*/
|
|
1226
|
+
addGadget(params) {
|
|
1227
|
+
const parentId = params.parentId ?? this.getCurrentLLMCallId() ?? this.parentNodeId;
|
|
1228
|
+
const parent = parentId ? this.nodes.get(parentId) : null;
|
|
1229
|
+
const depth = parent ? parent.depth + 1 : this.baseDepth;
|
|
1230
|
+
const path = parent ? [...parent.path] : [];
|
|
1231
|
+
const id = this.generateGadgetId(params.invocationId);
|
|
1232
|
+
path.push(id);
|
|
1233
|
+
const node = {
|
|
1234
|
+
id,
|
|
1235
|
+
type: "gadget",
|
|
1236
|
+
parentId,
|
|
1237
|
+
depth,
|
|
1238
|
+
path,
|
|
1239
|
+
createdAt: Date.now(),
|
|
1240
|
+
completedAt: null,
|
|
1241
|
+
invocationId: params.invocationId,
|
|
1242
|
+
name: params.name,
|
|
1243
|
+
parameters: params.parameters,
|
|
1244
|
+
dependencies: params.dependencies ?? [],
|
|
1245
|
+
state: "pending",
|
|
1246
|
+
children: [],
|
|
1247
|
+
isSubagent: false
|
|
1248
|
+
};
|
|
1249
|
+
this.nodes.set(id, node);
|
|
1250
|
+
this.invocationIdToNodeId.set(params.invocationId, id);
|
|
1251
|
+
if (parent) {
|
|
1252
|
+
parent.children.push(id);
|
|
1253
|
+
}
|
|
1254
|
+
this.emit({
|
|
1255
|
+
type: "gadget_call",
|
|
1256
|
+
...this.createBaseEventProps(node),
|
|
1257
|
+
invocationId: node.invocationId,
|
|
1258
|
+
name: node.name,
|
|
1259
|
+
parameters: node.parameters,
|
|
1260
|
+
dependencies: node.dependencies
|
|
1261
|
+
});
|
|
1262
|
+
return node;
|
|
1263
|
+
}
|
|
1264
|
+
/**
|
|
1265
|
+
* Mark a gadget as started (running).
|
|
1266
|
+
*/
|
|
1267
|
+
startGadget(nodeId) {
|
|
1268
|
+
const node = this.nodes.get(nodeId);
|
|
1269
|
+
if (!node || node.type !== "gadget") {
|
|
1270
|
+
throw new Error(`Gadget node not found: ${nodeId}`);
|
|
1271
|
+
}
|
|
1272
|
+
const gadgetNode = node;
|
|
1273
|
+
gadgetNode.state = "running";
|
|
1274
|
+
this.emit({
|
|
1275
|
+
type: "gadget_start",
|
|
1276
|
+
...this.createBaseEventProps(node),
|
|
1277
|
+
invocationId: gadgetNode.invocationId,
|
|
1278
|
+
name: gadgetNode.name
|
|
1279
|
+
});
|
|
1280
|
+
}
|
|
1281
|
+
/**
|
|
1282
|
+
* Complete a gadget node successfully.
|
|
1283
|
+
*/
|
|
1284
|
+
completeGadget(nodeId, params) {
|
|
1285
|
+
const node = this.nodes.get(nodeId);
|
|
1286
|
+
if (!node || node.type !== "gadget") {
|
|
1287
|
+
throw new Error(`Gadget node not found: ${nodeId}`);
|
|
1288
|
+
}
|
|
1289
|
+
const gadgetNode = node;
|
|
1290
|
+
gadgetNode.completedAt = Date.now();
|
|
1291
|
+
gadgetNode.state = params.error ? "failed" : "completed";
|
|
1292
|
+
if (params.result !== void 0) gadgetNode.result = params.result;
|
|
1293
|
+
if (params.error) gadgetNode.error = params.error;
|
|
1294
|
+
if (params.executionTimeMs !== void 0) gadgetNode.executionTimeMs = params.executionTimeMs;
|
|
1295
|
+
if (params.cost !== void 0) gadgetNode.cost = params.cost;
|
|
1296
|
+
if (params.media) gadgetNode.media = params.media;
|
|
1297
|
+
gadgetNode.isSubagent = gadgetNode.children.some((childId) => {
|
|
1298
|
+
const child = this.nodes.get(childId);
|
|
1299
|
+
return child?.type === "llm_call";
|
|
1300
|
+
});
|
|
1301
|
+
if (params.error) {
|
|
1302
|
+
this.emit({
|
|
1303
|
+
type: "gadget_error",
|
|
1304
|
+
...this.createBaseEventProps(node),
|
|
1305
|
+
invocationId: gadgetNode.invocationId,
|
|
1306
|
+
name: gadgetNode.name,
|
|
1307
|
+
error: params.error,
|
|
1308
|
+
executionTimeMs: params.executionTimeMs ?? 0
|
|
1309
|
+
});
|
|
1310
|
+
} else {
|
|
1311
|
+
this.emit({
|
|
1312
|
+
type: "gadget_complete",
|
|
1313
|
+
...this.createBaseEventProps(node),
|
|
1314
|
+
invocationId: gadgetNode.invocationId,
|
|
1315
|
+
name: gadgetNode.name,
|
|
1316
|
+
result: params.result ?? "",
|
|
1317
|
+
executionTimeMs: params.executionTimeMs ?? 0,
|
|
1318
|
+
cost: params.cost,
|
|
1319
|
+
media: params.media
|
|
1320
|
+
});
|
|
1321
|
+
}
|
|
1322
|
+
}
|
|
1323
|
+
/**
|
|
1324
|
+
* Mark a gadget as skipped due to dependency failure.
|
|
1325
|
+
*/
|
|
1326
|
+
skipGadget(nodeId, failedDependency, failedDependencyError, reason) {
|
|
1327
|
+
const node = this.nodes.get(nodeId);
|
|
1328
|
+
if (!node || node.type !== "gadget") {
|
|
1329
|
+
throw new Error(`Gadget node not found: ${nodeId}`);
|
|
1330
|
+
}
|
|
1331
|
+
const gadgetNode = node;
|
|
1332
|
+
gadgetNode.completedAt = Date.now();
|
|
1333
|
+
gadgetNode.state = "skipped";
|
|
1334
|
+
gadgetNode.failedDependency = failedDependency;
|
|
1335
|
+
gadgetNode.error = failedDependencyError;
|
|
1336
|
+
const error = reason === "controller_skip" ? "Skipped by controller" : `Dependency ${failedDependency} failed: ${failedDependencyError}`;
|
|
1337
|
+
this.emit({
|
|
1338
|
+
type: "gadget_skipped",
|
|
1339
|
+
...this.createBaseEventProps(node),
|
|
1340
|
+
invocationId: gadgetNode.invocationId,
|
|
1341
|
+
name: gadgetNode.name,
|
|
1342
|
+
reason,
|
|
1343
|
+
error,
|
|
1344
|
+
failedDependency,
|
|
1345
|
+
failedDependencyError
|
|
1346
|
+
});
|
|
1347
|
+
}
|
|
1348
|
+
// ===========================================================================
|
|
1349
|
+
// Text Events (pure notifications, not tree nodes)
|
|
1350
|
+
// ===========================================================================
|
|
1351
|
+
/**
|
|
1352
|
+
* Emit a text event (notification only, not stored in tree).
|
|
1353
|
+
*/
|
|
1354
|
+
emitText(content, llmCallNodeId) {
|
|
1355
|
+
const node = this.nodes.get(llmCallNodeId);
|
|
1356
|
+
if (!node) {
|
|
1357
|
+
throw new Error(`Node not found: ${llmCallNodeId}`);
|
|
1358
|
+
}
|
|
1359
|
+
this.emit({
|
|
1360
|
+
type: "text",
|
|
1361
|
+
...this.createBaseEventProps(node),
|
|
1362
|
+
content
|
|
1363
|
+
});
|
|
1364
|
+
}
|
|
1365
|
+
// ===========================================================================
|
|
1366
|
+
// Query Methods
|
|
1367
|
+
// ===========================================================================
|
|
1368
|
+
/**
|
|
1369
|
+
* Get a node by ID.
|
|
1370
|
+
*/
|
|
1371
|
+
getNode(id) {
|
|
1372
|
+
return this.nodes.get(id);
|
|
1373
|
+
}
|
|
1374
|
+
/**
|
|
1375
|
+
* Get a gadget node by invocation ID.
|
|
1376
|
+
*/
|
|
1377
|
+
getNodeByInvocationId(invocationId) {
|
|
1378
|
+
const nodeId = this.invocationIdToNodeId.get(invocationId);
|
|
1379
|
+
if (!nodeId) return void 0;
|
|
1380
|
+
const node = this.nodes.get(nodeId);
|
|
1381
|
+
return node?.type === "gadget" ? node : void 0;
|
|
1382
|
+
}
|
|
1383
|
+
/**
|
|
1384
|
+
* Get all root nodes (depth 0 for this tree).
|
|
1385
|
+
*/
|
|
1386
|
+
getRoots() {
|
|
1387
|
+
return this.rootIds.map((id) => this.nodes.get(id)).filter((node) => node !== void 0);
|
|
1388
|
+
}
|
|
1389
|
+
/**
|
|
1390
|
+
* Get children of a node.
|
|
1391
|
+
*/
|
|
1392
|
+
getChildren(id) {
|
|
1393
|
+
const node = this.nodes.get(id);
|
|
1394
|
+
if (!node) return [];
|
|
1395
|
+
return node.children.map((childId) => this.nodes.get(childId)).filter((child) => child !== void 0);
|
|
1396
|
+
}
|
|
1397
|
+
/**
|
|
1398
|
+
* Get ancestors of a node (from root to parent).
|
|
1399
|
+
*/
|
|
1400
|
+
getAncestors(id) {
|
|
1401
|
+
const node = this.nodes.get(id);
|
|
1402
|
+
if (!node) return [];
|
|
1403
|
+
const ancestors = [];
|
|
1404
|
+
let currentId = node.parentId;
|
|
1405
|
+
while (currentId) {
|
|
1406
|
+
const ancestor = this.nodes.get(currentId);
|
|
1407
|
+
if (ancestor) {
|
|
1408
|
+
ancestors.unshift(ancestor);
|
|
1409
|
+
currentId = ancestor.parentId;
|
|
1410
|
+
} else {
|
|
1411
|
+
break;
|
|
1412
|
+
}
|
|
1413
|
+
}
|
|
1414
|
+
return ancestors;
|
|
1415
|
+
}
|
|
1416
|
+
/**
|
|
1417
|
+
* Get all descendants of a node.
|
|
1418
|
+
*/
|
|
1419
|
+
getDescendants(id, type) {
|
|
1420
|
+
const node = this.nodes.get(id);
|
|
1421
|
+
if (!node) return [];
|
|
1422
|
+
const descendants = [];
|
|
1423
|
+
const stack = [...node.children];
|
|
1424
|
+
while (stack.length > 0) {
|
|
1425
|
+
const childId = stack.pop();
|
|
1426
|
+
const child = this.nodes.get(childId);
|
|
1427
|
+
if (child) {
|
|
1428
|
+
if (!type || child.type === type) {
|
|
1429
|
+
descendants.push(child);
|
|
1430
|
+
}
|
|
1431
|
+
stack.push(...child.children);
|
|
1432
|
+
}
|
|
1433
|
+
}
|
|
1434
|
+
return descendants;
|
|
1435
|
+
}
|
|
1436
|
+
/**
|
|
1437
|
+
* Get the current (most recent incomplete) LLM call node.
|
|
1438
|
+
*/
|
|
1439
|
+
getCurrentLLMCallId() {
|
|
1440
|
+
for (let i = this.rootIds.length - 1; i >= 0; i--) {
|
|
1441
|
+
const node = this.nodes.get(this.rootIds[i]);
|
|
1442
|
+
if (node?.type === "llm_call" && !node.completedAt) {
|
|
1443
|
+
return node.id;
|
|
1444
|
+
}
|
|
1445
|
+
}
|
|
1446
|
+
return void 0;
|
|
1447
|
+
}
|
|
1448
|
+
// ===========================================================================
|
|
1449
|
+
// Aggregation Methods (for subagent support)
|
|
1450
|
+
// ===========================================================================
|
|
1451
|
+
/**
|
|
1452
|
+
* Get total cost for entire tree.
|
|
1453
|
+
*/
|
|
1454
|
+
getTotalCost() {
|
|
1455
|
+
let total = 0;
|
|
1456
|
+
for (const node of this.nodes.values()) {
|
|
1457
|
+
if (node.type === "llm_call" && node.cost) {
|
|
1458
|
+
total += node.cost;
|
|
1459
|
+
} else if (node.type === "gadget" && node.cost) {
|
|
1460
|
+
total += node.cost;
|
|
1461
|
+
}
|
|
1462
|
+
}
|
|
1463
|
+
return total;
|
|
1464
|
+
}
|
|
1465
|
+
/**
|
|
1466
|
+
* Get total cost for a subtree (node and all descendants).
|
|
1467
|
+
*/
|
|
1468
|
+
getSubtreeCost(nodeId) {
|
|
1469
|
+
const node = this.nodes.get(nodeId);
|
|
1470
|
+
if (!node) return 0;
|
|
1471
|
+
let total = 0;
|
|
1472
|
+
if (node.type === "llm_call" && node.cost) {
|
|
1473
|
+
total += node.cost;
|
|
1474
|
+
} else if (node.type === "gadget" && node.cost) {
|
|
1475
|
+
total += node.cost;
|
|
1476
|
+
}
|
|
1477
|
+
for (const descendant of this.getDescendants(nodeId)) {
|
|
1478
|
+
if (descendant.type === "llm_call" && descendant.cost) {
|
|
1479
|
+
total += descendant.cost;
|
|
1480
|
+
} else if (descendant.type === "gadget" && descendant.cost) {
|
|
1481
|
+
total += descendant.cost;
|
|
1482
|
+
}
|
|
1483
|
+
}
|
|
1484
|
+
return total;
|
|
1485
|
+
}
|
|
1486
|
+
/**
|
|
1487
|
+
* Get token usage for entire tree.
|
|
1488
|
+
*/
|
|
1489
|
+
getTotalTokens() {
|
|
1490
|
+
let input = 0;
|
|
1491
|
+
let output = 0;
|
|
1492
|
+
let cached = 0;
|
|
1493
|
+
for (const node of this.nodes.values()) {
|
|
1494
|
+
if (node.type === "llm_call") {
|
|
1495
|
+
const llmNode = node;
|
|
1496
|
+
if (llmNode.usage) {
|
|
1497
|
+
input += llmNode.usage.inputTokens;
|
|
1498
|
+
output += llmNode.usage.outputTokens;
|
|
1499
|
+
cached += llmNode.usage.cachedInputTokens ?? 0;
|
|
1500
|
+
}
|
|
1501
|
+
}
|
|
1502
|
+
}
|
|
1503
|
+
return { input, output, cached };
|
|
1504
|
+
}
|
|
1505
|
+
/**
|
|
1506
|
+
* Get token usage for a subtree.
|
|
1507
|
+
*/
|
|
1508
|
+
getSubtreeTokens(nodeId) {
|
|
1509
|
+
const node = this.nodes.get(nodeId);
|
|
1510
|
+
if (!node) return { input: 0, output: 0, cached: 0 };
|
|
1511
|
+
let input = 0;
|
|
1512
|
+
let output = 0;
|
|
1513
|
+
let cached = 0;
|
|
1514
|
+
const nodesToProcess = [node, ...this.getDescendants(nodeId)];
|
|
1515
|
+
for (const n of nodesToProcess) {
|
|
1516
|
+
if (n.type === "llm_call") {
|
|
1517
|
+
const llmNode = n;
|
|
1518
|
+
if (llmNode.usage) {
|
|
1519
|
+
input += llmNode.usage.inputTokens;
|
|
1520
|
+
output += llmNode.usage.outputTokens;
|
|
1521
|
+
cached += llmNode.usage.cachedInputTokens ?? 0;
|
|
1522
|
+
}
|
|
1523
|
+
}
|
|
1524
|
+
}
|
|
1525
|
+
return { input, output, cached };
|
|
1526
|
+
}
|
|
1527
|
+
/**
|
|
1528
|
+
* Collect all media from a subtree.
|
|
1529
|
+
*/
|
|
1530
|
+
getSubtreeMedia(nodeId) {
|
|
1531
|
+
const node = this.nodes.get(nodeId);
|
|
1532
|
+
if (!node) return [];
|
|
1533
|
+
const media = [];
|
|
1534
|
+
const nodesToProcess = node.type === "gadget" ? [node] : [];
|
|
1535
|
+
nodesToProcess.push(...this.getDescendants(nodeId, "gadget"));
|
|
1536
|
+
for (const n of nodesToProcess) {
|
|
1537
|
+
if (n.type === "gadget") {
|
|
1538
|
+
const gadgetNode = n;
|
|
1539
|
+
if (gadgetNode.media) {
|
|
1540
|
+
media.push(...gadgetNode.media);
|
|
1541
|
+
}
|
|
1542
|
+
}
|
|
1543
|
+
}
|
|
1544
|
+
return media;
|
|
1545
|
+
}
|
|
1546
|
+
/**
|
|
1547
|
+
* Check if a subtree is complete (all nodes finished).
|
|
1548
|
+
*/
|
|
1549
|
+
isSubtreeComplete(nodeId) {
|
|
1550
|
+
const node = this.nodes.get(nodeId);
|
|
1551
|
+
if (!node) return true;
|
|
1552
|
+
if (!node.completedAt) return false;
|
|
1553
|
+
for (const descendant of this.getDescendants(nodeId)) {
|
|
1554
|
+
if (!descendant.completedAt) return false;
|
|
1555
|
+
}
|
|
1556
|
+
return true;
|
|
1557
|
+
}
|
|
1558
|
+
/**
|
|
1559
|
+
* Get node counts.
|
|
1560
|
+
*/
|
|
1561
|
+
getNodeCount() {
|
|
1562
|
+
let llmCalls = 0;
|
|
1563
|
+
let gadgets = 0;
|
|
1564
|
+
for (const node of this.nodes.values()) {
|
|
1565
|
+
if (node.type === "llm_call") llmCalls++;
|
|
1566
|
+
else if (node.type === "gadget") gadgets++;
|
|
1567
|
+
}
|
|
1568
|
+
return { llmCalls, gadgets };
|
|
1569
|
+
}
|
|
1570
|
+
// ===========================================================================
|
|
1571
|
+
// Event Subscription
|
|
1572
|
+
// ===========================================================================
|
|
1573
|
+
/**
|
|
1574
|
+
* Subscribe to events of a specific type.
|
|
1575
|
+
* Returns unsubscribe function.
|
|
1576
|
+
*
|
|
1577
|
+
* @param type - Event type to subscribe to (use "*" for all events)
|
|
1578
|
+
* @param listener - Callback function that receives matching events
|
|
1579
|
+
* @returns Unsubscribe function
|
|
1580
|
+
*
|
|
1581
|
+
* @example
|
|
1582
|
+
* ```typescript
|
|
1583
|
+
* const unsubscribe = tree.on("gadget_complete", (event) => {
|
|
1584
|
+
* if (event.type === "gadget_complete") {
|
|
1585
|
+
* console.log(`Gadget ${event.name} completed`);
|
|
1586
|
+
* }
|
|
1587
|
+
* });
|
|
1588
|
+
* ```
|
|
1589
|
+
*/
|
|
1590
|
+
on(type, listener) {
|
|
1591
|
+
if (!this.eventListeners.has(type)) {
|
|
1592
|
+
this.eventListeners.set(type, /* @__PURE__ */ new Set());
|
|
1593
|
+
}
|
|
1594
|
+
const listeners = this.eventListeners.get(type);
|
|
1595
|
+
listeners.add(listener);
|
|
1596
|
+
return () => {
|
|
1597
|
+
listeners.delete(listener);
|
|
1598
|
+
};
|
|
1599
|
+
}
|
|
1600
|
+
/**
|
|
1601
|
+
* Subscribe to all events.
|
|
1602
|
+
*/
|
|
1603
|
+
onAll(listener) {
|
|
1604
|
+
return this.on("*", listener);
|
|
1605
|
+
}
|
|
1606
|
+
/**
|
|
1607
|
+
* Get async iterable of all events.
|
|
1608
|
+
* Events are yielded as they occur.
|
|
1609
|
+
*/
|
|
1610
|
+
async *events() {
|
|
1611
|
+
while (!this.isCompleted) {
|
|
1612
|
+
while (this.eventQueue.length > 0) {
|
|
1613
|
+
yield this.eventQueue.shift();
|
|
1614
|
+
}
|
|
1615
|
+
if (this.isCompleted) break;
|
|
1616
|
+
const event = await new Promise((resolve) => {
|
|
1617
|
+
if (this.eventQueue.length > 0) {
|
|
1618
|
+
resolve(this.eventQueue.shift());
|
|
1619
|
+
} else {
|
|
1620
|
+
this.eventWaiters.push(resolve);
|
|
1621
|
+
}
|
|
1622
|
+
});
|
|
1623
|
+
yield event;
|
|
1624
|
+
}
|
|
1625
|
+
while (this.eventQueue.length > 0) {
|
|
1626
|
+
yield this.eventQueue.shift();
|
|
1627
|
+
}
|
|
1628
|
+
}
|
|
1629
|
+
/**
|
|
1630
|
+
* Mark the tree as complete (no more events will be emitted).
|
|
1631
|
+
*/
|
|
1632
|
+
complete() {
|
|
1633
|
+
this.isCompleted = true;
|
|
1634
|
+
for (const waiter of this.eventWaiters) {
|
|
1635
|
+
}
|
|
1636
|
+
this.eventWaiters = [];
|
|
1637
|
+
}
|
|
1638
|
+
/**
|
|
1639
|
+
* Check if the tree is complete.
|
|
1640
|
+
*/
|
|
1641
|
+
isComplete() {
|
|
1642
|
+
return this.isCompleted;
|
|
1643
|
+
}
|
|
1644
|
+
};
|
|
1645
|
+
}
|
|
1646
|
+
});
|
|
1647
|
+
|
|
1040
1648
|
// src/gadgets/media-store.ts
|
|
1041
1649
|
function getLlmistTmpDir() {
|
|
1042
1650
|
return (0, import_node_path2.join)((0, import_node_os.homedir)(), ".llmist", "tmp");
|
|
@@ -2333,8 +2941,8 @@ var init_conversation_manager = __esm({
|
|
|
2333
2941
|
addAssistantMessage(content) {
|
|
2334
2942
|
this.historyBuilder.addAssistant(content);
|
|
2335
2943
|
}
|
|
2336
|
-
addGadgetCallResult(gadgetName, parameters, result, media, mediaIds) {
|
|
2337
|
-
this.historyBuilder.addGadgetCallResult(gadgetName, parameters, result, media, mediaIds);
|
|
2944
|
+
addGadgetCallResult(gadgetName, parameters, result, invocationId, media, mediaIds) {
|
|
2945
|
+
this.historyBuilder.addGadgetCallResult(gadgetName, parameters, result, invocationId, media, mediaIds);
|
|
2338
2946
|
}
|
|
2339
2947
|
getMessages() {
|
|
2340
2948
|
return [...this.baseMessages, ...this.initialMessages, ...this.historyBuilder.build()];
|
|
@@ -3485,7 +4093,7 @@ var init_executor = __esm({
|
|
|
3485
4093
|
init_exceptions();
|
|
3486
4094
|
init_parser();
|
|
3487
4095
|
GadgetExecutor = class {
|
|
3488
|
-
constructor(registry, requestHumanInput, logger, defaultGadgetTimeoutMs, errorFormatterOptions, client, mediaStore, agentConfig, subagentConfig, onSubagentEvent) {
|
|
4096
|
+
constructor(registry, requestHumanInput, logger, defaultGadgetTimeoutMs, errorFormatterOptions, client, mediaStore, agentConfig, subagentConfig, onSubagentEvent, tree, parentNodeId, baseDepth) {
|
|
3489
4097
|
this.registry = registry;
|
|
3490
4098
|
this.requestHumanInput = requestHumanInput;
|
|
3491
4099
|
this.defaultGadgetTimeoutMs = defaultGadgetTimeoutMs;
|
|
@@ -3494,6 +4102,9 @@ var init_executor = __esm({
|
|
|
3494
4102
|
this.agentConfig = agentConfig;
|
|
3495
4103
|
this.subagentConfig = subagentConfig;
|
|
3496
4104
|
this.onSubagentEvent = onSubagentEvent;
|
|
4105
|
+
this.tree = tree;
|
|
4106
|
+
this.parentNodeId = parentNodeId;
|
|
4107
|
+
this.baseDepth = baseDepth;
|
|
3497
4108
|
this.logger = logger ?? createLogger({ name: "llmist:executor" });
|
|
3498
4109
|
this.errorFormatter = new GadgetExecutionErrorFormatter(errorFormatterOptions);
|
|
3499
4110
|
this.argPrefix = errorFormatterOptions?.argPrefix ?? GADGET_ARG_PREFIX;
|
|
@@ -3504,15 +4115,21 @@ var init_executor = __esm({
|
|
|
3504
4115
|
/**
|
|
3505
4116
|
* Creates a promise that rejects with a TimeoutException after the specified timeout.
|
|
3506
4117
|
* Aborts the provided AbortController before rejecting, allowing gadgets to clean up.
|
|
4118
|
+
* Returns both the promise and a cancel function to clear the timeout when no longer needed.
|
|
3507
4119
|
*/
|
|
3508
4120
|
createTimeoutPromise(gadgetName, timeoutMs, abortController) {
|
|
3509
|
-
|
|
3510
|
-
|
|
4121
|
+
let timeoutId;
|
|
4122
|
+
const promise = new Promise((_, reject) => {
|
|
4123
|
+
timeoutId = setTimeout(() => {
|
|
3511
4124
|
const timeoutError = new TimeoutException(gadgetName, timeoutMs);
|
|
3512
4125
|
abortController.abort(timeoutError.message);
|
|
3513
4126
|
reject(timeoutError);
|
|
3514
4127
|
}, timeoutMs);
|
|
3515
4128
|
});
|
|
4129
|
+
return {
|
|
4130
|
+
promise,
|
|
4131
|
+
cancel: () => clearTimeout(timeoutId)
|
|
4132
|
+
};
|
|
3516
4133
|
}
|
|
3517
4134
|
/**
|
|
3518
4135
|
* Unify gadget execute result to consistent internal format.
|
|
@@ -3634,6 +4251,8 @@ var init_executor = __esm({
|
|
|
3634
4251
|
});
|
|
3635
4252
|
}
|
|
3636
4253
|
};
|
|
4254
|
+
const gadgetNodeId = this.tree?.getNodeByInvocationId(call.invocationId)?.id;
|
|
4255
|
+
const gadgetDepth = gadgetNodeId ? this.tree?.getNode(gadgetNodeId)?.depth ?? this.baseDepth : this.baseDepth;
|
|
3637
4256
|
const ctx = {
|
|
3638
4257
|
reportCost,
|
|
3639
4258
|
llmist: this.client ? new CostReportingLLMistWrapper(this.client, reportCost) : void 0,
|
|
@@ -3641,7 +4260,11 @@ var init_executor = __esm({
|
|
|
3641
4260
|
agentConfig: this.agentConfig,
|
|
3642
4261
|
subagentConfig: this.subagentConfig,
|
|
3643
4262
|
invocationId: call.invocationId,
|
|
3644
|
-
onSubagentEvent: this.onSubagentEvent
|
|
4263
|
+
onSubagentEvent: this.onSubagentEvent,
|
|
4264
|
+
// Tree context for subagent support - use gadget's own node ID
|
|
4265
|
+
tree: this.tree,
|
|
4266
|
+
nodeId: gadgetNodeId,
|
|
4267
|
+
depth: gadgetDepth
|
|
3645
4268
|
};
|
|
3646
4269
|
let rawResult;
|
|
3647
4270
|
if (timeoutMs && timeoutMs > 0) {
|
|
@@ -3649,10 +4272,15 @@ var init_executor = __esm({
|
|
|
3649
4272
|
gadgetName: call.gadgetName,
|
|
3650
4273
|
timeoutMs
|
|
3651
4274
|
});
|
|
3652
|
-
|
|
3653
|
-
|
|
3654
|
-
|
|
3655
|
-
|
|
4275
|
+
const timeout = this.createTimeoutPromise(call.gadgetName, timeoutMs, abortController);
|
|
4276
|
+
try {
|
|
4277
|
+
rawResult = await Promise.race([
|
|
4278
|
+
Promise.resolve(gadget.execute(validatedParameters, ctx)),
|
|
4279
|
+
timeout.promise
|
|
4280
|
+
]);
|
|
4281
|
+
} finally {
|
|
4282
|
+
timeout.cancel();
|
|
4283
|
+
}
|
|
3656
4284
|
} else {
|
|
3657
4285
|
rawResult = await Promise.resolve(gadget.execute(validatedParameters, ctx));
|
|
3658
4286
|
}
|
|
@@ -3847,10 +4475,11 @@ var init_stream_processor = __esm({
|
|
|
3847
4475
|
logger;
|
|
3848
4476
|
parser;
|
|
3849
4477
|
executor;
|
|
3850
|
-
|
|
3851
|
-
|
|
4478
|
+
// Execution Tree context
|
|
4479
|
+
tree;
|
|
4480
|
+
parentNodeId;
|
|
4481
|
+
baseDepth;
|
|
3852
4482
|
responseText = "";
|
|
3853
|
-
executionHalted = false;
|
|
3854
4483
|
observerFailureCount = 0;
|
|
3855
4484
|
// Dependency tracking for gadget execution DAG
|
|
3856
4485
|
/** Gadgets waiting for their dependencies to complete */
|
|
@@ -3861,18 +4490,28 @@ var init_stream_processor = __esm({
|
|
|
3861
4490
|
failedInvocations = /* @__PURE__ */ new Set();
|
|
3862
4491
|
/** Promises for independent gadgets currently executing (fire-and-forget) */
|
|
3863
4492
|
inFlightExecutions = /* @__PURE__ */ new Map();
|
|
4493
|
+
/** Queue of completed gadget results ready to be yielded (for real-time streaming) */
|
|
4494
|
+
completedResultsQueue = [];
|
|
3864
4495
|
constructor(options) {
|
|
3865
4496
|
this.iteration = options.iteration;
|
|
3866
4497
|
this.registry = options.registry;
|
|
3867
4498
|
this.hooks = options.hooks ?? {};
|
|
3868
4499
|
this.logger = options.logger ?? createLogger({ name: "llmist:stream-processor" });
|
|
3869
|
-
this.
|
|
3870
|
-
this.
|
|
4500
|
+
this.tree = options.tree;
|
|
4501
|
+
this.parentNodeId = options.parentNodeId ?? null;
|
|
4502
|
+
this.baseDepth = options.baseDepth ?? 0;
|
|
3871
4503
|
this.parser = new GadgetCallParser({
|
|
3872
4504
|
startPrefix: options.gadgetStartPrefix,
|
|
3873
4505
|
endPrefix: options.gadgetEndPrefix,
|
|
3874
4506
|
argPrefix: options.gadgetArgPrefix
|
|
3875
4507
|
});
|
|
4508
|
+
const wrappedOnSubagentEvent = options.onSubagentEvent ? (event) => {
|
|
4509
|
+
this.completedResultsQueue.push({
|
|
4510
|
+
type: "subagent_event",
|
|
4511
|
+
subagentEvent: event
|
|
4512
|
+
});
|
|
4513
|
+
options.onSubagentEvent?.(event);
|
|
4514
|
+
} : void 0;
|
|
3876
4515
|
this.executor = new GadgetExecutor(
|
|
3877
4516
|
options.registry,
|
|
3878
4517
|
options.requestHumanInput,
|
|
@@ -3883,7 +4522,11 @@ var init_stream_processor = __esm({
|
|
|
3883
4522
|
options.mediaStore,
|
|
3884
4523
|
options.agentConfig,
|
|
3885
4524
|
options.subagentConfig,
|
|
3886
|
-
|
|
4525
|
+
wrappedOnSubagentEvent,
|
|
4526
|
+
// Tree context for gadget execution
|
|
4527
|
+
options.tree,
|
|
4528
|
+
options.parentNodeId,
|
|
4529
|
+
options.baseDepth
|
|
3887
4530
|
);
|
|
3888
4531
|
}
|
|
3889
4532
|
/**
|
|
@@ -3934,7 +4577,7 @@ var init_stream_processor = __esm({
|
|
|
3934
4577
|
usage,
|
|
3935
4578
|
logger: this.logger
|
|
3936
4579
|
};
|
|
3937
|
-
await this.hooks.observers
|
|
4580
|
+
await this.hooks.observers?.onStreamChunk?.(context);
|
|
3938
4581
|
});
|
|
3939
4582
|
await this.runObserversInParallel(chunkObservers);
|
|
3940
4583
|
}
|
|
@@ -3952,25 +4595,7 @@ var init_stream_processor = __esm({
|
|
|
3952
4595
|
}
|
|
3953
4596
|
}
|
|
3954
4597
|
}
|
|
3955
|
-
|
|
3956
|
-
this.logger.info("Breaking from LLM stream due to gadget error");
|
|
3957
|
-
break;
|
|
3958
|
-
}
|
|
3959
|
-
}
|
|
3960
|
-
if (!this.executionHalted) {
|
|
3961
|
-
for (const event of this.parser.finalize()) {
|
|
3962
|
-
for await (const processedEvent of this.processEventGenerator(event)) {
|
|
3963
|
-
yield processedEvent;
|
|
3964
|
-
if (processedEvent.type === "gadget_result") {
|
|
3965
|
-
didExecuteGadgets = true;
|
|
3966
|
-
if (processedEvent.result.breaksLoop) {
|
|
3967
|
-
shouldBreakLoop = true;
|
|
3968
|
-
}
|
|
3969
|
-
}
|
|
3970
|
-
}
|
|
3971
|
-
}
|
|
3972
|
-
const inFlightResults = await this.collectInFlightResults();
|
|
3973
|
-
for (const evt of inFlightResults) {
|
|
4598
|
+
for (const evt of this.drainCompletedResults()) {
|
|
3974
4599
|
yield evt;
|
|
3975
4600
|
if (evt.type === "gadget_result") {
|
|
3976
4601
|
didExecuteGadgets = true;
|
|
@@ -3979,16 +4604,45 @@ var init_stream_processor = __esm({
|
|
|
3979
4604
|
}
|
|
3980
4605
|
}
|
|
3981
4606
|
}
|
|
3982
|
-
|
|
3983
|
-
|
|
3984
|
-
|
|
4607
|
+
}
|
|
4608
|
+
for (const event of this.parser.finalize()) {
|
|
4609
|
+
for await (const processedEvent of this.processEventGenerator(event)) {
|
|
4610
|
+
yield processedEvent;
|
|
4611
|
+
if (processedEvent.type === "gadget_result") {
|
|
3985
4612
|
didExecuteGadgets = true;
|
|
3986
|
-
if (
|
|
4613
|
+
if (processedEvent.result.breaksLoop) {
|
|
3987
4614
|
shouldBreakLoop = true;
|
|
3988
4615
|
}
|
|
3989
4616
|
}
|
|
3990
4617
|
}
|
|
3991
4618
|
}
|
|
4619
|
+
for await (const evt of this.waitForInFlightExecutions()) {
|
|
4620
|
+
yield evt;
|
|
4621
|
+
if (evt.type === "gadget_result") {
|
|
4622
|
+
didExecuteGadgets = true;
|
|
4623
|
+
if (evt.result.breaksLoop) {
|
|
4624
|
+
shouldBreakLoop = true;
|
|
4625
|
+
}
|
|
4626
|
+
}
|
|
4627
|
+
}
|
|
4628
|
+
for (const evt of this.drainCompletedResults()) {
|
|
4629
|
+
yield evt;
|
|
4630
|
+
if (evt.type === "gadget_result") {
|
|
4631
|
+
didExecuteGadgets = true;
|
|
4632
|
+
if (evt.result.breaksLoop) {
|
|
4633
|
+
shouldBreakLoop = true;
|
|
4634
|
+
}
|
|
4635
|
+
}
|
|
4636
|
+
}
|
|
4637
|
+
for await (const evt of this.processPendingGadgetsGenerator()) {
|
|
4638
|
+
yield evt;
|
|
4639
|
+
if (evt.type === "gadget_result") {
|
|
4640
|
+
didExecuteGadgets = true;
|
|
4641
|
+
if (evt.result.breaksLoop) {
|
|
4642
|
+
shouldBreakLoop = true;
|
|
4643
|
+
}
|
|
4644
|
+
}
|
|
4645
|
+
}
|
|
3992
4646
|
let finalMessage = this.responseText;
|
|
3993
4647
|
if (this.hooks.interceptors?.interceptAssistantMessage) {
|
|
3994
4648
|
const context = {
|
|
@@ -4009,21 +4663,8 @@ var init_stream_processor = __esm({
|
|
|
4009
4663
|
};
|
|
4010
4664
|
yield completionEvent;
|
|
4011
4665
|
}
|
|
4012
|
-
/**
|
|
4013
|
-
* Process a single parsed event (text or gadget call).
|
|
4014
|
-
* @deprecated Use processEventGenerator for real-time streaming
|
|
4015
|
-
*/
|
|
4016
|
-
async processEvent(event) {
|
|
4017
|
-
if (event.type === "text") {
|
|
4018
|
-
return this.processTextEvent(event);
|
|
4019
|
-
} else if (event.type === "gadget_call") {
|
|
4020
|
-
return this.processGadgetCall(event.call);
|
|
4021
|
-
}
|
|
4022
|
-
return [event];
|
|
4023
|
-
}
|
|
4024
4666
|
/**
|
|
4025
4667
|
* Process a single parsed event, yielding events in real-time.
|
|
4026
|
-
* Generator version of processEvent for streaming support.
|
|
4027
4668
|
*/
|
|
4028
4669
|
async *processEventGenerator(event) {
|
|
4029
4670
|
if (event.type === "text") {
|
|
@@ -4065,12 +4706,6 @@ var init_stream_processor = __esm({
|
|
|
4065
4706
|
* After each execution, pending gadgets are checked to see if they can now run.
|
|
4066
4707
|
*/
|
|
4067
4708
|
async processGadgetCall(call) {
|
|
4068
|
-
if (this.executionHalted) {
|
|
4069
|
-
this.logger.debug("Skipping gadget execution due to previous error", {
|
|
4070
|
-
gadgetName: call.gadgetName
|
|
4071
|
-
});
|
|
4072
|
-
return [];
|
|
4073
|
-
}
|
|
4074
4709
|
const events = [];
|
|
4075
4710
|
events.push({ type: "gadget_call", call });
|
|
4076
4711
|
if (call.dependencies.length > 0) {
|
|
@@ -4121,13 +4756,16 @@ var init_stream_processor = __esm({
|
|
|
4121
4756
|
* when parsed (before execution), enabling real-time UI feedback.
|
|
4122
4757
|
*/
|
|
4123
4758
|
async *processGadgetCallGenerator(call) {
|
|
4124
|
-
|
|
4125
|
-
|
|
4126
|
-
|
|
4759
|
+
yield { type: "gadget_call", call };
|
|
4760
|
+
if (this.tree) {
|
|
4761
|
+
this.tree.addGadget({
|
|
4762
|
+
invocationId: call.invocationId,
|
|
4763
|
+
name: call.gadgetName,
|
|
4764
|
+
parameters: call.parameters ?? {},
|
|
4765
|
+
dependencies: call.dependencies,
|
|
4766
|
+
parentId: this.parentNodeId
|
|
4127
4767
|
});
|
|
4128
|
-
return;
|
|
4129
4768
|
}
|
|
4130
|
-
yield { type: "gadget_call", call };
|
|
4131
4769
|
if (call.dependencies.length > 0) {
|
|
4132
4770
|
if (call.dependencies.includes(call.invocationId)) {
|
|
4133
4771
|
this.logger.warn("Gadget has self-referential dependency (depends on itself)", {
|
|
@@ -4172,17 +4810,8 @@ var init_stream_processor = __esm({
|
|
|
4172
4810
|
}
|
|
4173
4811
|
return;
|
|
4174
4812
|
}
|
|
4175
|
-
|
|
4176
|
-
|
|
4177
|
-
yield evt;
|
|
4178
|
-
}
|
|
4179
|
-
for await (const evt of this.processPendingGadgetsGenerator()) {
|
|
4180
|
-
yield evt;
|
|
4181
|
-
}
|
|
4182
|
-
} else {
|
|
4183
|
-
const executionPromise = this.executeGadgetAndCollect(call);
|
|
4184
|
-
this.inFlightExecutions.set(call.invocationId, executionPromise);
|
|
4185
|
-
}
|
|
4813
|
+
const executionPromise = this.executeGadgetAndCollect(call);
|
|
4814
|
+
this.inFlightExecutions.set(call.invocationId, executionPromise);
|
|
4186
4815
|
}
|
|
4187
4816
|
/**
|
|
4188
4817
|
* Execute a gadget through the full hook lifecycle.
|
|
@@ -4197,15 +4826,6 @@ var init_stream_processor = __esm({
|
|
|
4197
4826
|
error: call.parseError,
|
|
4198
4827
|
rawParameters: call.parametersRaw
|
|
4199
4828
|
});
|
|
4200
|
-
const shouldContinue = await this.checkCanRecoverFromError(
|
|
4201
|
-
call.parseError,
|
|
4202
|
-
call.gadgetName,
|
|
4203
|
-
"parse",
|
|
4204
|
-
call.parameters
|
|
4205
|
-
);
|
|
4206
|
-
if (!shouldContinue) {
|
|
4207
|
-
this.executionHalted = true;
|
|
4208
|
-
}
|
|
4209
4829
|
}
|
|
4210
4830
|
let parameters = call.parameters ?? {};
|
|
4211
4831
|
if (this.hooks.interceptors?.interceptGadgetParameters) {
|
|
@@ -4248,7 +4868,7 @@ var init_stream_processor = __esm({
|
|
|
4248
4868
|
parameters,
|
|
4249
4869
|
logger: this.logger
|
|
4250
4870
|
};
|
|
4251
|
-
await this.hooks.observers
|
|
4871
|
+
await this.hooks.observers?.onGadgetExecutionStart?.(context);
|
|
4252
4872
|
});
|
|
4253
4873
|
}
|
|
4254
4874
|
await this.runObserversInParallel(startObservers);
|
|
@@ -4317,7 +4937,7 @@ var init_stream_processor = __esm({
|
|
|
4317
4937
|
cost: result.cost,
|
|
4318
4938
|
logger: this.logger
|
|
4319
4939
|
};
|
|
4320
|
-
await this.hooks.observers
|
|
4940
|
+
await this.hooks.observers?.onGadgetExecutionComplete?.(context);
|
|
4321
4941
|
});
|
|
4322
4942
|
}
|
|
4323
4943
|
await this.runObserversInParallel(completeObservers);
|
|
@@ -4326,18 +4946,6 @@ var init_stream_processor = __esm({
|
|
|
4326
4946
|
this.failedInvocations.add(result.invocationId);
|
|
4327
4947
|
}
|
|
4328
4948
|
events.push({ type: "gadget_result", result });
|
|
4329
|
-
if (result.error) {
|
|
4330
|
-
const errorType = this.determineErrorType(call, result);
|
|
4331
|
-
const shouldContinue = await this.checkCanRecoverFromError(
|
|
4332
|
-
result.error,
|
|
4333
|
-
result.gadgetName,
|
|
4334
|
-
errorType,
|
|
4335
|
-
result.parameters
|
|
4336
|
-
);
|
|
4337
|
-
if (!shouldContinue) {
|
|
4338
|
-
this.executionHalted = true;
|
|
4339
|
-
}
|
|
4340
|
-
}
|
|
4341
4949
|
return events;
|
|
4342
4950
|
}
|
|
4343
4951
|
/**
|
|
@@ -4351,15 +4959,6 @@ var init_stream_processor = __esm({
|
|
|
4351
4959
|
error: call.parseError,
|
|
4352
4960
|
rawParameters: call.parametersRaw
|
|
4353
4961
|
});
|
|
4354
|
-
const shouldContinue = await this.checkCanRecoverFromError(
|
|
4355
|
-
call.parseError,
|
|
4356
|
-
call.gadgetName,
|
|
4357
|
-
"parse",
|
|
4358
|
-
call.parameters
|
|
4359
|
-
);
|
|
4360
|
-
if (!shouldContinue) {
|
|
4361
|
-
this.executionHalted = true;
|
|
4362
|
-
}
|
|
4363
4962
|
}
|
|
4364
4963
|
let parameters = call.parameters ?? {};
|
|
4365
4964
|
if (this.hooks.interceptors?.interceptGadgetParameters) {
|
|
@@ -4402,10 +5001,16 @@ var init_stream_processor = __esm({
|
|
|
4402
5001
|
parameters,
|
|
4403
5002
|
logger: this.logger
|
|
4404
5003
|
};
|
|
4405
|
-
await this.hooks.observers
|
|
5004
|
+
await this.hooks.observers?.onGadgetExecutionStart?.(context);
|
|
4406
5005
|
});
|
|
4407
5006
|
}
|
|
4408
5007
|
await this.runObserversInParallel(startObservers);
|
|
5008
|
+
if (this.tree) {
|
|
5009
|
+
const gadgetNode = this.tree.getNodeByInvocationId(call.invocationId);
|
|
5010
|
+
if (gadgetNode) {
|
|
5011
|
+
this.tree.startGadget(gadgetNode.id);
|
|
5012
|
+
}
|
|
5013
|
+
}
|
|
4409
5014
|
let result;
|
|
4410
5015
|
if (shouldSkip) {
|
|
4411
5016
|
result = {
|
|
@@ -4471,57 +5076,84 @@ var init_stream_processor = __esm({
|
|
|
4471
5076
|
cost: result.cost,
|
|
4472
5077
|
logger: this.logger
|
|
4473
5078
|
};
|
|
4474
|
-
await this.hooks.observers
|
|
5079
|
+
await this.hooks.observers?.onGadgetExecutionComplete?.(context);
|
|
4475
5080
|
});
|
|
4476
5081
|
}
|
|
4477
5082
|
await this.runObserversInParallel(completeObservers);
|
|
5083
|
+
if (this.tree) {
|
|
5084
|
+
const gadgetNode = this.tree.getNodeByInvocationId(result.invocationId);
|
|
5085
|
+
if (gadgetNode) {
|
|
5086
|
+
if (result.error) {
|
|
5087
|
+
this.tree.completeGadget(gadgetNode.id, {
|
|
5088
|
+
error: result.error,
|
|
5089
|
+
executionTimeMs: result.executionTimeMs,
|
|
5090
|
+
cost: result.cost
|
|
5091
|
+
});
|
|
5092
|
+
} else {
|
|
5093
|
+
this.tree.completeGadget(gadgetNode.id, {
|
|
5094
|
+
result: result.result,
|
|
5095
|
+
executionTimeMs: result.executionTimeMs,
|
|
5096
|
+
cost: result.cost,
|
|
5097
|
+
media: result.media
|
|
5098
|
+
});
|
|
5099
|
+
}
|
|
5100
|
+
}
|
|
5101
|
+
}
|
|
4478
5102
|
this.completedResults.set(result.invocationId, result);
|
|
4479
5103
|
if (result.error) {
|
|
4480
5104
|
this.failedInvocations.add(result.invocationId);
|
|
4481
5105
|
}
|
|
4482
5106
|
yield { type: "gadget_result", result };
|
|
4483
|
-
if (result.error) {
|
|
4484
|
-
const errorType = this.determineErrorType(call, result);
|
|
4485
|
-
const shouldContinue = await this.checkCanRecoverFromError(
|
|
4486
|
-
result.error,
|
|
4487
|
-
result.gadgetName,
|
|
4488
|
-
errorType,
|
|
4489
|
-
result.parameters
|
|
4490
|
-
);
|
|
4491
|
-
if (!shouldContinue) {
|
|
4492
|
-
this.executionHalted = true;
|
|
4493
|
-
}
|
|
4494
|
-
}
|
|
4495
5107
|
}
|
|
4496
5108
|
/**
|
|
4497
|
-
* Execute a gadget and
|
|
5109
|
+
* Execute a gadget and push events to the completed results queue (non-blocking).
|
|
4498
5110
|
* Used for fire-and-forget parallel execution of independent gadgets.
|
|
5111
|
+
* Results are pushed to completedResultsQueue for real-time streaming to the caller.
|
|
4499
5112
|
*/
|
|
4500
5113
|
async executeGadgetAndCollect(call) {
|
|
4501
|
-
const events = [];
|
|
4502
5114
|
for await (const evt of this.executeGadgetGenerator(call)) {
|
|
4503
|
-
|
|
5115
|
+
this.completedResultsQueue.push(evt);
|
|
4504
5116
|
}
|
|
4505
|
-
return events;
|
|
4506
5117
|
}
|
|
4507
5118
|
/**
|
|
4508
|
-
*
|
|
4509
|
-
*
|
|
4510
|
-
*
|
|
4511
|
-
* @returns Array of all events from completed gadgets
|
|
5119
|
+
* Drain all completed results from the queue.
|
|
5120
|
+
* Used to yield results as they complete during stream processing.
|
|
5121
|
+
* @returns Generator that yields all events currently in the queue
|
|
4512
5122
|
*/
|
|
4513
|
-
|
|
5123
|
+
*drainCompletedResults() {
|
|
5124
|
+
while (this.completedResultsQueue.length > 0) {
|
|
5125
|
+
yield this.completedResultsQueue.shift();
|
|
5126
|
+
}
|
|
5127
|
+
}
|
|
5128
|
+
/**
|
|
5129
|
+
* Wait for all in-flight gadget executions to complete, yielding events in real-time.
|
|
5130
|
+
* Called at stream end to ensure all parallel executions finish.
|
|
5131
|
+
* Results and subagent events are pushed to completedResultsQueue during execution.
|
|
5132
|
+
* This generator yields queued events while polling, enabling real-time display
|
|
5133
|
+
* of subagent activity (LLM calls, nested gadgets) during long-running gadgets.
|
|
5134
|
+
* Clears the inFlightExecutions map after all gadgets complete.
|
|
5135
|
+
*/
|
|
5136
|
+
async *waitForInFlightExecutions() {
|
|
4514
5137
|
if (this.inFlightExecutions.size === 0) {
|
|
4515
|
-
return
|
|
5138
|
+
return;
|
|
4516
5139
|
}
|
|
4517
|
-
this.logger.debug("
|
|
5140
|
+
this.logger.debug("Waiting for in-flight gadget executions", {
|
|
4518
5141
|
count: this.inFlightExecutions.size,
|
|
4519
5142
|
invocationIds: Array.from(this.inFlightExecutions.keys())
|
|
4520
5143
|
});
|
|
4521
|
-
const
|
|
4522
|
-
const
|
|
5144
|
+
const allDone = Promise.all(this.inFlightExecutions.values()).then(() => "done");
|
|
5145
|
+
const POLL_INTERVAL_MS = 100;
|
|
5146
|
+
while (true) {
|
|
5147
|
+
const result = await Promise.race([
|
|
5148
|
+
allDone,
|
|
5149
|
+
new Promise((resolve) => setTimeout(() => resolve("poll"), POLL_INTERVAL_MS))
|
|
5150
|
+
]);
|
|
5151
|
+
yield* this.drainCompletedResults();
|
|
5152
|
+
if (result === "done") {
|
|
5153
|
+
break;
|
|
5154
|
+
}
|
|
5155
|
+
}
|
|
4523
5156
|
this.inFlightExecutions.clear();
|
|
4524
|
-
return results.flat();
|
|
4525
5157
|
}
|
|
4526
5158
|
/**
|
|
4527
5159
|
* Handle a gadget that cannot execute because a dependency failed.
|
|
@@ -4546,6 +5178,12 @@ var init_stream_processor = __esm({
|
|
|
4546
5178
|
}
|
|
4547
5179
|
if (action.action === "skip") {
|
|
4548
5180
|
this.failedInvocations.add(call.invocationId);
|
|
5181
|
+
if (this.tree) {
|
|
5182
|
+
const gadgetNode = this.tree.getNodeByInvocationId(call.invocationId);
|
|
5183
|
+
if (gadgetNode) {
|
|
5184
|
+
this.tree.skipGadget(gadgetNode.id, failedDep, depError, "dependency_failed");
|
|
5185
|
+
}
|
|
5186
|
+
}
|
|
4549
5187
|
const skipEvent = {
|
|
4550
5188
|
type: "gadget_skipped",
|
|
4551
5189
|
gadgetName: call.gadgetName,
|
|
@@ -4565,7 +5203,7 @@ var init_stream_processor = __esm({
|
|
|
4565
5203
|
failedDependencyError: depError,
|
|
4566
5204
|
logger: this.logger
|
|
4567
5205
|
};
|
|
4568
|
-
await this.safeObserve(() => this.hooks.observers
|
|
5206
|
+
await this.safeObserve(() => this.hooks.observers?.onGadgetSkipped?.(observeContext));
|
|
4569
5207
|
}
|
|
4570
5208
|
this.logger.info("Gadget skipped due to failed dependency", {
|
|
4571
5209
|
gadgetName: call.gadgetName,
|
|
@@ -4797,48 +5435,6 @@ var init_stream_processor = __esm({
|
|
|
4797
5435
|
observers.map((observer) => this.safeObserve(observer))
|
|
4798
5436
|
);
|
|
4799
5437
|
}
|
|
4800
|
-
/**
|
|
4801
|
-
* Check if execution can recover from an error.
|
|
4802
|
-
*
|
|
4803
|
-
* Returns true if we should continue processing subsequent gadgets, false if we should stop.
|
|
4804
|
-
*
|
|
4805
|
-
* Logic:
|
|
4806
|
-
* - If custom canRecoverFromGadgetError is provided, use it
|
|
4807
|
-
* - Otherwise, use stopOnGadgetError config:
|
|
4808
|
-
* - stopOnGadgetError=true → return false (stop execution)
|
|
4809
|
-
* - stopOnGadgetError=false → return true (continue execution)
|
|
4810
|
-
*/
|
|
4811
|
-
async checkCanRecoverFromError(error, gadgetName, errorType, parameters) {
|
|
4812
|
-
if (this.canRecoverFromGadgetError) {
|
|
4813
|
-
return await this.canRecoverFromGadgetError({
|
|
4814
|
-
error,
|
|
4815
|
-
gadgetName,
|
|
4816
|
-
errorType,
|
|
4817
|
-
parameters
|
|
4818
|
-
});
|
|
4819
|
-
}
|
|
4820
|
-
const shouldContinue = !this.stopOnGadgetError;
|
|
4821
|
-
this.logger.debug("Checking if should continue after error", {
|
|
4822
|
-
error,
|
|
4823
|
-
gadgetName,
|
|
4824
|
-
errorType,
|
|
4825
|
-
stopOnGadgetError: this.stopOnGadgetError,
|
|
4826
|
-
shouldContinue
|
|
4827
|
-
});
|
|
4828
|
-
return shouldContinue;
|
|
4829
|
-
}
|
|
4830
|
-
/**
|
|
4831
|
-
* Determine the type of error from a gadget execution.
|
|
4832
|
-
*/
|
|
4833
|
-
determineErrorType(call, result) {
|
|
4834
|
-
if (call.parseError) {
|
|
4835
|
-
return "parse";
|
|
4836
|
-
}
|
|
4837
|
-
if (result.error?.includes("Invalid parameters:")) {
|
|
4838
|
-
return "validation";
|
|
4839
|
-
}
|
|
4840
|
-
return "execution";
|
|
4841
|
-
}
|
|
4842
5438
|
};
|
|
4843
5439
|
}
|
|
4844
5440
|
});
|
|
@@ -4849,6 +5445,7 @@ var init_agent = __esm({
|
|
|
4849
5445
|
"src/agent/agent.ts"() {
|
|
4850
5446
|
"use strict";
|
|
4851
5447
|
init_constants();
|
|
5448
|
+
init_execution_tree();
|
|
4852
5449
|
init_messages();
|
|
4853
5450
|
init_model_shortcuts();
|
|
4854
5451
|
init_media_store();
|
|
@@ -4876,8 +5473,6 @@ var init_agent = __esm({
|
|
|
4876
5473
|
requestHumanInput;
|
|
4877
5474
|
textOnlyHandler;
|
|
4878
5475
|
textWithGadgetsHandler;
|
|
4879
|
-
stopOnGadgetError;
|
|
4880
|
-
canRecoverFromGadgetError;
|
|
4881
5476
|
defaultGadgetTimeoutMs;
|
|
4882
5477
|
defaultMaxTokens;
|
|
4883
5478
|
hasUserPrompt;
|
|
@@ -4900,6 +5495,12 @@ var init_agent = __esm({
|
|
|
4900
5495
|
pendingSubagentEvents = [];
|
|
4901
5496
|
// Combined callback that queues events AND calls user callback
|
|
4902
5497
|
onSubagentEvent;
|
|
5498
|
+
// Counter for generating synthetic invocation IDs for wrapped text content
|
|
5499
|
+
syntheticInvocationCounter = 0;
|
|
5500
|
+
// Execution Tree - first-class model for nested subagent support
|
|
5501
|
+
tree;
|
|
5502
|
+
parentNodeId;
|
|
5503
|
+
baseDepth;
|
|
4903
5504
|
/**
|
|
4904
5505
|
* Creates a new Agent instance.
|
|
4905
5506
|
* @internal This constructor is private. Use LLMist.createAgent() or AgentBuilder instead.
|
|
@@ -4922,8 +5523,6 @@ var init_agent = __esm({
|
|
|
4922
5523
|
this.requestHumanInput = options.requestHumanInput;
|
|
4923
5524
|
this.textOnlyHandler = options.textOnlyHandler ?? "terminate";
|
|
4924
5525
|
this.textWithGadgetsHandler = options.textWithGadgetsHandler;
|
|
4925
|
-
this.stopOnGadgetError = options.stopOnGadgetError ?? true;
|
|
4926
|
-
this.canRecoverFromGadgetError = options.canRecoverFromGadgetError;
|
|
4927
5526
|
this.defaultGadgetTimeoutMs = options.defaultGadgetTimeoutMs;
|
|
4928
5527
|
this.defaultMaxTokens = this.resolveMaxTokensFromCatalog(options.model);
|
|
4929
5528
|
this.outputLimitEnabled = options.gadgetOutputLimit ?? DEFAULT_GADGET_OUTPUT_LIMIT;
|
|
@@ -4977,6 +5576,9 @@ var init_agent = __esm({
|
|
|
4977
5576
|
temperature: this.temperature
|
|
4978
5577
|
};
|
|
4979
5578
|
this.subagentConfig = options.subagentConfig;
|
|
5579
|
+
this.tree = options.parentTree ?? new ExecutionTree();
|
|
5580
|
+
this.parentNodeId = options.parentNodeId ?? null;
|
|
5581
|
+
this.baseDepth = options.baseDepth ?? 0;
|
|
4980
5582
|
this.userSubagentEventCallback = options.onSubagentEvent;
|
|
4981
5583
|
this.onSubagentEvent = (event) => {
|
|
4982
5584
|
this.pendingSubagentEvents.push(event);
|
|
@@ -5041,7 +5643,9 @@ var init_agent = __esm({
|
|
|
5041
5643
|
*flushPendingSubagentEvents() {
|
|
5042
5644
|
while (this.pendingSubagentEvents.length > 0) {
|
|
5043
5645
|
const event = this.pendingSubagentEvents.shift();
|
|
5044
|
-
|
|
5646
|
+
if (event) {
|
|
5647
|
+
yield { type: "subagent_event", subagentEvent: event };
|
|
5648
|
+
}
|
|
5045
5649
|
}
|
|
5046
5650
|
}
|
|
5047
5651
|
/**
|
|
@@ -5095,6 +5699,48 @@ var init_agent = __esm({
|
|
|
5095
5699
|
getMediaStore() {
|
|
5096
5700
|
return this.mediaStore;
|
|
5097
5701
|
}
|
|
5702
|
+
/**
|
|
5703
|
+
* Get the execution tree for this agent.
|
|
5704
|
+
*
|
|
5705
|
+
* The execution tree provides a first-class model of all LLM calls and gadget executions,
|
|
5706
|
+
* including nested subagent activity. Use this to:
|
|
5707
|
+
* - Query execution state: `tree.getNode(id)`
|
|
5708
|
+
* - Get total cost: `tree.getTotalCost()`
|
|
5709
|
+
* - Get subtree cost/media/tokens: `tree.getSubtreeCost(nodeId)`
|
|
5710
|
+
* - Subscribe to events: `tree.on("llm_call_complete", handler)`
|
|
5711
|
+
* - Stream all events: `for await (const event of tree.events())`
|
|
5712
|
+
*
|
|
5713
|
+
* For subagents (created with `withParentContext`), the tree is shared with the parent,
|
|
5714
|
+
* enabling unified tracking and real-time visibility across all nesting levels.
|
|
5715
|
+
*
|
|
5716
|
+
* @returns The ExecutionTree instance
|
|
5717
|
+
*
|
|
5718
|
+
* @example
|
|
5719
|
+
* ```typescript
|
|
5720
|
+
* const agent = LLMist.createAgent()
|
|
5721
|
+
* .withModel("sonnet")
|
|
5722
|
+
* .withGadgets(BrowseWeb)
|
|
5723
|
+
* .ask("Research topic X");
|
|
5724
|
+
*
|
|
5725
|
+
* for await (const event of agent.run()) {
|
|
5726
|
+
* // Process events...
|
|
5727
|
+
* }
|
|
5728
|
+
*
|
|
5729
|
+
* // After execution, query the tree
|
|
5730
|
+
* const tree = agent.getTree();
|
|
5731
|
+
* console.log(`Total cost: $${tree.getTotalCost().toFixed(4)}`);
|
|
5732
|
+
*
|
|
5733
|
+
* // Inspect all LLM calls
|
|
5734
|
+
* for (const node of tree.getAllNodes()) {
|
|
5735
|
+
* if (node.type === "llm_call") {
|
|
5736
|
+
* console.log(`LLM #${node.iteration}: ${node.model}`);
|
|
5737
|
+
* }
|
|
5738
|
+
* }
|
|
5739
|
+
* ```
|
|
5740
|
+
*/
|
|
5741
|
+
getTree() {
|
|
5742
|
+
return this.tree;
|
|
5743
|
+
}
|
|
5098
5744
|
/**
|
|
5099
5745
|
* Manually trigger context compaction.
|
|
5100
5746
|
*
|
|
@@ -5196,6 +5842,7 @@ var init_agent = __esm({
|
|
|
5196
5842
|
await this.hooks.observers.onCompaction({
|
|
5197
5843
|
iteration: currentIteration,
|
|
5198
5844
|
event: compactionEvent,
|
|
5845
|
+
// biome-ignore lint/style/noNonNullAssertion: compactionManager exists if compactionEvent is truthy
|
|
5199
5846
|
stats: this.compactionManager.getStats(),
|
|
5200
5847
|
logger: this.logger
|
|
5201
5848
|
});
|
|
@@ -5257,6 +5904,13 @@ var init_agent = __esm({
|
|
|
5257
5904
|
messageCount: llmOptions.messages.length,
|
|
5258
5905
|
messages: llmOptions.messages
|
|
5259
5906
|
});
|
|
5907
|
+
const llmNode = this.tree.addLLMCall({
|
|
5908
|
+
iteration: currentIteration,
|
|
5909
|
+
model: llmOptions.model,
|
|
5910
|
+
parentId: this.parentNodeId,
|
|
5911
|
+
request: llmOptions.messages
|
|
5912
|
+
});
|
|
5913
|
+
const currentLLMNodeId = llmNode.id;
|
|
5260
5914
|
const stream2 = this.client.stream(llmOptions);
|
|
5261
5915
|
const processor = new StreamProcessor({
|
|
5262
5916
|
iteration: currentIteration,
|
|
@@ -5267,14 +5921,17 @@ var init_agent = __esm({
|
|
|
5267
5921
|
hooks: this.hooks,
|
|
5268
5922
|
logger: this.logger.getSubLogger({ name: "stream-processor" }),
|
|
5269
5923
|
requestHumanInput: this.requestHumanInput,
|
|
5270
|
-
stopOnGadgetError: this.stopOnGadgetError,
|
|
5271
|
-
canRecoverFromGadgetError: this.canRecoverFromGadgetError,
|
|
5272
5924
|
defaultGadgetTimeoutMs: this.defaultGadgetTimeoutMs,
|
|
5273
5925
|
client: this.client,
|
|
5274
5926
|
mediaStore: this.mediaStore,
|
|
5275
5927
|
agentConfig: this.agentContextConfig,
|
|
5276
5928
|
subagentConfig: this.subagentConfig,
|
|
5277
|
-
onSubagentEvent: this.onSubagentEvent
|
|
5929
|
+
onSubagentEvent: this.onSubagentEvent,
|
|
5930
|
+
// Tree context for execution tracking
|
|
5931
|
+
tree: this.tree,
|
|
5932
|
+
parentNodeId: currentLLMNodeId,
|
|
5933
|
+
// Gadgets are children of this LLM call
|
|
5934
|
+
baseDepth: this.baseDepth
|
|
5278
5935
|
});
|
|
5279
5936
|
let streamMetadata = null;
|
|
5280
5937
|
let gadgetCallCount = 0;
|
|
@@ -5320,6 +5977,11 @@ var init_agent = __esm({
|
|
|
5320
5977
|
await this.hooks.observers.onLLMCallComplete(context);
|
|
5321
5978
|
}
|
|
5322
5979
|
});
|
|
5980
|
+
this.tree.completeLLMCall(currentLLMNodeId, {
|
|
5981
|
+
response: result.rawResponse,
|
|
5982
|
+
usage: result.usage,
|
|
5983
|
+
finishReason: result.finishReason
|
|
5984
|
+
});
|
|
5323
5985
|
let finalMessage = result.finalMessage;
|
|
5324
5986
|
if (this.hooks.controllers?.afterLLMCall) {
|
|
5325
5987
|
const context = {
|
|
@@ -5354,10 +6016,12 @@ var init_agent = __esm({
|
|
|
5354
6016
|
const textContent = textOutputs.join("");
|
|
5355
6017
|
if (textContent.trim()) {
|
|
5356
6018
|
const { gadgetName, parameterMapping, resultMapping } = this.textWithGadgetsHandler;
|
|
6019
|
+
const syntheticId = `gc_text_${++this.syntheticInvocationCounter}`;
|
|
5357
6020
|
this.conversation.addGadgetCallResult(
|
|
5358
6021
|
gadgetName,
|
|
5359
6022
|
parameterMapping(textContent),
|
|
5360
|
-
resultMapping ? resultMapping(textContent) : textContent
|
|
6023
|
+
resultMapping ? resultMapping(textContent) : textContent,
|
|
6024
|
+
syntheticId
|
|
5361
6025
|
);
|
|
5362
6026
|
}
|
|
5363
6027
|
}
|
|
@@ -5368,6 +6032,7 @@ var init_agent = __esm({
|
|
|
5368
6032
|
gadgetResult.gadgetName,
|
|
5369
6033
|
gadgetResult.parameters,
|
|
5370
6034
|
gadgetResult.error ?? gadgetResult.result ?? "",
|
|
6035
|
+
gadgetResult.invocationId,
|
|
5371
6036
|
gadgetResult.media,
|
|
5372
6037
|
gadgetResult.mediaIds
|
|
5373
6038
|
);
|
|
@@ -5375,10 +6040,12 @@ var init_agent = __esm({
|
|
|
5375
6040
|
}
|
|
5376
6041
|
} else {
|
|
5377
6042
|
if (finalMessage.trim()) {
|
|
6043
|
+
const syntheticId = `gc_tell_${++this.syntheticInvocationCounter}`;
|
|
5378
6044
|
this.conversation.addGadgetCallResult(
|
|
5379
6045
|
"TellUser",
|
|
5380
6046
|
{ message: finalMessage, done: false, type: "info" },
|
|
5381
|
-
`\u2139\uFE0F ${finalMessage}
|
|
6047
|
+
`\u2139\uFE0F ${finalMessage}`,
|
|
6048
|
+
syntheticId
|
|
5382
6049
|
);
|
|
5383
6050
|
}
|
|
5384
6051
|
const shouldBreak = await this.handleTextOnlyResponse(finalMessage);
|
|
@@ -5589,8 +6256,6 @@ var init_builder = __esm({
|
|
|
5589
6256
|
gadgetArgPrefix;
|
|
5590
6257
|
textOnlyHandler;
|
|
5591
6258
|
textWithGadgetsHandler;
|
|
5592
|
-
stopOnGadgetError;
|
|
5593
|
-
canRecoverFromGadgetError;
|
|
5594
6259
|
defaultGadgetTimeoutMs;
|
|
5595
6260
|
gadgetOutputLimit;
|
|
5596
6261
|
gadgetOutputLimitPercent;
|
|
@@ -5599,6 +6264,8 @@ var init_builder = __esm({
|
|
|
5599
6264
|
trailingMessage;
|
|
5600
6265
|
subagentConfig;
|
|
5601
6266
|
subagentEventCallback;
|
|
6267
|
+
// Tree context for subagent support - enables shared tree model
|
|
6268
|
+
// When a gadget calls withParentContext(ctx), it shares the parent's tree
|
|
5602
6269
|
parentContext;
|
|
5603
6270
|
constructor(client) {
|
|
5604
6271
|
this.client = client;
|
|
@@ -5881,62 +6548,6 @@ var init_builder = __esm({
|
|
|
5881
6548
|
this.textWithGadgetsHandler = handler;
|
|
5882
6549
|
return this;
|
|
5883
6550
|
}
|
|
5884
|
-
/**
|
|
5885
|
-
* Set whether to stop gadget execution on first error.
|
|
5886
|
-
*
|
|
5887
|
-
* When true (default), if a gadget fails:
|
|
5888
|
-
* - Subsequent gadgets in the same response are skipped
|
|
5889
|
-
* - LLM stream is cancelled to save costs
|
|
5890
|
-
* - Agent loop continues with error in context
|
|
5891
|
-
*
|
|
5892
|
-
* When false:
|
|
5893
|
-
* - All gadgets in the response still execute
|
|
5894
|
-
* - LLM stream continues to completion
|
|
5895
|
-
*
|
|
5896
|
-
* @param stop - Whether to stop on gadget error
|
|
5897
|
-
* @returns This builder for chaining
|
|
5898
|
-
*
|
|
5899
|
-
* @example
|
|
5900
|
-
* ```typescript
|
|
5901
|
-
* .withStopOnGadgetError(false)
|
|
5902
|
-
* ```
|
|
5903
|
-
*/
|
|
5904
|
-
withStopOnGadgetError(stop) {
|
|
5905
|
-
this.stopOnGadgetError = stop;
|
|
5906
|
-
return this;
|
|
5907
|
-
}
|
|
5908
|
-
/**
|
|
5909
|
-
* Set custom error handling logic.
|
|
5910
|
-
*
|
|
5911
|
-
* Provides fine-grained control over whether to continue after different types of errors.
|
|
5912
|
-
* Overrides `stopOnGadgetError` when provided.
|
|
5913
|
-
*
|
|
5914
|
-
* **Note:** This builder method configures the underlying `canRecoverFromGadgetError` option
|
|
5915
|
-
* in `AgentOptions`. The method is named `withErrorHandler` for better developer experience,
|
|
5916
|
-
* but maps to the `canRecoverFromGadgetError` property internally.
|
|
5917
|
-
*
|
|
5918
|
-
* @param handler - Function that decides whether to continue after an error.
|
|
5919
|
-
* Return `true` to continue execution, `false` to stop.
|
|
5920
|
-
* @returns This builder for chaining
|
|
5921
|
-
*
|
|
5922
|
-
* @example
|
|
5923
|
-
* ```typescript
|
|
5924
|
-
* .withErrorHandler((context) => {
|
|
5925
|
-
* // Stop on parse errors, continue on validation/execution errors
|
|
5926
|
-
* if (context.errorType === "parse") {
|
|
5927
|
-
* return false;
|
|
5928
|
-
* }
|
|
5929
|
-
* if (context.error.includes("CRITICAL")) {
|
|
5930
|
-
* return false;
|
|
5931
|
-
* }
|
|
5932
|
-
* return true;
|
|
5933
|
-
* })
|
|
5934
|
-
* ```
|
|
5935
|
-
*/
|
|
5936
|
-
withErrorHandler(handler) {
|
|
5937
|
-
this.canRecoverFromGadgetError = handler;
|
|
5938
|
-
return this;
|
|
5939
|
-
}
|
|
5940
6551
|
/**
|
|
5941
6552
|
* Set default timeout for gadget execution.
|
|
5942
6553
|
*
|
|
@@ -6131,6 +6742,15 @@ var init_builder = __esm({
|
|
|
6131
6742
|
* The method extracts `invocationId` and `onSubagentEvent` from the execution
|
|
6132
6743
|
* context and sets up automatic forwarding via hooks and event wrapping.
|
|
6133
6744
|
*
|
|
6745
|
+
* **NEW: Shared Tree Model** - When the parent provides an ExecutionTree via context,
|
|
6746
|
+
* the subagent shares that tree instead of creating its own. This enables:
|
|
6747
|
+
* - Unified cost tracking across all nesting levels
|
|
6748
|
+
* - Automatic media aggregation via `tree.getSubtreeMedia(nodeId)`
|
|
6749
|
+
* - Real-time visibility of nested execution in the parent
|
|
6750
|
+
*
|
|
6751
|
+
* **Signal Forwarding** - When parent context includes a signal, it's automatically
|
|
6752
|
+
* forwarded to the subagent for proper cancellation propagation.
|
|
6753
|
+
*
|
|
6134
6754
|
* @param ctx - ExecutionContext passed to the gadget's execute() method
|
|
6135
6755
|
* @param depth - Nesting depth (default: 1 for direct child)
|
|
6136
6756
|
* @returns This builder for chaining
|
|
@@ -6151,17 +6771,25 @@ var init_builder = __esm({
|
|
|
6151
6771
|
* result = event.content;
|
|
6152
6772
|
* }
|
|
6153
6773
|
* }
|
|
6774
|
+
*
|
|
6775
|
+
* // After subagent completes, costs are automatically aggregated
|
|
6776
|
+
* // No manual tracking needed - use tree methods:
|
|
6777
|
+
* const totalCost = ctx.tree?.getSubtreeCost(ctx.nodeId!);
|
|
6778
|
+
* const allMedia = ctx.tree?.getSubtreeMedia(ctx.nodeId!);
|
|
6154
6779
|
* }
|
|
6155
6780
|
* ```
|
|
6156
6781
|
*/
|
|
6157
6782
|
withParentContext(ctx, depth = 1) {
|
|
6158
|
-
if (ctx.
|
|
6783
|
+
if (ctx.tree) {
|
|
6159
6784
|
this.parentContext = {
|
|
6160
|
-
|
|
6161
|
-
|
|
6785
|
+
tree: ctx.tree,
|
|
6786
|
+
nodeId: ctx.nodeId,
|
|
6162
6787
|
depth
|
|
6163
6788
|
};
|
|
6164
6789
|
}
|
|
6790
|
+
if (ctx.signal && !this.signal) {
|
|
6791
|
+
this.signal = ctx.signal;
|
|
6792
|
+
}
|
|
6165
6793
|
return this;
|
|
6166
6794
|
}
|
|
6167
6795
|
/**
|
|
@@ -6194,11 +6822,13 @@ var init_builder = __esm({
|
|
|
6194
6822
|
*
|
|
6195
6823
|
* This is useful for in-context learning - showing the LLM what "past self"
|
|
6196
6824
|
* did correctly so it mimics the pattern. The call is formatted with proper
|
|
6197
|
-
* markers and parameter format
|
|
6825
|
+
* markers and parameter format, including the invocation ID so the LLM can
|
|
6826
|
+
* reference previous calls when building dependencies.
|
|
6198
6827
|
*
|
|
6199
6828
|
* @param gadgetName - Name of the gadget
|
|
6200
6829
|
* @param parameters - Parameters passed to the gadget
|
|
6201
6830
|
* @param result - Result returned by the gadget
|
|
6831
|
+
* @param invocationId - Invocation ID (shown to LLM so it can reference for dependencies)
|
|
6202
6832
|
* @returns This builder for chaining
|
|
6203
6833
|
*
|
|
6204
6834
|
* @example
|
|
@@ -6210,124 +6840,36 @@ var init_builder = __esm({
|
|
|
6210
6840
|
* done: false,
|
|
6211
6841
|
* type: 'info'
|
|
6212
6842
|
* },
|
|
6213
|
-
* 'ℹ️ 👋 Hello!\n\nHere\'s what I can do:\n- Analyze code\n- Run commands'
|
|
6843
|
+
* 'ℹ️ 👋 Hello!\n\nHere\'s what I can do:\n- Analyze code\n- Run commands',
|
|
6844
|
+
* 'gc_1'
|
|
6214
6845
|
* )
|
|
6215
6846
|
* ```
|
|
6216
6847
|
*/
|
|
6217
|
-
withSyntheticGadgetCall(gadgetName, parameters, result) {
|
|
6848
|
+
withSyntheticGadgetCall(gadgetName, parameters, result, invocationId) {
|
|
6218
6849
|
const startPrefix = this.gadgetStartPrefix ?? GADGET_START_PREFIX;
|
|
6219
6850
|
const endPrefix = this.gadgetEndPrefix ?? GADGET_END_PREFIX;
|
|
6220
6851
|
const paramStr = this.formatBlockParameters(parameters, "");
|
|
6221
6852
|
this.initialMessages.push({
|
|
6222
6853
|
role: "assistant",
|
|
6223
|
-
content: `${startPrefix}${gadgetName}
|
|
6854
|
+
content: `${startPrefix}${gadgetName}:${invocationId}
|
|
6224
6855
|
${paramStr}
|
|
6225
6856
|
${endPrefix}`
|
|
6226
6857
|
});
|
|
6227
6858
|
this.initialMessages.push({
|
|
6228
6859
|
role: "user",
|
|
6229
|
-
content: `Result: ${result}`
|
|
6860
|
+
content: `Result (${invocationId}): ${result}`
|
|
6230
6861
|
});
|
|
6231
6862
|
return this;
|
|
6232
6863
|
}
|
|
6233
6864
|
/**
|
|
6234
|
-
* Compose the final hooks, including
|
|
6235
|
-
*
|
|
6236
|
-
*
|
|
6865
|
+
* Compose the final hooks, including trailing message injection if configured.
|
|
6866
|
+
*
|
|
6867
|
+
* Note: Subagent event visibility is now handled entirely by the ExecutionTree.
|
|
6868
|
+
* When a subagent uses withParentContext(ctx), it shares the parent's tree,
|
|
6869
|
+
* and all events are automatically visible to tree subscribers (like the TUI).
|
|
6237
6870
|
*/
|
|
6238
6871
|
composeHooks() {
|
|
6239
|
-
|
|
6240
|
-
if (this.parentContext) {
|
|
6241
|
-
const { invocationId, onSubagentEvent, depth } = this.parentContext;
|
|
6242
|
-
const existingOnLLMCallStart = hooks?.observers?.onLLMCallStart;
|
|
6243
|
-
const existingOnLLMCallComplete = hooks?.observers?.onLLMCallComplete;
|
|
6244
|
-
const existingOnGadgetExecutionStart = hooks?.observers?.onGadgetExecutionStart;
|
|
6245
|
-
const existingOnGadgetExecutionComplete = hooks?.observers?.onGadgetExecutionComplete;
|
|
6246
|
-
hooks = {
|
|
6247
|
-
...hooks,
|
|
6248
|
-
observers: {
|
|
6249
|
-
...hooks?.observers,
|
|
6250
|
-
onLLMCallStart: async (context) => {
|
|
6251
|
-
let inputTokens;
|
|
6252
|
-
try {
|
|
6253
|
-
if (this.client) {
|
|
6254
|
-
inputTokens = await this.client.countTokens(
|
|
6255
|
-
context.options.model,
|
|
6256
|
-
context.options.messages
|
|
6257
|
-
);
|
|
6258
|
-
}
|
|
6259
|
-
} catch {
|
|
6260
|
-
}
|
|
6261
|
-
onSubagentEvent({
|
|
6262
|
-
type: "llm_call_start",
|
|
6263
|
-
gadgetInvocationId: invocationId,
|
|
6264
|
-
depth,
|
|
6265
|
-
event: {
|
|
6266
|
-
iteration: context.iteration,
|
|
6267
|
-
model: context.options.model,
|
|
6268
|
-
inputTokens
|
|
6269
|
-
}
|
|
6270
|
-
});
|
|
6271
|
-
if (existingOnLLMCallStart) {
|
|
6272
|
-
await existingOnLLMCallStart(context);
|
|
6273
|
-
}
|
|
6274
|
-
},
|
|
6275
|
-
onLLMCallComplete: async (context) => {
|
|
6276
|
-
onSubagentEvent({
|
|
6277
|
-
type: "llm_call_end",
|
|
6278
|
-
gadgetInvocationId: invocationId,
|
|
6279
|
-
depth,
|
|
6280
|
-
event: {
|
|
6281
|
-
iteration: context.iteration,
|
|
6282
|
-
model: context.options.model,
|
|
6283
|
-
// Backward compat fields
|
|
6284
|
-
inputTokens: context.usage?.inputTokens,
|
|
6285
|
-
outputTokens: context.usage?.outputTokens,
|
|
6286
|
-
finishReason: context.finishReason ?? void 0,
|
|
6287
|
-
// Full usage object with cache details (for first-class display)
|
|
6288
|
-
usage: context.usage
|
|
6289
|
-
// Cost will be calculated by parent if it has model registry
|
|
6290
|
-
}
|
|
6291
|
-
});
|
|
6292
|
-
if (existingOnLLMCallComplete) {
|
|
6293
|
-
await existingOnLLMCallComplete(context);
|
|
6294
|
-
}
|
|
6295
|
-
},
|
|
6296
|
-
onGadgetExecutionStart: async (context) => {
|
|
6297
|
-
onSubagentEvent({
|
|
6298
|
-
type: "gadget_call",
|
|
6299
|
-
gadgetInvocationId: invocationId,
|
|
6300
|
-
depth,
|
|
6301
|
-
event: {
|
|
6302
|
-
call: {
|
|
6303
|
-
invocationId: context.invocationId,
|
|
6304
|
-
gadgetName: context.gadgetName,
|
|
6305
|
-
parameters: context.parameters
|
|
6306
|
-
}
|
|
6307
|
-
}
|
|
6308
|
-
});
|
|
6309
|
-
if (existingOnGadgetExecutionStart) {
|
|
6310
|
-
await existingOnGadgetExecutionStart(context);
|
|
6311
|
-
}
|
|
6312
|
-
},
|
|
6313
|
-
onGadgetExecutionComplete: async (context) => {
|
|
6314
|
-
onSubagentEvent({
|
|
6315
|
-
type: "gadget_result",
|
|
6316
|
-
gadgetInvocationId: invocationId,
|
|
6317
|
-
depth,
|
|
6318
|
-
event: {
|
|
6319
|
-
result: {
|
|
6320
|
-
invocationId: context.invocationId
|
|
6321
|
-
}
|
|
6322
|
-
}
|
|
6323
|
-
});
|
|
6324
|
-
if (existingOnGadgetExecutionComplete) {
|
|
6325
|
-
await existingOnGadgetExecutionComplete(context);
|
|
6326
|
-
}
|
|
6327
|
-
}
|
|
6328
|
-
}
|
|
6329
|
-
};
|
|
6330
|
-
}
|
|
6872
|
+
const hooks = this.hooks;
|
|
6331
6873
|
if (!this.trailingMessage) {
|
|
6332
6874
|
return hooks;
|
|
6333
6875
|
}
|
|
@@ -6410,19 +6952,6 @@ ${endPrefix}`
|
|
|
6410
6952
|
this.client = new LLMistClass();
|
|
6411
6953
|
}
|
|
6412
6954
|
const registry = GadgetRegistry.from(this.gadgets);
|
|
6413
|
-
let onSubagentEvent = this.subagentEventCallback;
|
|
6414
|
-
if (this.parentContext) {
|
|
6415
|
-
const { invocationId, onSubagentEvent: parentCallback, depth } = this.parentContext;
|
|
6416
|
-
const existingCallback = this.subagentEventCallback;
|
|
6417
|
-
onSubagentEvent = (event) => {
|
|
6418
|
-
parentCallback({
|
|
6419
|
-
...event,
|
|
6420
|
-
gadgetInvocationId: invocationId,
|
|
6421
|
-
depth: event.depth + depth
|
|
6422
|
-
});
|
|
6423
|
-
existingCallback?.(event);
|
|
6424
|
-
};
|
|
6425
|
-
}
|
|
6426
6955
|
return {
|
|
6427
6956
|
client: this.client,
|
|
6428
6957
|
model: this.model ?? "openai:gpt-5-nano",
|
|
@@ -6441,15 +6970,17 @@ ${endPrefix}`
|
|
|
6441
6970
|
gadgetArgPrefix: this.gadgetArgPrefix,
|
|
6442
6971
|
textOnlyHandler: this.textOnlyHandler,
|
|
6443
6972
|
textWithGadgetsHandler: this.textWithGadgetsHandler,
|
|
6444
|
-
stopOnGadgetError: this.stopOnGadgetError,
|
|
6445
|
-
canRecoverFromGadgetError: this.canRecoverFromGadgetError,
|
|
6446
6973
|
defaultGadgetTimeoutMs: this.defaultGadgetTimeoutMs,
|
|
6447
6974
|
gadgetOutputLimit: this.gadgetOutputLimit,
|
|
6448
6975
|
gadgetOutputLimitPercent: this.gadgetOutputLimitPercent,
|
|
6449
6976
|
compactionConfig: this.compactionConfig,
|
|
6450
6977
|
signal: this.signal,
|
|
6451
6978
|
subagentConfig: this.subagentConfig,
|
|
6452
|
-
onSubagentEvent
|
|
6979
|
+
onSubagentEvent: this.subagentEventCallback,
|
|
6980
|
+
// Tree context for shared tree model (subagents share parent's tree)
|
|
6981
|
+
parentTree: this.parentContext?.tree,
|
|
6982
|
+
parentNodeId: this.parentContext?.nodeId,
|
|
6983
|
+
baseDepth: this.parentContext ? (this.parentContext.depth ?? 0) + 1 : 0
|
|
6453
6984
|
};
|
|
6454
6985
|
}
|
|
6455
6986
|
ask(userPrompt) {
|
|
@@ -6606,19 +7137,6 @@ ${endPrefix}`
|
|
|
6606
7137
|
this.client = new LLMistClass();
|
|
6607
7138
|
}
|
|
6608
7139
|
const registry = GadgetRegistry.from(this.gadgets);
|
|
6609
|
-
let onSubagentEvent = this.subagentEventCallback;
|
|
6610
|
-
if (this.parentContext) {
|
|
6611
|
-
const { invocationId, onSubagentEvent: parentCallback, depth } = this.parentContext;
|
|
6612
|
-
const existingCallback = this.subagentEventCallback;
|
|
6613
|
-
onSubagentEvent = (event) => {
|
|
6614
|
-
parentCallback({
|
|
6615
|
-
...event,
|
|
6616
|
-
gadgetInvocationId: invocationId,
|
|
6617
|
-
depth: event.depth + depth
|
|
6618
|
-
});
|
|
6619
|
-
existingCallback?.(event);
|
|
6620
|
-
};
|
|
6621
|
-
}
|
|
6622
7140
|
const options = {
|
|
6623
7141
|
client: this.client,
|
|
6624
7142
|
model: this.model ?? "openai:gpt-5-nano",
|
|
@@ -6637,15 +7155,17 @@ ${endPrefix}`
|
|
|
6637
7155
|
gadgetArgPrefix: this.gadgetArgPrefix,
|
|
6638
7156
|
textOnlyHandler: this.textOnlyHandler,
|
|
6639
7157
|
textWithGadgetsHandler: this.textWithGadgetsHandler,
|
|
6640
|
-
stopOnGadgetError: this.stopOnGadgetError,
|
|
6641
|
-
canRecoverFromGadgetError: this.canRecoverFromGadgetError,
|
|
6642
7158
|
defaultGadgetTimeoutMs: this.defaultGadgetTimeoutMs,
|
|
6643
7159
|
gadgetOutputLimit: this.gadgetOutputLimit,
|
|
6644
7160
|
gadgetOutputLimitPercent: this.gadgetOutputLimitPercent,
|
|
6645
7161
|
compactionConfig: this.compactionConfig,
|
|
6646
7162
|
signal: this.signal,
|
|
6647
7163
|
subagentConfig: this.subagentConfig,
|
|
6648
|
-
onSubagentEvent
|
|
7164
|
+
onSubagentEvent: this.subagentEventCallback,
|
|
7165
|
+
// Tree context for shared tree model (subagents share parent's tree)
|
|
7166
|
+
parentTree: this.parentContext?.tree,
|
|
7167
|
+
parentNodeId: this.parentContext?.nodeId,
|
|
7168
|
+
baseDepth: this.parentContext ? (this.parentContext.depth ?? 0) + 1 : 0
|
|
6649
7169
|
};
|
|
6650
7170
|
return new Agent(AGENT_INTERNAL_KEY, options);
|
|
6651
7171
|
}
|
|
@@ -11278,16 +11798,16 @@ var MockConversationManager = class {
|
|
|
11278
11798
|
this.history.push(msg);
|
|
11279
11799
|
this.addedMessages.push(msg);
|
|
11280
11800
|
}
|
|
11281
|
-
addGadgetCallResult(gadgetName, parameters, result) {
|
|
11801
|
+
addGadgetCallResult(gadgetName, parameters, result, invocationId) {
|
|
11282
11802
|
const assistantMsg = {
|
|
11283
11803
|
role: "assistant",
|
|
11284
|
-
content: `!!!GADGET_START:${gadgetName}
|
|
11804
|
+
content: `!!!GADGET_START:${gadgetName}:${invocationId}
|
|
11285
11805
|
${JSON.stringify(parameters)}
|
|
11286
11806
|
!!!GADGET_END`
|
|
11287
11807
|
};
|
|
11288
11808
|
const resultMsg = {
|
|
11289
11809
|
role: "user",
|
|
11290
|
-
content: `Result: ${result}`
|
|
11810
|
+
content: `Result (${invocationId}): ${result}`
|
|
11291
11811
|
};
|
|
11292
11812
|
this.history.push(assistantMsg);
|
|
11293
11813
|
this.history.push(resultMsg);
|