llmist 5.1.0 → 6.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-F5QK5YVI.js → chunk-7BJX376V.js} +97 -2
- package/dist/chunk-7BJX376V.js.map +1 -0
- package/dist/{chunk-YJKUWFIC.js → chunk-VAJLPRJ6.js} +939 -363
- package/dist/chunk-VAJLPRJ6.js.map +1 -0
- package/dist/cli.cjs +4131 -1597
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +3040 -1081
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +1040 -359
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +152 -39
- package/dist/index.d.ts +152 -39
- package/dist/index.js +26 -2
- package/dist/{mock-stream-CAY53Q6u.d.cts → mock-stream-Cq1Sxezz.d.cts} +854 -166
- package/dist/{mock-stream-CAY53Q6u.d.ts → mock-stream-Cq1Sxezz.d.ts} +854 -166
- package/dist/testing/index.cjs +936 -362
- package/dist/testing/index.cjs.map +1 -1
- package/dist/testing/index.d.cts +3 -3
- package/dist/testing/index.d.ts +3 -3
- package/dist/testing/index.js +1 -1
- package/package.json +2 -1
- package/dist/chunk-F5QK5YVI.js.map +0 -1
- package/dist/chunk-YJKUWFIC.js.map +0 -1
|
@@ -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 */
|
|
@@ -3923,18 +4552,30 @@ var init_stream_processor = __esm({
|
|
|
3923
4552
|
completedResults = /* @__PURE__ */ new Map();
|
|
3924
4553
|
/** Invocation IDs of gadgets that have failed (error or skipped due to dependency) */
|
|
3925
4554
|
failedInvocations = /* @__PURE__ */ new Set();
|
|
4555
|
+
/** Promises for independent gadgets currently executing (fire-and-forget) */
|
|
4556
|
+
inFlightExecutions = /* @__PURE__ */ new Map();
|
|
4557
|
+
/** Queue of completed gadget results ready to be yielded (for real-time streaming) */
|
|
4558
|
+
completedResultsQueue = [];
|
|
3926
4559
|
constructor(options) {
|
|
3927
4560
|
this.iteration = options.iteration;
|
|
3928
4561
|
this.registry = options.registry;
|
|
3929
4562
|
this.hooks = options.hooks ?? {};
|
|
3930
4563
|
this.logger = options.logger ?? createLogger({ name: "llmist:stream-processor" });
|
|
3931
|
-
this.
|
|
3932
|
-
this.
|
|
4564
|
+
this.tree = options.tree;
|
|
4565
|
+
this.parentNodeId = options.parentNodeId ?? null;
|
|
4566
|
+
this.baseDepth = options.baseDepth ?? 0;
|
|
3933
4567
|
this.parser = new GadgetCallParser({
|
|
3934
4568
|
startPrefix: options.gadgetStartPrefix,
|
|
3935
4569
|
endPrefix: options.gadgetEndPrefix,
|
|
3936
4570
|
argPrefix: options.gadgetArgPrefix
|
|
3937
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;
|
|
3938
4579
|
this.executor = new GadgetExecutor(
|
|
3939
4580
|
options.registry,
|
|
3940
4581
|
options.requestHumanInput,
|
|
@@ -3945,7 +4586,11 @@ var init_stream_processor = __esm({
|
|
|
3945
4586
|
options.mediaStore,
|
|
3946
4587
|
options.agentConfig,
|
|
3947
4588
|
options.subagentConfig,
|
|
3948
|
-
|
|
4589
|
+
wrappedOnSubagentEvent,
|
|
4590
|
+
// Tree context for gadget execution
|
|
4591
|
+
options.tree,
|
|
4592
|
+
options.parentNodeId,
|
|
4593
|
+
options.baseDepth
|
|
3949
4594
|
);
|
|
3950
4595
|
}
|
|
3951
4596
|
/**
|
|
@@ -3996,7 +4641,7 @@ var init_stream_processor = __esm({
|
|
|
3996
4641
|
usage,
|
|
3997
4642
|
logger: this.logger
|
|
3998
4643
|
};
|
|
3999
|
-
await this.hooks.observers
|
|
4644
|
+
await this.hooks.observers?.onStreamChunk?.(context);
|
|
4000
4645
|
});
|
|
4001
4646
|
await this.runObserversInParallel(chunkObservers);
|
|
4002
4647
|
}
|
|
@@ -4014,24 +4659,7 @@ var init_stream_processor = __esm({
|
|
|
4014
4659
|
}
|
|
4015
4660
|
}
|
|
4016
4661
|
}
|
|
4017
|
-
|
|
4018
|
-
this.logger.info("Breaking from LLM stream due to gadget error");
|
|
4019
|
-
break;
|
|
4020
|
-
}
|
|
4021
|
-
}
|
|
4022
|
-
if (!this.executionHalted) {
|
|
4023
|
-
for (const event of this.parser.finalize()) {
|
|
4024
|
-
for await (const processedEvent of this.processEventGenerator(event)) {
|
|
4025
|
-
yield processedEvent;
|
|
4026
|
-
if (processedEvent.type === "gadget_result") {
|
|
4027
|
-
didExecuteGadgets = true;
|
|
4028
|
-
if (processedEvent.result.breaksLoop) {
|
|
4029
|
-
shouldBreakLoop = true;
|
|
4030
|
-
}
|
|
4031
|
-
}
|
|
4032
|
-
}
|
|
4033
|
-
}
|
|
4034
|
-
for await (const evt of this.processPendingGadgetsGenerator()) {
|
|
4662
|
+
for (const evt of this.drainCompletedResults()) {
|
|
4035
4663
|
yield evt;
|
|
4036
4664
|
if (evt.type === "gadget_result") {
|
|
4037
4665
|
didExecuteGadgets = true;
|
|
@@ -4041,6 +4669,44 @@ var init_stream_processor = __esm({
|
|
|
4041
4669
|
}
|
|
4042
4670
|
}
|
|
4043
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") {
|
|
4676
|
+
didExecuteGadgets = true;
|
|
4677
|
+
if (processedEvent.result.breaksLoop) {
|
|
4678
|
+
shouldBreakLoop = true;
|
|
4679
|
+
}
|
|
4680
|
+
}
|
|
4681
|
+
}
|
|
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
|
+
}
|
|
4044
4710
|
let finalMessage = this.responseText;
|
|
4045
4711
|
if (this.hooks.interceptors?.interceptAssistantMessage) {
|
|
4046
4712
|
const context = {
|
|
@@ -4061,21 +4727,8 @@ var init_stream_processor = __esm({
|
|
|
4061
4727
|
};
|
|
4062
4728
|
yield completionEvent;
|
|
4063
4729
|
}
|
|
4064
|
-
/**
|
|
4065
|
-
* Process a single parsed event (text or gadget call).
|
|
4066
|
-
* @deprecated Use processEventGenerator for real-time streaming
|
|
4067
|
-
*/
|
|
4068
|
-
async processEvent(event) {
|
|
4069
|
-
if (event.type === "text") {
|
|
4070
|
-
return this.processTextEvent(event);
|
|
4071
|
-
} else if (event.type === "gadget_call") {
|
|
4072
|
-
return this.processGadgetCall(event.call);
|
|
4073
|
-
}
|
|
4074
|
-
return [event];
|
|
4075
|
-
}
|
|
4076
4730
|
/**
|
|
4077
4731
|
* Process a single parsed event, yielding events in real-time.
|
|
4078
|
-
* Generator version of processEvent for streaming support.
|
|
4079
4732
|
*/
|
|
4080
4733
|
async *processEventGenerator(event) {
|
|
4081
4734
|
if (event.type === "text") {
|
|
@@ -4117,12 +4770,6 @@ var init_stream_processor = __esm({
|
|
|
4117
4770
|
* After each execution, pending gadgets are checked to see if they can now run.
|
|
4118
4771
|
*/
|
|
4119
4772
|
async processGadgetCall(call) {
|
|
4120
|
-
if (this.executionHalted) {
|
|
4121
|
-
this.logger.debug("Skipping gadget execution due to previous error", {
|
|
4122
|
-
gadgetName: call.gadgetName
|
|
4123
|
-
});
|
|
4124
|
-
return [];
|
|
4125
|
-
}
|
|
4126
4773
|
const events = [];
|
|
4127
4774
|
events.push({ type: "gadget_call", call });
|
|
4128
4775
|
if (call.dependencies.length > 0) {
|
|
@@ -4173,13 +4820,16 @@ var init_stream_processor = __esm({
|
|
|
4173
4820
|
* when parsed (before execution), enabling real-time UI feedback.
|
|
4174
4821
|
*/
|
|
4175
4822
|
async *processGadgetCallGenerator(call) {
|
|
4176
|
-
|
|
4177
|
-
|
|
4178
|
-
|
|
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
|
|
4179
4831
|
});
|
|
4180
|
-
return;
|
|
4181
4832
|
}
|
|
4182
|
-
yield { type: "gadget_call", call };
|
|
4183
4833
|
if (call.dependencies.length > 0) {
|
|
4184
4834
|
if (call.dependencies.includes(call.invocationId)) {
|
|
4185
4835
|
this.logger.warn("Gadget has self-referential dependency (depends on itself)", {
|
|
@@ -4216,13 +4866,16 @@ var init_stream_processor = __esm({
|
|
|
4216
4866
|
this.gadgetsAwaitingDependencies.set(call.invocationId, call);
|
|
4217
4867
|
return;
|
|
4218
4868
|
}
|
|
4869
|
+
for await (const evt of this.executeGadgetGenerator(call)) {
|
|
4870
|
+
yield evt;
|
|
4871
|
+
}
|
|
4872
|
+
for await (const evt of this.processPendingGadgetsGenerator()) {
|
|
4873
|
+
yield evt;
|
|
4874
|
+
}
|
|
4875
|
+
return;
|
|
4219
4876
|
}
|
|
4220
|
-
|
|
4221
|
-
|
|
4222
|
-
}
|
|
4223
|
-
for await (const evt of this.processPendingGadgetsGenerator()) {
|
|
4224
|
-
yield evt;
|
|
4225
|
-
}
|
|
4877
|
+
const executionPromise = this.executeGadgetAndCollect(call);
|
|
4878
|
+
this.inFlightExecutions.set(call.invocationId, executionPromise);
|
|
4226
4879
|
}
|
|
4227
4880
|
/**
|
|
4228
4881
|
* Execute a gadget through the full hook lifecycle.
|
|
@@ -4237,15 +4890,6 @@ var init_stream_processor = __esm({
|
|
|
4237
4890
|
error: call.parseError,
|
|
4238
4891
|
rawParameters: call.parametersRaw
|
|
4239
4892
|
});
|
|
4240
|
-
const shouldContinue = await this.checkCanRecoverFromError(
|
|
4241
|
-
call.parseError,
|
|
4242
|
-
call.gadgetName,
|
|
4243
|
-
"parse",
|
|
4244
|
-
call.parameters
|
|
4245
|
-
);
|
|
4246
|
-
if (!shouldContinue) {
|
|
4247
|
-
this.executionHalted = true;
|
|
4248
|
-
}
|
|
4249
4893
|
}
|
|
4250
4894
|
let parameters = call.parameters ?? {};
|
|
4251
4895
|
if (this.hooks.interceptors?.interceptGadgetParameters) {
|
|
@@ -4288,7 +4932,7 @@ var init_stream_processor = __esm({
|
|
|
4288
4932
|
parameters,
|
|
4289
4933
|
logger: this.logger
|
|
4290
4934
|
};
|
|
4291
|
-
await this.hooks.observers
|
|
4935
|
+
await this.hooks.observers?.onGadgetExecutionStart?.(context);
|
|
4292
4936
|
});
|
|
4293
4937
|
}
|
|
4294
4938
|
await this.runObserversInParallel(startObservers);
|
|
@@ -4357,7 +5001,7 @@ var init_stream_processor = __esm({
|
|
|
4357
5001
|
cost: result.cost,
|
|
4358
5002
|
logger: this.logger
|
|
4359
5003
|
};
|
|
4360
|
-
await this.hooks.observers
|
|
5004
|
+
await this.hooks.observers?.onGadgetExecutionComplete?.(context);
|
|
4361
5005
|
});
|
|
4362
5006
|
}
|
|
4363
5007
|
await this.runObserversInParallel(completeObservers);
|
|
@@ -4366,18 +5010,6 @@ var init_stream_processor = __esm({
|
|
|
4366
5010
|
this.failedInvocations.add(result.invocationId);
|
|
4367
5011
|
}
|
|
4368
5012
|
events.push({ type: "gadget_result", result });
|
|
4369
|
-
if (result.error) {
|
|
4370
|
-
const errorType = this.determineErrorType(call, result);
|
|
4371
|
-
const shouldContinue = await this.checkCanRecoverFromError(
|
|
4372
|
-
result.error,
|
|
4373
|
-
result.gadgetName,
|
|
4374
|
-
errorType,
|
|
4375
|
-
result.parameters
|
|
4376
|
-
);
|
|
4377
|
-
if (!shouldContinue) {
|
|
4378
|
-
this.executionHalted = true;
|
|
4379
|
-
}
|
|
4380
|
-
}
|
|
4381
5013
|
return events;
|
|
4382
5014
|
}
|
|
4383
5015
|
/**
|
|
@@ -4391,15 +5023,6 @@ var init_stream_processor = __esm({
|
|
|
4391
5023
|
error: call.parseError,
|
|
4392
5024
|
rawParameters: call.parametersRaw
|
|
4393
5025
|
});
|
|
4394
|
-
const shouldContinue = await this.checkCanRecoverFromError(
|
|
4395
|
-
call.parseError,
|
|
4396
|
-
call.gadgetName,
|
|
4397
|
-
"parse",
|
|
4398
|
-
call.parameters
|
|
4399
|
-
);
|
|
4400
|
-
if (!shouldContinue) {
|
|
4401
|
-
this.executionHalted = true;
|
|
4402
|
-
}
|
|
4403
5026
|
}
|
|
4404
5027
|
let parameters = call.parameters ?? {};
|
|
4405
5028
|
if (this.hooks.interceptors?.interceptGadgetParameters) {
|
|
@@ -4442,10 +5065,16 @@ var init_stream_processor = __esm({
|
|
|
4442
5065
|
parameters,
|
|
4443
5066
|
logger: this.logger
|
|
4444
5067
|
};
|
|
4445
|
-
await this.hooks.observers
|
|
5068
|
+
await this.hooks.observers?.onGadgetExecutionStart?.(context);
|
|
4446
5069
|
});
|
|
4447
5070
|
}
|
|
4448
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
|
+
}
|
|
4449
5078
|
let result;
|
|
4450
5079
|
if (shouldSkip) {
|
|
4451
5080
|
result = {
|
|
@@ -4511,27 +5140,84 @@ var init_stream_processor = __esm({
|
|
|
4511
5140
|
cost: result.cost,
|
|
4512
5141
|
logger: this.logger
|
|
4513
5142
|
};
|
|
4514
|
-
await this.hooks.observers
|
|
5143
|
+
await this.hooks.observers?.onGadgetExecutionComplete?.(context);
|
|
4515
5144
|
});
|
|
4516
5145
|
}
|
|
4517
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
|
+
}
|
|
4518
5166
|
this.completedResults.set(result.invocationId, result);
|
|
4519
5167
|
if (result.error) {
|
|
4520
5168
|
this.failedInvocations.add(result.invocationId);
|
|
4521
5169
|
}
|
|
4522
5170
|
yield { type: "gadget_result", result };
|
|
4523
|
-
|
|
4524
|
-
|
|
4525
|
-
|
|
4526
|
-
|
|
4527
|
-
|
|
4528
|
-
|
|
4529
|
-
|
|
4530
|
-
|
|
4531
|
-
|
|
4532
|
-
|
|
5171
|
+
}
|
|
5172
|
+
/**
|
|
5173
|
+
* Execute a gadget and push events to the completed results queue (non-blocking).
|
|
5174
|
+
* Used for fire-and-forget parallel execution of independent gadgets.
|
|
5175
|
+
* Results are pushed to completedResultsQueue for real-time streaming to the caller.
|
|
5176
|
+
*/
|
|
5177
|
+
async executeGadgetAndCollect(call) {
|
|
5178
|
+
for await (const evt of this.executeGadgetGenerator(call)) {
|
|
5179
|
+
this.completedResultsQueue.push(evt);
|
|
5180
|
+
}
|
|
5181
|
+
}
|
|
5182
|
+
/**
|
|
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
|
|
5186
|
+
*/
|
|
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() {
|
|
5201
|
+
if (this.inFlightExecutions.size === 0) {
|
|
5202
|
+
return;
|
|
5203
|
+
}
|
|
5204
|
+
this.logger.debug("Waiting for in-flight gadget executions", {
|
|
5205
|
+
count: this.inFlightExecutions.size,
|
|
5206
|
+
invocationIds: Array.from(this.inFlightExecutions.keys())
|
|
5207
|
+
});
|
|
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;
|
|
4533
5218
|
}
|
|
4534
5219
|
}
|
|
5220
|
+
this.inFlightExecutions.clear();
|
|
4535
5221
|
}
|
|
4536
5222
|
/**
|
|
4537
5223
|
* Handle a gadget that cannot execute because a dependency failed.
|
|
@@ -4556,6 +5242,12 @@ var init_stream_processor = __esm({
|
|
|
4556
5242
|
}
|
|
4557
5243
|
if (action.action === "skip") {
|
|
4558
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
|
+
}
|
|
4559
5251
|
const skipEvent = {
|
|
4560
5252
|
type: "gadget_skipped",
|
|
4561
5253
|
gadgetName: call.gadgetName,
|
|
@@ -4575,7 +5267,7 @@ var init_stream_processor = __esm({
|
|
|
4575
5267
|
failedDependencyError: depError,
|
|
4576
5268
|
logger: this.logger
|
|
4577
5269
|
};
|
|
4578
|
-
await this.safeObserve(() => this.hooks.observers
|
|
5270
|
+
await this.safeObserve(() => this.hooks.observers?.onGadgetSkipped?.(observeContext));
|
|
4579
5271
|
}
|
|
4580
5272
|
this.logger.info("Gadget skipped due to failed dependency", {
|
|
4581
5273
|
gadgetName: call.gadgetName,
|
|
@@ -4807,48 +5499,6 @@ var init_stream_processor = __esm({
|
|
|
4807
5499
|
observers.map((observer) => this.safeObserve(observer))
|
|
4808
5500
|
);
|
|
4809
5501
|
}
|
|
4810
|
-
/**
|
|
4811
|
-
* Check if execution can recover from an error.
|
|
4812
|
-
*
|
|
4813
|
-
* Returns true if we should continue processing subsequent gadgets, false if we should stop.
|
|
4814
|
-
*
|
|
4815
|
-
* Logic:
|
|
4816
|
-
* - If custom canRecoverFromGadgetError is provided, use it
|
|
4817
|
-
* - Otherwise, use stopOnGadgetError config:
|
|
4818
|
-
* - stopOnGadgetError=true → return false (stop execution)
|
|
4819
|
-
* - stopOnGadgetError=false → return true (continue execution)
|
|
4820
|
-
*/
|
|
4821
|
-
async checkCanRecoverFromError(error, gadgetName, errorType, parameters) {
|
|
4822
|
-
if (this.canRecoverFromGadgetError) {
|
|
4823
|
-
return await this.canRecoverFromGadgetError({
|
|
4824
|
-
error,
|
|
4825
|
-
gadgetName,
|
|
4826
|
-
errorType,
|
|
4827
|
-
parameters
|
|
4828
|
-
});
|
|
4829
|
-
}
|
|
4830
|
-
const shouldContinue = !this.stopOnGadgetError;
|
|
4831
|
-
this.logger.debug("Checking if should continue after error", {
|
|
4832
|
-
error,
|
|
4833
|
-
gadgetName,
|
|
4834
|
-
errorType,
|
|
4835
|
-
stopOnGadgetError: this.stopOnGadgetError,
|
|
4836
|
-
shouldContinue
|
|
4837
|
-
});
|
|
4838
|
-
return shouldContinue;
|
|
4839
|
-
}
|
|
4840
|
-
/**
|
|
4841
|
-
* Determine the type of error from a gadget execution.
|
|
4842
|
-
*/
|
|
4843
|
-
determineErrorType(call, result) {
|
|
4844
|
-
if (call.parseError) {
|
|
4845
|
-
return "parse";
|
|
4846
|
-
}
|
|
4847
|
-
if (result.error?.includes("Invalid parameters:")) {
|
|
4848
|
-
return "validation";
|
|
4849
|
-
}
|
|
4850
|
-
return "execution";
|
|
4851
|
-
}
|
|
4852
5502
|
};
|
|
4853
5503
|
}
|
|
4854
5504
|
});
|
|
@@ -4859,6 +5509,7 @@ var init_agent = __esm({
|
|
|
4859
5509
|
"src/agent/agent.ts"() {
|
|
4860
5510
|
"use strict";
|
|
4861
5511
|
init_constants();
|
|
5512
|
+
init_execution_tree();
|
|
4862
5513
|
init_messages();
|
|
4863
5514
|
init_model_shortcuts();
|
|
4864
5515
|
init_media_store();
|
|
@@ -4886,8 +5537,6 @@ var init_agent = __esm({
|
|
|
4886
5537
|
requestHumanInput;
|
|
4887
5538
|
textOnlyHandler;
|
|
4888
5539
|
textWithGadgetsHandler;
|
|
4889
|
-
stopOnGadgetError;
|
|
4890
|
-
canRecoverFromGadgetError;
|
|
4891
5540
|
defaultGadgetTimeoutMs;
|
|
4892
5541
|
defaultMaxTokens;
|
|
4893
5542
|
hasUserPrompt;
|
|
@@ -4910,6 +5559,12 @@ var init_agent = __esm({
|
|
|
4910
5559
|
pendingSubagentEvents = [];
|
|
4911
5560
|
// Combined callback that queues events AND calls user callback
|
|
4912
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;
|
|
4913
5568
|
/**
|
|
4914
5569
|
* Creates a new Agent instance.
|
|
4915
5570
|
* @internal This constructor is private. Use LLMist.createAgent() or AgentBuilder instead.
|
|
@@ -4932,8 +5587,6 @@ var init_agent = __esm({
|
|
|
4932
5587
|
this.requestHumanInput = options.requestHumanInput;
|
|
4933
5588
|
this.textOnlyHandler = options.textOnlyHandler ?? "terminate";
|
|
4934
5589
|
this.textWithGadgetsHandler = options.textWithGadgetsHandler;
|
|
4935
|
-
this.stopOnGadgetError = options.stopOnGadgetError ?? true;
|
|
4936
|
-
this.canRecoverFromGadgetError = options.canRecoverFromGadgetError;
|
|
4937
5590
|
this.defaultGadgetTimeoutMs = options.defaultGadgetTimeoutMs;
|
|
4938
5591
|
this.defaultMaxTokens = this.resolveMaxTokensFromCatalog(options.model);
|
|
4939
5592
|
this.outputLimitEnabled = options.gadgetOutputLimit ?? DEFAULT_GADGET_OUTPUT_LIMIT;
|
|
@@ -4987,6 +5640,9 @@ var init_agent = __esm({
|
|
|
4987
5640
|
temperature: this.temperature
|
|
4988
5641
|
};
|
|
4989
5642
|
this.subagentConfig = options.subagentConfig;
|
|
5643
|
+
this.tree = options.parentTree ?? new ExecutionTree();
|
|
5644
|
+
this.parentNodeId = options.parentNodeId ?? null;
|
|
5645
|
+
this.baseDepth = options.baseDepth ?? 0;
|
|
4990
5646
|
this.userSubagentEventCallback = options.onSubagentEvent;
|
|
4991
5647
|
this.onSubagentEvent = (event) => {
|
|
4992
5648
|
this.pendingSubagentEvents.push(event);
|
|
@@ -5051,7 +5707,9 @@ var init_agent = __esm({
|
|
|
5051
5707
|
*flushPendingSubagentEvents() {
|
|
5052
5708
|
while (this.pendingSubagentEvents.length > 0) {
|
|
5053
5709
|
const event = this.pendingSubagentEvents.shift();
|
|
5054
|
-
|
|
5710
|
+
if (event) {
|
|
5711
|
+
yield { type: "subagent_event", subagentEvent: event };
|
|
5712
|
+
}
|
|
5055
5713
|
}
|
|
5056
5714
|
}
|
|
5057
5715
|
/**
|
|
@@ -5105,6 +5763,48 @@ var init_agent = __esm({
|
|
|
5105
5763
|
getMediaStore() {
|
|
5106
5764
|
return this.mediaStore;
|
|
5107
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
|
+
}
|
|
5108
5808
|
/**
|
|
5109
5809
|
* Manually trigger context compaction.
|
|
5110
5810
|
*
|
|
@@ -5206,6 +5906,7 @@ var init_agent = __esm({
|
|
|
5206
5906
|
await this.hooks.observers.onCompaction({
|
|
5207
5907
|
iteration: currentIteration,
|
|
5208
5908
|
event: compactionEvent,
|
|
5909
|
+
// biome-ignore lint/style/noNonNullAssertion: compactionManager exists if compactionEvent is truthy
|
|
5209
5910
|
stats: this.compactionManager.getStats(),
|
|
5210
5911
|
logger: this.logger
|
|
5211
5912
|
});
|
|
@@ -5267,6 +5968,13 @@ var init_agent = __esm({
|
|
|
5267
5968
|
messageCount: llmOptions.messages.length,
|
|
5268
5969
|
messages: llmOptions.messages
|
|
5269
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;
|
|
5270
5978
|
const stream2 = this.client.stream(llmOptions);
|
|
5271
5979
|
const processor = new StreamProcessor({
|
|
5272
5980
|
iteration: currentIteration,
|
|
@@ -5277,14 +5985,17 @@ var init_agent = __esm({
|
|
|
5277
5985
|
hooks: this.hooks,
|
|
5278
5986
|
logger: this.logger.getSubLogger({ name: "stream-processor" }),
|
|
5279
5987
|
requestHumanInput: this.requestHumanInput,
|
|
5280
|
-
stopOnGadgetError: this.stopOnGadgetError,
|
|
5281
|
-
canRecoverFromGadgetError: this.canRecoverFromGadgetError,
|
|
5282
5988
|
defaultGadgetTimeoutMs: this.defaultGadgetTimeoutMs,
|
|
5283
5989
|
client: this.client,
|
|
5284
5990
|
mediaStore: this.mediaStore,
|
|
5285
5991
|
agentConfig: this.agentContextConfig,
|
|
5286
5992
|
subagentConfig: this.subagentConfig,
|
|
5287
|
-
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
|
|
5288
5999
|
});
|
|
5289
6000
|
let streamMetadata = null;
|
|
5290
6001
|
let gadgetCallCount = 0;
|
|
@@ -5330,6 +6041,11 @@ var init_agent = __esm({
|
|
|
5330
6041
|
await this.hooks.observers.onLLMCallComplete(context);
|
|
5331
6042
|
}
|
|
5332
6043
|
});
|
|
6044
|
+
this.tree.completeLLMCall(currentLLMNodeId, {
|
|
6045
|
+
response: result.rawResponse,
|
|
6046
|
+
usage: result.usage,
|
|
6047
|
+
finishReason: result.finishReason
|
|
6048
|
+
});
|
|
5333
6049
|
let finalMessage = result.finalMessage;
|
|
5334
6050
|
if (this.hooks.controllers?.afterLLMCall) {
|
|
5335
6051
|
const context = {
|
|
@@ -5364,10 +6080,12 @@ var init_agent = __esm({
|
|
|
5364
6080
|
const textContent = textOutputs.join("");
|
|
5365
6081
|
if (textContent.trim()) {
|
|
5366
6082
|
const { gadgetName, parameterMapping, resultMapping } = this.textWithGadgetsHandler;
|
|
6083
|
+
const syntheticId = `gc_text_${++this.syntheticInvocationCounter}`;
|
|
5367
6084
|
this.conversation.addGadgetCallResult(
|
|
5368
6085
|
gadgetName,
|
|
5369
6086
|
parameterMapping(textContent),
|
|
5370
|
-
resultMapping ? resultMapping(textContent) : textContent
|
|
6087
|
+
resultMapping ? resultMapping(textContent) : textContent,
|
|
6088
|
+
syntheticId
|
|
5371
6089
|
);
|
|
5372
6090
|
}
|
|
5373
6091
|
}
|
|
@@ -5378,6 +6096,7 @@ var init_agent = __esm({
|
|
|
5378
6096
|
gadgetResult.gadgetName,
|
|
5379
6097
|
gadgetResult.parameters,
|
|
5380
6098
|
gadgetResult.error ?? gadgetResult.result ?? "",
|
|
6099
|
+
gadgetResult.invocationId,
|
|
5381
6100
|
gadgetResult.media,
|
|
5382
6101
|
gadgetResult.mediaIds
|
|
5383
6102
|
);
|
|
@@ -5385,10 +6104,12 @@ var init_agent = __esm({
|
|
|
5385
6104
|
}
|
|
5386
6105
|
} else {
|
|
5387
6106
|
if (finalMessage.trim()) {
|
|
6107
|
+
const syntheticId = `gc_tell_${++this.syntheticInvocationCounter}`;
|
|
5388
6108
|
this.conversation.addGadgetCallResult(
|
|
5389
6109
|
"TellUser",
|
|
5390
6110
|
{ message: finalMessage, done: false, type: "info" },
|
|
5391
|
-
`\u2139\uFE0F ${finalMessage}
|
|
6111
|
+
`\u2139\uFE0F ${finalMessage}`,
|
|
6112
|
+
syntheticId
|
|
5392
6113
|
);
|
|
5393
6114
|
}
|
|
5394
6115
|
const shouldBreak = await this.handleTextOnlyResponse(finalMessage);
|
|
@@ -5599,8 +6320,6 @@ var init_builder = __esm({
|
|
|
5599
6320
|
gadgetArgPrefix;
|
|
5600
6321
|
textOnlyHandler;
|
|
5601
6322
|
textWithGadgetsHandler;
|
|
5602
|
-
stopOnGadgetError;
|
|
5603
|
-
canRecoverFromGadgetError;
|
|
5604
6323
|
defaultGadgetTimeoutMs;
|
|
5605
6324
|
gadgetOutputLimit;
|
|
5606
6325
|
gadgetOutputLimitPercent;
|
|
@@ -5609,6 +6328,8 @@ var init_builder = __esm({
|
|
|
5609
6328
|
trailingMessage;
|
|
5610
6329
|
subagentConfig;
|
|
5611
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
|
|
5612
6333
|
parentContext;
|
|
5613
6334
|
constructor(client) {
|
|
5614
6335
|
this.client = client;
|
|
@@ -5891,62 +6612,6 @@ var init_builder = __esm({
|
|
|
5891
6612
|
this.textWithGadgetsHandler = handler;
|
|
5892
6613
|
return this;
|
|
5893
6614
|
}
|
|
5894
|
-
/**
|
|
5895
|
-
* Set whether to stop gadget execution on first error.
|
|
5896
|
-
*
|
|
5897
|
-
* When true (default), if a gadget fails:
|
|
5898
|
-
* - Subsequent gadgets in the same response are skipped
|
|
5899
|
-
* - LLM stream is cancelled to save costs
|
|
5900
|
-
* - Agent loop continues with error in context
|
|
5901
|
-
*
|
|
5902
|
-
* When false:
|
|
5903
|
-
* - All gadgets in the response still execute
|
|
5904
|
-
* - LLM stream continues to completion
|
|
5905
|
-
*
|
|
5906
|
-
* @param stop - Whether to stop on gadget error
|
|
5907
|
-
* @returns This builder for chaining
|
|
5908
|
-
*
|
|
5909
|
-
* @example
|
|
5910
|
-
* ```typescript
|
|
5911
|
-
* .withStopOnGadgetError(false)
|
|
5912
|
-
* ```
|
|
5913
|
-
*/
|
|
5914
|
-
withStopOnGadgetError(stop) {
|
|
5915
|
-
this.stopOnGadgetError = stop;
|
|
5916
|
-
return this;
|
|
5917
|
-
}
|
|
5918
|
-
/**
|
|
5919
|
-
* Set custom error handling logic.
|
|
5920
|
-
*
|
|
5921
|
-
* Provides fine-grained control over whether to continue after different types of errors.
|
|
5922
|
-
* Overrides `stopOnGadgetError` when provided.
|
|
5923
|
-
*
|
|
5924
|
-
* **Note:** This builder method configures the underlying `canRecoverFromGadgetError` option
|
|
5925
|
-
* in `AgentOptions`. The method is named `withErrorHandler` for better developer experience,
|
|
5926
|
-
* but maps to the `canRecoverFromGadgetError` property internally.
|
|
5927
|
-
*
|
|
5928
|
-
* @param handler - Function that decides whether to continue after an error.
|
|
5929
|
-
* Return `true` to continue execution, `false` to stop.
|
|
5930
|
-
* @returns This builder for chaining
|
|
5931
|
-
*
|
|
5932
|
-
* @example
|
|
5933
|
-
* ```typescript
|
|
5934
|
-
* .withErrorHandler((context) => {
|
|
5935
|
-
* // Stop on parse errors, continue on validation/execution errors
|
|
5936
|
-
* if (context.errorType === "parse") {
|
|
5937
|
-
* return false;
|
|
5938
|
-
* }
|
|
5939
|
-
* if (context.error.includes("CRITICAL")) {
|
|
5940
|
-
* return false;
|
|
5941
|
-
* }
|
|
5942
|
-
* return true;
|
|
5943
|
-
* })
|
|
5944
|
-
* ```
|
|
5945
|
-
*/
|
|
5946
|
-
withErrorHandler(handler) {
|
|
5947
|
-
this.canRecoverFromGadgetError = handler;
|
|
5948
|
-
return this;
|
|
5949
|
-
}
|
|
5950
6615
|
/**
|
|
5951
6616
|
* Set default timeout for gadget execution.
|
|
5952
6617
|
*
|
|
@@ -6141,6 +6806,15 @@ var init_builder = __esm({
|
|
|
6141
6806
|
* The method extracts `invocationId` and `onSubagentEvent` from the execution
|
|
6142
6807
|
* context and sets up automatic forwarding via hooks and event wrapping.
|
|
6143
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
|
+
*
|
|
6144
6818
|
* @param ctx - ExecutionContext passed to the gadget's execute() method
|
|
6145
6819
|
* @param depth - Nesting depth (default: 1 for direct child)
|
|
6146
6820
|
* @returns This builder for chaining
|
|
@@ -6161,17 +6835,25 @@ var init_builder = __esm({
|
|
|
6161
6835
|
* result = event.content;
|
|
6162
6836
|
* }
|
|
6163
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!);
|
|
6164
6843
|
* }
|
|
6165
6844
|
* ```
|
|
6166
6845
|
*/
|
|
6167
6846
|
withParentContext(ctx, depth = 1) {
|
|
6168
|
-
if (ctx.
|
|
6847
|
+
if (ctx.tree) {
|
|
6169
6848
|
this.parentContext = {
|
|
6170
|
-
|
|
6171
|
-
|
|
6849
|
+
tree: ctx.tree,
|
|
6850
|
+
nodeId: ctx.nodeId,
|
|
6172
6851
|
depth
|
|
6173
6852
|
};
|
|
6174
6853
|
}
|
|
6854
|
+
if (ctx.signal && !this.signal) {
|
|
6855
|
+
this.signal = ctx.signal;
|
|
6856
|
+
}
|
|
6175
6857
|
return this;
|
|
6176
6858
|
}
|
|
6177
6859
|
/**
|
|
@@ -6204,11 +6886,13 @@ var init_builder = __esm({
|
|
|
6204
6886
|
*
|
|
6205
6887
|
* This is useful for in-context learning - showing the LLM what "past self"
|
|
6206
6888
|
* did correctly so it mimics the pattern. The call is formatted with proper
|
|
6207
|
-
* markers and parameter format
|
|
6889
|
+
* markers and parameter format, including the invocation ID so the LLM can
|
|
6890
|
+
* reference previous calls when building dependencies.
|
|
6208
6891
|
*
|
|
6209
6892
|
* @param gadgetName - Name of the gadget
|
|
6210
6893
|
* @param parameters - Parameters passed to the gadget
|
|
6211
6894
|
* @param result - Result returned by the gadget
|
|
6895
|
+
* @param invocationId - Invocation ID (shown to LLM so it can reference for dependencies)
|
|
6212
6896
|
* @returns This builder for chaining
|
|
6213
6897
|
*
|
|
6214
6898
|
* @example
|
|
@@ -6220,124 +6904,36 @@ var init_builder = __esm({
|
|
|
6220
6904
|
* done: false,
|
|
6221
6905
|
* type: 'info'
|
|
6222
6906
|
* },
|
|
6223
|
-
* 'ℹ️ 👋 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'
|
|
6224
6909
|
* )
|
|
6225
6910
|
* ```
|
|
6226
6911
|
*/
|
|
6227
|
-
withSyntheticGadgetCall(gadgetName, parameters, result) {
|
|
6912
|
+
withSyntheticGadgetCall(gadgetName, parameters, result, invocationId) {
|
|
6228
6913
|
const startPrefix = this.gadgetStartPrefix ?? GADGET_START_PREFIX;
|
|
6229
6914
|
const endPrefix = this.gadgetEndPrefix ?? GADGET_END_PREFIX;
|
|
6230
6915
|
const paramStr = this.formatBlockParameters(parameters, "");
|
|
6231
6916
|
this.initialMessages.push({
|
|
6232
6917
|
role: "assistant",
|
|
6233
|
-
content: `${startPrefix}${gadgetName}
|
|
6918
|
+
content: `${startPrefix}${gadgetName}:${invocationId}
|
|
6234
6919
|
${paramStr}
|
|
6235
6920
|
${endPrefix}`
|
|
6236
6921
|
});
|
|
6237
6922
|
this.initialMessages.push({
|
|
6238
6923
|
role: "user",
|
|
6239
|
-
content: `Result: ${result}`
|
|
6924
|
+
content: `Result (${invocationId}): ${result}`
|
|
6240
6925
|
});
|
|
6241
6926
|
return this;
|
|
6242
6927
|
}
|
|
6243
6928
|
/**
|
|
6244
|
-
* Compose the final hooks, including
|
|
6245
|
-
*
|
|
6246
|
-
*
|
|
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).
|
|
6247
6934
|
*/
|
|
6248
6935
|
composeHooks() {
|
|
6249
|
-
|
|
6250
|
-
if (this.parentContext) {
|
|
6251
|
-
const { invocationId, onSubagentEvent, depth } = this.parentContext;
|
|
6252
|
-
const existingOnLLMCallStart = hooks?.observers?.onLLMCallStart;
|
|
6253
|
-
const existingOnLLMCallComplete = hooks?.observers?.onLLMCallComplete;
|
|
6254
|
-
const existingOnGadgetExecutionStart = hooks?.observers?.onGadgetExecutionStart;
|
|
6255
|
-
const existingOnGadgetExecutionComplete = hooks?.observers?.onGadgetExecutionComplete;
|
|
6256
|
-
hooks = {
|
|
6257
|
-
...hooks,
|
|
6258
|
-
observers: {
|
|
6259
|
-
...hooks?.observers,
|
|
6260
|
-
onLLMCallStart: async (context) => {
|
|
6261
|
-
let inputTokens;
|
|
6262
|
-
try {
|
|
6263
|
-
if (this.client) {
|
|
6264
|
-
inputTokens = await this.client.countTokens(
|
|
6265
|
-
context.options.model,
|
|
6266
|
-
context.options.messages
|
|
6267
|
-
);
|
|
6268
|
-
}
|
|
6269
|
-
} catch {
|
|
6270
|
-
}
|
|
6271
|
-
onSubagentEvent({
|
|
6272
|
-
type: "llm_call_start",
|
|
6273
|
-
gadgetInvocationId: invocationId,
|
|
6274
|
-
depth,
|
|
6275
|
-
event: {
|
|
6276
|
-
iteration: context.iteration,
|
|
6277
|
-
model: context.options.model,
|
|
6278
|
-
inputTokens
|
|
6279
|
-
}
|
|
6280
|
-
});
|
|
6281
|
-
if (existingOnLLMCallStart) {
|
|
6282
|
-
await existingOnLLMCallStart(context);
|
|
6283
|
-
}
|
|
6284
|
-
},
|
|
6285
|
-
onLLMCallComplete: async (context) => {
|
|
6286
|
-
onSubagentEvent({
|
|
6287
|
-
type: "llm_call_end",
|
|
6288
|
-
gadgetInvocationId: invocationId,
|
|
6289
|
-
depth,
|
|
6290
|
-
event: {
|
|
6291
|
-
iteration: context.iteration,
|
|
6292
|
-
model: context.options.model,
|
|
6293
|
-
// Backward compat fields
|
|
6294
|
-
inputTokens: context.usage?.inputTokens,
|
|
6295
|
-
outputTokens: context.usage?.outputTokens,
|
|
6296
|
-
finishReason: context.finishReason ?? void 0,
|
|
6297
|
-
// Full usage object with cache details (for first-class display)
|
|
6298
|
-
usage: context.usage
|
|
6299
|
-
// Cost will be calculated by parent if it has model registry
|
|
6300
|
-
}
|
|
6301
|
-
});
|
|
6302
|
-
if (existingOnLLMCallComplete) {
|
|
6303
|
-
await existingOnLLMCallComplete(context);
|
|
6304
|
-
}
|
|
6305
|
-
},
|
|
6306
|
-
onGadgetExecutionStart: async (context) => {
|
|
6307
|
-
onSubagentEvent({
|
|
6308
|
-
type: "gadget_call",
|
|
6309
|
-
gadgetInvocationId: invocationId,
|
|
6310
|
-
depth,
|
|
6311
|
-
event: {
|
|
6312
|
-
call: {
|
|
6313
|
-
invocationId: context.invocationId,
|
|
6314
|
-
gadgetName: context.gadgetName,
|
|
6315
|
-
parameters: context.parameters
|
|
6316
|
-
}
|
|
6317
|
-
}
|
|
6318
|
-
});
|
|
6319
|
-
if (existingOnGadgetExecutionStart) {
|
|
6320
|
-
await existingOnGadgetExecutionStart(context);
|
|
6321
|
-
}
|
|
6322
|
-
},
|
|
6323
|
-
onGadgetExecutionComplete: async (context) => {
|
|
6324
|
-
onSubagentEvent({
|
|
6325
|
-
type: "gadget_result",
|
|
6326
|
-
gadgetInvocationId: invocationId,
|
|
6327
|
-
depth,
|
|
6328
|
-
event: {
|
|
6329
|
-
result: {
|
|
6330
|
-
invocationId: context.invocationId
|
|
6331
|
-
}
|
|
6332
|
-
}
|
|
6333
|
-
});
|
|
6334
|
-
if (existingOnGadgetExecutionComplete) {
|
|
6335
|
-
await existingOnGadgetExecutionComplete(context);
|
|
6336
|
-
}
|
|
6337
|
-
}
|
|
6338
|
-
}
|
|
6339
|
-
};
|
|
6340
|
-
}
|
|
6936
|
+
const hooks = this.hooks;
|
|
6341
6937
|
if (!this.trailingMessage) {
|
|
6342
6938
|
return hooks;
|
|
6343
6939
|
}
|
|
@@ -6420,19 +7016,6 @@ ${endPrefix}`
|
|
|
6420
7016
|
this.client = new LLMistClass();
|
|
6421
7017
|
}
|
|
6422
7018
|
const registry = GadgetRegistry.from(this.gadgets);
|
|
6423
|
-
let onSubagentEvent = this.subagentEventCallback;
|
|
6424
|
-
if (this.parentContext) {
|
|
6425
|
-
const { invocationId, onSubagentEvent: parentCallback, depth } = this.parentContext;
|
|
6426
|
-
const existingCallback = this.subagentEventCallback;
|
|
6427
|
-
onSubagentEvent = (event) => {
|
|
6428
|
-
parentCallback({
|
|
6429
|
-
...event,
|
|
6430
|
-
gadgetInvocationId: invocationId,
|
|
6431
|
-
depth: event.depth + depth
|
|
6432
|
-
});
|
|
6433
|
-
existingCallback?.(event);
|
|
6434
|
-
};
|
|
6435
|
-
}
|
|
6436
7019
|
return {
|
|
6437
7020
|
client: this.client,
|
|
6438
7021
|
model: this.model ?? "openai:gpt-5-nano",
|
|
@@ -6451,15 +7034,17 @@ ${endPrefix}`
|
|
|
6451
7034
|
gadgetArgPrefix: this.gadgetArgPrefix,
|
|
6452
7035
|
textOnlyHandler: this.textOnlyHandler,
|
|
6453
7036
|
textWithGadgetsHandler: this.textWithGadgetsHandler,
|
|
6454
|
-
stopOnGadgetError: this.stopOnGadgetError,
|
|
6455
|
-
canRecoverFromGadgetError: this.canRecoverFromGadgetError,
|
|
6456
7037
|
defaultGadgetTimeoutMs: this.defaultGadgetTimeoutMs,
|
|
6457
7038
|
gadgetOutputLimit: this.gadgetOutputLimit,
|
|
6458
7039
|
gadgetOutputLimitPercent: this.gadgetOutputLimitPercent,
|
|
6459
7040
|
compactionConfig: this.compactionConfig,
|
|
6460
7041
|
signal: this.signal,
|
|
6461
7042
|
subagentConfig: this.subagentConfig,
|
|
6462
|
-
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
|
|
6463
7048
|
};
|
|
6464
7049
|
}
|
|
6465
7050
|
ask(userPrompt) {
|
|
@@ -6616,19 +7201,6 @@ ${endPrefix}`
|
|
|
6616
7201
|
this.client = new LLMistClass();
|
|
6617
7202
|
}
|
|
6618
7203
|
const registry = GadgetRegistry.from(this.gadgets);
|
|
6619
|
-
let onSubagentEvent = this.subagentEventCallback;
|
|
6620
|
-
if (this.parentContext) {
|
|
6621
|
-
const { invocationId, onSubagentEvent: parentCallback, depth } = this.parentContext;
|
|
6622
|
-
const existingCallback = this.subagentEventCallback;
|
|
6623
|
-
onSubagentEvent = (event) => {
|
|
6624
|
-
parentCallback({
|
|
6625
|
-
...event,
|
|
6626
|
-
gadgetInvocationId: invocationId,
|
|
6627
|
-
depth: event.depth + depth
|
|
6628
|
-
});
|
|
6629
|
-
existingCallback?.(event);
|
|
6630
|
-
};
|
|
6631
|
-
}
|
|
6632
7204
|
const options = {
|
|
6633
7205
|
client: this.client,
|
|
6634
7206
|
model: this.model ?? "openai:gpt-5-nano",
|
|
@@ -6647,15 +7219,17 @@ ${endPrefix}`
|
|
|
6647
7219
|
gadgetArgPrefix: this.gadgetArgPrefix,
|
|
6648
7220
|
textOnlyHandler: this.textOnlyHandler,
|
|
6649
7221
|
textWithGadgetsHandler: this.textWithGadgetsHandler,
|
|
6650
|
-
stopOnGadgetError: this.stopOnGadgetError,
|
|
6651
|
-
canRecoverFromGadgetError: this.canRecoverFromGadgetError,
|
|
6652
7222
|
defaultGadgetTimeoutMs: this.defaultGadgetTimeoutMs,
|
|
6653
7223
|
gadgetOutputLimit: this.gadgetOutputLimit,
|
|
6654
7224
|
gadgetOutputLimitPercent: this.gadgetOutputLimitPercent,
|
|
6655
7225
|
compactionConfig: this.compactionConfig,
|
|
6656
7226
|
signal: this.signal,
|
|
6657
7227
|
subagentConfig: this.subagentConfig,
|
|
6658
|
-
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
|
|
6659
7233
|
};
|
|
6660
7234
|
return new Agent(AGENT_INTERNAL_KEY, options);
|
|
6661
7235
|
}
|
|
@@ -11243,16 +11817,16 @@ var MockConversationManager = class {
|
|
|
11243
11817
|
this.history.push(msg);
|
|
11244
11818
|
this.addedMessages.push(msg);
|
|
11245
11819
|
}
|
|
11246
|
-
addGadgetCallResult(gadgetName, parameters, result) {
|
|
11820
|
+
addGadgetCallResult(gadgetName, parameters, result, invocationId) {
|
|
11247
11821
|
const assistantMsg = {
|
|
11248
11822
|
role: "assistant",
|
|
11249
|
-
content: `!!!GADGET_START:${gadgetName}
|
|
11823
|
+
content: `!!!GADGET_START:${gadgetName}:${invocationId}
|
|
11250
11824
|
${JSON.stringify(parameters)}
|
|
11251
11825
|
!!!GADGET_END`
|
|
11252
11826
|
};
|
|
11253
11827
|
const resultMsg = {
|
|
11254
11828
|
role: "user",
|
|
11255
|
-
content: `Result: ${result}`
|
|
11829
|
+
content: `Result (${invocationId}): ${result}`
|
|
11256
11830
|
};
|
|
11257
11831
|
this.history.push(assistantMsg);
|
|
11258
11832
|
this.history.push(resultMsg);
|
|
@@ -11600,6 +12174,8 @@ export {
|
|
|
11600
12174
|
init_schema_validator,
|
|
11601
12175
|
GadgetRegistry,
|
|
11602
12176
|
init_registry,
|
|
12177
|
+
ExecutionTree,
|
|
12178
|
+
init_execution_tree,
|
|
11603
12179
|
DEFAULT_HINTS,
|
|
11604
12180
|
DEFAULT_PROMPTS,
|
|
11605
12181
|
resolvePromptTemplate,
|
|
@@ -11718,4 +12294,4 @@ export {
|
|
|
11718
12294
|
createEmptyStream,
|
|
11719
12295
|
createErrorStream
|
|
11720
12296
|
};
|
|
11721
|
-
//# sourceMappingURL=chunk-
|
|
12297
|
+
//# sourceMappingURL=chunk-VAJLPRJ6.js.map
|