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
|
@@ -690,23 +690,26 @@ Produces: { "items": ["first", "second"] }`);
|
|
|
690
690
|
* Record a gadget execution result in the message history.
|
|
691
691
|
* Creates an assistant message with the gadget invocation and a user message with the result.
|
|
692
692
|
*
|
|
693
|
+
* The invocationId is shown to the LLM so it can reference previous calls when building dependencies.
|
|
694
|
+
*
|
|
693
695
|
* @param gadget - Name of the gadget that was executed
|
|
694
696
|
* @param parameters - Parameters that were passed to the gadget
|
|
695
697
|
* @param result - Text result from the gadget execution
|
|
698
|
+
* @param invocationId - Invocation ID (shown to LLM so it can reference for dependencies)
|
|
696
699
|
* @param media - Optional media outputs from the gadget
|
|
697
700
|
* @param mediaIds - Optional IDs for the media outputs
|
|
698
701
|
*/
|
|
699
|
-
addGadgetCallResult(gadget, parameters, result, media, mediaIds) {
|
|
702
|
+
addGadgetCallResult(gadget, parameters, result, invocationId, media, mediaIds) {
|
|
700
703
|
const paramStr = this.formatBlockParameters(parameters, "");
|
|
701
704
|
this.messages.push({
|
|
702
705
|
role: "assistant",
|
|
703
|
-
content: `${this.startPrefix}${gadget}
|
|
706
|
+
content: `${this.startPrefix}${gadget}:${invocationId}
|
|
704
707
|
${paramStr}
|
|
705
708
|
${this.endPrefix}`
|
|
706
709
|
});
|
|
707
710
|
if (media && media.length > 0 && mediaIds && mediaIds.length > 0) {
|
|
708
711
|
const idRefs = media.map((m, i) => `[Media: ${mediaIds[i]} (${m.kind})]`).join("\n");
|
|
709
|
-
const textWithIds = `Result: ${result}
|
|
712
|
+
const textWithIds = `Result (${invocationId}): ${result}
|
|
710
713
|
${idRefs}`;
|
|
711
714
|
const parts = [text(textWithIds)];
|
|
712
715
|
for (const item of media) {
|
|
@@ -720,7 +723,7 @@ ${idRefs}`;
|
|
|
720
723
|
} else {
|
|
721
724
|
this.messages.push({
|
|
722
725
|
role: "user",
|
|
723
|
-
content: `Result: ${result}`
|
|
726
|
+
content: `Result (${invocationId}): ${result}`
|
|
724
727
|
});
|
|
725
728
|
}
|
|
726
729
|
return this;
|
|
@@ -1072,6 +1075,611 @@ var init_registry = __esm({
|
|
|
1072
1075
|
}
|
|
1073
1076
|
});
|
|
1074
1077
|
|
|
1078
|
+
// src/core/execution-tree.ts
|
|
1079
|
+
var ExecutionTree;
|
|
1080
|
+
var init_execution_tree = __esm({
|
|
1081
|
+
"src/core/execution-tree.ts"() {
|
|
1082
|
+
"use strict";
|
|
1083
|
+
ExecutionTree = class {
|
|
1084
|
+
nodes = /* @__PURE__ */ new Map();
|
|
1085
|
+
rootIds = [];
|
|
1086
|
+
eventListeners = /* @__PURE__ */ new Map();
|
|
1087
|
+
eventIdCounter = 0;
|
|
1088
|
+
invocationIdToNodeId = /* @__PURE__ */ new Map();
|
|
1089
|
+
// For async event streaming
|
|
1090
|
+
eventQueue = [];
|
|
1091
|
+
eventWaiters = [];
|
|
1092
|
+
isCompleted = false;
|
|
1093
|
+
/**
|
|
1094
|
+
* Base depth for all nodes in this tree.
|
|
1095
|
+
* Used when this tree is a subagent's view into a parent tree.
|
|
1096
|
+
*/
|
|
1097
|
+
baseDepth;
|
|
1098
|
+
/**
|
|
1099
|
+
* Parent node ID for subagent trees.
|
|
1100
|
+
* All root nodes in this tree will have this as their parentId.
|
|
1101
|
+
*/
|
|
1102
|
+
parentNodeId;
|
|
1103
|
+
constructor(options) {
|
|
1104
|
+
this.baseDepth = options?.baseDepth ?? 0;
|
|
1105
|
+
this.parentNodeId = options?.parentNodeId ?? null;
|
|
1106
|
+
}
|
|
1107
|
+
// ===========================================================================
|
|
1108
|
+
// Node ID Generation
|
|
1109
|
+
// ===========================================================================
|
|
1110
|
+
generateLLMCallId(iteration, parentId) {
|
|
1111
|
+
if (parentId) {
|
|
1112
|
+
return `llm_${parentId}_${iteration}`;
|
|
1113
|
+
}
|
|
1114
|
+
return `llm_${iteration}`;
|
|
1115
|
+
}
|
|
1116
|
+
gadgetIdCounter = 0;
|
|
1117
|
+
generateGadgetId(invocationId) {
|
|
1118
|
+
return `gadget_${invocationId}_${++this.gadgetIdCounter}`;
|
|
1119
|
+
}
|
|
1120
|
+
// ===========================================================================
|
|
1121
|
+
// Event Emission
|
|
1122
|
+
// ===========================================================================
|
|
1123
|
+
emit(event) {
|
|
1124
|
+
const listeners = this.eventListeners.get(event.type);
|
|
1125
|
+
if (listeners) {
|
|
1126
|
+
for (const listener of listeners) {
|
|
1127
|
+
try {
|
|
1128
|
+
listener(event);
|
|
1129
|
+
} catch (error) {
|
|
1130
|
+
console.error(`Error in event listener for ${event.type}:`, error);
|
|
1131
|
+
}
|
|
1132
|
+
}
|
|
1133
|
+
}
|
|
1134
|
+
const allListeners = this.eventListeners.get("*");
|
|
1135
|
+
if (allListeners) {
|
|
1136
|
+
for (const listener of allListeners) {
|
|
1137
|
+
try {
|
|
1138
|
+
listener(event);
|
|
1139
|
+
} catch (error) {
|
|
1140
|
+
console.error("Error in wildcard event listener:", error);
|
|
1141
|
+
}
|
|
1142
|
+
}
|
|
1143
|
+
}
|
|
1144
|
+
if (this.eventWaiters.length > 0) {
|
|
1145
|
+
const waiter = this.eventWaiters.shift();
|
|
1146
|
+
if (waiter) waiter(event);
|
|
1147
|
+
} else {
|
|
1148
|
+
this.eventQueue.push(event);
|
|
1149
|
+
}
|
|
1150
|
+
}
|
|
1151
|
+
createBaseEventProps(node) {
|
|
1152
|
+
return {
|
|
1153
|
+
eventId: ++this.eventIdCounter,
|
|
1154
|
+
timestamp: Date.now(),
|
|
1155
|
+
nodeId: node.id,
|
|
1156
|
+
parentId: node.parentId,
|
|
1157
|
+
depth: node.depth,
|
|
1158
|
+
path: node.path
|
|
1159
|
+
};
|
|
1160
|
+
}
|
|
1161
|
+
// ===========================================================================
|
|
1162
|
+
// Node Creation
|
|
1163
|
+
// ===========================================================================
|
|
1164
|
+
/**
|
|
1165
|
+
* Add a new LLM call node to the tree.
|
|
1166
|
+
*/
|
|
1167
|
+
addLLMCall(params) {
|
|
1168
|
+
const parentId = params.parentId ?? this.parentNodeId;
|
|
1169
|
+
const parent = parentId ? this.nodes.get(parentId) : null;
|
|
1170
|
+
const depth = parent ? parent.depth + 1 : this.baseDepth;
|
|
1171
|
+
const path = parent ? [...parent.path] : [];
|
|
1172
|
+
const id = this.generateLLMCallId(params.iteration, parentId);
|
|
1173
|
+
path.push(id);
|
|
1174
|
+
const node = {
|
|
1175
|
+
id,
|
|
1176
|
+
type: "llm_call",
|
|
1177
|
+
parentId,
|
|
1178
|
+
depth,
|
|
1179
|
+
path,
|
|
1180
|
+
createdAt: Date.now(),
|
|
1181
|
+
completedAt: null,
|
|
1182
|
+
iteration: params.iteration,
|
|
1183
|
+
model: params.model,
|
|
1184
|
+
request: params.request,
|
|
1185
|
+
response: "",
|
|
1186
|
+
children: []
|
|
1187
|
+
};
|
|
1188
|
+
this.nodes.set(id, node);
|
|
1189
|
+
if (!parentId) {
|
|
1190
|
+
this.rootIds.push(id);
|
|
1191
|
+
} else if (parent) {
|
|
1192
|
+
parent.children.push(id);
|
|
1193
|
+
}
|
|
1194
|
+
this.emit({
|
|
1195
|
+
type: "llm_call_start",
|
|
1196
|
+
...this.createBaseEventProps(node),
|
|
1197
|
+
iteration: node.iteration,
|
|
1198
|
+
model: node.model,
|
|
1199
|
+
request: node.request
|
|
1200
|
+
});
|
|
1201
|
+
return node;
|
|
1202
|
+
}
|
|
1203
|
+
/**
|
|
1204
|
+
* Add text to an LLM call's response (for streaming).
|
|
1205
|
+
*/
|
|
1206
|
+
appendLLMResponse(nodeId, chunk) {
|
|
1207
|
+
const node = this.nodes.get(nodeId);
|
|
1208
|
+
if (!node || node.type !== "llm_call") {
|
|
1209
|
+
throw new Error(`LLM call node not found: ${nodeId}`);
|
|
1210
|
+
}
|
|
1211
|
+
node.response += chunk;
|
|
1212
|
+
this.emit({
|
|
1213
|
+
type: "llm_call_stream",
|
|
1214
|
+
...this.createBaseEventProps(node),
|
|
1215
|
+
chunk
|
|
1216
|
+
});
|
|
1217
|
+
}
|
|
1218
|
+
/**
|
|
1219
|
+
* Complete an LLM call node.
|
|
1220
|
+
*/
|
|
1221
|
+
completeLLMCall(nodeId, params) {
|
|
1222
|
+
const node = this.nodes.get(nodeId);
|
|
1223
|
+
if (!node || node.type !== "llm_call") {
|
|
1224
|
+
throw new Error(`LLM call node not found: ${nodeId}`);
|
|
1225
|
+
}
|
|
1226
|
+
const llmNode = node;
|
|
1227
|
+
llmNode.completedAt = Date.now();
|
|
1228
|
+
if (params.response !== void 0) llmNode.response = params.response;
|
|
1229
|
+
if (params.usage) llmNode.usage = params.usage;
|
|
1230
|
+
if (params.finishReason !== void 0) llmNode.finishReason = params.finishReason;
|
|
1231
|
+
if (params.cost !== void 0) llmNode.cost = params.cost;
|
|
1232
|
+
this.emit({
|
|
1233
|
+
type: "llm_call_complete",
|
|
1234
|
+
...this.createBaseEventProps(node),
|
|
1235
|
+
response: llmNode.response,
|
|
1236
|
+
usage: llmNode.usage,
|
|
1237
|
+
finishReason: llmNode.finishReason,
|
|
1238
|
+
cost: llmNode.cost
|
|
1239
|
+
});
|
|
1240
|
+
}
|
|
1241
|
+
/**
|
|
1242
|
+
* Mark an LLM call as failed.
|
|
1243
|
+
*/
|
|
1244
|
+
failLLMCall(nodeId, error, recovered) {
|
|
1245
|
+
const node = this.nodes.get(nodeId);
|
|
1246
|
+
if (!node || node.type !== "llm_call") {
|
|
1247
|
+
throw new Error(`LLM call node not found: ${nodeId}`);
|
|
1248
|
+
}
|
|
1249
|
+
const llmNode = node;
|
|
1250
|
+
llmNode.completedAt = Date.now();
|
|
1251
|
+
this.emit({
|
|
1252
|
+
type: "llm_call_error",
|
|
1253
|
+
...this.createBaseEventProps(node),
|
|
1254
|
+
error,
|
|
1255
|
+
recovered
|
|
1256
|
+
});
|
|
1257
|
+
}
|
|
1258
|
+
/**
|
|
1259
|
+
* Add a new gadget node to the tree.
|
|
1260
|
+
*/
|
|
1261
|
+
addGadget(params) {
|
|
1262
|
+
const parentId = params.parentId ?? this.getCurrentLLMCallId() ?? this.parentNodeId;
|
|
1263
|
+
const parent = parentId ? this.nodes.get(parentId) : null;
|
|
1264
|
+
const depth = parent ? parent.depth + 1 : this.baseDepth;
|
|
1265
|
+
const path = parent ? [...parent.path] : [];
|
|
1266
|
+
const id = this.generateGadgetId(params.invocationId);
|
|
1267
|
+
path.push(id);
|
|
1268
|
+
const node = {
|
|
1269
|
+
id,
|
|
1270
|
+
type: "gadget",
|
|
1271
|
+
parentId,
|
|
1272
|
+
depth,
|
|
1273
|
+
path,
|
|
1274
|
+
createdAt: Date.now(),
|
|
1275
|
+
completedAt: null,
|
|
1276
|
+
invocationId: params.invocationId,
|
|
1277
|
+
name: params.name,
|
|
1278
|
+
parameters: params.parameters,
|
|
1279
|
+
dependencies: params.dependencies ?? [],
|
|
1280
|
+
state: "pending",
|
|
1281
|
+
children: [],
|
|
1282
|
+
isSubagent: false
|
|
1283
|
+
};
|
|
1284
|
+
this.nodes.set(id, node);
|
|
1285
|
+
this.invocationIdToNodeId.set(params.invocationId, id);
|
|
1286
|
+
if (parent) {
|
|
1287
|
+
parent.children.push(id);
|
|
1288
|
+
}
|
|
1289
|
+
this.emit({
|
|
1290
|
+
type: "gadget_call",
|
|
1291
|
+
...this.createBaseEventProps(node),
|
|
1292
|
+
invocationId: node.invocationId,
|
|
1293
|
+
name: node.name,
|
|
1294
|
+
parameters: node.parameters,
|
|
1295
|
+
dependencies: node.dependencies
|
|
1296
|
+
});
|
|
1297
|
+
return node;
|
|
1298
|
+
}
|
|
1299
|
+
/**
|
|
1300
|
+
* Mark a gadget as started (running).
|
|
1301
|
+
*/
|
|
1302
|
+
startGadget(nodeId) {
|
|
1303
|
+
const node = this.nodes.get(nodeId);
|
|
1304
|
+
if (!node || node.type !== "gadget") {
|
|
1305
|
+
throw new Error(`Gadget node not found: ${nodeId}`);
|
|
1306
|
+
}
|
|
1307
|
+
const gadgetNode = node;
|
|
1308
|
+
gadgetNode.state = "running";
|
|
1309
|
+
this.emit({
|
|
1310
|
+
type: "gadget_start",
|
|
1311
|
+
...this.createBaseEventProps(node),
|
|
1312
|
+
invocationId: gadgetNode.invocationId,
|
|
1313
|
+
name: gadgetNode.name
|
|
1314
|
+
});
|
|
1315
|
+
}
|
|
1316
|
+
/**
|
|
1317
|
+
* Complete a gadget node successfully.
|
|
1318
|
+
*/
|
|
1319
|
+
completeGadget(nodeId, params) {
|
|
1320
|
+
const node = this.nodes.get(nodeId);
|
|
1321
|
+
if (!node || node.type !== "gadget") {
|
|
1322
|
+
throw new Error(`Gadget node not found: ${nodeId}`);
|
|
1323
|
+
}
|
|
1324
|
+
const gadgetNode = node;
|
|
1325
|
+
gadgetNode.completedAt = Date.now();
|
|
1326
|
+
gadgetNode.state = params.error ? "failed" : "completed";
|
|
1327
|
+
if (params.result !== void 0) gadgetNode.result = params.result;
|
|
1328
|
+
if (params.error) gadgetNode.error = params.error;
|
|
1329
|
+
if (params.executionTimeMs !== void 0) gadgetNode.executionTimeMs = params.executionTimeMs;
|
|
1330
|
+
if (params.cost !== void 0) gadgetNode.cost = params.cost;
|
|
1331
|
+
if (params.media) gadgetNode.media = params.media;
|
|
1332
|
+
gadgetNode.isSubagent = gadgetNode.children.some((childId) => {
|
|
1333
|
+
const child = this.nodes.get(childId);
|
|
1334
|
+
return child?.type === "llm_call";
|
|
1335
|
+
});
|
|
1336
|
+
if (params.error) {
|
|
1337
|
+
this.emit({
|
|
1338
|
+
type: "gadget_error",
|
|
1339
|
+
...this.createBaseEventProps(node),
|
|
1340
|
+
invocationId: gadgetNode.invocationId,
|
|
1341
|
+
name: gadgetNode.name,
|
|
1342
|
+
error: params.error,
|
|
1343
|
+
executionTimeMs: params.executionTimeMs ?? 0
|
|
1344
|
+
});
|
|
1345
|
+
} else {
|
|
1346
|
+
this.emit({
|
|
1347
|
+
type: "gadget_complete",
|
|
1348
|
+
...this.createBaseEventProps(node),
|
|
1349
|
+
invocationId: gadgetNode.invocationId,
|
|
1350
|
+
name: gadgetNode.name,
|
|
1351
|
+
result: params.result ?? "",
|
|
1352
|
+
executionTimeMs: params.executionTimeMs ?? 0,
|
|
1353
|
+
cost: params.cost,
|
|
1354
|
+
media: params.media
|
|
1355
|
+
});
|
|
1356
|
+
}
|
|
1357
|
+
}
|
|
1358
|
+
/**
|
|
1359
|
+
* Mark a gadget as skipped due to dependency failure.
|
|
1360
|
+
*/
|
|
1361
|
+
skipGadget(nodeId, failedDependency, failedDependencyError, reason) {
|
|
1362
|
+
const node = this.nodes.get(nodeId);
|
|
1363
|
+
if (!node || node.type !== "gadget") {
|
|
1364
|
+
throw new Error(`Gadget node not found: ${nodeId}`);
|
|
1365
|
+
}
|
|
1366
|
+
const gadgetNode = node;
|
|
1367
|
+
gadgetNode.completedAt = Date.now();
|
|
1368
|
+
gadgetNode.state = "skipped";
|
|
1369
|
+
gadgetNode.failedDependency = failedDependency;
|
|
1370
|
+
gadgetNode.error = failedDependencyError;
|
|
1371
|
+
const error = reason === "controller_skip" ? "Skipped by controller" : `Dependency ${failedDependency} failed: ${failedDependencyError}`;
|
|
1372
|
+
this.emit({
|
|
1373
|
+
type: "gadget_skipped",
|
|
1374
|
+
...this.createBaseEventProps(node),
|
|
1375
|
+
invocationId: gadgetNode.invocationId,
|
|
1376
|
+
name: gadgetNode.name,
|
|
1377
|
+
reason,
|
|
1378
|
+
error,
|
|
1379
|
+
failedDependency,
|
|
1380
|
+
failedDependencyError
|
|
1381
|
+
});
|
|
1382
|
+
}
|
|
1383
|
+
// ===========================================================================
|
|
1384
|
+
// Text Events (pure notifications, not tree nodes)
|
|
1385
|
+
// ===========================================================================
|
|
1386
|
+
/**
|
|
1387
|
+
* Emit a text event (notification only, not stored in tree).
|
|
1388
|
+
*/
|
|
1389
|
+
emitText(content, llmCallNodeId) {
|
|
1390
|
+
const node = this.nodes.get(llmCallNodeId);
|
|
1391
|
+
if (!node) {
|
|
1392
|
+
throw new Error(`Node not found: ${llmCallNodeId}`);
|
|
1393
|
+
}
|
|
1394
|
+
this.emit({
|
|
1395
|
+
type: "text",
|
|
1396
|
+
...this.createBaseEventProps(node),
|
|
1397
|
+
content
|
|
1398
|
+
});
|
|
1399
|
+
}
|
|
1400
|
+
// ===========================================================================
|
|
1401
|
+
// Query Methods
|
|
1402
|
+
// ===========================================================================
|
|
1403
|
+
/**
|
|
1404
|
+
* Get a node by ID.
|
|
1405
|
+
*/
|
|
1406
|
+
getNode(id) {
|
|
1407
|
+
return this.nodes.get(id);
|
|
1408
|
+
}
|
|
1409
|
+
/**
|
|
1410
|
+
* Get a gadget node by invocation ID.
|
|
1411
|
+
*/
|
|
1412
|
+
getNodeByInvocationId(invocationId) {
|
|
1413
|
+
const nodeId = this.invocationIdToNodeId.get(invocationId);
|
|
1414
|
+
if (!nodeId) return void 0;
|
|
1415
|
+
const node = this.nodes.get(nodeId);
|
|
1416
|
+
return node?.type === "gadget" ? node : void 0;
|
|
1417
|
+
}
|
|
1418
|
+
/**
|
|
1419
|
+
* Get all root nodes (depth 0 for this tree).
|
|
1420
|
+
*/
|
|
1421
|
+
getRoots() {
|
|
1422
|
+
return this.rootIds.map((id) => this.nodes.get(id)).filter((node) => node !== void 0);
|
|
1423
|
+
}
|
|
1424
|
+
/**
|
|
1425
|
+
* Get children of a node.
|
|
1426
|
+
*/
|
|
1427
|
+
getChildren(id) {
|
|
1428
|
+
const node = this.nodes.get(id);
|
|
1429
|
+
if (!node) return [];
|
|
1430
|
+
return node.children.map((childId) => this.nodes.get(childId)).filter((child) => child !== void 0);
|
|
1431
|
+
}
|
|
1432
|
+
/**
|
|
1433
|
+
* Get ancestors of a node (from root to parent).
|
|
1434
|
+
*/
|
|
1435
|
+
getAncestors(id) {
|
|
1436
|
+
const node = this.nodes.get(id);
|
|
1437
|
+
if (!node) return [];
|
|
1438
|
+
const ancestors = [];
|
|
1439
|
+
let currentId = node.parentId;
|
|
1440
|
+
while (currentId) {
|
|
1441
|
+
const ancestor = this.nodes.get(currentId);
|
|
1442
|
+
if (ancestor) {
|
|
1443
|
+
ancestors.unshift(ancestor);
|
|
1444
|
+
currentId = ancestor.parentId;
|
|
1445
|
+
} else {
|
|
1446
|
+
break;
|
|
1447
|
+
}
|
|
1448
|
+
}
|
|
1449
|
+
return ancestors;
|
|
1450
|
+
}
|
|
1451
|
+
/**
|
|
1452
|
+
* Get all descendants of a node.
|
|
1453
|
+
*/
|
|
1454
|
+
getDescendants(id, type) {
|
|
1455
|
+
const node = this.nodes.get(id);
|
|
1456
|
+
if (!node) return [];
|
|
1457
|
+
const descendants = [];
|
|
1458
|
+
const stack = [...node.children];
|
|
1459
|
+
while (stack.length > 0) {
|
|
1460
|
+
const childId = stack.pop();
|
|
1461
|
+
const child = this.nodes.get(childId);
|
|
1462
|
+
if (child) {
|
|
1463
|
+
if (!type || child.type === type) {
|
|
1464
|
+
descendants.push(child);
|
|
1465
|
+
}
|
|
1466
|
+
stack.push(...child.children);
|
|
1467
|
+
}
|
|
1468
|
+
}
|
|
1469
|
+
return descendants;
|
|
1470
|
+
}
|
|
1471
|
+
/**
|
|
1472
|
+
* Get the current (most recent incomplete) LLM call node.
|
|
1473
|
+
*/
|
|
1474
|
+
getCurrentLLMCallId() {
|
|
1475
|
+
for (let i = this.rootIds.length - 1; i >= 0; i--) {
|
|
1476
|
+
const node = this.nodes.get(this.rootIds[i]);
|
|
1477
|
+
if (node?.type === "llm_call" && !node.completedAt) {
|
|
1478
|
+
return node.id;
|
|
1479
|
+
}
|
|
1480
|
+
}
|
|
1481
|
+
return void 0;
|
|
1482
|
+
}
|
|
1483
|
+
// ===========================================================================
|
|
1484
|
+
// Aggregation Methods (for subagent support)
|
|
1485
|
+
// ===========================================================================
|
|
1486
|
+
/**
|
|
1487
|
+
* Get total cost for entire tree.
|
|
1488
|
+
*/
|
|
1489
|
+
getTotalCost() {
|
|
1490
|
+
let total = 0;
|
|
1491
|
+
for (const node of this.nodes.values()) {
|
|
1492
|
+
if (node.type === "llm_call" && node.cost) {
|
|
1493
|
+
total += node.cost;
|
|
1494
|
+
} else if (node.type === "gadget" && node.cost) {
|
|
1495
|
+
total += node.cost;
|
|
1496
|
+
}
|
|
1497
|
+
}
|
|
1498
|
+
return total;
|
|
1499
|
+
}
|
|
1500
|
+
/**
|
|
1501
|
+
* Get total cost for a subtree (node and all descendants).
|
|
1502
|
+
*/
|
|
1503
|
+
getSubtreeCost(nodeId) {
|
|
1504
|
+
const node = this.nodes.get(nodeId);
|
|
1505
|
+
if (!node) return 0;
|
|
1506
|
+
let total = 0;
|
|
1507
|
+
if (node.type === "llm_call" && node.cost) {
|
|
1508
|
+
total += node.cost;
|
|
1509
|
+
} else if (node.type === "gadget" && node.cost) {
|
|
1510
|
+
total += node.cost;
|
|
1511
|
+
}
|
|
1512
|
+
for (const descendant of this.getDescendants(nodeId)) {
|
|
1513
|
+
if (descendant.type === "llm_call" && descendant.cost) {
|
|
1514
|
+
total += descendant.cost;
|
|
1515
|
+
} else if (descendant.type === "gadget" && descendant.cost) {
|
|
1516
|
+
total += descendant.cost;
|
|
1517
|
+
}
|
|
1518
|
+
}
|
|
1519
|
+
return total;
|
|
1520
|
+
}
|
|
1521
|
+
/**
|
|
1522
|
+
* Get token usage for entire tree.
|
|
1523
|
+
*/
|
|
1524
|
+
getTotalTokens() {
|
|
1525
|
+
let input = 0;
|
|
1526
|
+
let output = 0;
|
|
1527
|
+
let cached = 0;
|
|
1528
|
+
for (const node of this.nodes.values()) {
|
|
1529
|
+
if (node.type === "llm_call") {
|
|
1530
|
+
const llmNode = node;
|
|
1531
|
+
if (llmNode.usage) {
|
|
1532
|
+
input += llmNode.usage.inputTokens;
|
|
1533
|
+
output += llmNode.usage.outputTokens;
|
|
1534
|
+
cached += llmNode.usage.cachedInputTokens ?? 0;
|
|
1535
|
+
}
|
|
1536
|
+
}
|
|
1537
|
+
}
|
|
1538
|
+
return { input, output, cached };
|
|
1539
|
+
}
|
|
1540
|
+
/**
|
|
1541
|
+
* Get token usage for a subtree.
|
|
1542
|
+
*/
|
|
1543
|
+
getSubtreeTokens(nodeId) {
|
|
1544
|
+
const node = this.nodes.get(nodeId);
|
|
1545
|
+
if (!node) return { input: 0, output: 0, cached: 0 };
|
|
1546
|
+
let input = 0;
|
|
1547
|
+
let output = 0;
|
|
1548
|
+
let cached = 0;
|
|
1549
|
+
const nodesToProcess = [node, ...this.getDescendants(nodeId)];
|
|
1550
|
+
for (const n of nodesToProcess) {
|
|
1551
|
+
if (n.type === "llm_call") {
|
|
1552
|
+
const llmNode = n;
|
|
1553
|
+
if (llmNode.usage) {
|
|
1554
|
+
input += llmNode.usage.inputTokens;
|
|
1555
|
+
output += llmNode.usage.outputTokens;
|
|
1556
|
+
cached += llmNode.usage.cachedInputTokens ?? 0;
|
|
1557
|
+
}
|
|
1558
|
+
}
|
|
1559
|
+
}
|
|
1560
|
+
return { input, output, cached };
|
|
1561
|
+
}
|
|
1562
|
+
/**
|
|
1563
|
+
* Collect all media from a subtree.
|
|
1564
|
+
*/
|
|
1565
|
+
getSubtreeMedia(nodeId) {
|
|
1566
|
+
const node = this.nodes.get(nodeId);
|
|
1567
|
+
if (!node) return [];
|
|
1568
|
+
const media = [];
|
|
1569
|
+
const nodesToProcess = node.type === "gadget" ? [node] : [];
|
|
1570
|
+
nodesToProcess.push(...this.getDescendants(nodeId, "gadget"));
|
|
1571
|
+
for (const n of nodesToProcess) {
|
|
1572
|
+
if (n.type === "gadget") {
|
|
1573
|
+
const gadgetNode = n;
|
|
1574
|
+
if (gadgetNode.media) {
|
|
1575
|
+
media.push(...gadgetNode.media);
|
|
1576
|
+
}
|
|
1577
|
+
}
|
|
1578
|
+
}
|
|
1579
|
+
return media;
|
|
1580
|
+
}
|
|
1581
|
+
/**
|
|
1582
|
+
* Check if a subtree is complete (all nodes finished).
|
|
1583
|
+
*/
|
|
1584
|
+
isSubtreeComplete(nodeId) {
|
|
1585
|
+
const node = this.nodes.get(nodeId);
|
|
1586
|
+
if (!node) return true;
|
|
1587
|
+
if (!node.completedAt) return false;
|
|
1588
|
+
for (const descendant of this.getDescendants(nodeId)) {
|
|
1589
|
+
if (!descendant.completedAt) return false;
|
|
1590
|
+
}
|
|
1591
|
+
return true;
|
|
1592
|
+
}
|
|
1593
|
+
/**
|
|
1594
|
+
* Get node counts.
|
|
1595
|
+
*/
|
|
1596
|
+
getNodeCount() {
|
|
1597
|
+
let llmCalls = 0;
|
|
1598
|
+
let gadgets = 0;
|
|
1599
|
+
for (const node of this.nodes.values()) {
|
|
1600
|
+
if (node.type === "llm_call") llmCalls++;
|
|
1601
|
+
else if (node.type === "gadget") gadgets++;
|
|
1602
|
+
}
|
|
1603
|
+
return { llmCalls, gadgets };
|
|
1604
|
+
}
|
|
1605
|
+
// ===========================================================================
|
|
1606
|
+
// Event Subscription
|
|
1607
|
+
// ===========================================================================
|
|
1608
|
+
/**
|
|
1609
|
+
* Subscribe to events of a specific type.
|
|
1610
|
+
* Returns unsubscribe function.
|
|
1611
|
+
*
|
|
1612
|
+
* @param type - Event type to subscribe to (use "*" for all events)
|
|
1613
|
+
* @param listener - Callback function that receives matching events
|
|
1614
|
+
* @returns Unsubscribe function
|
|
1615
|
+
*
|
|
1616
|
+
* @example
|
|
1617
|
+
* ```typescript
|
|
1618
|
+
* const unsubscribe = tree.on("gadget_complete", (event) => {
|
|
1619
|
+
* if (event.type === "gadget_complete") {
|
|
1620
|
+
* console.log(`Gadget ${event.name} completed`);
|
|
1621
|
+
* }
|
|
1622
|
+
* });
|
|
1623
|
+
* ```
|
|
1624
|
+
*/
|
|
1625
|
+
on(type, listener) {
|
|
1626
|
+
if (!this.eventListeners.has(type)) {
|
|
1627
|
+
this.eventListeners.set(type, /* @__PURE__ */ new Set());
|
|
1628
|
+
}
|
|
1629
|
+
const listeners = this.eventListeners.get(type);
|
|
1630
|
+
listeners.add(listener);
|
|
1631
|
+
return () => {
|
|
1632
|
+
listeners.delete(listener);
|
|
1633
|
+
};
|
|
1634
|
+
}
|
|
1635
|
+
/**
|
|
1636
|
+
* Subscribe to all events.
|
|
1637
|
+
*/
|
|
1638
|
+
onAll(listener) {
|
|
1639
|
+
return this.on("*", listener);
|
|
1640
|
+
}
|
|
1641
|
+
/**
|
|
1642
|
+
* Get async iterable of all events.
|
|
1643
|
+
* Events are yielded as they occur.
|
|
1644
|
+
*/
|
|
1645
|
+
async *events() {
|
|
1646
|
+
while (!this.isCompleted) {
|
|
1647
|
+
while (this.eventQueue.length > 0) {
|
|
1648
|
+
yield this.eventQueue.shift();
|
|
1649
|
+
}
|
|
1650
|
+
if (this.isCompleted) break;
|
|
1651
|
+
const event = await new Promise((resolve) => {
|
|
1652
|
+
if (this.eventQueue.length > 0) {
|
|
1653
|
+
resolve(this.eventQueue.shift());
|
|
1654
|
+
} else {
|
|
1655
|
+
this.eventWaiters.push(resolve);
|
|
1656
|
+
}
|
|
1657
|
+
});
|
|
1658
|
+
yield event;
|
|
1659
|
+
}
|
|
1660
|
+
while (this.eventQueue.length > 0) {
|
|
1661
|
+
yield this.eventQueue.shift();
|
|
1662
|
+
}
|
|
1663
|
+
}
|
|
1664
|
+
/**
|
|
1665
|
+
* Mark the tree as complete (no more events will be emitted).
|
|
1666
|
+
*/
|
|
1667
|
+
complete() {
|
|
1668
|
+
this.isCompleted = true;
|
|
1669
|
+
for (const waiter of this.eventWaiters) {
|
|
1670
|
+
}
|
|
1671
|
+
this.eventWaiters = [];
|
|
1672
|
+
}
|
|
1673
|
+
/**
|
|
1674
|
+
* Check if the tree is complete.
|
|
1675
|
+
*/
|
|
1676
|
+
isComplete() {
|
|
1677
|
+
return this.isCompleted;
|
|
1678
|
+
}
|
|
1679
|
+
};
|
|
1680
|
+
}
|
|
1681
|
+
});
|
|
1682
|
+
|
|
1075
1683
|
// src/gadgets/media-store.ts
|
|
1076
1684
|
import { randomBytes } from "node:crypto";
|
|
1077
1685
|
import { mkdir, rm, writeFile } from "node:fs/promises";
|
|
@@ -2367,8 +2975,8 @@ var init_conversation_manager = __esm({
|
|
|
2367
2975
|
addAssistantMessage(content) {
|
|
2368
2976
|
this.historyBuilder.addAssistant(content);
|
|
2369
2977
|
}
|
|
2370
|
-
addGadgetCallResult(gadgetName, parameters, result, media, mediaIds) {
|
|
2371
|
-
this.historyBuilder.addGadgetCallResult(gadgetName, parameters, result, media, mediaIds);
|
|
2978
|
+
addGadgetCallResult(gadgetName, parameters, result, invocationId, media, mediaIds) {
|
|
2979
|
+
this.historyBuilder.addGadgetCallResult(gadgetName, parameters, result, invocationId, media, mediaIds);
|
|
2372
2980
|
}
|
|
2373
2981
|
getMessages() {
|
|
2374
2982
|
return [...this.baseMessages, ...this.initialMessages, ...this.historyBuilder.build()];
|
|
@@ -3549,7 +4157,7 @@ var init_executor = __esm({
|
|
|
3549
4157
|
init_exceptions();
|
|
3550
4158
|
init_parser();
|
|
3551
4159
|
GadgetExecutor = class {
|
|
3552
|
-
constructor(registry, requestHumanInput, logger, defaultGadgetTimeoutMs, errorFormatterOptions, client, mediaStore, agentConfig, subagentConfig, onSubagentEvent) {
|
|
4160
|
+
constructor(registry, requestHumanInput, logger, defaultGadgetTimeoutMs, errorFormatterOptions, client, mediaStore, agentConfig, subagentConfig, onSubagentEvent, tree, parentNodeId, baseDepth) {
|
|
3553
4161
|
this.registry = registry;
|
|
3554
4162
|
this.requestHumanInput = requestHumanInput;
|
|
3555
4163
|
this.defaultGadgetTimeoutMs = defaultGadgetTimeoutMs;
|
|
@@ -3558,6 +4166,9 @@ var init_executor = __esm({
|
|
|
3558
4166
|
this.agentConfig = agentConfig;
|
|
3559
4167
|
this.subagentConfig = subagentConfig;
|
|
3560
4168
|
this.onSubagentEvent = onSubagentEvent;
|
|
4169
|
+
this.tree = tree;
|
|
4170
|
+
this.parentNodeId = parentNodeId;
|
|
4171
|
+
this.baseDepth = baseDepth;
|
|
3561
4172
|
this.logger = logger ?? createLogger({ name: "llmist:executor" });
|
|
3562
4173
|
this.errorFormatter = new GadgetExecutionErrorFormatter(errorFormatterOptions);
|
|
3563
4174
|
this.argPrefix = errorFormatterOptions?.argPrefix ?? GADGET_ARG_PREFIX;
|
|
@@ -3568,15 +4179,21 @@ var init_executor = __esm({
|
|
|
3568
4179
|
/**
|
|
3569
4180
|
* Creates a promise that rejects with a TimeoutException after the specified timeout.
|
|
3570
4181
|
* Aborts the provided AbortController before rejecting, allowing gadgets to clean up.
|
|
4182
|
+
* Returns both the promise and a cancel function to clear the timeout when no longer needed.
|
|
3571
4183
|
*/
|
|
3572
4184
|
createTimeoutPromise(gadgetName, timeoutMs, abortController) {
|
|
3573
|
-
|
|
3574
|
-
|
|
4185
|
+
let timeoutId;
|
|
4186
|
+
const promise = new Promise((_, reject) => {
|
|
4187
|
+
timeoutId = setTimeout(() => {
|
|
3575
4188
|
const timeoutError = new TimeoutException(gadgetName, timeoutMs);
|
|
3576
4189
|
abortController.abort(timeoutError.message);
|
|
3577
4190
|
reject(timeoutError);
|
|
3578
4191
|
}, timeoutMs);
|
|
3579
4192
|
});
|
|
4193
|
+
return {
|
|
4194
|
+
promise,
|
|
4195
|
+
cancel: () => clearTimeout(timeoutId)
|
|
4196
|
+
};
|
|
3580
4197
|
}
|
|
3581
4198
|
/**
|
|
3582
4199
|
* Unify gadget execute result to consistent internal format.
|
|
@@ -3698,6 +4315,8 @@ var init_executor = __esm({
|
|
|
3698
4315
|
});
|
|
3699
4316
|
}
|
|
3700
4317
|
};
|
|
4318
|
+
const gadgetNodeId = this.tree?.getNodeByInvocationId(call.invocationId)?.id;
|
|
4319
|
+
const gadgetDepth = gadgetNodeId ? this.tree?.getNode(gadgetNodeId)?.depth ?? this.baseDepth : this.baseDepth;
|
|
3701
4320
|
const ctx = {
|
|
3702
4321
|
reportCost,
|
|
3703
4322
|
llmist: this.client ? new CostReportingLLMistWrapper(this.client, reportCost) : void 0,
|
|
@@ -3705,7 +4324,11 @@ var init_executor = __esm({
|
|
|
3705
4324
|
agentConfig: this.agentConfig,
|
|
3706
4325
|
subagentConfig: this.subagentConfig,
|
|
3707
4326
|
invocationId: call.invocationId,
|
|
3708
|
-
onSubagentEvent: this.onSubagentEvent
|
|
4327
|
+
onSubagentEvent: this.onSubagentEvent,
|
|
4328
|
+
// Tree context for subagent support - use gadget's own node ID
|
|
4329
|
+
tree: this.tree,
|
|
4330
|
+
nodeId: gadgetNodeId,
|
|
4331
|
+
depth: gadgetDepth
|
|
3709
4332
|
};
|
|
3710
4333
|
let rawResult;
|
|
3711
4334
|
if (timeoutMs && timeoutMs > 0) {
|
|
@@ -3713,10 +4336,15 @@ var init_executor = __esm({
|
|
|
3713
4336
|
gadgetName: call.gadgetName,
|
|
3714
4337
|
timeoutMs
|
|
3715
4338
|
});
|
|
3716
|
-
|
|
3717
|
-
|
|
3718
|
-
|
|
3719
|
-
|
|
4339
|
+
const timeout = this.createTimeoutPromise(call.gadgetName, timeoutMs, abortController);
|
|
4340
|
+
try {
|
|
4341
|
+
rawResult = await Promise.race([
|
|
4342
|
+
Promise.resolve(gadget.execute(validatedParameters, ctx)),
|
|
4343
|
+
timeout.promise
|
|
4344
|
+
]);
|
|
4345
|
+
} finally {
|
|
4346
|
+
timeout.cancel();
|
|
4347
|
+
}
|
|
3720
4348
|
} else {
|
|
3721
4349
|
rawResult = await Promise.resolve(gadget.execute(validatedParameters, ctx));
|
|
3722
4350
|
}
|
|
@@ -3911,10 +4539,11 @@ var init_stream_processor = __esm({
|
|
|
3911
4539
|
logger;
|
|
3912
4540
|
parser;
|
|
3913
4541
|
executor;
|
|
3914
|
-
|
|
3915
|
-
|
|
4542
|
+
// Execution Tree context
|
|
4543
|
+
tree;
|
|
4544
|
+
parentNodeId;
|
|
4545
|
+
baseDepth;
|
|
3916
4546
|
responseText = "";
|
|
3917
|
-
executionHalted = false;
|
|
3918
4547
|
observerFailureCount = 0;
|
|
3919
4548
|
// Dependency tracking for gadget execution DAG
|
|
3920
4549
|
/** Gadgets waiting for their dependencies to complete */
|
|
@@ -3925,18 +4554,28 @@ var init_stream_processor = __esm({
|
|
|
3925
4554
|
failedInvocations = /* @__PURE__ */ new Set();
|
|
3926
4555
|
/** Promises for independent gadgets currently executing (fire-and-forget) */
|
|
3927
4556
|
inFlightExecutions = /* @__PURE__ */ new Map();
|
|
4557
|
+
/** Queue of completed gadget results ready to be yielded (for real-time streaming) */
|
|
4558
|
+
completedResultsQueue = [];
|
|
3928
4559
|
constructor(options) {
|
|
3929
4560
|
this.iteration = options.iteration;
|
|
3930
4561
|
this.registry = options.registry;
|
|
3931
4562
|
this.hooks = options.hooks ?? {};
|
|
3932
4563
|
this.logger = options.logger ?? createLogger({ name: "llmist:stream-processor" });
|
|
3933
|
-
this.
|
|
3934
|
-
this.
|
|
4564
|
+
this.tree = options.tree;
|
|
4565
|
+
this.parentNodeId = options.parentNodeId ?? null;
|
|
4566
|
+
this.baseDepth = options.baseDepth ?? 0;
|
|
3935
4567
|
this.parser = new GadgetCallParser({
|
|
3936
4568
|
startPrefix: options.gadgetStartPrefix,
|
|
3937
4569
|
endPrefix: options.gadgetEndPrefix,
|
|
3938
4570
|
argPrefix: options.gadgetArgPrefix
|
|
3939
4571
|
});
|
|
4572
|
+
const wrappedOnSubagentEvent = options.onSubagentEvent ? (event) => {
|
|
4573
|
+
this.completedResultsQueue.push({
|
|
4574
|
+
type: "subagent_event",
|
|
4575
|
+
subagentEvent: event
|
|
4576
|
+
});
|
|
4577
|
+
options.onSubagentEvent?.(event);
|
|
4578
|
+
} : void 0;
|
|
3940
4579
|
this.executor = new GadgetExecutor(
|
|
3941
4580
|
options.registry,
|
|
3942
4581
|
options.requestHumanInput,
|
|
@@ -3947,7 +4586,11 @@ var init_stream_processor = __esm({
|
|
|
3947
4586
|
options.mediaStore,
|
|
3948
4587
|
options.agentConfig,
|
|
3949
4588
|
options.subagentConfig,
|
|
3950
|
-
|
|
4589
|
+
wrappedOnSubagentEvent,
|
|
4590
|
+
// Tree context for gadget execution
|
|
4591
|
+
options.tree,
|
|
4592
|
+
options.parentNodeId,
|
|
4593
|
+
options.baseDepth
|
|
3951
4594
|
);
|
|
3952
4595
|
}
|
|
3953
4596
|
/**
|
|
@@ -3998,7 +4641,7 @@ var init_stream_processor = __esm({
|
|
|
3998
4641
|
usage,
|
|
3999
4642
|
logger: this.logger
|
|
4000
4643
|
};
|
|
4001
|
-
await this.hooks.observers
|
|
4644
|
+
await this.hooks.observers?.onStreamChunk?.(context);
|
|
4002
4645
|
});
|
|
4003
4646
|
await this.runObserversInParallel(chunkObservers);
|
|
4004
4647
|
}
|
|
@@ -4016,25 +4659,7 @@ var init_stream_processor = __esm({
|
|
|
4016
4659
|
}
|
|
4017
4660
|
}
|
|
4018
4661
|
}
|
|
4019
|
-
|
|
4020
|
-
this.logger.info("Breaking from LLM stream due to gadget error");
|
|
4021
|
-
break;
|
|
4022
|
-
}
|
|
4023
|
-
}
|
|
4024
|
-
if (!this.executionHalted) {
|
|
4025
|
-
for (const event of this.parser.finalize()) {
|
|
4026
|
-
for await (const processedEvent of this.processEventGenerator(event)) {
|
|
4027
|
-
yield processedEvent;
|
|
4028
|
-
if (processedEvent.type === "gadget_result") {
|
|
4029
|
-
didExecuteGadgets = true;
|
|
4030
|
-
if (processedEvent.result.breaksLoop) {
|
|
4031
|
-
shouldBreakLoop = true;
|
|
4032
|
-
}
|
|
4033
|
-
}
|
|
4034
|
-
}
|
|
4035
|
-
}
|
|
4036
|
-
const inFlightResults = await this.collectInFlightResults();
|
|
4037
|
-
for (const evt of inFlightResults) {
|
|
4662
|
+
for (const evt of this.drainCompletedResults()) {
|
|
4038
4663
|
yield evt;
|
|
4039
4664
|
if (evt.type === "gadget_result") {
|
|
4040
4665
|
didExecuteGadgets = true;
|
|
@@ -4043,16 +4668,45 @@ var init_stream_processor = __esm({
|
|
|
4043
4668
|
}
|
|
4044
4669
|
}
|
|
4045
4670
|
}
|
|
4046
|
-
|
|
4047
|
-
|
|
4048
|
-
|
|
4671
|
+
}
|
|
4672
|
+
for (const event of this.parser.finalize()) {
|
|
4673
|
+
for await (const processedEvent of this.processEventGenerator(event)) {
|
|
4674
|
+
yield processedEvent;
|
|
4675
|
+
if (processedEvent.type === "gadget_result") {
|
|
4049
4676
|
didExecuteGadgets = true;
|
|
4050
|
-
if (
|
|
4677
|
+
if (processedEvent.result.breaksLoop) {
|
|
4051
4678
|
shouldBreakLoop = true;
|
|
4052
4679
|
}
|
|
4053
4680
|
}
|
|
4054
4681
|
}
|
|
4055
4682
|
}
|
|
4683
|
+
for await (const evt of this.waitForInFlightExecutions()) {
|
|
4684
|
+
yield evt;
|
|
4685
|
+
if (evt.type === "gadget_result") {
|
|
4686
|
+
didExecuteGadgets = true;
|
|
4687
|
+
if (evt.result.breaksLoop) {
|
|
4688
|
+
shouldBreakLoop = true;
|
|
4689
|
+
}
|
|
4690
|
+
}
|
|
4691
|
+
}
|
|
4692
|
+
for (const evt of this.drainCompletedResults()) {
|
|
4693
|
+
yield evt;
|
|
4694
|
+
if (evt.type === "gadget_result") {
|
|
4695
|
+
didExecuteGadgets = true;
|
|
4696
|
+
if (evt.result.breaksLoop) {
|
|
4697
|
+
shouldBreakLoop = true;
|
|
4698
|
+
}
|
|
4699
|
+
}
|
|
4700
|
+
}
|
|
4701
|
+
for await (const evt of this.processPendingGadgetsGenerator()) {
|
|
4702
|
+
yield evt;
|
|
4703
|
+
if (evt.type === "gadget_result") {
|
|
4704
|
+
didExecuteGadgets = true;
|
|
4705
|
+
if (evt.result.breaksLoop) {
|
|
4706
|
+
shouldBreakLoop = true;
|
|
4707
|
+
}
|
|
4708
|
+
}
|
|
4709
|
+
}
|
|
4056
4710
|
let finalMessage = this.responseText;
|
|
4057
4711
|
if (this.hooks.interceptors?.interceptAssistantMessage) {
|
|
4058
4712
|
const context = {
|
|
@@ -4073,21 +4727,8 @@ var init_stream_processor = __esm({
|
|
|
4073
4727
|
};
|
|
4074
4728
|
yield completionEvent;
|
|
4075
4729
|
}
|
|
4076
|
-
/**
|
|
4077
|
-
* Process a single parsed event (text or gadget call).
|
|
4078
|
-
* @deprecated Use processEventGenerator for real-time streaming
|
|
4079
|
-
*/
|
|
4080
|
-
async processEvent(event) {
|
|
4081
|
-
if (event.type === "text") {
|
|
4082
|
-
return this.processTextEvent(event);
|
|
4083
|
-
} else if (event.type === "gadget_call") {
|
|
4084
|
-
return this.processGadgetCall(event.call);
|
|
4085
|
-
}
|
|
4086
|
-
return [event];
|
|
4087
|
-
}
|
|
4088
4730
|
/**
|
|
4089
4731
|
* Process a single parsed event, yielding events in real-time.
|
|
4090
|
-
* Generator version of processEvent for streaming support.
|
|
4091
4732
|
*/
|
|
4092
4733
|
async *processEventGenerator(event) {
|
|
4093
4734
|
if (event.type === "text") {
|
|
@@ -4129,12 +4770,6 @@ var init_stream_processor = __esm({
|
|
|
4129
4770
|
* After each execution, pending gadgets are checked to see if they can now run.
|
|
4130
4771
|
*/
|
|
4131
4772
|
async processGadgetCall(call) {
|
|
4132
|
-
if (this.executionHalted) {
|
|
4133
|
-
this.logger.debug("Skipping gadget execution due to previous error", {
|
|
4134
|
-
gadgetName: call.gadgetName
|
|
4135
|
-
});
|
|
4136
|
-
return [];
|
|
4137
|
-
}
|
|
4138
4773
|
const events = [];
|
|
4139
4774
|
events.push({ type: "gadget_call", call });
|
|
4140
4775
|
if (call.dependencies.length > 0) {
|
|
@@ -4185,13 +4820,16 @@ var init_stream_processor = __esm({
|
|
|
4185
4820
|
* when parsed (before execution), enabling real-time UI feedback.
|
|
4186
4821
|
*/
|
|
4187
4822
|
async *processGadgetCallGenerator(call) {
|
|
4188
|
-
|
|
4189
|
-
|
|
4190
|
-
|
|
4823
|
+
yield { type: "gadget_call", call };
|
|
4824
|
+
if (this.tree) {
|
|
4825
|
+
this.tree.addGadget({
|
|
4826
|
+
invocationId: call.invocationId,
|
|
4827
|
+
name: call.gadgetName,
|
|
4828
|
+
parameters: call.parameters ?? {},
|
|
4829
|
+
dependencies: call.dependencies,
|
|
4830
|
+
parentId: this.parentNodeId
|
|
4191
4831
|
});
|
|
4192
|
-
return;
|
|
4193
4832
|
}
|
|
4194
|
-
yield { type: "gadget_call", call };
|
|
4195
4833
|
if (call.dependencies.length > 0) {
|
|
4196
4834
|
if (call.dependencies.includes(call.invocationId)) {
|
|
4197
4835
|
this.logger.warn("Gadget has self-referential dependency (depends on itself)", {
|
|
@@ -4236,17 +4874,8 @@ var init_stream_processor = __esm({
|
|
|
4236
4874
|
}
|
|
4237
4875
|
return;
|
|
4238
4876
|
}
|
|
4239
|
-
|
|
4240
|
-
|
|
4241
|
-
yield evt;
|
|
4242
|
-
}
|
|
4243
|
-
for await (const evt of this.processPendingGadgetsGenerator()) {
|
|
4244
|
-
yield evt;
|
|
4245
|
-
}
|
|
4246
|
-
} else {
|
|
4247
|
-
const executionPromise = this.executeGadgetAndCollect(call);
|
|
4248
|
-
this.inFlightExecutions.set(call.invocationId, executionPromise);
|
|
4249
|
-
}
|
|
4877
|
+
const executionPromise = this.executeGadgetAndCollect(call);
|
|
4878
|
+
this.inFlightExecutions.set(call.invocationId, executionPromise);
|
|
4250
4879
|
}
|
|
4251
4880
|
/**
|
|
4252
4881
|
* Execute a gadget through the full hook lifecycle.
|
|
@@ -4261,15 +4890,6 @@ var init_stream_processor = __esm({
|
|
|
4261
4890
|
error: call.parseError,
|
|
4262
4891
|
rawParameters: call.parametersRaw
|
|
4263
4892
|
});
|
|
4264
|
-
const shouldContinue = await this.checkCanRecoverFromError(
|
|
4265
|
-
call.parseError,
|
|
4266
|
-
call.gadgetName,
|
|
4267
|
-
"parse",
|
|
4268
|
-
call.parameters
|
|
4269
|
-
);
|
|
4270
|
-
if (!shouldContinue) {
|
|
4271
|
-
this.executionHalted = true;
|
|
4272
|
-
}
|
|
4273
4893
|
}
|
|
4274
4894
|
let parameters = call.parameters ?? {};
|
|
4275
4895
|
if (this.hooks.interceptors?.interceptGadgetParameters) {
|
|
@@ -4312,7 +4932,7 @@ var init_stream_processor = __esm({
|
|
|
4312
4932
|
parameters,
|
|
4313
4933
|
logger: this.logger
|
|
4314
4934
|
};
|
|
4315
|
-
await this.hooks.observers
|
|
4935
|
+
await this.hooks.observers?.onGadgetExecutionStart?.(context);
|
|
4316
4936
|
});
|
|
4317
4937
|
}
|
|
4318
4938
|
await this.runObserversInParallel(startObservers);
|
|
@@ -4381,7 +5001,7 @@ var init_stream_processor = __esm({
|
|
|
4381
5001
|
cost: result.cost,
|
|
4382
5002
|
logger: this.logger
|
|
4383
5003
|
};
|
|
4384
|
-
await this.hooks.observers
|
|
5004
|
+
await this.hooks.observers?.onGadgetExecutionComplete?.(context);
|
|
4385
5005
|
});
|
|
4386
5006
|
}
|
|
4387
5007
|
await this.runObserversInParallel(completeObservers);
|
|
@@ -4390,18 +5010,6 @@ var init_stream_processor = __esm({
|
|
|
4390
5010
|
this.failedInvocations.add(result.invocationId);
|
|
4391
5011
|
}
|
|
4392
5012
|
events.push({ type: "gadget_result", result });
|
|
4393
|
-
if (result.error) {
|
|
4394
|
-
const errorType = this.determineErrorType(call, result);
|
|
4395
|
-
const shouldContinue = await this.checkCanRecoverFromError(
|
|
4396
|
-
result.error,
|
|
4397
|
-
result.gadgetName,
|
|
4398
|
-
errorType,
|
|
4399
|
-
result.parameters
|
|
4400
|
-
);
|
|
4401
|
-
if (!shouldContinue) {
|
|
4402
|
-
this.executionHalted = true;
|
|
4403
|
-
}
|
|
4404
|
-
}
|
|
4405
5013
|
return events;
|
|
4406
5014
|
}
|
|
4407
5015
|
/**
|
|
@@ -4415,15 +5023,6 @@ var init_stream_processor = __esm({
|
|
|
4415
5023
|
error: call.parseError,
|
|
4416
5024
|
rawParameters: call.parametersRaw
|
|
4417
5025
|
});
|
|
4418
|
-
const shouldContinue = await this.checkCanRecoverFromError(
|
|
4419
|
-
call.parseError,
|
|
4420
|
-
call.gadgetName,
|
|
4421
|
-
"parse",
|
|
4422
|
-
call.parameters
|
|
4423
|
-
);
|
|
4424
|
-
if (!shouldContinue) {
|
|
4425
|
-
this.executionHalted = true;
|
|
4426
|
-
}
|
|
4427
5026
|
}
|
|
4428
5027
|
let parameters = call.parameters ?? {};
|
|
4429
5028
|
if (this.hooks.interceptors?.interceptGadgetParameters) {
|
|
@@ -4466,10 +5065,16 @@ var init_stream_processor = __esm({
|
|
|
4466
5065
|
parameters,
|
|
4467
5066
|
logger: this.logger
|
|
4468
5067
|
};
|
|
4469
|
-
await this.hooks.observers
|
|
5068
|
+
await this.hooks.observers?.onGadgetExecutionStart?.(context);
|
|
4470
5069
|
});
|
|
4471
5070
|
}
|
|
4472
5071
|
await this.runObserversInParallel(startObservers);
|
|
5072
|
+
if (this.tree) {
|
|
5073
|
+
const gadgetNode = this.tree.getNodeByInvocationId(call.invocationId);
|
|
5074
|
+
if (gadgetNode) {
|
|
5075
|
+
this.tree.startGadget(gadgetNode.id);
|
|
5076
|
+
}
|
|
5077
|
+
}
|
|
4473
5078
|
let result;
|
|
4474
5079
|
if (shouldSkip) {
|
|
4475
5080
|
result = {
|
|
@@ -4535,57 +5140,84 @@ var init_stream_processor = __esm({
|
|
|
4535
5140
|
cost: result.cost,
|
|
4536
5141
|
logger: this.logger
|
|
4537
5142
|
};
|
|
4538
|
-
await this.hooks.observers
|
|
5143
|
+
await this.hooks.observers?.onGadgetExecutionComplete?.(context);
|
|
4539
5144
|
});
|
|
4540
5145
|
}
|
|
4541
5146
|
await this.runObserversInParallel(completeObservers);
|
|
5147
|
+
if (this.tree) {
|
|
5148
|
+
const gadgetNode = this.tree.getNodeByInvocationId(result.invocationId);
|
|
5149
|
+
if (gadgetNode) {
|
|
5150
|
+
if (result.error) {
|
|
5151
|
+
this.tree.completeGadget(gadgetNode.id, {
|
|
5152
|
+
error: result.error,
|
|
5153
|
+
executionTimeMs: result.executionTimeMs,
|
|
5154
|
+
cost: result.cost
|
|
5155
|
+
});
|
|
5156
|
+
} else {
|
|
5157
|
+
this.tree.completeGadget(gadgetNode.id, {
|
|
5158
|
+
result: result.result,
|
|
5159
|
+
executionTimeMs: result.executionTimeMs,
|
|
5160
|
+
cost: result.cost,
|
|
5161
|
+
media: result.media
|
|
5162
|
+
});
|
|
5163
|
+
}
|
|
5164
|
+
}
|
|
5165
|
+
}
|
|
4542
5166
|
this.completedResults.set(result.invocationId, result);
|
|
4543
5167
|
if (result.error) {
|
|
4544
5168
|
this.failedInvocations.add(result.invocationId);
|
|
4545
5169
|
}
|
|
4546
5170
|
yield { type: "gadget_result", result };
|
|
4547
|
-
if (result.error) {
|
|
4548
|
-
const errorType = this.determineErrorType(call, result);
|
|
4549
|
-
const shouldContinue = await this.checkCanRecoverFromError(
|
|
4550
|
-
result.error,
|
|
4551
|
-
result.gadgetName,
|
|
4552
|
-
errorType,
|
|
4553
|
-
result.parameters
|
|
4554
|
-
);
|
|
4555
|
-
if (!shouldContinue) {
|
|
4556
|
-
this.executionHalted = true;
|
|
4557
|
-
}
|
|
4558
|
-
}
|
|
4559
5171
|
}
|
|
4560
5172
|
/**
|
|
4561
|
-
* Execute a gadget and
|
|
5173
|
+
* Execute a gadget and push events to the completed results queue (non-blocking).
|
|
4562
5174
|
* Used for fire-and-forget parallel execution of independent gadgets.
|
|
5175
|
+
* Results are pushed to completedResultsQueue for real-time streaming to the caller.
|
|
4563
5176
|
*/
|
|
4564
5177
|
async executeGadgetAndCollect(call) {
|
|
4565
|
-
const events = [];
|
|
4566
5178
|
for await (const evt of this.executeGadgetGenerator(call)) {
|
|
4567
|
-
|
|
5179
|
+
this.completedResultsQueue.push(evt);
|
|
4568
5180
|
}
|
|
4569
|
-
return events;
|
|
4570
5181
|
}
|
|
4571
5182
|
/**
|
|
4572
|
-
*
|
|
4573
|
-
*
|
|
4574
|
-
*
|
|
4575
|
-
* @returns Array of all events from completed gadgets
|
|
5183
|
+
* Drain all completed results from the queue.
|
|
5184
|
+
* Used to yield results as they complete during stream processing.
|
|
5185
|
+
* @returns Generator that yields all events currently in the queue
|
|
4576
5186
|
*/
|
|
4577
|
-
|
|
5187
|
+
*drainCompletedResults() {
|
|
5188
|
+
while (this.completedResultsQueue.length > 0) {
|
|
5189
|
+
yield this.completedResultsQueue.shift();
|
|
5190
|
+
}
|
|
5191
|
+
}
|
|
5192
|
+
/**
|
|
5193
|
+
* Wait for all in-flight gadget executions to complete, yielding events in real-time.
|
|
5194
|
+
* Called at stream end to ensure all parallel executions finish.
|
|
5195
|
+
* Results and subagent events are pushed to completedResultsQueue during execution.
|
|
5196
|
+
* This generator yields queued events while polling, enabling real-time display
|
|
5197
|
+
* of subagent activity (LLM calls, nested gadgets) during long-running gadgets.
|
|
5198
|
+
* Clears the inFlightExecutions map after all gadgets complete.
|
|
5199
|
+
*/
|
|
5200
|
+
async *waitForInFlightExecutions() {
|
|
4578
5201
|
if (this.inFlightExecutions.size === 0) {
|
|
4579
|
-
return
|
|
5202
|
+
return;
|
|
4580
5203
|
}
|
|
4581
|
-
this.logger.debug("
|
|
5204
|
+
this.logger.debug("Waiting for in-flight gadget executions", {
|
|
4582
5205
|
count: this.inFlightExecutions.size,
|
|
4583
5206
|
invocationIds: Array.from(this.inFlightExecutions.keys())
|
|
4584
5207
|
});
|
|
4585
|
-
const
|
|
4586
|
-
const
|
|
5208
|
+
const allDone = Promise.all(this.inFlightExecutions.values()).then(() => "done");
|
|
5209
|
+
const POLL_INTERVAL_MS = 100;
|
|
5210
|
+
while (true) {
|
|
5211
|
+
const result = await Promise.race([
|
|
5212
|
+
allDone,
|
|
5213
|
+
new Promise((resolve) => setTimeout(() => resolve("poll"), POLL_INTERVAL_MS))
|
|
5214
|
+
]);
|
|
5215
|
+
yield* this.drainCompletedResults();
|
|
5216
|
+
if (result === "done") {
|
|
5217
|
+
break;
|
|
5218
|
+
}
|
|
5219
|
+
}
|
|
4587
5220
|
this.inFlightExecutions.clear();
|
|
4588
|
-
return results.flat();
|
|
4589
5221
|
}
|
|
4590
5222
|
/**
|
|
4591
5223
|
* Handle a gadget that cannot execute because a dependency failed.
|
|
@@ -4610,6 +5242,12 @@ var init_stream_processor = __esm({
|
|
|
4610
5242
|
}
|
|
4611
5243
|
if (action.action === "skip") {
|
|
4612
5244
|
this.failedInvocations.add(call.invocationId);
|
|
5245
|
+
if (this.tree) {
|
|
5246
|
+
const gadgetNode = this.tree.getNodeByInvocationId(call.invocationId);
|
|
5247
|
+
if (gadgetNode) {
|
|
5248
|
+
this.tree.skipGadget(gadgetNode.id, failedDep, depError, "dependency_failed");
|
|
5249
|
+
}
|
|
5250
|
+
}
|
|
4613
5251
|
const skipEvent = {
|
|
4614
5252
|
type: "gadget_skipped",
|
|
4615
5253
|
gadgetName: call.gadgetName,
|
|
@@ -4629,7 +5267,7 @@ var init_stream_processor = __esm({
|
|
|
4629
5267
|
failedDependencyError: depError,
|
|
4630
5268
|
logger: this.logger
|
|
4631
5269
|
};
|
|
4632
|
-
await this.safeObserve(() => this.hooks.observers
|
|
5270
|
+
await this.safeObserve(() => this.hooks.observers?.onGadgetSkipped?.(observeContext));
|
|
4633
5271
|
}
|
|
4634
5272
|
this.logger.info("Gadget skipped due to failed dependency", {
|
|
4635
5273
|
gadgetName: call.gadgetName,
|
|
@@ -4861,48 +5499,6 @@ var init_stream_processor = __esm({
|
|
|
4861
5499
|
observers.map((observer) => this.safeObserve(observer))
|
|
4862
5500
|
);
|
|
4863
5501
|
}
|
|
4864
|
-
/**
|
|
4865
|
-
* Check if execution can recover from an error.
|
|
4866
|
-
*
|
|
4867
|
-
* Returns true if we should continue processing subsequent gadgets, false if we should stop.
|
|
4868
|
-
*
|
|
4869
|
-
* Logic:
|
|
4870
|
-
* - If custom canRecoverFromGadgetError is provided, use it
|
|
4871
|
-
* - Otherwise, use stopOnGadgetError config:
|
|
4872
|
-
* - stopOnGadgetError=true → return false (stop execution)
|
|
4873
|
-
* - stopOnGadgetError=false → return true (continue execution)
|
|
4874
|
-
*/
|
|
4875
|
-
async checkCanRecoverFromError(error, gadgetName, errorType, parameters) {
|
|
4876
|
-
if (this.canRecoverFromGadgetError) {
|
|
4877
|
-
return await this.canRecoverFromGadgetError({
|
|
4878
|
-
error,
|
|
4879
|
-
gadgetName,
|
|
4880
|
-
errorType,
|
|
4881
|
-
parameters
|
|
4882
|
-
});
|
|
4883
|
-
}
|
|
4884
|
-
const shouldContinue = !this.stopOnGadgetError;
|
|
4885
|
-
this.logger.debug("Checking if should continue after error", {
|
|
4886
|
-
error,
|
|
4887
|
-
gadgetName,
|
|
4888
|
-
errorType,
|
|
4889
|
-
stopOnGadgetError: this.stopOnGadgetError,
|
|
4890
|
-
shouldContinue
|
|
4891
|
-
});
|
|
4892
|
-
return shouldContinue;
|
|
4893
|
-
}
|
|
4894
|
-
/**
|
|
4895
|
-
* Determine the type of error from a gadget execution.
|
|
4896
|
-
*/
|
|
4897
|
-
determineErrorType(call, result) {
|
|
4898
|
-
if (call.parseError) {
|
|
4899
|
-
return "parse";
|
|
4900
|
-
}
|
|
4901
|
-
if (result.error?.includes("Invalid parameters:")) {
|
|
4902
|
-
return "validation";
|
|
4903
|
-
}
|
|
4904
|
-
return "execution";
|
|
4905
|
-
}
|
|
4906
5502
|
};
|
|
4907
5503
|
}
|
|
4908
5504
|
});
|
|
@@ -4913,6 +5509,7 @@ var init_agent = __esm({
|
|
|
4913
5509
|
"src/agent/agent.ts"() {
|
|
4914
5510
|
"use strict";
|
|
4915
5511
|
init_constants();
|
|
5512
|
+
init_execution_tree();
|
|
4916
5513
|
init_messages();
|
|
4917
5514
|
init_model_shortcuts();
|
|
4918
5515
|
init_media_store();
|
|
@@ -4940,8 +5537,6 @@ var init_agent = __esm({
|
|
|
4940
5537
|
requestHumanInput;
|
|
4941
5538
|
textOnlyHandler;
|
|
4942
5539
|
textWithGadgetsHandler;
|
|
4943
|
-
stopOnGadgetError;
|
|
4944
|
-
canRecoverFromGadgetError;
|
|
4945
5540
|
defaultGadgetTimeoutMs;
|
|
4946
5541
|
defaultMaxTokens;
|
|
4947
5542
|
hasUserPrompt;
|
|
@@ -4964,6 +5559,12 @@ var init_agent = __esm({
|
|
|
4964
5559
|
pendingSubagentEvents = [];
|
|
4965
5560
|
// Combined callback that queues events AND calls user callback
|
|
4966
5561
|
onSubagentEvent;
|
|
5562
|
+
// Counter for generating synthetic invocation IDs for wrapped text content
|
|
5563
|
+
syntheticInvocationCounter = 0;
|
|
5564
|
+
// Execution Tree - first-class model for nested subagent support
|
|
5565
|
+
tree;
|
|
5566
|
+
parentNodeId;
|
|
5567
|
+
baseDepth;
|
|
4967
5568
|
/**
|
|
4968
5569
|
* Creates a new Agent instance.
|
|
4969
5570
|
* @internal This constructor is private. Use LLMist.createAgent() or AgentBuilder instead.
|
|
@@ -4986,8 +5587,6 @@ var init_agent = __esm({
|
|
|
4986
5587
|
this.requestHumanInput = options.requestHumanInput;
|
|
4987
5588
|
this.textOnlyHandler = options.textOnlyHandler ?? "terminate";
|
|
4988
5589
|
this.textWithGadgetsHandler = options.textWithGadgetsHandler;
|
|
4989
|
-
this.stopOnGadgetError = options.stopOnGadgetError ?? true;
|
|
4990
|
-
this.canRecoverFromGadgetError = options.canRecoverFromGadgetError;
|
|
4991
5590
|
this.defaultGadgetTimeoutMs = options.defaultGadgetTimeoutMs;
|
|
4992
5591
|
this.defaultMaxTokens = this.resolveMaxTokensFromCatalog(options.model);
|
|
4993
5592
|
this.outputLimitEnabled = options.gadgetOutputLimit ?? DEFAULT_GADGET_OUTPUT_LIMIT;
|
|
@@ -5041,6 +5640,9 @@ var init_agent = __esm({
|
|
|
5041
5640
|
temperature: this.temperature
|
|
5042
5641
|
};
|
|
5043
5642
|
this.subagentConfig = options.subagentConfig;
|
|
5643
|
+
this.tree = options.parentTree ?? new ExecutionTree();
|
|
5644
|
+
this.parentNodeId = options.parentNodeId ?? null;
|
|
5645
|
+
this.baseDepth = options.baseDepth ?? 0;
|
|
5044
5646
|
this.userSubagentEventCallback = options.onSubagentEvent;
|
|
5045
5647
|
this.onSubagentEvent = (event) => {
|
|
5046
5648
|
this.pendingSubagentEvents.push(event);
|
|
@@ -5105,7 +5707,9 @@ var init_agent = __esm({
|
|
|
5105
5707
|
*flushPendingSubagentEvents() {
|
|
5106
5708
|
while (this.pendingSubagentEvents.length > 0) {
|
|
5107
5709
|
const event = this.pendingSubagentEvents.shift();
|
|
5108
|
-
|
|
5710
|
+
if (event) {
|
|
5711
|
+
yield { type: "subagent_event", subagentEvent: event };
|
|
5712
|
+
}
|
|
5109
5713
|
}
|
|
5110
5714
|
}
|
|
5111
5715
|
/**
|
|
@@ -5159,6 +5763,48 @@ var init_agent = __esm({
|
|
|
5159
5763
|
getMediaStore() {
|
|
5160
5764
|
return this.mediaStore;
|
|
5161
5765
|
}
|
|
5766
|
+
/**
|
|
5767
|
+
* Get the execution tree for this agent.
|
|
5768
|
+
*
|
|
5769
|
+
* The execution tree provides a first-class model of all LLM calls and gadget executions,
|
|
5770
|
+
* including nested subagent activity. Use this to:
|
|
5771
|
+
* - Query execution state: `tree.getNode(id)`
|
|
5772
|
+
* - Get total cost: `tree.getTotalCost()`
|
|
5773
|
+
* - Get subtree cost/media/tokens: `tree.getSubtreeCost(nodeId)`
|
|
5774
|
+
* - Subscribe to events: `tree.on("llm_call_complete", handler)`
|
|
5775
|
+
* - Stream all events: `for await (const event of tree.events())`
|
|
5776
|
+
*
|
|
5777
|
+
* For subagents (created with `withParentContext`), the tree is shared with the parent,
|
|
5778
|
+
* enabling unified tracking and real-time visibility across all nesting levels.
|
|
5779
|
+
*
|
|
5780
|
+
* @returns The ExecutionTree instance
|
|
5781
|
+
*
|
|
5782
|
+
* @example
|
|
5783
|
+
* ```typescript
|
|
5784
|
+
* const agent = LLMist.createAgent()
|
|
5785
|
+
* .withModel("sonnet")
|
|
5786
|
+
* .withGadgets(BrowseWeb)
|
|
5787
|
+
* .ask("Research topic X");
|
|
5788
|
+
*
|
|
5789
|
+
* for await (const event of agent.run()) {
|
|
5790
|
+
* // Process events...
|
|
5791
|
+
* }
|
|
5792
|
+
*
|
|
5793
|
+
* // After execution, query the tree
|
|
5794
|
+
* const tree = agent.getTree();
|
|
5795
|
+
* console.log(`Total cost: $${tree.getTotalCost().toFixed(4)}`);
|
|
5796
|
+
*
|
|
5797
|
+
* // Inspect all LLM calls
|
|
5798
|
+
* for (const node of tree.getAllNodes()) {
|
|
5799
|
+
* if (node.type === "llm_call") {
|
|
5800
|
+
* console.log(`LLM #${node.iteration}: ${node.model}`);
|
|
5801
|
+
* }
|
|
5802
|
+
* }
|
|
5803
|
+
* ```
|
|
5804
|
+
*/
|
|
5805
|
+
getTree() {
|
|
5806
|
+
return this.tree;
|
|
5807
|
+
}
|
|
5162
5808
|
/**
|
|
5163
5809
|
* Manually trigger context compaction.
|
|
5164
5810
|
*
|
|
@@ -5260,6 +5906,7 @@ var init_agent = __esm({
|
|
|
5260
5906
|
await this.hooks.observers.onCompaction({
|
|
5261
5907
|
iteration: currentIteration,
|
|
5262
5908
|
event: compactionEvent,
|
|
5909
|
+
// biome-ignore lint/style/noNonNullAssertion: compactionManager exists if compactionEvent is truthy
|
|
5263
5910
|
stats: this.compactionManager.getStats(),
|
|
5264
5911
|
logger: this.logger
|
|
5265
5912
|
});
|
|
@@ -5321,6 +5968,13 @@ var init_agent = __esm({
|
|
|
5321
5968
|
messageCount: llmOptions.messages.length,
|
|
5322
5969
|
messages: llmOptions.messages
|
|
5323
5970
|
});
|
|
5971
|
+
const llmNode = this.tree.addLLMCall({
|
|
5972
|
+
iteration: currentIteration,
|
|
5973
|
+
model: llmOptions.model,
|
|
5974
|
+
parentId: this.parentNodeId,
|
|
5975
|
+
request: llmOptions.messages
|
|
5976
|
+
});
|
|
5977
|
+
const currentLLMNodeId = llmNode.id;
|
|
5324
5978
|
const stream2 = this.client.stream(llmOptions);
|
|
5325
5979
|
const processor = new StreamProcessor({
|
|
5326
5980
|
iteration: currentIteration,
|
|
@@ -5331,14 +5985,17 @@ var init_agent = __esm({
|
|
|
5331
5985
|
hooks: this.hooks,
|
|
5332
5986
|
logger: this.logger.getSubLogger({ name: "stream-processor" }),
|
|
5333
5987
|
requestHumanInput: this.requestHumanInput,
|
|
5334
|
-
stopOnGadgetError: this.stopOnGadgetError,
|
|
5335
|
-
canRecoverFromGadgetError: this.canRecoverFromGadgetError,
|
|
5336
5988
|
defaultGadgetTimeoutMs: this.defaultGadgetTimeoutMs,
|
|
5337
5989
|
client: this.client,
|
|
5338
5990
|
mediaStore: this.mediaStore,
|
|
5339
5991
|
agentConfig: this.agentContextConfig,
|
|
5340
5992
|
subagentConfig: this.subagentConfig,
|
|
5341
|
-
onSubagentEvent: this.onSubagentEvent
|
|
5993
|
+
onSubagentEvent: this.onSubagentEvent,
|
|
5994
|
+
// Tree context for execution tracking
|
|
5995
|
+
tree: this.tree,
|
|
5996
|
+
parentNodeId: currentLLMNodeId,
|
|
5997
|
+
// Gadgets are children of this LLM call
|
|
5998
|
+
baseDepth: this.baseDepth
|
|
5342
5999
|
});
|
|
5343
6000
|
let streamMetadata = null;
|
|
5344
6001
|
let gadgetCallCount = 0;
|
|
@@ -5384,6 +6041,11 @@ var init_agent = __esm({
|
|
|
5384
6041
|
await this.hooks.observers.onLLMCallComplete(context);
|
|
5385
6042
|
}
|
|
5386
6043
|
});
|
|
6044
|
+
this.tree.completeLLMCall(currentLLMNodeId, {
|
|
6045
|
+
response: result.rawResponse,
|
|
6046
|
+
usage: result.usage,
|
|
6047
|
+
finishReason: result.finishReason
|
|
6048
|
+
});
|
|
5387
6049
|
let finalMessage = result.finalMessage;
|
|
5388
6050
|
if (this.hooks.controllers?.afterLLMCall) {
|
|
5389
6051
|
const context = {
|
|
@@ -5418,10 +6080,12 @@ var init_agent = __esm({
|
|
|
5418
6080
|
const textContent = textOutputs.join("");
|
|
5419
6081
|
if (textContent.trim()) {
|
|
5420
6082
|
const { gadgetName, parameterMapping, resultMapping } = this.textWithGadgetsHandler;
|
|
6083
|
+
const syntheticId = `gc_text_${++this.syntheticInvocationCounter}`;
|
|
5421
6084
|
this.conversation.addGadgetCallResult(
|
|
5422
6085
|
gadgetName,
|
|
5423
6086
|
parameterMapping(textContent),
|
|
5424
|
-
resultMapping ? resultMapping(textContent) : textContent
|
|
6087
|
+
resultMapping ? resultMapping(textContent) : textContent,
|
|
6088
|
+
syntheticId
|
|
5425
6089
|
);
|
|
5426
6090
|
}
|
|
5427
6091
|
}
|
|
@@ -5432,6 +6096,7 @@ var init_agent = __esm({
|
|
|
5432
6096
|
gadgetResult.gadgetName,
|
|
5433
6097
|
gadgetResult.parameters,
|
|
5434
6098
|
gadgetResult.error ?? gadgetResult.result ?? "",
|
|
6099
|
+
gadgetResult.invocationId,
|
|
5435
6100
|
gadgetResult.media,
|
|
5436
6101
|
gadgetResult.mediaIds
|
|
5437
6102
|
);
|
|
@@ -5439,10 +6104,12 @@ var init_agent = __esm({
|
|
|
5439
6104
|
}
|
|
5440
6105
|
} else {
|
|
5441
6106
|
if (finalMessage.trim()) {
|
|
6107
|
+
const syntheticId = `gc_tell_${++this.syntheticInvocationCounter}`;
|
|
5442
6108
|
this.conversation.addGadgetCallResult(
|
|
5443
6109
|
"TellUser",
|
|
5444
6110
|
{ message: finalMessage, done: false, type: "info" },
|
|
5445
|
-
`\u2139\uFE0F ${finalMessage}
|
|
6111
|
+
`\u2139\uFE0F ${finalMessage}`,
|
|
6112
|
+
syntheticId
|
|
5446
6113
|
);
|
|
5447
6114
|
}
|
|
5448
6115
|
const shouldBreak = await this.handleTextOnlyResponse(finalMessage);
|
|
@@ -5653,8 +6320,6 @@ var init_builder = __esm({
|
|
|
5653
6320
|
gadgetArgPrefix;
|
|
5654
6321
|
textOnlyHandler;
|
|
5655
6322
|
textWithGadgetsHandler;
|
|
5656
|
-
stopOnGadgetError;
|
|
5657
|
-
canRecoverFromGadgetError;
|
|
5658
6323
|
defaultGadgetTimeoutMs;
|
|
5659
6324
|
gadgetOutputLimit;
|
|
5660
6325
|
gadgetOutputLimitPercent;
|
|
@@ -5663,6 +6328,8 @@ var init_builder = __esm({
|
|
|
5663
6328
|
trailingMessage;
|
|
5664
6329
|
subagentConfig;
|
|
5665
6330
|
subagentEventCallback;
|
|
6331
|
+
// Tree context for subagent support - enables shared tree model
|
|
6332
|
+
// When a gadget calls withParentContext(ctx), it shares the parent's tree
|
|
5666
6333
|
parentContext;
|
|
5667
6334
|
constructor(client) {
|
|
5668
6335
|
this.client = client;
|
|
@@ -5945,62 +6612,6 @@ var init_builder = __esm({
|
|
|
5945
6612
|
this.textWithGadgetsHandler = handler;
|
|
5946
6613
|
return this;
|
|
5947
6614
|
}
|
|
5948
|
-
/**
|
|
5949
|
-
* Set whether to stop gadget execution on first error.
|
|
5950
|
-
*
|
|
5951
|
-
* When true (default), if a gadget fails:
|
|
5952
|
-
* - Subsequent gadgets in the same response are skipped
|
|
5953
|
-
* - LLM stream is cancelled to save costs
|
|
5954
|
-
* - Agent loop continues with error in context
|
|
5955
|
-
*
|
|
5956
|
-
* When false:
|
|
5957
|
-
* - All gadgets in the response still execute
|
|
5958
|
-
* - LLM stream continues to completion
|
|
5959
|
-
*
|
|
5960
|
-
* @param stop - Whether to stop on gadget error
|
|
5961
|
-
* @returns This builder for chaining
|
|
5962
|
-
*
|
|
5963
|
-
* @example
|
|
5964
|
-
* ```typescript
|
|
5965
|
-
* .withStopOnGadgetError(false)
|
|
5966
|
-
* ```
|
|
5967
|
-
*/
|
|
5968
|
-
withStopOnGadgetError(stop) {
|
|
5969
|
-
this.stopOnGadgetError = stop;
|
|
5970
|
-
return this;
|
|
5971
|
-
}
|
|
5972
|
-
/**
|
|
5973
|
-
* Set custom error handling logic.
|
|
5974
|
-
*
|
|
5975
|
-
* Provides fine-grained control over whether to continue after different types of errors.
|
|
5976
|
-
* Overrides `stopOnGadgetError` when provided.
|
|
5977
|
-
*
|
|
5978
|
-
* **Note:** This builder method configures the underlying `canRecoverFromGadgetError` option
|
|
5979
|
-
* in `AgentOptions`. The method is named `withErrorHandler` for better developer experience,
|
|
5980
|
-
* but maps to the `canRecoverFromGadgetError` property internally.
|
|
5981
|
-
*
|
|
5982
|
-
* @param handler - Function that decides whether to continue after an error.
|
|
5983
|
-
* Return `true` to continue execution, `false` to stop.
|
|
5984
|
-
* @returns This builder for chaining
|
|
5985
|
-
*
|
|
5986
|
-
* @example
|
|
5987
|
-
* ```typescript
|
|
5988
|
-
* .withErrorHandler((context) => {
|
|
5989
|
-
* // Stop on parse errors, continue on validation/execution errors
|
|
5990
|
-
* if (context.errorType === "parse") {
|
|
5991
|
-
* return false;
|
|
5992
|
-
* }
|
|
5993
|
-
* if (context.error.includes("CRITICAL")) {
|
|
5994
|
-
* return false;
|
|
5995
|
-
* }
|
|
5996
|
-
* return true;
|
|
5997
|
-
* })
|
|
5998
|
-
* ```
|
|
5999
|
-
*/
|
|
6000
|
-
withErrorHandler(handler) {
|
|
6001
|
-
this.canRecoverFromGadgetError = handler;
|
|
6002
|
-
return this;
|
|
6003
|
-
}
|
|
6004
6615
|
/**
|
|
6005
6616
|
* Set default timeout for gadget execution.
|
|
6006
6617
|
*
|
|
@@ -6195,6 +6806,15 @@ var init_builder = __esm({
|
|
|
6195
6806
|
* The method extracts `invocationId` and `onSubagentEvent` from the execution
|
|
6196
6807
|
* context and sets up automatic forwarding via hooks and event wrapping.
|
|
6197
6808
|
*
|
|
6809
|
+
* **NEW: Shared Tree Model** - When the parent provides an ExecutionTree via context,
|
|
6810
|
+
* the subagent shares that tree instead of creating its own. This enables:
|
|
6811
|
+
* - Unified cost tracking across all nesting levels
|
|
6812
|
+
* - Automatic media aggregation via `tree.getSubtreeMedia(nodeId)`
|
|
6813
|
+
* - Real-time visibility of nested execution in the parent
|
|
6814
|
+
*
|
|
6815
|
+
* **Signal Forwarding** - When parent context includes a signal, it's automatically
|
|
6816
|
+
* forwarded to the subagent for proper cancellation propagation.
|
|
6817
|
+
*
|
|
6198
6818
|
* @param ctx - ExecutionContext passed to the gadget's execute() method
|
|
6199
6819
|
* @param depth - Nesting depth (default: 1 for direct child)
|
|
6200
6820
|
* @returns This builder for chaining
|
|
@@ -6215,17 +6835,25 @@ var init_builder = __esm({
|
|
|
6215
6835
|
* result = event.content;
|
|
6216
6836
|
* }
|
|
6217
6837
|
* }
|
|
6838
|
+
*
|
|
6839
|
+
* // After subagent completes, costs are automatically aggregated
|
|
6840
|
+
* // No manual tracking needed - use tree methods:
|
|
6841
|
+
* const totalCost = ctx.tree?.getSubtreeCost(ctx.nodeId!);
|
|
6842
|
+
* const allMedia = ctx.tree?.getSubtreeMedia(ctx.nodeId!);
|
|
6218
6843
|
* }
|
|
6219
6844
|
* ```
|
|
6220
6845
|
*/
|
|
6221
6846
|
withParentContext(ctx, depth = 1) {
|
|
6222
|
-
if (ctx.
|
|
6847
|
+
if (ctx.tree) {
|
|
6223
6848
|
this.parentContext = {
|
|
6224
|
-
|
|
6225
|
-
|
|
6849
|
+
tree: ctx.tree,
|
|
6850
|
+
nodeId: ctx.nodeId,
|
|
6226
6851
|
depth
|
|
6227
6852
|
};
|
|
6228
6853
|
}
|
|
6854
|
+
if (ctx.signal && !this.signal) {
|
|
6855
|
+
this.signal = ctx.signal;
|
|
6856
|
+
}
|
|
6229
6857
|
return this;
|
|
6230
6858
|
}
|
|
6231
6859
|
/**
|
|
@@ -6258,11 +6886,13 @@ var init_builder = __esm({
|
|
|
6258
6886
|
*
|
|
6259
6887
|
* This is useful for in-context learning - showing the LLM what "past self"
|
|
6260
6888
|
* did correctly so it mimics the pattern. The call is formatted with proper
|
|
6261
|
-
* markers and parameter format
|
|
6889
|
+
* markers and parameter format, including the invocation ID so the LLM can
|
|
6890
|
+
* reference previous calls when building dependencies.
|
|
6262
6891
|
*
|
|
6263
6892
|
* @param gadgetName - Name of the gadget
|
|
6264
6893
|
* @param parameters - Parameters passed to the gadget
|
|
6265
6894
|
* @param result - Result returned by the gadget
|
|
6895
|
+
* @param invocationId - Invocation ID (shown to LLM so it can reference for dependencies)
|
|
6266
6896
|
* @returns This builder for chaining
|
|
6267
6897
|
*
|
|
6268
6898
|
* @example
|
|
@@ -6274,124 +6904,36 @@ var init_builder = __esm({
|
|
|
6274
6904
|
* done: false,
|
|
6275
6905
|
* type: 'info'
|
|
6276
6906
|
* },
|
|
6277
|
-
* 'ℹ️ 👋 Hello!\n\nHere\'s what I can do:\n- Analyze code\n- Run commands'
|
|
6907
|
+
* 'ℹ️ 👋 Hello!\n\nHere\'s what I can do:\n- Analyze code\n- Run commands',
|
|
6908
|
+
* 'gc_1'
|
|
6278
6909
|
* )
|
|
6279
6910
|
* ```
|
|
6280
6911
|
*/
|
|
6281
|
-
withSyntheticGadgetCall(gadgetName, parameters, result) {
|
|
6912
|
+
withSyntheticGadgetCall(gadgetName, parameters, result, invocationId) {
|
|
6282
6913
|
const startPrefix = this.gadgetStartPrefix ?? GADGET_START_PREFIX;
|
|
6283
6914
|
const endPrefix = this.gadgetEndPrefix ?? GADGET_END_PREFIX;
|
|
6284
6915
|
const paramStr = this.formatBlockParameters(parameters, "");
|
|
6285
6916
|
this.initialMessages.push({
|
|
6286
6917
|
role: "assistant",
|
|
6287
|
-
content: `${startPrefix}${gadgetName}
|
|
6918
|
+
content: `${startPrefix}${gadgetName}:${invocationId}
|
|
6288
6919
|
${paramStr}
|
|
6289
6920
|
${endPrefix}`
|
|
6290
6921
|
});
|
|
6291
6922
|
this.initialMessages.push({
|
|
6292
6923
|
role: "user",
|
|
6293
|
-
content: `Result: ${result}`
|
|
6924
|
+
content: `Result (${invocationId}): ${result}`
|
|
6294
6925
|
});
|
|
6295
6926
|
return this;
|
|
6296
6927
|
}
|
|
6297
6928
|
/**
|
|
6298
|
-
* Compose the final hooks, including
|
|
6299
|
-
*
|
|
6300
|
-
*
|
|
6929
|
+
* Compose the final hooks, including trailing message injection if configured.
|
|
6930
|
+
*
|
|
6931
|
+
* Note: Subagent event visibility is now handled entirely by the ExecutionTree.
|
|
6932
|
+
* When a subagent uses withParentContext(ctx), it shares the parent's tree,
|
|
6933
|
+
* and all events are automatically visible to tree subscribers (like the TUI).
|
|
6301
6934
|
*/
|
|
6302
6935
|
composeHooks() {
|
|
6303
|
-
|
|
6304
|
-
if (this.parentContext) {
|
|
6305
|
-
const { invocationId, onSubagentEvent, depth } = this.parentContext;
|
|
6306
|
-
const existingOnLLMCallStart = hooks?.observers?.onLLMCallStart;
|
|
6307
|
-
const existingOnLLMCallComplete = hooks?.observers?.onLLMCallComplete;
|
|
6308
|
-
const existingOnGadgetExecutionStart = hooks?.observers?.onGadgetExecutionStart;
|
|
6309
|
-
const existingOnGadgetExecutionComplete = hooks?.observers?.onGadgetExecutionComplete;
|
|
6310
|
-
hooks = {
|
|
6311
|
-
...hooks,
|
|
6312
|
-
observers: {
|
|
6313
|
-
...hooks?.observers,
|
|
6314
|
-
onLLMCallStart: async (context) => {
|
|
6315
|
-
let inputTokens;
|
|
6316
|
-
try {
|
|
6317
|
-
if (this.client) {
|
|
6318
|
-
inputTokens = await this.client.countTokens(
|
|
6319
|
-
context.options.model,
|
|
6320
|
-
context.options.messages
|
|
6321
|
-
);
|
|
6322
|
-
}
|
|
6323
|
-
} catch {
|
|
6324
|
-
}
|
|
6325
|
-
onSubagentEvent({
|
|
6326
|
-
type: "llm_call_start",
|
|
6327
|
-
gadgetInvocationId: invocationId,
|
|
6328
|
-
depth,
|
|
6329
|
-
event: {
|
|
6330
|
-
iteration: context.iteration,
|
|
6331
|
-
model: context.options.model,
|
|
6332
|
-
inputTokens
|
|
6333
|
-
}
|
|
6334
|
-
});
|
|
6335
|
-
if (existingOnLLMCallStart) {
|
|
6336
|
-
await existingOnLLMCallStart(context);
|
|
6337
|
-
}
|
|
6338
|
-
},
|
|
6339
|
-
onLLMCallComplete: async (context) => {
|
|
6340
|
-
onSubagentEvent({
|
|
6341
|
-
type: "llm_call_end",
|
|
6342
|
-
gadgetInvocationId: invocationId,
|
|
6343
|
-
depth,
|
|
6344
|
-
event: {
|
|
6345
|
-
iteration: context.iteration,
|
|
6346
|
-
model: context.options.model,
|
|
6347
|
-
// Backward compat fields
|
|
6348
|
-
inputTokens: context.usage?.inputTokens,
|
|
6349
|
-
outputTokens: context.usage?.outputTokens,
|
|
6350
|
-
finishReason: context.finishReason ?? void 0,
|
|
6351
|
-
// Full usage object with cache details (for first-class display)
|
|
6352
|
-
usage: context.usage
|
|
6353
|
-
// Cost will be calculated by parent if it has model registry
|
|
6354
|
-
}
|
|
6355
|
-
});
|
|
6356
|
-
if (existingOnLLMCallComplete) {
|
|
6357
|
-
await existingOnLLMCallComplete(context);
|
|
6358
|
-
}
|
|
6359
|
-
},
|
|
6360
|
-
onGadgetExecutionStart: async (context) => {
|
|
6361
|
-
onSubagentEvent({
|
|
6362
|
-
type: "gadget_call",
|
|
6363
|
-
gadgetInvocationId: invocationId,
|
|
6364
|
-
depth,
|
|
6365
|
-
event: {
|
|
6366
|
-
call: {
|
|
6367
|
-
invocationId: context.invocationId,
|
|
6368
|
-
gadgetName: context.gadgetName,
|
|
6369
|
-
parameters: context.parameters
|
|
6370
|
-
}
|
|
6371
|
-
}
|
|
6372
|
-
});
|
|
6373
|
-
if (existingOnGadgetExecutionStart) {
|
|
6374
|
-
await existingOnGadgetExecutionStart(context);
|
|
6375
|
-
}
|
|
6376
|
-
},
|
|
6377
|
-
onGadgetExecutionComplete: async (context) => {
|
|
6378
|
-
onSubagentEvent({
|
|
6379
|
-
type: "gadget_result",
|
|
6380
|
-
gadgetInvocationId: invocationId,
|
|
6381
|
-
depth,
|
|
6382
|
-
event: {
|
|
6383
|
-
result: {
|
|
6384
|
-
invocationId: context.invocationId
|
|
6385
|
-
}
|
|
6386
|
-
}
|
|
6387
|
-
});
|
|
6388
|
-
if (existingOnGadgetExecutionComplete) {
|
|
6389
|
-
await existingOnGadgetExecutionComplete(context);
|
|
6390
|
-
}
|
|
6391
|
-
}
|
|
6392
|
-
}
|
|
6393
|
-
};
|
|
6394
|
-
}
|
|
6936
|
+
const hooks = this.hooks;
|
|
6395
6937
|
if (!this.trailingMessage) {
|
|
6396
6938
|
return hooks;
|
|
6397
6939
|
}
|
|
@@ -6474,19 +7016,6 @@ ${endPrefix}`
|
|
|
6474
7016
|
this.client = new LLMistClass();
|
|
6475
7017
|
}
|
|
6476
7018
|
const registry = GadgetRegistry.from(this.gadgets);
|
|
6477
|
-
let onSubagentEvent = this.subagentEventCallback;
|
|
6478
|
-
if (this.parentContext) {
|
|
6479
|
-
const { invocationId, onSubagentEvent: parentCallback, depth } = this.parentContext;
|
|
6480
|
-
const existingCallback = this.subagentEventCallback;
|
|
6481
|
-
onSubagentEvent = (event) => {
|
|
6482
|
-
parentCallback({
|
|
6483
|
-
...event,
|
|
6484
|
-
gadgetInvocationId: invocationId,
|
|
6485
|
-
depth: event.depth + depth
|
|
6486
|
-
});
|
|
6487
|
-
existingCallback?.(event);
|
|
6488
|
-
};
|
|
6489
|
-
}
|
|
6490
7019
|
return {
|
|
6491
7020
|
client: this.client,
|
|
6492
7021
|
model: this.model ?? "openai:gpt-5-nano",
|
|
@@ -6505,15 +7034,17 @@ ${endPrefix}`
|
|
|
6505
7034
|
gadgetArgPrefix: this.gadgetArgPrefix,
|
|
6506
7035
|
textOnlyHandler: this.textOnlyHandler,
|
|
6507
7036
|
textWithGadgetsHandler: this.textWithGadgetsHandler,
|
|
6508
|
-
stopOnGadgetError: this.stopOnGadgetError,
|
|
6509
|
-
canRecoverFromGadgetError: this.canRecoverFromGadgetError,
|
|
6510
7037
|
defaultGadgetTimeoutMs: this.defaultGadgetTimeoutMs,
|
|
6511
7038
|
gadgetOutputLimit: this.gadgetOutputLimit,
|
|
6512
7039
|
gadgetOutputLimitPercent: this.gadgetOutputLimitPercent,
|
|
6513
7040
|
compactionConfig: this.compactionConfig,
|
|
6514
7041
|
signal: this.signal,
|
|
6515
7042
|
subagentConfig: this.subagentConfig,
|
|
6516
|
-
onSubagentEvent
|
|
7043
|
+
onSubagentEvent: this.subagentEventCallback,
|
|
7044
|
+
// Tree context for shared tree model (subagents share parent's tree)
|
|
7045
|
+
parentTree: this.parentContext?.tree,
|
|
7046
|
+
parentNodeId: this.parentContext?.nodeId,
|
|
7047
|
+
baseDepth: this.parentContext ? (this.parentContext.depth ?? 0) + 1 : 0
|
|
6517
7048
|
};
|
|
6518
7049
|
}
|
|
6519
7050
|
ask(userPrompt) {
|
|
@@ -6670,19 +7201,6 @@ ${endPrefix}`
|
|
|
6670
7201
|
this.client = new LLMistClass();
|
|
6671
7202
|
}
|
|
6672
7203
|
const registry = GadgetRegistry.from(this.gadgets);
|
|
6673
|
-
let onSubagentEvent = this.subagentEventCallback;
|
|
6674
|
-
if (this.parentContext) {
|
|
6675
|
-
const { invocationId, onSubagentEvent: parentCallback, depth } = this.parentContext;
|
|
6676
|
-
const existingCallback = this.subagentEventCallback;
|
|
6677
|
-
onSubagentEvent = (event) => {
|
|
6678
|
-
parentCallback({
|
|
6679
|
-
...event,
|
|
6680
|
-
gadgetInvocationId: invocationId,
|
|
6681
|
-
depth: event.depth + depth
|
|
6682
|
-
});
|
|
6683
|
-
existingCallback?.(event);
|
|
6684
|
-
};
|
|
6685
|
-
}
|
|
6686
7204
|
const options = {
|
|
6687
7205
|
client: this.client,
|
|
6688
7206
|
model: this.model ?? "openai:gpt-5-nano",
|
|
@@ -6701,15 +7219,17 @@ ${endPrefix}`
|
|
|
6701
7219
|
gadgetArgPrefix: this.gadgetArgPrefix,
|
|
6702
7220
|
textOnlyHandler: this.textOnlyHandler,
|
|
6703
7221
|
textWithGadgetsHandler: this.textWithGadgetsHandler,
|
|
6704
|
-
stopOnGadgetError: this.stopOnGadgetError,
|
|
6705
|
-
canRecoverFromGadgetError: this.canRecoverFromGadgetError,
|
|
6706
7222
|
defaultGadgetTimeoutMs: this.defaultGadgetTimeoutMs,
|
|
6707
7223
|
gadgetOutputLimit: this.gadgetOutputLimit,
|
|
6708
7224
|
gadgetOutputLimitPercent: this.gadgetOutputLimitPercent,
|
|
6709
7225
|
compactionConfig: this.compactionConfig,
|
|
6710
7226
|
signal: this.signal,
|
|
6711
7227
|
subagentConfig: this.subagentConfig,
|
|
6712
|
-
onSubagentEvent
|
|
7228
|
+
onSubagentEvent: this.subagentEventCallback,
|
|
7229
|
+
// Tree context for shared tree model (subagents share parent's tree)
|
|
7230
|
+
parentTree: this.parentContext?.tree,
|
|
7231
|
+
parentNodeId: this.parentContext?.nodeId,
|
|
7232
|
+
baseDepth: this.parentContext ? (this.parentContext.depth ?? 0) + 1 : 0
|
|
6713
7233
|
};
|
|
6714
7234
|
return new Agent(AGENT_INTERNAL_KEY, options);
|
|
6715
7235
|
}
|
|
@@ -11297,16 +11817,16 @@ var MockConversationManager = class {
|
|
|
11297
11817
|
this.history.push(msg);
|
|
11298
11818
|
this.addedMessages.push(msg);
|
|
11299
11819
|
}
|
|
11300
|
-
addGadgetCallResult(gadgetName, parameters, result) {
|
|
11820
|
+
addGadgetCallResult(gadgetName, parameters, result, invocationId) {
|
|
11301
11821
|
const assistantMsg = {
|
|
11302
11822
|
role: "assistant",
|
|
11303
|
-
content: `!!!GADGET_START:${gadgetName}
|
|
11823
|
+
content: `!!!GADGET_START:${gadgetName}:${invocationId}
|
|
11304
11824
|
${JSON.stringify(parameters)}
|
|
11305
11825
|
!!!GADGET_END`
|
|
11306
11826
|
};
|
|
11307
11827
|
const resultMsg = {
|
|
11308
11828
|
role: "user",
|
|
11309
|
-
content: `Result: ${result}`
|
|
11829
|
+
content: `Result (${invocationId}): ${result}`
|
|
11310
11830
|
};
|
|
11311
11831
|
this.history.push(assistantMsg);
|
|
11312
11832
|
this.history.push(resultMsg);
|
|
@@ -11654,6 +12174,8 @@ export {
|
|
|
11654
12174
|
init_schema_validator,
|
|
11655
12175
|
GadgetRegistry,
|
|
11656
12176
|
init_registry,
|
|
12177
|
+
ExecutionTree,
|
|
12178
|
+
init_execution_tree,
|
|
11657
12179
|
DEFAULT_HINTS,
|
|
11658
12180
|
DEFAULT_PROMPTS,
|
|
11659
12181
|
resolvePromptTemplate,
|
|
@@ -11772,4 +12294,4 @@ export {
|
|
|
11772
12294
|
createEmptyStream,
|
|
11773
12295
|
createErrorStream
|
|
11774
12296
|
};
|
|
11775
|
-
//# sourceMappingURL=chunk-
|
|
12297
|
+
//# sourceMappingURL=chunk-VAJLPRJ6.js.map
|