llmist 6.0.0 → 6.2.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-EIE5VRSI.js → chunk-36YSBSGB.js} +967 -396
- package/dist/chunk-36YSBSGB.js.map +1 -0
- package/dist/{chunk-F62X5W2G.js → chunk-EJEP5MHQ.js} +108 -25
- package/dist/chunk-EJEP5MHQ.js.map +1 -0
- package/dist/cli.cjs +11874 -9401
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +3008 -1101
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +6589 -5923
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +184 -311
- package/dist/index.d.ts +184 -311
- package/dist/index.js +29 -3
- package/dist/{mock-stream-CAY53Q6u.d.cts → mock-stream-DG4wF-NH.d.cts} +1161 -161
- package/dist/{mock-stream-CAY53Q6u.d.ts → mock-stream-DG4wF-NH.d.ts} +1161 -161
- package/dist/testing/index.cjs +956 -389
- package/dist/testing/index.cjs.map +1 -1
- package/dist/testing/index.d.cts +3 -3
- package/dist/testing/index.d.ts +3 -3
- package/dist/testing/index.js +1 -1
- package/package.json +2 -1
- package/dist/chunk-EIE5VRSI.js.map +0 -1
- package/dist/chunk-F62X5W2G.js.map +0 -1
package/dist/testing/index.cjs
CHANGED
|
@@ -671,23 +671,26 @@ Produces: { "items": ["first", "second"] }`);
|
|
|
671
671
|
* Record a gadget execution result in the message history.
|
|
672
672
|
* Creates an assistant message with the gadget invocation and a user message with the result.
|
|
673
673
|
*
|
|
674
|
+
* The invocationId is shown to the LLM so it can reference previous calls when building dependencies.
|
|
675
|
+
*
|
|
674
676
|
* @param gadget - Name of the gadget that was executed
|
|
675
677
|
* @param parameters - Parameters that were passed to the gadget
|
|
676
678
|
* @param result - Text result from the gadget execution
|
|
679
|
+
* @param invocationId - Invocation ID (shown to LLM so it can reference for dependencies)
|
|
677
680
|
* @param media - Optional media outputs from the gadget
|
|
678
681
|
* @param mediaIds - Optional IDs for the media outputs
|
|
679
682
|
*/
|
|
680
|
-
addGadgetCallResult(gadget, parameters, result, media, mediaIds) {
|
|
683
|
+
addGadgetCallResult(gadget, parameters, result, invocationId, media, mediaIds) {
|
|
681
684
|
const paramStr = this.formatBlockParameters(parameters, "");
|
|
682
685
|
this.messages.push({
|
|
683
686
|
role: "assistant",
|
|
684
|
-
content: `${this.startPrefix}${gadget}
|
|
687
|
+
content: `${this.startPrefix}${gadget}:${invocationId}
|
|
685
688
|
${paramStr}
|
|
686
689
|
${this.endPrefix}`
|
|
687
690
|
});
|
|
688
691
|
if (media && media.length > 0 && mediaIds && mediaIds.length > 0) {
|
|
689
692
|
const idRefs = media.map((m, i) => `[Media: ${mediaIds[i]} (${m.kind})]`).join("\n");
|
|
690
|
-
const textWithIds = `Result: ${result}
|
|
693
|
+
const textWithIds = `Result (${invocationId}): ${result}
|
|
691
694
|
${idRefs}`;
|
|
692
695
|
const parts = [text(textWithIds)];
|
|
693
696
|
for (const item of media) {
|
|
@@ -701,7 +704,7 @@ ${idRefs}`;
|
|
|
701
704
|
} else {
|
|
702
705
|
this.messages.push({
|
|
703
706
|
role: "user",
|
|
704
|
-
content: `Result: ${result}`
|
|
707
|
+
content: `Result (${invocationId}): ${result}`
|
|
705
708
|
});
|
|
706
709
|
}
|
|
707
710
|
return this;
|
|
@@ -1037,6 +1040,611 @@ var init_registry = __esm({
|
|
|
1037
1040
|
}
|
|
1038
1041
|
});
|
|
1039
1042
|
|
|
1043
|
+
// src/core/execution-tree.ts
|
|
1044
|
+
var ExecutionTree;
|
|
1045
|
+
var init_execution_tree = __esm({
|
|
1046
|
+
"src/core/execution-tree.ts"() {
|
|
1047
|
+
"use strict";
|
|
1048
|
+
ExecutionTree = class {
|
|
1049
|
+
nodes = /* @__PURE__ */ new Map();
|
|
1050
|
+
rootIds = [];
|
|
1051
|
+
eventListeners = /* @__PURE__ */ new Map();
|
|
1052
|
+
eventIdCounter = 0;
|
|
1053
|
+
invocationIdToNodeId = /* @__PURE__ */ new Map();
|
|
1054
|
+
// For async event streaming
|
|
1055
|
+
eventQueue = [];
|
|
1056
|
+
eventWaiters = [];
|
|
1057
|
+
isCompleted = false;
|
|
1058
|
+
/**
|
|
1059
|
+
* Base depth for all nodes in this tree.
|
|
1060
|
+
* Used when this tree is a subagent's view into a parent tree.
|
|
1061
|
+
*/
|
|
1062
|
+
baseDepth;
|
|
1063
|
+
/**
|
|
1064
|
+
* Parent node ID for subagent trees.
|
|
1065
|
+
* All root nodes in this tree will have this as their parentId.
|
|
1066
|
+
*/
|
|
1067
|
+
parentNodeId;
|
|
1068
|
+
constructor(options) {
|
|
1069
|
+
this.baseDepth = options?.baseDepth ?? 0;
|
|
1070
|
+
this.parentNodeId = options?.parentNodeId ?? null;
|
|
1071
|
+
}
|
|
1072
|
+
// ===========================================================================
|
|
1073
|
+
// Node ID Generation
|
|
1074
|
+
// ===========================================================================
|
|
1075
|
+
generateLLMCallId(iteration, parentId) {
|
|
1076
|
+
if (parentId) {
|
|
1077
|
+
return `llm_${parentId}_${iteration}`;
|
|
1078
|
+
}
|
|
1079
|
+
return `llm_${iteration}`;
|
|
1080
|
+
}
|
|
1081
|
+
gadgetIdCounter = 0;
|
|
1082
|
+
generateGadgetId(invocationId) {
|
|
1083
|
+
return `gadget_${invocationId}_${++this.gadgetIdCounter}`;
|
|
1084
|
+
}
|
|
1085
|
+
// ===========================================================================
|
|
1086
|
+
// Event Emission
|
|
1087
|
+
// ===========================================================================
|
|
1088
|
+
emit(event) {
|
|
1089
|
+
const listeners = this.eventListeners.get(event.type);
|
|
1090
|
+
if (listeners) {
|
|
1091
|
+
for (const listener of listeners) {
|
|
1092
|
+
try {
|
|
1093
|
+
listener(event);
|
|
1094
|
+
} catch (error) {
|
|
1095
|
+
console.error(`Error in event listener for ${event.type}:`, error);
|
|
1096
|
+
}
|
|
1097
|
+
}
|
|
1098
|
+
}
|
|
1099
|
+
const allListeners = this.eventListeners.get("*");
|
|
1100
|
+
if (allListeners) {
|
|
1101
|
+
for (const listener of allListeners) {
|
|
1102
|
+
try {
|
|
1103
|
+
listener(event);
|
|
1104
|
+
} catch (error) {
|
|
1105
|
+
console.error("Error in wildcard event listener:", error);
|
|
1106
|
+
}
|
|
1107
|
+
}
|
|
1108
|
+
}
|
|
1109
|
+
if (this.eventWaiters.length > 0) {
|
|
1110
|
+
const waiter = this.eventWaiters.shift();
|
|
1111
|
+
if (waiter) waiter(event);
|
|
1112
|
+
} else {
|
|
1113
|
+
this.eventQueue.push(event);
|
|
1114
|
+
}
|
|
1115
|
+
}
|
|
1116
|
+
createBaseEventProps(node) {
|
|
1117
|
+
return {
|
|
1118
|
+
eventId: ++this.eventIdCounter,
|
|
1119
|
+
timestamp: Date.now(),
|
|
1120
|
+
nodeId: node.id,
|
|
1121
|
+
parentId: node.parentId,
|
|
1122
|
+
depth: node.depth,
|
|
1123
|
+
path: node.path
|
|
1124
|
+
};
|
|
1125
|
+
}
|
|
1126
|
+
// ===========================================================================
|
|
1127
|
+
// Node Creation
|
|
1128
|
+
// ===========================================================================
|
|
1129
|
+
/**
|
|
1130
|
+
* Add a new LLM call node to the tree.
|
|
1131
|
+
*/
|
|
1132
|
+
addLLMCall(params) {
|
|
1133
|
+
const parentId = params.parentId ?? this.parentNodeId;
|
|
1134
|
+
const parent = parentId ? this.nodes.get(parentId) : null;
|
|
1135
|
+
const depth = parent ? parent.depth + 1 : this.baseDepth;
|
|
1136
|
+
const path = parent ? [...parent.path] : [];
|
|
1137
|
+
const id = this.generateLLMCallId(params.iteration, parentId);
|
|
1138
|
+
path.push(id);
|
|
1139
|
+
const node = {
|
|
1140
|
+
id,
|
|
1141
|
+
type: "llm_call",
|
|
1142
|
+
parentId,
|
|
1143
|
+
depth,
|
|
1144
|
+
path,
|
|
1145
|
+
createdAt: Date.now(),
|
|
1146
|
+
completedAt: null,
|
|
1147
|
+
iteration: params.iteration,
|
|
1148
|
+
model: params.model,
|
|
1149
|
+
request: params.request,
|
|
1150
|
+
response: "",
|
|
1151
|
+
children: []
|
|
1152
|
+
};
|
|
1153
|
+
this.nodes.set(id, node);
|
|
1154
|
+
if (!parentId) {
|
|
1155
|
+
this.rootIds.push(id);
|
|
1156
|
+
} else if (parent) {
|
|
1157
|
+
parent.children.push(id);
|
|
1158
|
+
}
|
|
1159
|
+
this.emit({
|
|
1160
|
+
type: "llm_call_start",
|
|
1161
|
+
...this.createBaseEventProps(node),
|
|
1162
|
+
iteration: node.iteration,
|
|
1163
|
+
model: node.model,
|
|
1164
|
+
request: node.request
|
|
1165
|
+
});
|
|
1166
|
+
return node;
|
|
1167
|
+
}
|
|
1168
|
+
/**
|
|
1169
|
+
* Add text to an LLM call's response (for streaming).
|
|
1170
|
+
*/
|
|
1171
|
+
appendLLMResponse(nodeId, chunk) {
|
|
1172
|
+
const node = this.nodes.get(nodeId);
|
|
1173
|
+
if (!node || node.type !== "llm_call") {
|
|
1174
|
+
throw new Error(`LLM call node not found: ${nodeId}`);
|
|
1175
|
+
}
|
|
1176
|
+
node.response += chunk;
|
|
1177
|
+
this.emit({
|
|
1178
|
+
type: "llm_call_stream",
|
|
1179
|
+
...this.createBaseEventProps(node),
|
|
1180
|
+
chunk
|
|
1181
|
+
});
|
|
1182
|
+
}
|
|
1183
|
+
/**
|
|
1184
|
+
* Complete an LLM call node.
|
|
1185
|
+
*/
|
|
1186
|
+
completeLLMCall(nodeId, params) {
|
|
1187
|
+
const node = this.nodes.get(nodeId);
|
|
1188
|
+
if (!node || node.type !== "llm_call") {
|
|
1189
|
+
throw new Error(`LLM call node not found: ${nodeId}`);
|
|
1190
|
+
}
|
|
1191
|
+
const llmNode = node;
|
|
1192
|
+
llmNode.completedAt = Date.now();
|
|
1193
|
+
if (params.response !== void 0) llmNode.response = params.response;
|
|
1194
|
+
if (params.usage) llmNode.usage = params.usage;
|
|
1195
|
+
if (params.finishReason !== void 0) llmNode.finishReason = params.finishReason;
|
|
1196
|
+
if (params.cost !== void 0) llmNode.cost = params.cost;
|
|
1197
|
+
this.emit({
|
|
1198
|
+
type: "llm_call_complete",
|
|
1199
|
+
...this.createBaseEventProps(node),
|
|
1200
|
+
response: llmNode.response,
|
|
1201
|
+
usage: llmNode.usage,
|
|
1202
|
+
finishReason: llmNode.finishReason,
|
|
1203
|
+
cost: llmNode.cost
|
|
1204
|
+
});
|
|
1205
|
+
}
|
|
1206
|
+
/**
|
|
1207
|
+
* Mark an LLM call as failed.
|
|
1208
|
+
*/
|
|
1209
|
+
failLLMCall(nodeId, error, recovered) {
|
|
1210
|
+
const node = this.nodes.get(nodeId);
|
|
1211
|
+
if (!node || node.type !== "llm_call") {
|
|
1212
|
+
throw new Error(`LLM call node not found: ${nodeId}`);
|
|
1213
|
+
}
|
|
1214
|
+
const llmNode = node;
|
|
1215
|
+
llmNode.completedAt = Date.now();
|
|
1216
|
+
this.emit({
|
|
1217
|
+
type: "llm_call_error",
|
|
1218
|
+
...this.createBaseEventProps(node),
|
|
1219
|
+
error,
|
|
1220
|
+
recovered
|
|
1221
|
+
});
|
|
1222
|
+
}
|
|
1223
|
+
/**
|
|
1224
|
+
* Add a new gadget node to the tree.
|
|
1225
|
+
*/
|
|
1226
|
+
addGadget(params) {
|
|
1227
|
+
const parentId = params.parentId ?? this.getCurrentLLMCallId() ?? this.parentNodeId;
|
|
1228
|
+
const parent = parentId ? this.nodes.get(parentId) : null;
|
|
1229
|
+
const depth = parent ? parent.depth + 1 : this.baseDepth;
|
|
1230
|
+
const path = parent ? [...parent.path] : [];
|
|
1231
|
+
const id = this.generateGadgetId(params.invocationId);
|
|
1232
|
+
path.push(id);
|
|
1233
|
+
const node = {
|
|
1234
|
+
id,
|
|
1235
|
+
type: "gadget",
|
|
1236
|
+
parentId,
|
|
1237
|
+
depth,
|
|
1238
|
+
path,
|
|
1239
|
+
createdAt: Date.now(),
|
|
1240
|
+
completedAt: null,
|
|
1241
|
+
invocationId: params.invocationId,
|
|
1242
|
+
name: params.name,
|
|
1243
|
+
parameters: params.parameters,
|
|
1244
|
+
dependencies: params.dependencies ?? [],
|
|
1245
|
+
state: "pending",
|
|
1246
|
+
children: [],
|
|
1247
|
+
isSubagent: false
|
|
1248
|
+
};
|
|
1249
|
+
this.nodes.set(id, node);
|
|
1250
|
+
this.invocationIdToNodeId.set(params.invocationId, id);
|
|
1251
|
+
if (parent) {
|
|
1252
|
+
parent.children.push(id);
|
|
1253
|
+
}
|
|
1254
|
+
this.emit({
|
|
1255
|
+
type: "gadget_call",
|
|
1256
|
+
...this.createBaseEventProps(node),
|
|
1257
|
+
invocationId: node.invocationId,
|
|
1258
|
+
name: node.name,
|
|
1259
|
+
parameters: node.parameters,
|
|
1260
|
+
dependencies: node.dependencies
|
|
1261
|
+
});
|
|
1262
|
+
return node;
|
|
1263
|
+
}
|
|
1264
|
+
/**
|
|
1265
|
+
* Mark a gadget as started (running).
|
|
1266
|
+
*/
|
|
1267
|
+
startGadget(nodeId) {
|
|
1268
|
+
const node = this.nodes.get(nodeId);
|
|
1269
|
+
if (!node || node.type !== "gadget") {
|
|
1270
|
+
throw new Error(`Gadget node not found: ${nodeId}`);
|
|
1271
|
+
}
|
|
1272
|
+
const gadgetNode = node;
|
|
1273
|
+
gadgetNode.state = "running";
|
|
1274
|
+
this.emit({
|
|
1275
|
+
type: "gadget_start",
|
|
1276
|
+
...this.createBaseEventProps(node),
|
|
1277
|
+
invocationId: gadgetNode.invocationId,
|
|
1278
|
+
name: gadgetNode.name
|
|
1279
|
+
});
|
|
1280
|
+
}
|
|
1281
|
+
/**
|
|
1282
|
+
* Complete a gadget node successfully.
|
|
1283
|
+
*/
|
|
1284
|
+
completeGadget(nodeId, params) {
|
|
1285
|
+
const node = this.nodes.get(nodeId);
|
|
1286
|
+
if (!node || node.type !== "gadget") {
|
|
1287
|
+
throw new Error(`Gadget node not found: ${nodeId}`);
|
|
1288
|
+
}
|
|
1289
|
+
const gadgetNode = node;
|
|
1290
|
+
gadgetNode.completedAt = Date.now();
|
|
1291
|
+
gadgetNode.state = params.error ? "failed" : "completed";
|
|
1292
|
+
if (params.result !== void 0) gadgetNode.result = params.result;
|
|
1293
|
+
if (params.error) gadgetNode.error = params.error;
|
|
1294
|
+
if (params.executionTimeMs !== void 0) gadgetNode.executionTimeMs = params.executionTimeMs;
|
|
1295
|
+
if (params.cost !== void 0) gadgetNode.cost = params.cost;
|
|
1296
|
+
if (params.media) gadgetNode.media = params.media;
|
|
1297
|
+
gadgetNode.isSubagent = gadgetNode.children.some((childId) => {
|
|
1298
|
+
const child = this.nodes.get(childId);
|
|
1299
|
+
return child?.type === "llm_call";
|
|
1300
|
+
});
|
|
1301
|
+
if (params.error) {
|
|
1302
|
+
this.emit({
|
|
1303
|
+
type: "gadget_error",
|
|
1304
|
+
...this.createBaseEventProps(node),
|
|
1305
|
+
invocationId: gadgetNode.invocationId,
|
|
1306
|
+
name: gadgetNode.name,
|
|
1307
|
+
error: params.error,
|
|
1308
|
+
executionTimeMs: params.executionTimeMs ?? 0
|
|
1309
|
+
});
|
|
1310
|
+
} else {
|
|
1311
|
+
this.emit({
|
|
1312
|
+
type: "gadget_complete",
|
|
1313
|
+
...this.createBaseEventProps(node),
|
|
1314
|
+
invocationId: gadgetNode.invocationId,
|
|
1315
|
+
name: gadgetNode.name,
|
|
1316
|
+
result: params.result ?? "",
|
|
1317
|
+
executionTimeMs: params.executionTimeMs ?? 0,
|
|
1318
|
+
cost: params.cost,
|
|
1319
|
+
media: params.media
|
|
1320
|
+
});
|
|
1321
|
+
}
|
|
1322
|
+
}
|
|
1323
|
+
/**
|
|
1324
|
+
* Mark a gadget as skipped due to dependency failure.
|
|
1325
|
+
*/
|
|
1326
|
+
skipGadget(nodeId, failedDependency, failedDependencyError, reason) {
|
|
1327
|
+
const node = this.nodes.get(nodeId);
|
|
1328
|
+
if (!node || node.type !== "gadget") {
|
|
1329
|
+
throw new Error(`Gadget node not found: ${nodeId}`);
|
|
1330
|
+
}
|
|
1331
|
+
const gadgetNode = node;
|
|
1332
|
+
gadgetNode.completedAt = Date.now();
|
|
1333
|
+
gadgetNode.state = "skipped";
|
|
1334
|
+
gadgetNode.failedDependency = failedDependency;
|
|
1335
|
+
gadgetNode.error = failedDependencyError;
|
|
1336
|
+
const error = reason === "controller_skip" ? "Skipped by controller" : `Dependency ${failedDependency} failed: ${failedDependencyError}`;
|
|
1337
|
+
this.emit({
|
|
1338
|
+
type: "gadget_skipped",
|
|
1339
|
+
...this.createBaseEventProps(node),
|
|
1340
|
+
invocationId: gadgetNode.invocationId,
|
|
1341
|
+
name: gadgetNode.name,
|
|
1342
|
+
reason,
|
|
1343
|
+
error,
|
|
1344
|
+
failedDependency,
|
|
1345
|
+
failedDependencyError
|
|
1346
|
+
});
|
|
1347
|
+
}
|
|
1348
|
+
// ===========================================================================
|
|
1349
|
+
// Text Events (pure notifications, not tree nodes)
|
|
1350
|
+
// ===========================================================================
|
|
1351
|
+
/**
|
|
1352
|
+
* Emit a text event (notification only, not stored in tree).
|
|
1353
|
+
*/
|
|
1354
|
+
emitText(content, llmCallNodeId) {
|
|
1355
|
+
const node = this.nodes.get(llmCallNodeId);
|
|
1356
|
+
if (!node) {
|
|
1357
|
+
throw new Error(`Node not found: ${llmCallNodeId}`);
|
|
1358
|
+
}
|
|
1359
|
+
this.emit({
|
|
1360
|
+
type: "text",
|
|
1361
|
+
...this.createBaseEventProps(node),
|
|
1362
|
+
content
|
|
1363
|
+
});
|
|
1364
|
+
}
|
|
1365
|
+
// ===========================================================================
|
|
1366
|
+
// Query Methods
|
|
1367
|
+
// ===========================================================================
|
|
1368
|
+
/**
|
|
1369
|
+
* Get a node by ID.
|
|
1370
|
+
*/
|
|
1371
|
+
getNode(id) {
|
|
1372
|
+
return this.nodes.get(id);
|
|
1373
|
+
}
|
|
1374
|
+
/**
|
|
1375
|
+
* Get a gadget node by invocation ID.
|
|
1376
|
+
*/
|
|
1377
|
+
getNodeByInvocationId(invocationId) {
|
|
1378
|
+
const nodeId = this.invocationIdToNodeId.get(invocationId);
|
|
1379
|
+
if (!nodeId) return void 0;
|
|
1380
|
+
const node = this.nodes.get(nodeId);
|
|
1381
|
+
return node?.type === "gadget" ? node : void 0;
|
|
1382
|
+
}
|
|
1383
|
+
/**
|
|
1384
|
+
* Get all root nodes (depth 0 for this tree).
|
|
1385
|
+
*/
|
|
1386
|
+
getRoots() {
|
|
1387
|
+
return this.rootIds.map((id) => this.nodes.get(id)).filter((node) => node !== void 0);
|
|
1388
|
+
}
|
|
1389
|
+
/**
|
|
1390
|
+
* Get children of a node.
|
|
1391
|
+
*/
|
|
1392
|
+
getChildren(id) {
|
|
1393
|
+
const node = this.nodes.get(id);
|
|
1394
|
+
if (!node) return [];
|
|
1395
|
+
return node.children.map((childId) => this.nodes.get(childId)).filter((child) => child !== void 0);
|
|
1396
|
+
}
|
|
1397
|
+
/**
|
|
1398
|
+
* Get ancestors of a node (from root to parent).
|
|
1399
|
+
*/
|
|
1400
|
+
getAncestors(id) {
|
|
1401
|
+
const node = this.nodes.get(id);
|
|
1402
|
+
if (!node) return [];
|
|
1403
|
+
const ancestors = [];
|
|
1404
|
+
let currentId = node.parentId;
|
|
1405
|
+
while (currentId) {
|
|
1406
|
+
const ancestor = this.nodes.get(currentId);
|
|
1407
|
+
if (ancestor) {
|
|
1408
|
+
ancestors.unshift(ancestor);
|
|
1409
|
+
currentId = ancestor.parentId;
|
|
1410
|
+
} else {
|
|
1411
|
+
break;
|
|
1412
|
+
}
|
|
1413
|
+
}
|
|
1414
|
+
return ancestors;
|
|
1415
|
+
}
|
|
1416
|
+
/**
|
|
1417
|
+
* Get all descendants of a node.
|
|
1418
|
+
*/
|
|
1419
|
+
getDescendants(id, type) {
|
|
1420
|
+
const node = this.nodes.get(id);
|
|
1421
|
+
if (!node) return [];
|
|
1422
|
+
const descendants = [];
|
|
1423
|
+
const stack = [...node.children];
|
|
1424
|
+
while (stack.length > 0) {
|
|
1425
|
+
const childId = stack.pop();
|
|
1426
|
+
const child = this.nodes.get(childId);
|
|
1427
|
+
if (child) {
|
|
1428
|
+
if (!type || child.type === type) {
|
|
1429
|
+
descendants.push(child);
|
|
1430
|
+
}
|
|
1431
|
+
stack.push(...child.children);
|
|
1432
|
+
}
|
|
1433
|
+
}
|
|
1434
|
+
return descendants;
|
|
1435
|
+
}
|
|
1436
|
+
/**
|
|
1437
|
+
* Get the current (most recent incomplete) LLM call node.
|
|
1438
|
+
*/
|
|
1439
|
+
getCurrentLLMCallId() {
|
|
1440
|
+
for (let i = this.rootIds.length - 1; i >= 0; i--) {
|
|
1441
|
+
const node = this.nodes.get(this.rootIds[i]);
|
|
1442
|
+
if (node?.type === "llm_call" && !node.completedAt) {
|
|
1443
|
+
return node.id;
|
|
1444
|
+
}
|
|
1445
|
+
}
|
|
1446
|
+
return void 0;
|
|
1447
|
+
}
|
|
1448
|
+
// ===========================================================================
|
|
1449
|
+
// Aggregation Methods (for subagent support)
|
|
1450
|
+
// ===========================================================================
|
|
1451
|
+
/**
|
|
1452
|
+
* Get total cost for entire tree.
|
|
1453
|
+
*/
|
|
1454
|
+
getTotalCost() {
|
|
1455
|
+
let total = 0;
|
|
1456
|
+
for (const node of this.nodes.values()) {
|
|
1457
|
+
if (node.type === "llm_call" && node.cost) {
|
|
1458
|
+
total += node.cost;
|
|
1459
|
+
} else if (node.type === "gadget" && node.cost) {
|
|
1460
|
+
total += node.cost;
|
|
1461
|
+
}
|
|
1462
|
+
}
|
|
1463
|
+
return total;
|
|
1464
|
+
}
|
|
1465
|
+
/**
|
|
1466
|
+
* Get total cost for a subtree (node and all descendants).
|
|
1467
|
+
*/
|
|
1468
|
+
getSubtreeCost(nodeId) {
|
|
1469
|
+
const node = this.nodes.get(nodeId);
|
|
1470
|
+
if (!node) return 0;
|
|
1471
|
+
let total = 0;
|
|
1472
|
+
if (node.type === "llm_call" && node.cost) {
|
|
1473
|
+
total += node.cost;
|
|
1474
|
+
} else if (node.type === "gadget" && node.cost) {
|
|
1475
|
+
total += node.cost;
|
|
1476
|
+
}
|
|
1477
|
+
for (const descendant of this.getDescendants(nodeId)) {
|
|
1478
|
+
if (descendant.type === "llm_call" && descendant.cost) {
|
|
1479
|
+
total += descendant.cost;
|
|
1480
|
+
} else if (descendant.type === "gadget" && descendant.cost) {
|
|
1481
|
+
total += descendant.cost;
|
|
1482
|
+
}
|
|
1483
|
+
}
|
|
1484
|
+
return total;
|
|
1485
|
+
}
|
|
1486
|
+
/**
|
|
1487
|
+
* Get token usage for entire tree.
|
|
1488
|
+
*/
|
|
1489
|
+
getTotalTokens() {
|
|
1490
|
+
let input = 0;
|
|
1491
|
+
let output = 0;
|
|
1492
|
+
let cached = 0;
|
|
1493
|
+
for (const node of this.nodes.values()) {
|
|
1494
|
+
if (node.type === "llm_call") {
|
|
1495
|
+
const llmNode = node;
|
|
1496
|
+
if (llmNode.usage) {
|
|
1497
|
+
input += llmNode.usage.inputTokens;
|
|
1498
|
+
output += llmNode.usage.outputTokens;
|
|
1499
|
+
cached += llmNode.usage.cachedInputTokens ?? 0;
|
|
1500
|
+
}
|
|
1501
|
+
}
|
|
1502
|
+
}
|
|
1503
|
+
return { input, output, cached };
|
|
1504
|
+
}
|
|
1505
|
+
/**
|
|
1506
|
+
* Get token usage for a subtree.
|
|
1507
|
+
*/
|
|
1508
|
+
getSubtreeTokens(nodeId) {
|
|
1509
|
+
const node = this.nodes.get(nodeId);
|
|
1510
|
+
if (!node) return { input: 0, output: 0, cached: 0 };
|
|
1511
|
+
let input = 0;
|
|
1512
|
+
let output = 0;
|
|
1513
|
+
let cached = 0;
|
|
1514
|
+
const nodesToProcess = [node, ...this.getDescendants(nodeId)];
|
|
1515
|
+
for (const n of nodesToProcess) {
|
|
1516
|
+
if (n.type === "llm_call") {
|
|
1517
|
+
const llmNode = n;
|
|
1518
|
+
if (llmNode.usage) {
|
|
1519
|
+
input += llmNode.usage.inputTokens;
|
|
1520
|
+
output += llmNode.usage.outputTokens;
|
|
1521
|
+
cached += llmNode.usage.cachedInputTokens ?? 0;
|
|
1522
|
+
}
|
|
1523
|
+
}
|
|
1524
|
+
}
|
|
1525
|
+
return { input, output, cached };
|
|
1526
|
+
}
|
|
1527
|
+
/**
|
|
1528
|
+
* Collect all media from a subtree.
|
|
1529
|
+
*/
|
|
1530
|
+
getSubtreeMedia(nodeId) {
|
|
1531
|
+
const node = this.nodes.get(nodeId);
|
|
1532
|
+
if (!node) return [];
|
|
1533
|
+
const media = [];
|
|
1534
|
+
const nodesToProcess = node.type === "gadget" ? [node] : [];
|
|
1535
|
+
nodesToProcess.push(...this.getDescendants(nodeId, "gadget"));
|
|
1536
|
+
for (const n of nodesToProcess) {
|
|
1537
|
+
if (n.type === "gadget") {
|
|
1538
|
+
const gadgetNode = n;
|
|
1539
|
+
if (gadgetNode.media) {
|
|
1540
|
+
media.push(...gadgetNode.media);
|
|
1541
|
+
}
|
|
1542
|
+
}
|
|
1543
|
+
}
|
|
1544
|
+
return media;
|
|
1545
|
+
}
|
|
1546
|
+
/**
|
|
1547
|
+
* Check if a subtree is complete (all nodes finished).
|
|
1548
|
+
*/
|
|
1549
|
+
isSubtreeComplete(nodeId) {
|
|
1550
|
+
const node = this.nodes.get(nodeId);
|
|
1551
|
+
if (!node) return true;
|
|
1552
|
+
if (!node.completedAt) return false;
|
|
1553
|
+
for (const descendant of this.getDescendants(nodeId)) {
|
|
1554
|
+
if (!descendant.completedAt) return false;
|
|
1555
|
+
}
|
|
1556
|
+
return true;
|
|
1557
|
+
}
|
|
1558
|
+
/**
|
|
1559
|
+
* Get node counts.
|
|
1560
|
+
*/
|
|
1561
|
+
getNodeCount() {
|
|
1562
|
+
let llmCalls = 0;
|
|
1563
|
+
let gadgets = 0;
|
|
1564
|
+
for (const node of this.nodes.values()) {
|
|
1565
|
+
if (node.type === "llm_call") llmCalls++;
|
|
1566
|
+
else if (node.type === "gadget") gadgets++;
|
|
1567
|
+
}
|
|
1568
|
+
return { llmCalls, gadgets };
|
|
1569
|
+
}
|
|
1570
|
+
// ===========================================================================
|
|
1571
|
+
// Event Subscription
|
|
1572
|
+
// ===========================================================================
|
|
1573
|
+
/**
|
|
1574
|
+
* Subscribe to events of a specific type.
|
|
1575
|
+
* Returns unsubscribe function.
|
|
1576
|
+
*
|
|
1577
|
+
* @param type - Event type to subscribe to (use "*" for all events)
|
|
1578
|
+
* @param listener - Callback function that receives matching events
|
|
1579
|
+
* @returns Unsubscribe function
|
|
1580
|
+
*
|
|
1581
|
+
* @example
|
|
1582
|
+
* ```typescript
|
|
1583
|
+
* const unsubscribe = tree.on("gadget_complete", (event) => {
|
|
1584
|
+
* if (event.type === "gadget_complete") {
|
|
1585
|
+
* console.log(`Gadget ${event.name} completed`);
|
|
1586
|
+
* }
|
|
1587
|
+
* });
|
|
1588
|
+
* ```
|
|
1589
|
+
*/
|
|
1590
|
+
on(type, listener) {
|
|
1591
|
+
if (!this.eventListeners.has(type)) {
|
|
1592
|
+
this.eventListeners.set(type, /* @__PURE__ */ new Set());
|
|
1593
|
+
}
|
|
1594
|
+
const listeners = this.eventListeners.get(type);
|
|
1595
|
+
listeners.add(listener);
|
|
1596
|
+
return () => {
|
|
1597
|
+
listeners.delete(listener);
|
|
1598
|
+
};
|
|
1599
|
+
}
|
|
1600
|
+
/**
|
|
1601
|
+
* Subscribe to all events.
|
|
1602
|
+
*/
|
|
1603
|
+
onAll(listener) {
|
|
1604
|
+
return this.on("*", listener);
|
|
1605
|
+
}
|
|
1606
|
+
/**
|
|
1607
|
+
* Get async iterable of all events.
|
|
1608
|
+
* Events are yielded as they occur.
|
|
1609
|
+
*/
|
|
1610
|
+
async *events() {
|
|
1611
|
+
while (!this.isCompleted) {
|
|
1612
|
+
while (this.eventQueue.length > 0) {
|
|
1613
|
+
yield this.eventQueue.shift();
|
|
1614
|
+
}
|
|
1615
|
+
if (this.isCompleted) break;
|
|
1616
|
+
const event = await new Promise((resolve) => {
|
|
1617
|
+
if (this.eventQueue.length > 0) {
|
|
1618
|
+
resolve(this.eventQueue.shift());
|
|
1619
|
+
} else {
|
|
1620
|
+
this.eventWaiters.push(resolve);
|
|
1621
|
+
}
|
|
1622
|
+
});
|
|
1623
|
+
yield event;
|
|
1624
|
+
}
|
|
1625
|
+
while (this.eventQueue.length > 0) {
|
|
1626
|
+
yield this.eventQueue.shift();
|
|
1627
|
+
}
|
|
1628
|
+
}
|
|
1629
|
+
/**
|
|
1630
|
+
* Mark the tree as complete (no more events will be emitted).
|
|
1631
|
+
*/
|
|
1632
|
+
complete() {
|
|
1633
|
+
this.isCompleted = true;
|
|
1634
|
+
for (const waiter of this.eventWaiters) {
|
|
1635
|
+
}
|
|
1636
|
+
this.eventWaiters = [];
|
|
1637
|
+
}
|
|
1638
|
+
/**
|
|
1639
|
+
* Check if the tree is complete.
|
|
1640
|
+
*/
|
|
1641
|
+
isComplete() {
|
|
1642
|
+
return this.isCompleted;
|
|
1643
|
+
}
|
|
1644
|
+
};
|
|
1645
|
+
}
|
|
1646
|
+
});
|
|
1647
|
+
|
|
1040
1648
|
// src/gadgets/media-store.ts
|
|
1041
1649
|
function getLlmistTmpDir() {
|
|
1042
1650
|
return (0, import_node_path2.join)((0, import_node_os.homedir)(), ".llmist", "tmp");
|
|
@@ -2333,8 +2941,8 @@ var init_conversation_manager = __esm({
|
|
|
2333
2941
|
addAssistantMessage(content) {
|
|
2334
2942
|
this.historyBuilder.addAssistant(content);
|
|
2335
2943
|
}
|
|
2336
|
-
addGadgetCallResult(gadgetName, parameters, result, media, mediaIds) {
|
|
2337
|
-
this.historyBuilder.addGadgetCallResult(gadgetName, parameters, result, media, mediaIds);
|
|
2944
|
+
addGadgetCallResult(gadgetName, parameters, result, invocationId, media, mediaIds) {
|
|
2945
|
+
this.historyBuilder.addGadgetCallResult(gadgetName, parameters, result, invocationId, media, mediaIds);
|
|
2338
2946
|
}
|
|
2339
2947
|
getMessages() {
|
|
2340
2948
|
return [...this.baseMessages, ...this.initialMessages, ...this.historyBuilder.build()];
|
|
@@ -3472,20 +4080,65 @@ var init_parser = __esm({
|
|
|
3472
4080
|
}
|
|
3473
4081
|
});
|
|
3474
4082
|
|
|
4083
|
+
// src/gadgets/typed-gadget.ts
|
|
4084
|
+
function Gadget(config) {
|
|
4085
|
+
class GadgetBase extends AbstractGadget {
|
|
4086
|
+
description = config.description;
|
|
4087
|
+
parameterSchema = config.schema;
|
|
4088
|
+
name = config.name;
|
|
4089
|
+
timeoutMs = config.timeoutMs;
|
|
4090
|
+
examples = config.examples;
|
|
4091
|
+
/**
|
|
4092
|
+
* Type helper property for accessing inferred parameter type.
|
|
4093
|
+
* This is used in the execute method signature: `execute(params: this['params'])`
|
|
4094
|
+
*
|
|
4095
|
+
* Note: This is just for type inference - the actual params in execute()
|
|
4096
|
+
* will be Record<string, unknown> which you can safely cast to this['params']
|
|
4097
|
+
*/
|
|
4098
|
+
params;
|
|
4099
|
+
}
|
|
4100
|
+
return GadgetBase;
|
|
4101
|
+
}
|
|
4102
|
+
var init_typed_gadget = __esm({
|
|
4103
|
+
"src/gadgets/typed-gadget.ts"() {
|
|
4104
|
+
"use strict";
|
|
4105
|
+
init_gadget();
|
|
4106
|
+
}
|
|
4107
|
+
});
|
|
4108
|
+
|
|
3475
4109
|
// src/gadgets/executor.ts
|
|
3476
|
-
|
|
4110
|
+
function getHostExportsInternal() {
|
|
4111
|
+
if (!cachedHostExports) {
|
|
4112
|
+
cachedHostExports = {
|
|
4113
|
+
AgentBuilder,
|
|
4114
|
+
Gadget,
|
|
4115
|
+
createGadget,
|
|
4116
|
+
ExecutionTree,
|
|
4117
|
+
LLMist,
|
|
4118
|
+
z: import_zod2.z
|
|
4119
|
+
};
|
|
4120
|
+
}
|
|
4121
|
+
return cachedHostExports;
|
|
4122
|
+
}
|
|
4123
|
+
var import_zod2, cachedHostExports, GadgetExecutor;
|
|
3477
4124
|
var init_executor = __esm({
|
|
3478
4125
|
"src/gadgets/executor.ts"() {
|
|
3479
4126
|
"use strict";
|
|
4127
|
+
import_zod2 = require("zod");
|
|
4128
|
+
init_builder();
|
|
4129
|
+
init_client();
|
|
3480
4130
|
init_constants();
|
|
4131
|
+
init_execution_tree();
|
|
3481
4132
|
init_logger();
|
|
3482
4133
|
init_block_params();
|
|
3483
4134
|
init_cost_reporting_client();
|
|
4135
|
+
init_create_gadget();
|
|
3484
4136
|
init_error_formatter();
|
|
3485
4137
|
init_exceptions();
|
|
3486
4138
|
init_parser();
|
|
4139
|
+
init_typed_gadget();
|
|
3487
4140
|
GadgetExecutor = class {
|
|
3488
|
-
constructor(registry, requestHumanInput, logger, defaultGadgetTimeoutMs, errorFormatterOptions, client, mediaStore, agentConfig, subagentConfig, onSubagentEvent) {
|
|
4141
|
+
constructor(registry, requestHumanInput, logger, defaultGadgetTimeoutMs, errorFormatterOptions, client, mediaStore, agentConfig, subagentConfig, onSubagentEvent, tree, parentNodeId, baseDepth) {
|
|
3489
4142
|
this.registry = registry;
|
|
3490
4143
|
this.requestHumanInput = requestHumanInput;
|
|
3491
4144
|
this.defaultGadgetTimeoutMs = defaultGadgetTimeoutMs;
|
|
@@ -3494,6 +4147,9 @@ var init_executor = __esm({
|
|
|
3494
4147
|
this.agentConfig = agentConfig;
|
|
3495
4148
|
this.subagentConfig = subagentConfig;
|
|
3496
4149
|
this.onSubagentEvent = onSubagentEvent;
|
|
4150
|
+
this.tree = tree;
|
|
4151
|
+
this.parentNodeId = parentNodeId;
|
|
4152
|
+
this.baseDepth = baseDepth;
|
|
3497
4153
|
this.logger = logger ?? createLogger({ name: "llmist:executor" });
|
|
3498
4154
|
this.errorFormatter = new GadgetExecutionErrorFormatter(errorFormatterOptions);
|
|
3499
4155
|
this.argPrefix = errorFormatterOptions?.argPrefix ?? GADGET_ARG_PREFIX;
|
|
@@ -3504,15 +4160,21 @@ var init_executor = __esm({
|
|
|
3504
4160
|
/**
|
|
3505
4161
|
* Creates a promise that rejects with a TimeoutException after the specified timeout.
|
|
3506
4162
|
* Aborts the provided AbortController before rejecting, allowing gadgets to clean up.
|
|
4163
|
+
* Returns both the promise and a cancel function to clear the timeout when no longer needed.
|
|
3507
4164
|
*/
|
|
3508
4165
|
createTimeoutPromise(gadgetName, timeoutMs, abortController) {
|
|
3509
|
-
|
|
3510
|
-
|
|
4166
|
+
let timeoutId;
|
|
4167
|
+
const promise = new Promise((_, reject) => {
|
|
4168
|
+
timeoutId = setTimeout(() => {
|
|
3511
4169
|
const timeoutError = new TimeoutException(gadgetName, timeoutMs);
|
|
3512
4170
|
abortController.abort(timeoutError.message);
|
|
3513
4171
|
reject(timeoutError);
|
|
3514
4172
|
}, timeoutMs);
|
|
3515
4173
|
});
|
|
4174
|
+
return {
|
|
4175
|
+
promise,
|
|
4176
|
+
cancel: () => clearTimeout(timeoutId)
|
|
4177
|
+
};
|
|
3516
4178
|
}
|
|
3517
4179
|
/**
|
|
3518
4180
|
* Unify gadget execute result to consistent internal format.
|
|
@@ -3634,6 +4296,8 @@ var init_executor = __esm({
|
|
|
3634
4296
|
});
|
|
3635
4297
|
}
|
|
3636
4298
|
};
|
|
4299
|
+
const gadgetNodeId = this.tree?.getNodeByInvocationId(call.invocationId)?.id;
|
|
4300
|
+
const gadgetDepth = gadgetNodeId ? this.tree?.getNode(gadgetNodeId)?.depth ?? this.baseDepth : this.baseDepth;
|
|
3637
4301
|
const ctx = {
|
|
3638
4302
|
reportCost,
|
|
3639
4303
|
llmist: this.client ? new CostReportingLLMistWrapper(this.client, reportCost) : void 0,
|
|
@@ -3641,7 +4305,13 @@ var init_executor = __esm({
|
|
|
3641
4305
|
agentConfig: this.agentConfig,
|
|
3642
4306
|
subagentConfig: this.subagentConfig,
|
|
3643
4307
|
invocationId: call.invocationId,
|
|
3644
|
-
onSubagentEvent: this.onSubagentEvent
|
|
4308
|
+
onSubagentEvent: this.onSubagentEvent,
|
|
4309
|
+
// Tree context for subagent support - use gadget's own node ID
|
|
4310
|
+
tree: this.tree,
|
|
4311
|
+
nodeId: gadgetNodeId,
|
|
4312
|
+
depth: gadgetDepth,
|
|
4313
|
+
// Host exports for external gadgets to use host's llmist classes
|
|
4314
|
+
hostExports: getHostExportsInternal()
|
|
3645
4315
|
};
|
|
3646
4316
|
let rawResult;
|
|
3647
4317
|
if (timeoutMs && timeoutMs > 0) {
|
|
@@ -3649,10 +4319,15 @@ var init_executor = __esm({
|
|
|
3649
4319
|
gadgetName: call.gadgetName,
|
|
3650
4320
|
timeoutMs
|
|
3651
4321
|
});
|
|
3652
|
-
|
|
3653
|
-
|
|
3654
|
-
|
|
3655
|
-
|
|
4322
|
+
const timeout = this.createTimeoutPromise(call.gadgetName, timeoutMs, abortController);
|
|
4323
|
+
try {
|
|
4324
|
+
rawResult = await Promise.race([
|
|
4325
|
+
Promise.resolve(gadget.execute(validatedParameters, ctx)),
|
|
4326
|
+
timeout.promise
|
|
4327
|
+
]);
|
|
4328
|
+
} finally {
|
|
4329
|
+
timeout.cancel();
|
|
4330
|
+
}
|
|
3656
4331
|
} else {
|
|
3657
4332
|
rawResult = await Promise.resolve(gadget.execute(validatedParameters, ctx));
|
|
3658
4333
|
}
|
|
@@ -3847,10 +4522,11 @@ var init_stream_processor = __esm({
|
|
|
3847
4522
|
logger;
|
|
3848
4523
|
parser;
|
|
3849
4524
|
executor;
|
|
3850
|
-
|
|
3851
|
-
|
|
4525
|
+
// Execution Tree context
|
|
4526
|
+
tree;
|
|
4527
|
+
parentNodeId;
|
|
4528
|
+
baseDepth;
|
|
3852
4529
|
responseText = "";
|
|
3853
|
-
executionHalted = false;
|
|
3854
4530
|
observerFailureCount = 0;
|
|
3855
4531
|
// Dependency tracking for gadget execution DAG
|
|
3856
4532
|
/** Gadgets waiting for their dependencies to complete */
|
|
@@ -3861,18 +4537,28 @@ var init_stream_processor = __esm({
|
|
|
3861
4537
|
failedInvocations = /* @__PURE__ */ new Set();
|
|
3862
4538
|
/** Promises for independent gadgets currently executing (fire-and-forget) */
|
|
3863
4539
|
inFlightExecutions = /* @__PURE__ */ new Map();
|
|
4540
|
+
/** Queue of completed gadget results ready to be yielded (for real-time streaming) */
|
|
4541
|
+
completedResultsQueue = [];
|
|
3864
4542
|
constructor(options) {
|
|
3865
4543
|
this.iteration = options.iteration;
|
|
3866
4544
|
this.registry = options.registry;
|
|
3867
4545
|
this.hooks = options.hooks ?? {};
|
|
3868
4546
|
this.logger = options.logger ?? createLogger({ name: "llmist:stream-processor" });
|
|
3869
|
-
this.
|
|
3870
|
-
this.
|
|
4547
|
+
this.tree = options.tree;
|
|
4548
|
+
this.parentNodeId = options.parentNodeId ?? null;
|
|
4549
|
+
this.baseDepth = options.baseDepth ?? 0;
|
|
3871
4550
|
this.parser = new GadgetCallParser({
|
|
3872
4551
|
startPrefix: options.gadgetStartPrefix,
|
|
3873
4552
|
endPrefix: options.gadgetEndPrefix,
|
|
3874
4553
|
argPrefix: options.gadgetArgPrefix
|
|
3875
4554
|
});
|
|
4555
|
+
const wrappedOnSubagentEvent = options.onSubagentEvent ? (event) => {
|
|
4556
|
+
this.completedResultsQueue.push({
|
|
4557
|
+
type: "subagent_event",
|
|
4558
|
+
subagentEvent: event
|
|
4559
|
+
});
|
|
4560
|
+
options.onSubagentEvent?.(event);
|
|
4561
|
+
} : void 0;
|
|
3876
4562
|
this.executor = new GadgetExecutor(
|
|
3877
4563
|
options.registry,
|
|
3878
4564
|
options.requestHumanInput,
|
|
@@ -3883,7 +4569,11 @@ var init_stream_processor = __esm({
|
|
|
3883
4569
|
options.mediaStore,
|
|
3884
4570
|
options.agentConfig,
|
|
3885
4571
|
options.subagentConfig,
|
|
3886
|
-
|
|
4572
|
+
wrappedOnSubagentEvent,
|
|
4573
|
+
// Tree context for gadget execution
|
|
4574
|
+
options.tree,
|
|
4575
|
+
options.parentNodeId,
|
|
4576
|
+
options.baseDepth
|
|
3887
4577
|
);
|
|
3888
4578
|
}
|
|
3889
4579
|
/**
|
|
@@ -3934,7 +4624,7 @@ var init_stream_processor = __esm({
|
|
|
3934
4624
|
usage,
|
|
3935
4625
|
logger: this.logger
|
|
3936
4626
|
};
|
|
3937
|
-
await this.hooks.observers
|
|
4627
|
+
await this.hooks.observers?.onStreamChunk?.(context);
|
|
3938
4628
|
});
|
|
3939
4629
|
await this.runObserversInParallel(chunkObservers);
|
|
3940
4630
|
}
|
|
@@ -3952,25 +4642,7 @@ var init_stream_processor = __esm({
|
|
|
3952
4642
|
}
|
|
3953
4643
|
}
|
|
3954
4644
|
}
|
|
3955
|
-
|
|
3956
|
-
this.logger.info("Breaking from LLM stream due to gadget error");
|
|
3957
|
-
break;
|
|
3958
|
-
}
|
|
3959
|
-
}
|
|
3960
|
-
if (!this.executionHalted) {
|
|
3961
|
-
for (const event of this.parser.finalize()) {
|
|
3962
|
-
for await (const processedEvent of this.processEventGenerator(event)) {
|
|
3963
|
-
yield processedEvent;
|
|
3964
|
-
if (processedEvent.type === "gadget_result") {
|
|
3965
|
-
didExecuteGadgets = true;
|
|
3966
|
-
if (processedEvent.result.breaksLoop) {
|
|
3967
|
-
shouldBreakLoop = true;
|
|
3968
|
-
}
|
|
3969
|
-
}
|
|
3970
|
-
}
|
|
3971
|
-
}
|
|
3972
|
-
const inFlightResults = await this.collectInFlightResults();
|
|
3973
|
-
for (const evt of inFlightResults) {
|
|
4645
|
+
for (const evt of this.drainCompletedResults()) {
|
|
3974
4646
|
yield evt;
|
|
3975
4647
|
if (evt.type === "gadget_result") {
|
|
3976
4648
|
didExecuteGadgets = true;
|
|
@@ -3979,16 +4651,45 @@ var init_stream_processor = __esm({
|
|
|
3979
4651
|
}
|
|
3980
4652
|
}
|
|
3981
4653
|
}
|
|
3982
|
-
|
|
3983
|
-
|
|
3984
|
-
|
|
4654
|
+
}
|
|
4655
|
+
for (const event of this.parser.finalize()) {
|
|
4656
|
+
for await (const processedEvent of this.processEventGenerator(event)) {
|
|
4657
|
+
yield processedEvent;
|
|
4658
|
+
if (processedEvent.type === "gadget_result") {
|
|
3985
4659
|
didExecuteGadgets = true;
|
|
3986
|
-
if (
|
|
4660
|
+
if (processedEvent.result.breaksLoop) {
|
|
3987
4661
|
shouldBreakLoop = true;
|
|
3988
4662
|
}
|
|
3989
4663
|
}
|
|
3990
4664
|
}
|
|
3991
4665
|
}
|
|
4666
|
+
for await (const evt of this.waitForInFlightExecutions()) {
|
|
4667
|
+
yield evt;
|
|
4668
|
+
if (evt.type === "gadget_result") {
|
|
4669
|
+
didExecuteGadgets = true;
|
|
4670
|
+
if (evt.result.breaksLoop) {
|
|
4671
|
+
shouldBreakLoop = true;
|
|
4672
|
+
}
|
|
4673
|
+
}
|
|
4674
|
+
}
|
|
4675
|
+
for (const evt of this.drainCompletedResults()) {
|
|
4676
|
+
yield evt;
|
|
4677
|
+
if (evt.type === "gadget_result") {
|
|
4678
|
+
didExecuteGadgets = true;
|
|
4679
|
+
if (evt.result.breaksLoop) {
|
|
4680
|
+
shouldBreakLoop = true;
|
|
4681
|
+
}
|
|
4682
|
+
}
|
|
4683
|
+
}
|
|
4684
|
+
for await (const evt of this.processPendingGadgetsGenerator()) {
|
|
4685
|
+
yield evt;
|
|
4686
|
+
if (evt.type === "gadget_result") {
|
|
4687
|
+
didExecuteGadgets = true;
|
|
4688
|
+
if (evt.result.breaksLoop) {
|
|
4689
|
+
shouldBreakLoop = true;
|
|
4690
|
+
}
|
|
4691
|
+
}
|
|
4692
|
+
}
|
|
3992
4693
|
let finalMessage = this.responseText;
|
|
3993
4694
|
if (this.hooks.interceptors?.interceptAssistantMessage) {
|
|
3994
4695
|
const context = {
|
|
@@ -4009,21 +4710,8 @@ var init_stream_processor = __esm({
|
|
|
4009
4710
|
};
|
|
4010
4711
|
yield completionEvent;
|
|
4011
4712
|
}
|
|
4012
|
-
/**
|
|
4013
|
-
* Process a single parsed event (text or gadget call).
|
|
4014
|
-
* @deprecated Use processEventGenerator for real-time streaming
|
|
4015
|
-
*/
|
|
4016
|
-
async processEvent(event) {
|
|
4017
|
-
if (event.type === "text") {
|
|
4018
|
-
return this.processTextEvent(event);
|
|
4019
|
-
} else if (event.type === "gadget_call") {
|
|
4020
|
-
return this.processGadgetCall(event.call);
|
|
4021
|
-
}
|
|
4022
|
-
return [event];
|
|
4023
|
-
}
|
|
4024
4713
|
/**
|
|
4025
4714
|
* Process a single parsed event, yielding events in real-time.
|
|
4026
|
-
* Generator version of processEvent for streaming support.
|
|
4027
4715
|
*/
|
|
4028
4716
|
async *processEventGenerator(event) {
|
|
4029
4717
|
if (event.type === "text") {
|
|
@@ -4065,12 +4753,6 @@ var init_stream_processor = __esm({
|
|
|
4065
4753
|
* After each execution, pending gadgets are checked to see if they can now run.
|
|
4066
4754
|
*/
|
|
4067
4755
|
async processGadgetCall(call) {
|
|
4068
|
-
if (this.executionHalted) {
|
|
4069
|
-
this.logger.debug("Skipping gadget execution due to previous error", {
|
|
4070
|
-
gadgetName: call.gadgetName
|
|
4071
|
-
});
|
|
4072
|
-
return [];
|
|
4073
|
-
}
|
|
4074
4756
|
const events = [];
|
|
4075
4757
|
events.push({ type: "gadget_call", call });
|
|
4076
4758
|
if (call.dependencies.length > 0) {
|
|
@@ -4121,13 +4803,16 @@ var init_stream_processor = __esm({
|
|
|
4121
4803
|
* when parsed (before execution), enabling real-time UI feedback.
|
|
4122
4804
|
*/
|
|
4123
4805
|
async *processGadgetCallGenerator(call) {
|
|
4124
|
-
|
|
4125
|
-
|
|
4126
|
-
|
|
4806
|
+
yield { type: "gadget_call", call };
|
|
4807
|
+
if (this.tree) {
|
|
4808
|
+
this.tree.addGadget({
|
|
4809
|
+
invocationId: call.invocationId,
|
|
4810
|
+
name: call.gadgetName,
|
|
4811
|
+
parameters: call.parameters ?? {},
|
|
4812
|
+
dependencies: call.dependencies,
|
|
4813
|
+
parentId: this.parentNodeId
|
|
4127
4814
|
});
|
|
4128
|
-
return;
|
|
4129
4815
|
}
|
|
4130
|
-
yield { type: "gadget_call", call };
|
|
4131
4816
|
if (call.dependencies.length > 0) {
|
|
4132
4817
|
if (call.dependencies.includes(call.invocationId)) {
|
|
4133
4818
|
this.logger.warn("Gadget has self-referential dependency (depends on itself)", {
|
|
@@ -4172,17 +4857,8 @@ var init_stream_processor = __esm({
|
|
|
4172
4857
|
}
|
|
4173
4858
|
return;
|
|
4174
4859
|
}
|
|
4175
|
-
|
|
4176
|
-
|
|
4177
|
-
yield evt;
|
|
4178
|
-
}
|
|
4179
|
-
for await (const evt of this.processPendingGadgetsGenerator()) {
|
|
4180
|
-
yield evt;
|
|
4181
|
-
}
|
|
4182
|
-
} else {
|
|
4183
|
-
const executionPromise = this.executeGadgetAndCollect(call);
|
|
4184
|
-
this.inFlightExecutions.set(call.invocationId, executionPromise);
|
|
4185
|
-
}
|
|
4860
|
+
const executionPromise = this.executeGadgetAndCollect(call);
|
|
4861
|
+
this.inFlightExecutions.set(call.invocationId, executionPromise);
|
|
4186
4862
|
}
|
|
4187
4863
|
/**
|
|
4188
4864
|
* Execute a gadget through the full hook lifecycle.
|
|
@@ -4197,15 +4873,6 @@ var init_stream_processor = __esm({
|
|
|
4197
4873
|
error: call.parseError,
|
|
4198
4874
|
rawParameters: call.parametersRaw
|
|
4199
4875
|
});
|
|
4200
|
-
const shouldContinue = await this.checkCanRecoverFromError(
|
|
4201
|
-
call.parseError,
|
|
4202
|
-
call.gadgetName,
|
|
4203
|
-
"parse",
|
|
4204
|
-
call.parameters
|
|
4205
|
-
);
|
|
4206
|
-
if (!shouldContinue) {
|
|
4207
|
-
this.executionHalted = true;
|
|
4208
|
-
}
|
|
4209
4876
|
}
|
|
4210
4877
|
let parameters = call.parameters ?? {};
|
|
4211
4878
|
if (this.hooks.interceptors?.interceptGadgetParameters) {
|
|
@@ -4248,7 +4915,7 @@ var init_stream_processor = __esm({
|
|
|
4248
4915
|
parameters,
|
|
4249
4916
|
logger: this.logger
|
|
4250
4917
|
};
|
|
4251
|
-
await this.hooks.observers
|
|
4918
|
+
await this.hooks.observers?.onGadgetExecutionStart?.(context);
|
|
4252
4919
|
});
|
|
4253
4920
|
}
|
|
4254
4921
|
await this.runObserversInParallel(startObservers);
|
|
@@ -4317,7 +4984,7 @@ var init_stream_processor = __esm({
|
|
|
4317
4984
|
cost: result.cost,
|
|
4318
4985
|
logger: this.logger
|
|
4319
4986
|
};
|
|
4320
|
-
await this.hooks.observers
|
|
4987
|
+
await this.hooks.observers?.onGadgetExecutionComplete?.(context);
|
|
4321
4988
|
});
|
|
4322
4989
|
}
|
|
4323
4990
|
await this.runObserversInParallel(completeObservers);
|
|
@@ -4326,18 +4993,6 @@ var init_stream_processor = __esm({
|
|
|
4326
4993
|
this.failedInvocations.add(result.invocationId);
|
|
4327
4994
|
}
|
|
4328
4995
|
events.push({ type: "gadget_result", result });
|
|
4329
|
-
if (result.error) {
|
|
4330
|
-
const errorType = this.determineErrorType(call, result);
|
|
4331
|
-
const shouldContinue = await this.checkCanRecoverFromError(
|
|
4332
|
-
result.error,
|
|
4333
|
-
result.gadgetName,
|
|
4334
|
-
errorType,
|
|
4335
|
-
result.parameters
|
|
4336
|
-
);
|
|
4337
|
-
if (!shouldContinue) {
|
|
4338
|
-
this.executionHalted = true;
|
|
4339
|
-
}
|
|
4340
|
-
}
|
|
4341
4996
|
return events;
|
|
4342
4997
|
}
|
|
4343
4998
|
/**
|
|
@@ -4351,15 +5006,6 @@ var init_stream_processor = __esm({
|
|
|
4351
5006
|
error: call.parseError,
|
|
4352
5007
|
rawParameters: call.parametersRaw
|
|
4353
5008
|
});
|
|
4354
|
-
const shouldContinue = await this.checkCanRecoverFromError(
|
|
4355
|
-
call.parseError,
|
|
4356
|
-
call.gadgetName,
|
|
4357
|
-
"parse",
|
|
4358
|
-
call.parameters
|
|
4359
|
-
);
|
|
4360
|
-
if (!shouldContinue) {
|
|
4361
|
-
this.executionHalted = true;
|
|
4362
|
-
}
|
|
4363
5009
|
}
|
|
4364
5010
|
let parameters = call.parameters ?? {};
|
|
4365
5011
|
if (this.hooks.interceptors?.interceptGadgetParameters) {
|
|
@@ -4402,10 +5048,16 @@ var init_stream_processor = __esm({
|
|
|
4402
5048
|
parameters,
|
|
4403
5049
|
logger: this.logger
|
|
4404
5050
|
};
|
|
4405
|
-
await this.hooks.observers
|
|
5051
|
+
await this.hooks.observers?.onGadgetExecutionStart?.(context);
|
|
4406
5052
|
});
|
|
4407
5053
|
}
|
|
4408
5054
|
await this.runObserversInParallel(startObservers);
|
|
5055
|
+
if (this.tree) {
|
|
5056
|
+
const gadgetNode = this.tree.getNodeByInvocationId(call.invocationId);
|
|
5057
|
+
if (gadgetNode) {
|
|
5058
|
+
this.tree.startGadget(gadgetNode.id);
|
|
5059
|
+
}
|
|
5060
|
+
}
|
|
4409
5061
|
let result;
|
|
4410
5062
|
if (shouldSkip) {
|
|
4411
5063
|
result = {
|
|
@@ -4471,57 +5123,84 @@ var init_stream_processor = __esm({
|
|
|
4471
5123
|
cost: result.cost,
|
|
4472
5124
|
logger: this.logger
|
|
4473
5125
|
};
|
|
4474
|
-
await this.hooks.observers
|
|
5126
|
+
await this.hooks.observers?.onGadgetExecutionComplete?.(context);
|
|
4475
5127
|
});
|
|
4476
5128
|
}
|
|
4477
5129
|
await this.runObserversInParallel(completeObservers);
|
|
5130
|
+
if (this.tree) {
|
|
5131
|
+
const gadgetNode = this.tree.getNodeByInvocationId(result.invocationId);
|
|
5132
|
+
if (gadgetNode) {
|
|
5133
|
+
if (result.error) {
|
|
5134
|
+
this.tree.completeGadget(gadgetNode.id, {
|
|
5135
|
+
error: result.error,
|
|
5136
|
+
executionTimeMs: result.executionTimeMs,
|
|
5137
|
+
cost: result.cost
|
|
5138
|
+
});
|
|
5139
|
+
} else {
|
|
5140
|
+
this.tree.completeGadget(gadgetNode.id, {
|
|
5141
|
+
result: result.result,
|
|
5142
|
+
executionTimeMs: result.executionTimeMs,
|
|
5143
|
+
cost: result.cost,
|
|
5144
|
+
media: result.media
|
|
5145
|
+
});
|
|
5146
|
+
}
|
|
5147
|
+
}
|
|
5148
|
+
}
|
|
4478
5149
|
this.completedResults.set(result.invocationId, result);
|
|
4479
5150
|
if (result.error) {
|
|
4480
5151
|
this.failedInvocations.add(result.invocationId);
|
|
4481
5152
|
}
|
|
4482
5153
|
yield { type: "gadget_result", result };
|
|
4483
|
-
if (result.error) {
|
|
4484
|
-
const errorType = this.determineErrorType(call, result);
|
|
4485
|
-
const shouldContinue = await this.checkCanRecoverFromError(
|
|
4486
|
-
result.error,
|
|
4487
|
-
result.gadgetName,
|
|
4488
|
-
errorType,
|
|
4489
|
-
result.parameters
|
|
4490
|
-
);
|
|
4491
|
-
if (!shouldContinue) {
|
|
4492
|
-
this.executionHalted = true;
|
|
4493
|
-
}
|
|
4494
|
-
}
|
|
4495
5154
|
}
|
|
4496
5155
|
/**
|
|
4497
|
-
* Execute a gadget and
|
|
5156
|
+
* Execute a gadget and push events to the completed results queue (non-blocking).
|
|
4498
5157
|
* Used for fire-and-forget parallel execution of independent gadgets.
|
|
5158
|
+
* Results are pushed to completedResultsQueue for real-time streaming to the caller.
|
|
4499
5159
|
*/
|
|
4500
5160
|
async executeGadgetAndCollect(call) {
|
|
4501
|
-
const events = [];
|
|
4502
5161
|
for await (const evt of this.executeGadgetGenerator(call)) {
|
|
4503
|
-
|
|
5162
|
+
this.completedResultsQueue.push(evt);
|
|
5163
|
+
}
|
|
5164
|
+
}
|
|
5165
|
+
/**
|
|
5166
|
+
* Drain all completed results from the queue.
|
|
5167
|
+
* Used to yield results as they complete during stream processing.
|
|
5168
|
+
* @returns Generator that yields all events currently in the queue
|
|
5169
|
+
*/
|
|
5170
|
+
*drainCompletedResults() {
|
|
5171
|
+
while (this.completedResultsQueue.length > 0) {
|
|
5172
|
+
yield this.completedResultsQueue.shift();
|
|
4504
5173
|
}
|
|
4505
|
-
return events;
|
|
4506
5174
|
}
|
|
4507
5175
|
/**
|
|
4508
|
-
*
|
|
4509
|
-
* Called at stream end to
|
|
4510
|
-
*
|
|
4511
|
-
*
|
|
5176
|
+
* Wait for all in-flight gadget executions to complete, yielding events in real-time.
|
|
5177
|
+
* Called at stream end to ensure all parallel executions finish.
|
|
5178
|
+
* Results and subagent events are pushed to completedResultsQueue during execution.
|
|
5179
|
+
* This generator yields queued events while polling, enabling real-time display
|
|
5180
|
+
* of subagent activity (LLM calls, nested gadgets) during long-running gadgets.
|
|
5181
|
+
* Clears the inFlightExecutions map after all gadgets complete.
|
|
4512
5182
|
*/
|
|
4513
|
-
async
|
|
5183
|
+
async *waitForInFlightExecutions() {
|
|
4514
5184
|
if (this.inFlightExecutions.size === 0) {
|
|
4515
|
-
return
|
|
5185
|
+
return;
|
|
4516
5186
|
}
|
|
4517
|
-
this.logger.debug("
|
|
5187
|
+
this.logger.debug("Waiting for in-flight gadget executions", {
|
|
4518
5188
|
count: this.inFlightExecutions.size,
|
|
4519
5189
|
invocationIds: Array.from(this.inFlightExecutions.keys())
|
|
4520
5190
|
});
|
|
4521
|
-
const
|
|
4522
|
-
const
|
|
5191
|
+
const allDone = Promise.all(this.inFlightExecutions.values()).then(() => "done");
|
|
5192
|
+
const POLL_INTERVAL_MS = 100;
|
|
5193
|
+
while (true) {
|
|
5194
|
+
const result = await Promise.race([
|
|
5195
|
+
allDone,
|
|
5196
|
+
new Promise((resolve) => setTimeout(() => resolve("poll"), POLL_INTERVAL_MS))
|
|
5197
|
+
]);
|
|
5198
|
+
yield* this.drainCompletedResults();
|
|
5199
|
+
if (result === "done") {
|
|
5200
|
+
break;
|
|
5201
|
+
}
|
|
5202
|
+
}
|
|
4523
5203
|
this.inFlightExecutions.clear();
|
|
4524
|
-
return results.flat();
|
|
4525
5204
|
}
|
|
4526
5205
|
/**
|
|
4527
5206
|
* Handle a gadget that cannot execute because a dependency failed.
|
|
@@ -4546,6 +5225,12 @@ var init_stream_processor = __esm({
|
|
|
4546
5225
|
}
|
|
4547
5226
|
if (action.action === "skip") {
|
|
4548
5227
|
this.failedInvocations.add(call.invocationId);
|
|
5228
|
+
if (this.tree) {
|
|
5229
|
+
const gadgetNode = this.tree.getNodeByInvocationId(call.invocationId);
|
|
5230
|
+
if (gadgetNode) {
|
|
5231
|
+
this.tree.skipGadget(gadgetNode.id, failedDep, depError, "dependency_failed");
|
|
5232
|
+
}
|
|
5233
|
+
}
|
|
4549
5234
|
const skipEvent = {
|
|
4550
5235
|
type: "gadget_skipped",
|
|
4551
5236
|
gadgetName: call.gadgetName,
|
|
@@ -4565,7 +5250,7 @@ var init_stream_processor = __esm({
|
|
|
4565
5250
|
failedDependencyError: depError,
|
|
4566
5251
|
logger: this.logger
|
|
4567
5252
|
};
|
|
4568
|
-
await this.safeObserve(() => this.hooks.observers
|
|
5253
|
+
await this.safeObserve(() => this.hooks.observers?.onGadgetSkipped?.(observeContext));
|
|
4569
5254
|
}
|
|
4570
5255
|
this.logger.info("Gadget skipped due to failed dependency", {
|
|
4571
5256
|
gadgetName: call.gadgetName,
|
|
@@ -4797,48 +5482,6 @@ var init_stream_processor = __esm({
|
|
|
4797
5482
|
observers.map((observer) => this.safeObserve(observer))
|
|
4798
5483
|
);
|
|
4799
5484
|
}
|
|
4800
|
-
/**
|
|
4801
|
-
* Check if execution can recover from an error.
|
|
4802
|
-
*
|
|
4803
|
-
* Returns true if we should continue processing subsequent gadgets, false if we should stop.
|
|
4804
|
-
*
|
|
4805
|
-
* Logic:
|
|
4806
|
-
* - If custom canRecoverFromGadgetError is provided, use it
|
|
4807
|
-
* - Otherwise, use stopOnGadgetError config:
|
|
4808
|
-
* - stopOnGadgetError=true → return false (stop execution)
|
|
4809
|
-
* - stopOnGadgetError=false → return true (continue execution)
|
|
4810
|
-
*/
|
|
4811
|
-
async checkCanRecoverFromError(error, gadgetName, errorType, parameters) {
|
|
4812
|
-
if (this.canRecoverFromGadgetError) {
|
|
4813
|
-
return await this.canRecoverFromGadgetError({
|
|
4814
|
-
error,
|
|
4815
|
-
gadgetName,
|
|
4816
|
-
errorType,
|
|
4817
|
-
parameters
|
|
4818
|
-
});
|
|
4819
|
-
}
|
|
4820
|
-
const shouldContinue = !this.stopOnGadgetError;
|
|
4821
|
-
this.logger.debug("Checking if should continue after error", {
|
|
4822
|
-
error,
|
|
4823
|
-
gadgetName,
|
|
4824
|
-
errorType,
|
|
4825
|
-
stopOnGadgetError: this.stopOnGadgetError,
|
|
4826
|
-
shouldContinue
|
|
4827
|
-
});
|
|
4828
|
-
return shouldContinue;
|
|
4829
|
-
}
|
|
4830
|
-
/**
|
|
4831
|
-
* Determine the type of error from a gadget execution.
|
|
4832
|
-
*/
|
|
4833
|
-
determineErrorType(call, result) {
|
|
4834
|
-
if (call.parseError) {
|
|
4835
|
-
return "parse";
|
|
4836
|
-
}
|
|
4837
|
-
if (result.error?.includes("Invalid parameters:")) {
|
|
4838
|
-
return "validation";
|
|
4839
|
-
}
|
|
4840
|
-
return "execution";
|
|
4841
|
-
}
|
|
4842
5485
|
};
|
|
4843
5486
|
}
|
|
4844
5487
|
});
|
|
@@ -4849,6 +5492,7 @@ var init_agent = __esm({
|
|
|
4849
5492
|
"src/agent/agent.ts"() {
|
|
4850
5493
|
"use strict";
|
|
4851
5494
|
init_constants();
|
|
5495
|
+
init_execution_tree();
|
|
4852
5496
|
init_messages();
|
|
4853
5497
|
init_model_shortcuts();
|
|
4854
5498
|
init_media_store();
|
|
@@ -4876,8 +5520,6 @@ var init_agent = __esm({
|
|
|
4876
5520
|
requestHumanInput;
|
|
4877
5521
|
textOnlyHandler;
|
|
4878
5522
|
textWithGadgetsHandler;
|
|
4879
|
-
stopOnGadgetError;
|
|
4880
|
-
canRecoverFromGadgetError;
|
|
4881
5523
|
defaultGadgetTimeoutMs;
|
|
4882
5524
|
defaultMaxTokens;
|
|
4883
5525
|
hasUserPrompt;
|
|
@@ -4900,6 +5542,12 @@ var init_agent = __esm({
|
|
|
4900
5542
|
pendingSubagentEvents = [];
|
|
4901
5543
|
// Combined callback that queues events AND calls user callback
|
|
4902
5544
|
onSubagentEvent;
|
|
5545
|
+
// Counter for generating synthetic invocation IDs for wrapped text content
|
|
5546
|
+
syntheticInvocationCounter = 0;
|
|
5547
|
+
// Execution Tree - first-class model for nested subagent support
|
|
5548
|
+
tree;
|
|
5549
|
+
parentNodeId;
|
|
5550
|
+
baseDepth;
|
|
4903
5551
|
/**
|
|
4904
5552
|
* Creates a new Agent instance.
|
|
4905
5553
|
* @internal This constructor is private. Use LLMist.createAgent() or AgentBuilder instead.
|
|
@@ -4922,8 +5570,6 @@ var init_agent = __esm({
|
|
|
4922
5570
|
this.requestHumanInput = options.requestHumanInput;
|
|
4923
5571
|
this.textOnlyHandler = options.textOnlyHandler ?? "terminate";
|
|
4924
5572
|
this.textWithGadgetsHandler = options.textWithGadgetsHandler;
|
|
4925
|
-
this.stopOnGadgetError = options.stopOnGadgetError ?? true;
|
|
4926
|
-
this.canRecoverFromGadgetError = options.canRecoverFromGadgetError;
|
|
4927
5573
|
this.defaultGadgetTimeoutMs = options.defaultGadgetTimeoutMs;
|
|
4928
5574
|
this.defaultMaxTokens = this.resolveMaxTokensFromCatalog(options.model);
|
|
4929
5575
|
this.outputLimitEnabled = options.gadgetOutputLimit ?? DEFAULT_GADGET_OUTPUT_LIMIT;
|
|
@@ -4977,6 +5623,9 @@ var init_agent = __esm({
|
|
|
4977
5623
|
temperature: this.temperature
|
|
4978
5624
|
};
|
|
4979
5625
|
this.subagentConfig = options.subagentConfig;
|
|
5626
|
+
this.tree = options.parentTree ?? new ExecutionTree();
|
|
5627
|
+
this.parentNodeId = options.parentNodeId ?? null;
|
|
5628
|
+
this.baseDepth = options.baseDepth ?? 0;
|
|
4980
5629
|
this.userSubagentEventCallback = options.onSubagentEvent;
|
|
4981
5630
|
this.onSubagentEvent = (event) => {
|
|
4982
5631
|
this.pendingSubagentEvents.push(event);
|
|
@@ -5041,7 +5690,9 @@ var init_agent = __esm({
|
|
|
5041
5690
|
*flushPendingSubagentEvents() {
|
|
5042
5691
|
while (this.pendingSubagentEvents.length > 0) {
|
|
5043
5692
|
const event = this.pendingSubagentEvents.shift();
|
|
5044
|
-
|
|
5693
|
+
if (event) {
|
|
5694
|
+
yield { type: "subagent_event", subagentEvent: event };
|
|
5695
|
+
}
|
|
5045
5696
|
}
|
|
5046
5697
|
}
|
|
5047
5698
|
/**
|
|
@@ -5095,6 +5746,48 @@ var init_agent = __esm({
|
|
|
5095
5746
|
getMediaStore() {
|
|
5096
5747
|
return this.mediaStore;
|
|
5097
5748
|
}
|
|
5749
|
+
/**
|
|
5750
|
+
* Get the execution tree for this agent.
|
|
5751
|
+
*
|
|
5752
|
+
* The execution tree provides a first-class model of all LLM calls and gadget executions,
|
|
5753
|
+
* including nested subagent activity. Use this to:
|
|
5754
|
+
* - Query execution state: `tree.getNode(id)`
|
|
5755
|
+
* - Get total cost: `tree.getTotalCost()`
|
|
5756
|
+
* - Get subtree cost/media/tokens: `tree.getSubtreeCost(nodeId)`
|
|
5757
|
+
* - Subscribe to events: `tree.on("llm_call_complete", handler)`
|
|
5758
|
+
* - Stream all events: `for await (const event of tree.events())`
|
|
5759
|
+
*
|
|
5760
|
+
* For subagents (created with `withParentContext`), the tree is shared with the parent,
|
|
5761
|
+
* enabling unified tracking and real-time visibility across all nesting levels.
|
|
5762
|
+
*
|
|
5763
|
+
* @returns The ExecutionTree instance
|
|
5764
|
+
*
|
|
5765
|
+
* @example
|
|
5766
|
+
* ```typescript
|
|
5767
|
+
* const agent = LLMist.createAgent()
|
|
5768
|
+
* .withModel("sonnet")
|
|
5769
|
+
* .withGadgets(BrowseWeb)
|
|
5770
|
+
* .ask("Research topic X");
|
|
5771
|
+
*
|
|
5772
|
+
* for await (const event of agent.run()) {
|
|
5773
|
+
* // Process events...
|
|
5774
|
+
* }
|
|
5775
|
+
*
|
|
5776
|
+
* // After execution, query the tree
|
|
5777
|
+
* const tree = agent.getTree();
|
|
5778
|
+
* console.log(`Total cost: $${tree.getTotalCost().toFixed(4)}`);
|
|
5779
|
+
*
|
|
5780
|
+
* // Inspect all LLM calls
|
|
5781
|
+
* for (const node of tree.getAllNodes()) {
|
|
5782
|
+
* if (node.type === "llm_call") {
|
|
5783
|
+
* console.log(`LLM #${node.iteration}: ${node.model}`);
|
|
5784
|
+
* }
|
|
5785
|
+
* }
|
|
5786
|
+
* ```
|
|
5787
|
+
*/
|
|
5788
|
+
getTree() {
|
|
5789
|
+
return this.tree;
|
|
5790
|
+
}
|
|
5098
5791
|
/**
|
|
5099
5792
|
* Manually trigger context compaction.
|
|
5100
5793
|
*
|
|
@@ -5196,6 +5889,7 @@ var init_agent = __esm({
|
|
|
5196
5889
|
await this.hooks.observers.onCompaction({
|
|
5197
5890
|
iteration: currentIteration,
|
|
5198
5891
|
event: compactionEvent,
|
|
5892
|
+
// biome-ignore lint/style/noNonNullAssertion: compactionManager exists if compactionEvent is truthy
|
|
5199
5893
|
stats: this.compactionManager.getStats(),
|
|
5200
5894
|
logger: this.logger
|
|
5201
5895
|
});
|
|
@@ -5257,6 +5951,13 @@ var init_agent = __esm({
|
|
|
5257
5951
|
messageCount: llmOptions.messages.length,
|
|
5258
5952
|
messages: llmOptions.messages
|
|
5259
5953
|
});
|
|
5954
|
+
const llmNode = this.tree.addLLMCall({
|
|
5955
|
+
iteration: currentIteration,
|
|
5956
|
+
model: llmOptions.model,
|
|
5957
|
+
parentId: this.parentNodeId,
|
|
5958
|
+
request: llmOptions.messages
|
|
5959
|
+
});
|
|
5960
|
+
const currentLLMNodeId = llmNode.id;
|
|
5260
5961
|
const stream2 = this.client.stream(llmOptions);
|
|
5261
5962
|
const processor = new StreamProcessor({
|
|
5262
5963
|
iteration: currentIteration,
|
|
@@ -5267,14 +5968,17 @@ var init_agent = __esm({
|
|
|
5267
5968
|
hooks: this.hooks,
|
|
5268
5969
|
logger: this.logger.getSubLogger({ name: "stream-processor" }),
|
|
5269
5970
|
requestHumanInput: this.requestHumanInput,
|
|
5270
|
-
stopOnGadgetError: this.stopOnGadgetError,
|
|
5271
|
-
canRecoverFromGadgetError: this.canRecoverFromGadgetError,
|
|
5272
5971
|
defaultGadgetTimeoutMs: this.defaultGadgetTimeoutMs,
|
|
5273
5972
|
client: this.client,
|
|
5274
5973
|
mediaStore: this.mediaStore,
|
|
5275
5974
|
agentConfig: this.agentContextConfig,
|
|
5276
5975
|
subagentConfig: this.subagentConfig,
|
|
5277
|
-
onSubagentEvent: this.onSubagentEvent
|
|
5976
|
+
onSubagentEvent: this.onSubagentEvent,
|
|
5977
|
+
// Tree context for execution tracking
|
|
5978
|
+
tree: this.tree,
|
|
5979
|
+
parentNodeId: currentLLMNodeId,
|
|
5980
|
+
// Gadgets are children of this LLM call
|
|
5981
|
+
baseDepth: this.baseDepth
|
|
5278
5982
|
});
|
|
5279
5983
|
let streamMetadata = null;
|
|
5280
5984
|
let gadgetCallCount = 0;
|
|
@@ -5320,6 +6024,11 @@ var init_agent = __esm({
|
|
|
5320
6024
|
await this.hooks.observers.onLLMCallComplete(context);
|
|
5321
6025
|
}
|
|
5322
6026
|
});
|
|
6027
|
+
this.tree.completeLLMCall(currentLLMNodeId, {
|
|
6028
|
+
response: result.rawResponse,
|
|
6029
|
+
usage: result.usage,
|
|
6030
|
+
finishReason: result.finishReason
|
|
6031
|
+
});
|
|
5323
6032
|
let finalMessage = result.finalMessage;
|
|
5324
6033
|
if (this.hooks.controllers?.afterLLMCall) {
|
|
5325
6034
|
const context = {
|
|
@@ -5354,10 +6063,12 @@ var init_agent = __esm({
|
|
|
5354
6063
|
const textContent = textOutputs.join("");
|
|
5355
6064
|
if (textContent.trim()) {
|
|
5356
6065
|
const { gadgetName, parameterMapping, resultMapping } = this.textWithGadgetsHandler;
|
|
6066
|
+
const syntheticId = `gc_text_${++this.syntheticInvocationCounter}`;
|
|
5357
6067
|
this.conversation.addGadgetCallResult(
|
|
5358
6068
|
gadgetName,
|
|
5359
6069
|
parameterMapping(textContent),
|
|
5360
|
-
resultMapping ? resultMapping(textContent) : textContent
|
|
6070
|
+
resultMapping ? resultMapping(textContent) : textContent,
|
|
6071
|
+
syntheticId
|
|
5361
6072
|
);
|
|
5362
6073
|
}
|
|
5363
6074
|
}
|
|
@@ -5368,6 +6079,7 @@ var init_agent = __esm({
|
|
|
5368
6079
|
gadgetResult.gadgetName,
|
|
5369
6080
|
gadgetResult.parameters,
|
|
5370
6081
|
gadgetResult.error ?? gadgetResult.result ?? "",
|
|
6082
|
+
gadgetResult.invocationId,
|
|
5371
6083
|
gadgetResult.media,
|
|
5372
6084
|
gadgetResult.mediaIds
|
|
5373
6085
|
);
|
|
@@ -5375,10 +6087,12 @@ var init_agent = __esm({
|
|
|
5375
6087
|
}
|
|
5376
6088
|
} else {
|
|
5377
6089
|
if (finalMessage.trim()) {
|
|
6090
|
+
const syntheticId = `gc_tell_${++this.syntheticInvocationCounter}`;
|
|
5378
6091
|
this.conversation.addGadgetCallResult(
|
|
5379
6092
|
"TellUser",
|
|
5380
6093
|
{ message: finalMessage, done: false, type: "info" },
|
|
5381
|
-
`\u2139\uFE0F ${finalMessage}
|
|
6094
|
+
`\u2139\uFE0F ${finalMessage}`,
|
|
6095
|
+
syntheticId
|
|
5382
6096
|
);
|
|
5383
6097
|
}
|
|
5384
6098
|
const shouldBreak = await this.handleTextOnlyResponse(finalMessage);
|
|
@@ -5589,8 +6303,6 @@ var init_builder = __esm({
|
|
|
5589
6303
|
gadgetArgPrefix;
|
|
5590
6304
|
textOnlyHandler;
|
|
5591
6305
|
textWithGadgetsHandler;
|
|
5592
|
-
stopOnGadgetError;
|
|
5593
|
-
canRecoverFromGadgetError;
|
|
5594
6306
|
defaultGadgetTimeoutMs;
|
|
5595
6307
|
gadgetOutputLimit;
|
|
5596
6308
|
gadgetOutputLimitPercent;
|
|
@@ -5599,6 +6311,8 @@ var init_builder = __esm({
|
|
|
5599
6311
|
trailingMessage;
|
|
5600
6312
|
subagentConfig;
|
|
5601
6313
|
subagentEventCallback;
|
|
6314
|
+
// Tree context for subagent support - enables shared tree model
|
|
6315
|
+
// When a gadget calls withParentContext(ctx), it shares the parent's tree
|
|
5602
6316
|
parentContext;
|
|
5603
6317
|
constructor(client) {
|
|
5604
6318
|
this.client = client;
|
|
@@ -5881,62 +6595,6 @@ var init_builder = __esm({
|
|
|
5881
6595
|
this.textWithGadgetsHandler = handler;
|
|
5882
6596
|
return this;
|
|
5883
6597
|
}
|
|
5884
|
-
/**
|
|
5885
|
-
* Set whether to stop gadget execution on first error.
|
|
5886
|
-
*
|
|
5887
|
-
* When true (default), if a gadget fails:
|
|
5888
|
-
* - Subsequent gadgets in the same response are skipped
|
|
5889
|
-
* - LLM stream is cancelled to save costs
|
|
5890
|
-
* - Agent loop continues with error in context
|
|
5891
|
-
*
|
|
5892
|
-
* When false:
|
|
5893
|
-
* - All gadgets in the response still execute
|
|
5894
|
-
* - LLM stream continues to completion
|
|
5895
|
-
*
|
|
5896
|
-
* @param stop - Whether to stop on gadget error
|
|
5897
|
-
* @returns This builder for chaining
|
|
5898
|
-
*
|
|
5899
|
-
* @example
|
|
5900
|
-
* ```typescript
|
|
5901
|
-
* .withStopOnGadgetError(false)
|
|
5902
|
-
* ```
|
|
5903
|
-
*/
|
|
5904
|
-
withStopOnGadgetError(stop) {
|
|
5905
|
-
this.stopOnGadgetError = stop;
|
|
5906
|
-
return this;
|
|
5907
|
-
}
|
|
5908
|
-
/**
|
|
5909
|
-
* Set custom error handling logic.
|
|
5910
|
-
*
|
|
5911
|
-
* Provides fine-grained control over whether to continue after different types of errors.
|
|
5912
|
-
* Overrides `stopOnGadgetError` when provided.
|
|
5913
|
-
*
|
|
5914
|
-
* **Note:** This builder method configures the underlying `canRecoverFromGadgetError` option
|
|
5915
|
-
* in `AgentOptions`. The method is named `withErrorHandler` for better developer experience,
|
|
5916
|
-
* but maps to the `canRecoverFromGadgetError` property internally.
|
|
5917
|
-
*
|
|
5918
|
-
* @param handler - Function that decides whether to continue after an error.
|
|
5919
|
-
* Return `true` to continue execution, `false` to stop.
|
|
5920
|
-
* @returns This builder for chaining
|
|
5921
|
-
*
|
|
5922
|
-
* @example
|
|
5923
|
-
* ```typescript
|
|
5924
|
-
* .withErrorHandler((context) => {
|
|
5925
|
-
* // Stop on parse errors, continue on validation/execution errors
|
|
5926
|
-
* if (context.errorType === "parse") {
|
|
5927
|
-
* return false;
|
|
5928
|
-
* }
|
|
5929
|
-
* if (context.error.includes("CRITICAL")) {
|
|
5930
|
-
* return false;
|
|
5931
|
-
* }
|
|
5932
|
-
* return true;
|
|
5933
|
-
* })
|
|
5934
|
-
* ```
|
|
5935
|
-
*/
|
|
5936
|
-
withErrorHandler(handler) {
|
|
5937
|
-
this.canRecoverFromGadgetError = handler;
|
|
5938
|
-
return this;
|
|
5939
|
-
}
|
|
5940
6598
|
/**
|
|
5941
6599
|
* Set default timeout for gadget execution.
|
|
5942
6600
|
*
|
|
@@ -6131,6 +6789,15 @@ var init_builder = __esm({
|
|
|
6131
6789
|
* The method extracts `invocationId` and `onSubagentEvent` from the execution
|
|
6132
6790
|
* context and sets up automatic forwarding via hooks and event wrapping.
|
|
6133
6791
|
*
|
|
6792
|
+
* **NEW: Shared Tree Model** - When the parent provides an ExecutionTree via context,
|
|
6793
|
+
* the subagent shares that tree instead of creating its own. This enables:
|
|
6794
|
+
* - Unified cost tracking across all nesting levels
|
|
6795
|
+
* - Automatic media aggregation via `tree.getSubtreeMedia(nodeId)`
|
|
6796
|
+
* - Real-time visibility of nested execution in the parent
|
|
6797
|
+
*
|
|
6798
|
+
* **Signal Forwarding** - When parent context includes a signal, it's automatically
|
|
6799
|
+
* forwarded to the subagent for proper cancellation propagation.
|
|
6800
|
+
*
|
|
6134
6801
|
* @param ctx - ExecutionContext passed to the gadget's execute() method
|
|
6135
6802
|
* @param depth - Nesting depth (default: 1 for direct child)
|
|
6136
6803
|
* @returns This builder for chaining
|
|
@@ -6151,17 +6818,25 @@ var init_builder = __esm({
|
|
|
6151
6818
|
* result = event.content;
|
|
6152
6819
|
* }
|
|
6153
6820
|
* }
|
|
6821
|
+
*
|
|
6822
|
+
* // After subagent completes, costs are automatically aggregated
|
|
6823
|
+
* // No manual tracking needed - use tree methods:
|
|
6824
|
+
* const totalCost = ctx.tree?.getSubtreeCost(ctx.nodeId!);
|
|
6825
|
+
* const allMedia = ctx.tree?.getSubtreeMedia(ctx.nodeId!);
|
|
6154
6826
|
* }
|
|
6155
6827
|
* ```
|
|
6156
6828
|
*/
|
|
6157
6829
|
withParentContext(ctx, depth = 1) {
|
|
6158
|
-
if (ctx.
|
|
6830
|
+
if (ctx.tree) {
|
|
6159
6831
|
this.parentContext = {
|
|
6160
|
-
|
|
6161
|
-
|
|
6832
|
+
tree: ctx.tree,
|
|
6833
|
+
nodeId: ctx.nodeId,
|
|
6162
6834
|
depth
|
|
6163
6835
|
};
|
|
6164
6836
|
}
|
|
6837
|
+
if (ctx.signal && !this.signal) {
|
|
6838
|
+
this.signal = ctx.signal;
|
|
6839
|
+
}
|
|
6165
6840
|
return this;
|
|
6166
6841
|
}
|
|
6167
6842
|
/**
|
|
@@ -6194,11 +6869,13 @@ var init_builder = __esm({
|
|
|
6194
6869
|
*
|
|
6195
6870
|
* This is useful for in-context learning - showing the LLM what "past self"
|
|
6196
6871
|
* did correctly so it mimics the pattern. The call is formatted with proper
|
|
6197
|
-
* markers and parameter format
|
|
6872
|
+
* markers and parameter format, including the invocation ID so the LLM can
|
|
6873
|
+
* reference previous calls when building dependencies.
|
|
6198
6874
|
*
|
|
6199
6875
|
* @param gadgetName - Name of the gadget
|
|
6200
6876
|
* @param parameters - Parameters passed to the gadget
|
|
6201
6877
|
* @param result - Result returned by the gadget
|
|
6878
|
+
* @param invocationId - Invocation ID (shown to LLM so it can reference for dependencies)
|
|
6202
6879
|
* @returns This builder for chaining
|
|
6203
6880
|
*
|
|
6204
6881
|
* @example
|
|
@@ -6210,124 +6887,36 @@ var init_builder = __esm({
|
|
|
6210
6887
|
* done: false,
|
|
6211
6888
|
* type: 'info'
|
|
6212
6889
|
* },
|
|
6213
|
-
* 'ℹ️ 👋 Hello!\n\nHere\'s what I can do:\n- Analyze code\n- Run commands'
|
|
6890
|
+
* 'ℹ️ 👋 Hello!\n\nHere\'s what I can do:\n- Analyze code\n- Run commands',
|
|
6891
|
+
* 'gc_1'
|
|
6214
6892
|
* )
|
|
6215
6893
|
* ```
|
|
6216
6894
|
*/
|
|
6217
|
-
withSyntheticGadgetCall(gadgetName, parameters, result) {
|
|
6895
|
+
withSyntheticGadgetCall(gadgetName, parameters, result, invocationId) {
|
|
6218
6896
|
const startPrefix = this.gadgetStartPrefix ?? GADGET_START_PREFIX;
|
|
6219
6897
|
const endPrefix = this.gadgetEndPrefix ?? GADGET_END_PREFIX;
|
|
6220
6898
|
const paramStr = this.formatBlockParameters(parameters, "");
|
|
6221
6899
|
this.initialMessages.push({
|
|
6222
6900
|
role: "assistant",
|
|
6223
|
-
content: `${startPrefix}${gadgetName}
|
|
6901
|
+
content: `${startPrefix}${gadgetName}:${invocationId}
|
|
6224
6902
|
${paramStr}
|
|
6225
6903
|
${endPrefix}`
|
|
6226
6904
|
});
|
|
6227
6905
|
this.initialMessages.push({
|
|
6228
6906
|
role: "user",
|
|
6229
|
-
content: `Result: ${result}`
|
|
6907
|
+
content: `Result (${invocationId}): ${result}`
|
|
6230
6908
|
});
|
|
6231
6909
|
return this;
|
|
6232
6910
|
}
|
|
6233
6911
|
/**
|
|
6234
|
-
* Compose the final hooks, including
|
|
6235
|
-
*
|
|
6236
|
-
*
|
|
6912
|
+
* Compose the final hooks, including trailing message injection if configured.
|
|
6913
|
+
*
|
|
6914
|
+
* Note: Subagent event visibility is now handled entirely by the ExecutionTree.
|
|
6915
|
+
* When a subagent uses withParentContext(ctx), it shares the parent's tree,
|
|
6916
|
+
* and all events are automatically visible to tree subscribers (like the TUI).
|
|
6237
6917
|
*/
|
|
6238
6918
|
composeHooks() {
|
|
6239
|
-
|
|
6240
|
-
if (this.parentContext) {
|
|
6241
|
-
const { invocationId, onSubagentEvent, depth } = this.parentContext;
|
|
6242
|
-
const existingOnLLMCallStart = hooks?.observers?.onLLMCallStart;
|
|
6243
|
-
const existingOnLLMCallComplete = hooks?.observers?.onLLMCallComplete;
|
|
6244
|
-
const existingOnGadgetExecutionStart = hooks?.observers?.onGadgetExecutionStart;
|
|
6245
|
-
const existingOnGadgetExecutionComplete = hooks?.observers?.onGadgetExecutionComplete;
|
|
6246
|
-
hooks = {
|
|
6247
|
-
...hooks,
|
|
6248
|
-
observers: {
|
|
6249
|
-
...hooks?.observers,
|
|
6250
|
-
onLLMCallStart: async (context) => {
|
|
6251
|
-
let inputTokens;
|
|
6252
|
-
try {
|
|
6253
|
-
if (this.client) {
|
|
6254
|
-
inputTokens = await this.client.countTokens(
|
|
6255
|
-
context.options.model,
|
|
6256
|
-
context.options.messages
|
|
6257
|
-
);
|
|
6258
|
-
}
|
|
6259
|
-
} catch {
|
|
6260
|
-
}
|
|
6261
|
-
onSubagentEvent({
|
|
6262
|
-
type: "llm_call_start",
|
|
6263
|
-
gadgetInvocationId: invocationId,
|
|
6264
|
-
depth,
|
|
6265
|
-
event: {
|
|
6266
|
-
iteration: context.iteration,
|
|
6267
|
-
model: context.options.model,
|
|
6268
|
-
inputTokens
|
|
6269
|
-
}
|
|
6270
|
-
});
|
|
6271
|
-
if (existingOnLLMCallStart) {
|
|
6272
|
-
await existingOnLLMCallStart(context);
|
|
6273
|
-
}
|
|
6274
|
-
},
|
|
6275
|
-
onLLMCallComplete: async (context) => {
|
|
6276
|
-
onSubagentEvent({
|
|
6277
|
-
type: "llm_call_end",
|
|
6278
|
-
gadgetInvocationId: invocationId,
|
|
6279
|
-
depth,
|
|
6280
|
-
event: {
|
|
6281
|
-
iteration: context.iteration,
|
|
6282
|
-
model: context.options.model,
|
|
6283
|
-
// Backward compat fields
|
|
6284
|
-
inputTokens: context.usage?.inputTokens,
|
|
6285
|
-
outputTokens: context.usage?.outputTokens,
|
|
6286
|
-
finishReason: context.finishReason ?? void 0,
|
|
6287
|
-
// Full usage object with cache details (for first-class display)
|
|
6288
|
-
usage: context.usage
|
|
6289
|
-
// Cost will be calculated by parent if it has model registry
|
|
6290
|
-
}
|
|
6291
|
-
});
|
|
6292
|
-
if (existingOnLLMCallComplete) {
|
|
6293
|
-
await existingOnLLMCallComplete(context);
|
|
6294
|
-
}
|
|
6295
|
-
},
|
|
6296
|
-
onGadgetExecutionStart: async (context) => {
|
|
6297
|
-
onSubagentEvent({
|
|
6298
|
-
type: "gadget_call",
|
|
6299
|
-
gadgetInvocationId: invocationId,
|
|
6300
|
-
depth,
|
|
6301
|
-
event: {
|
|
6302
|
-
call: {
|
|
6303
|
-
invocationId: context.invocationId,
|
|
6304
|
-
gadgetName: context.gadgetName,
|
|
6305
|
-
parameters: context.parameters
|
|
6306
|
-
}
|
|
6307
|
-
}
|
|
6308
|
-
});
|
|
6309
|
-
if (existingOnGadgetExecutionStart) {
|
|
6310
|
-
await existingOnGadgetExecutionStart(context);
|
|
6311
|
-
}
|
|
6312
|
-
},
|
|
6313
|
-
onGadgetExecutionComplete: async (context) => {
|
|
6314
|
-
onSubagentEvent({
|
|
6315
|
-
type: "gadget_result",
|
|
6316
|
-
gadgetInvocationId: invocationId,
|
|
6317
|
-
depth,
|
|
6318
|
-
event: {
|
|
6319
|
-
result: {
|
|
6320
|
-
invocationId: context.invocationId
|
|
6321
|
-
}
|
|
6322
|
-
}
|
|
6323
|
-
});
|
|
6324
|
-
if (existingOnGadgetExecutionComplete) {
|
|
6325
|
-
await existingOnGadgetExecutionComplete(context);
|
|
6326
|
-
}
|
|
6327
|
-
}
|
|
6328
|
-
}
|
|
6329
|
-
};
|
|
6330
|
-
}
|
|
6919
|
+
const hooks = this.hooks;
|
|
6331
6920
|
if (!this.trailingMessage) {
|
|
6332
6921
|
return hooks;
|
|
6333
6922
|
}
|
|
@@ -6410,19 +6999,6 @@ ${endPrefix}`
|
|
|
6410
6999
|
this.client = new LLMistClass();
|
|
6411
7000
|
}
|
|
6412
7001
|
const registry = GadgetRegistry.from(this.gadgets);
|
|
6413
|
-
let onSubagentEvent = this.subagentEventCallback;
|
|
6414
|
-
if (this.parentContext) {
|
|
6415
|
-
const { invocationId, onSubagentEvent: parentCallback, depth } = this.parentContext;
|
|
6416
|
-
const existingCallback = this.subagentEventCallback;
|
|
6417
|
-
onSubagentEvent = (event) => {
|
|
6418
|
-
parentCallback({
|
|
6419
|
-
...event,
|
|
6420
|
-
gadgetInvocationId: invocationId,
|
|
6421
|
-
depth: event.depth + depth
|
|
6422
|
-
});
|
|
6423
|
-
existingCallback?.(event);
|
|
6424
|
-
};
|
|
6425
|
-
}
|
|
6426
7002
|
return {
|
|
6427
7003
|
client: this.client,
|
|
6428
7004
|
model: this.model ?? "openai:gpt-5-nano",
|
|
@@ -6441,15 +7017,17 @@ ${endPrefix}`
|
|
|
6441
7017
|
gadgetArgPrefix: this.gadgetArgPrefix,
|
|
6442
7018
|
textOnlyHandler: this.textOnlyHandler,
|
|
6443
7019
|
textWithGadgetsHandler: this.textWithGadgetsHandler,
|
|
6444
|
-
stopOnGadgetError: this.stopOnGadgetError,
|
|
6445
|
-
canRecoverFromGadgetError: this.canRecoverFromGadgetError,
|
|
6446
7020
|
defaultGadgetTimeoutMs: this.defaultGadgetTimeoutMs,
|
|
6447
7021
|
gadgetOutputLimit: this.gadgetOutputLimit,
|
|
6448
7022
|
gadgetOutputLimitPercent: this.gadgetOutputLimitPercent,
|
|
6449
7023
|
compactionConfig: this.compactionConfig,
|
|
6450
7024
|
signal: this.signal,
|
|
6451
7025
|
subagentConfig: this.subagentConfig,
|
|
6452
|
-
onSubagentEvent
|
|
7026
|
+
onSubagentEvent: this.subagentEventCallback,
|
|
7027
|
+
// Tree context for shared tree model (subagents share parent's tree)
|
|
7028
|
+
parentTree: this.parentContext?.tree,
|
|
7029
|
+
parentNodeId: this.parentContext?.nodeId,
|
|
7030
|
+
baseDepth: this.parentContext ? (this.parentContext.depth ?? 0) + 1 : 0
|
|
6453
7031
|
};
|
|
6454
7032
|
}
|
|
6455
7033
|
ask(userPrompt) {
|
|
@@ -6606,19 +7184,6 @@ ${endPrefix}`
|
|
|
6606
7184
|
this.client = new LLMistClass();
|
|
6607
7185
|
}
|
|
6608
7186
|
const registry = GadgetRegistry.from(this.gadgets);
|
|
6609
|
-
let onSubagentEvent = this.subagentEventCallback;
|
|
6610
|
-
if (this.parentContext) {
|
|
6611
|
-
const { invocationId, onSubagentEvent: parentCallback, depth } = this.parentContext;
|
|
6612
|
-
const existingCallback = this.subagentEventCallback;
|
|
6613
|
-
onSubagentEvent = (event) => {
|
|
6614
|
-
parentCallback({
|
|
6615
|
-
...event,
|
|
6616
|
-
gadgetInvocationId: invocationId,
|
|
6617
|
-
depth: event.depth + depth
|
|
6618
|
-
});
|
|
6619
|
-
existingCallback?.(event);
|
|
6620
|
-
};
|
|
6621
|
-
}
|
|
6622
7187
|
const options = {
|
|
6623
7188
|
client: this.client,
|
|
6624
7189
|
model: this.model ?? "openai:gpt-5-nano",
|
|
@@ -6637,15 +7202,17 @@ ${endPrefix}`
|
|
|
6637
7202
|
gadgetArgPrefix: this.gadgetArgPrefix,
|
|
6638
7203
|
textOnlyHandler: this.textOnlyHandler,
|
|
6639
7204
|
textWithGadgetsHandler: this.textWithGadgetsHandler,
|
|
6640
|
-
stopOnGadgetError: this.stopOnGadgetError,
|
|
6641
|
-
canRecoverFromGadgetError: this.canRecoverFromGadgetError,
|
|
6642
7205
|
defaultGadgetTimeoutMs: this.defaultGadgetTimeoutMs,
|
|
6643
7206
|
gadgetOutputLimit: this.gadgetOutputLimit,
|
|
6644
7207
|
gadgetOutputLimitPercent: this.gadgetOutputLimitPercent,
|
|
6645
7208
|
compactionConfig: this.compactionConfig,
|
|
6646
7209
|
signal: this.signal,
|
|
6647
7210
|
subagentConfig: this.subagentConfig,
|
|
6648
|
-
onSubagentEvent
|
|
7211
|
+
onSubagentEvent: this.subagentEventCallback,
|
|
7212
|
+
// Tree context for shared tree model (subagents share parent's tree)
|
|
7213
|
+
parentTree: this.parentContext?.tree,
|
|
7214
|
+
parentNodeId: this.parentContext?.nodeId,
|
|
7215
|
+
baseDepth: this.parentContext ? (this.parentContext.depth ?? 0) + 1 : 0
|
|
6649
7216
|
};
|
|
6650
7217
|
return new Agent(AGENT_INTERNAL_KEY, options);
|
|
6651
7218
|
}
|
|
@@ -11278,16 +11845,16 @@ var MockConversationManager = class {
|
|
|
11278
11845
|
this.history.push(msg);
|
|
11279
11846
|
this.addedMessages.push(msg);
|
|
11280
11847
|
}
|
|
11281
|
-
addGadgetCallResult(gadgetName, parameters, result) {
|
|
11848
|
+
addGadgetCallResult(gadgetName, parameters, result, invocationId) {
|
|
11282
11849
|
const assistantMsg = {
|
|
11283
11850
|
role: "assistant",
|
|
11284
|
-
content: `!!!GADGET_START:${gadgetName}
|
|
11851
|
+
content: `!!!GADGET_START:${gadgetName}:${invocationId}
|
|
11285
11852
|
${JSON.stringify(parameters)}
|
|
11286
11853
|
!!!GADGET_END`
|
|
11287
11854
|
};
|
|
11288
11855
|
const resultMsg = {
|
|
11289
11856
|
role: "user",
|
|
11290
|
-
content: `Result: ${result}`
|
|
11857
|
+
content: `Result (${invocationId}): ${result}`
|
|
11291
11858
|
};
|
|
11292
11859
|
this.history.push(assistantMsg);
|
|
11293
11860
|
this.history.push(resultMsg);
|