llmist 2.6.0 → 3.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/README.md +10 -1
- package/dist/{chunk-364PEMVT.js → chunk-JCFPJMRQ.js} +754 -218
- package/dist/chunk-JCFPJMRQ.js.map +1 -0
- package/dist/{chunk-4IHLIYW5.js → chunk-LFI4WQVV.js} +6 -6
- package/dist/chunk-LFI4WQVV.js.map +1 -0
- package/dist/cli.cjs +1458 -279
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +702 -58
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +763 -226
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +139 -54
- package/dist/index.d.ts +139 -54
- package/dist/index.js +20 -18
- package/dist/{mock-stream-Jgg5u6Uf.d.cts → mock-stream-CTLm00_q.d.cts} +344 -56
- package/dist/{mock-stream-Jgg5u6Uf.d.ts → mock-stream-CTLm00_q.d.ts} +344 -56
- package/dist/testing/index.cjs +745 -210
- package/dist/testing/index.cjs.map +1 -1
- package/dist/testing/index.d.cts +6 -6
- package/dist/testing/index.d.ts +6 -6
- package/dist/testing/index.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-364PEMVT.js.map +0 -1
- package/dist/chunk-4IHLIYW5.js.map +0 -1
package/dist/testing/index.cjs
CHANGED
|
@@ -301,13 +301,13 @@ var init_prompt_config = __esm({
|
|
|
301
301
|
});
|
|
302
302
|
|
|
303
303
|
// src/core/messages.ts
|
|
304
|
-
function
|
|
304
|
+
function normalizeMessageContent(content) {
|
|
305
305
|
if (typeof content === "string") {
|
|
306
306
|
return [{ type: "text", text: content }];
|
|
307
307
|
}
|
|
308
308
|
return content;
|
|
309
309
|
}
|
|
310
|
-
function
|
|
310
|
+
function extractMessageText(content) {
|
|
311
311
|
if (typeof content === "string") {
|
|
312
312
|
return content;
|
|
313
313
|
}
|
|
@@ -667,7 +667,17 @@ Produces: { "items": ["first", "second"] }`);
|
|
|
667
667
|
this.messages.push({ role: "user", content: parts });
|
|
668
668
|
return this;
|
|
669
669
|
}
|
|
670
|
-
|
|
670
|
+
/**
|
|
671
|
+
* Record a gadget execution result in the message history.
|
|
672
|
+
* Creates an assistant message with the gadget invocation and a user message with the result.
|
|
673
|
+
*
|
|
674
|
+
* @param gadget - Name of the gadget that was executed
|
|
675
|
+
* @param parameters - Parameters that were passed to the gadget
|
|
676
|
+
* @param result - Text result from the gadget execution
|
|
677
|
+
* @param media - Optional media outputs from the gadget
|
|
678
|
+
* @param mediaIds - Optional IDs for the media outputs
|
|
679
|
+
*/
|
|
680
|
+
addGadgetCallResult(gadget, parameters, result, media, mediaIds) {
|
|
671
681
|
const paramStr = this.formatBlockParameters(parameters, "");
|
|
672
682
|
this.messages.push({
|
|
673
683
|
role: "assistant",
|
|
@@ -1232,21 +1242,21 @@ var init_media_store = __esm({
|
|
|
1232
1242
|
});
|
|
1233
1243
|
|
|
1234
1244
|
// src/gadgets/exceptions.ts
|
|
1235
|
-
var
|
|
1245
|
+
var TaskCompletionSignal, HumanInputRequiredException, TimeoutException, AbortException;
|
|
1236
1246
|
var init_exceptions = __esm({
|
|
1237
1247
|
"src/gadgets/exceptions.ts"() {
|
|
1238
1248
|
"use strict";
|
|
1239
|
-
|
|
1249
|
+
TaskCompletionSignal = class extends Error {
|
|
1240
1250
|
constructor(message) {
|
|
1241
1251
|
super(message ?? "Agent loop terminated by gadget");
|
|
1242
|
-
this.name = "
|
|
1252
|
+
this.name = "TaskCompletionSignal";
|
|
1243
1253
|
}
|
|
1244
1254
|
};
|
|
1245
|
-
|
|
1255
|
+
HumanInputRequiredException = class extends Error {
|
|
1246
1256
|
question;
|
|
1247
1257
|
constructor(question) {
|
|
1248
1258
|
super(`Human input required: ${question}`);
|
|
1249
|
-
this.name = "
|
|
1259
|
+
this.name = "HumanInputRequiredException";
|
|
1250
1260
|
this.question = question;
|
|
1251
1261
|
}
|
|
1252
1262
|
};
|
|
@@ -1260,10 +1270,10 @@ var init_exceptions = __esm({
|
|
|
1260
1270
|
this.timeoutMs = timeoutMs;
|
|
1261
1271
|
}
|
|
1262
1272
|
};
|
|
1263
|
-
|
|
1273
|
+
AbortException = class extends Error {
|
|
1264
1274
|
constructor(message) {
|
|
1265
1275
|
super(message || "Gadget execution was aborted");
|
|
1266
|
-
this.name = "
|
|
1276
|
+
this.name = "AbortException";
|
|
1267
1277
|
}
|
|
1268
1278
|
};
|
|
1269
1279
|
}
|
|
@@ -1353,7 +1363,7 @@ var init_schema_to_json = __esm({
|
|
|
1353
1363
|
});
|
|
1354
1364
|
|
|
1355
1365
|
// src/gadgets/gadget.ts
|
|
1356
|
-
function
|
|
1366
|
+
function formatParamsForBlockExample(params, prefix = "", argPrefix = GADGET_ARG_PREFIX) {
|
|
1357
1367
|
const lines = [];
|
|
1358
1368
|
for (const [key, value] of Object.entries(params)) {
|
|
1359
1369
|
const fullPath = prefix ? `${prefix}/${key}` : key;
|
|
@@ -1361,14 +1371,14 @@ function formatParamsAsBlock(params, prefix = "", argPrefix = GADGET_ARG_PREFIX)
|
|
|
1361
1371
|
value.forEach((item, index) => {
|
|
1362
1372
|
const itemPath = `${fullPath}/${index}`;
|
|
1363
1373
|
if (typeof item === "object" && item !== null) {
|
|
1364
|
-
lines.push(
|
|
1374
|
+
lines.push(formatParamsForBlockExample(item, itemPath, argPrefix));
|
|
1365
1375
|
} else {
|
|
1366
1376
|
lines.push(`${argPrefix}${itemPath}`);
|
|
1367
1377
|
lines.push(String(item));
|
|
1368
1378
|
}
|
|
1369
1379
|
});
|
|
1370
1380
|
} else if (typeof value === "object" && value !== null) {
|
|
1371
|
-
lines.push(
|
|
1381
|
+
lines.push(formatParamsForBlockExample(value, fullPath, argPrefix));
|
|
1372
1382
|
} else {
|
|
1373
1383
|
lines.push(`${argPrefix}${fullPath}`);
|
|
1374
1384
|
lines.push(String(value));
|
|
@@ -1457,7 +1467,7 @@ function formatSchemaAsPlainText(schema, indent = "", atRoot = true) {
|
|
|
1457
1467
|
}
|
|
1458
1468
|
return lines.join("\n");
|
|
1459
1469
|
}
|
|
1460
|
-
var
|
|
1470
|
+
var AbstractGadget;
|
|
1461
1471
|
var init_gadget = __esm({
|
|
1462
1472
|
"src/gadgets/gadget.ts"() {
|
|
1463
1473
|
"use strict";
|
|
@@ -1465,7 +1475,7 @@ var init_gadget = __esm({
|
|
|
1465
1475
|
init_exceptions();
|
|
1466
1476
|
init_schema_to_json();
|
|
1467
1477
|
init_schema_validator();
|
|
1468
|
-
|
|
1478
|
+
AbstractGadget = class {
|
|
1469
1479
|
/**
|
|
1470
1480
|
* The name of the gadget. Used for identification when LLM calls it.
|
|
1471
1481
|
* If not provided, defaults to the class name.
|
|
@@ -1493,14 +1503,14 @@ var init_gadget = __esm({
|
|
|
1493
1503
|
*/
|
|
1494
1504
|
examples;
|
|
1495
1505
|
/**
|
|
1496
|
-
* Throws an
|
|
1506
|
+
* Throws an AbortException if the execution has been aborted.
|
|
1497
1507
|
*
|
|
1498
1508
|
* Call this at key checkpoints in long-running gadgets to allow early exit
|
|
1499
1509
|
* when the gadget has been cancelled (e.g., due to timeout). This enables
|
|
1500
1510
|
* resource cleanup and prevents unnecessary work after cancellation.
|
|
1501
1511
|
*
|
|
1502
1512
|
* @param ctx - The execution context containing the abort signal
|
|
1503
|
-
* @throws
|
|
1513
|
+
* @throws AbortException if ctx.signal.aborted is true
|
|
1504
1514
|
*
|
|
1505
1515
|
* @example
|
|
1506
1516
|
* ```typescript
|
|
@@ -1525,7 +1535,7 @@ var init_gadget = __esm({
|
|
|
1525
1535
|
*/
|
|
1526
1536
|
throwIfAborted(ctx) {
|
|
1527
1537
|
if (ctx?.signal?.aborted) {
|
|
1528
|
-
throw new
|
|
1538
|
+
throw new AbortException();
|
|
1529
1539
|
}
|
|
1530
1540
|
}
|
|
1531
1541
|
/**
|
|
@@ -1667,7 +1677,7 @@ var init_gadget = __esm({
|
|
|
1667
1677
|
}
|
|
1668
1678
|
parts.push(`${effectiveStartPrefix}${gadgetName}`);
|
|
1669
1679
|
parts.push(
|
|
1670
|
-
|
|
1680
|
+
formatParamsForBlockExample(example.params, "", effectiveArgPrefix)
|
|
1671
1681
|
);
|
|
1672
1682
|
parts.push(effectiveEndPrefix);
|
|
1673
1683
|
if (example.output !== void 0) {
|
|
@@ -1685,7 +1695,7 @@ var init_gadget = __esm({
|
|
|
1685
1695
|
|
|
1686
1696
|
// src/gadgets/create-gadget.ts
|
|
1687
1697
|
function createGadget(config) {
|
|
1688
|
-
class DynamicGadget extends
|
|
1698
|
+
class DynamicGadget extends AbstractGadget {
|
|
1689
1699
|
name = config.name;
|
|
1690
1700
|
description = config.description;
|
|
1691
1701
|
parameterSchema = config.schema;
|
|
@@ -2323,8 +2333,8 @@ var init_conversation_manager = __esm({
|
|
|
2323
2333
|
addAssistantMessage(content) {
|
|
2324
2334
|
this.historyBuilder.addAssistant(content);
|
|
2325
2335
|
}
|
|
2326
|
-
|
|
2327
|
-
this.historyBuilder.
|
|
2336
|
+
addGadgetCallResult(gadgetName, parameters, result, media, mediaIds) {
|
|
2337
|
+
this.historyBuilder.addGadgetCallResult(gadgetName, parameters, result, media, mediaIds);
|
|
2328
2338
|
}
|
|
2329
2339
|
getMessages() {
|
|
2330
2340
|
return [...this.baseMessages, ...this.initialMessages, ...this.historyBuilder.build()];
|
|
@@ -2344,7 +2354,7 @@ var init_conversation_manager = __esm({
|
|
|
2344
2354
|
if (msg.role === "user") {
|
|
2345
2355
|
this.historyBuilder.addUser(msg.content);
|
|
2346
2356
|
} else if (msg.role === "assistant") {
|
|
2347
|
-
this.historyBuilder.addAssistant(
|
|
2357
|
+
this.historyBuilder.addAssistant(extractMessageText(msg.content));
|
|
2348
2358
|
}
|
|
2349
2359
|
}
|
|
2350
2360
|
}
|
|
@@ -3194,12 +3204,12 @@ var init_cost_reporting_client = __esm({
|
|
|
3194
3204
|
});
|
|
3195
3205
|
|
|
3196
3206
|
// src/gadgets/error-formatter.ts
|
|
3197
|
-
var
|
|
3207
|
+
var GadgetExecutionErrorFormatter;
|
|
3198
3208
|
var init_error_formatter = __esm({
|
|
3199
3209
|
"src/gadgets/error-formatter.ts"() {
|
|
3200
3210
|
"use strict";
|
|
3201
3211
|
init_constants();
|
|
3202
|
-
|
|
3212
|
+
GadgetExecutionErrorFormatter = class {
|
|
3203
3213
|
argPrefix;
|
|
3204
3214
|
startPrefix;
|
|
3205
3215
|
endPrefix;
|
|
@@ -3285,16 +3295,16 @@ function stripMarkdownFences(content) {
|
|
|
3285
3295
|
cleaned = cleaned.replace(closingFence, "");
|
|
3286
3296
|
return cleaned.trim();
|
|
3287
3297
|
}
|
|
3288
|
-
var globalInvocationCounter,
|
|
3298
|
+
var globalInvocationCounter, GadgetCallParser;
|
|
3289
3299
|
var init_parser = __esm({
|
|
3290
3300
|
"src/gadgets/parser.ts"() {
|
|
3291
3301
|
"use strict";
|
|
3292
3302
|
init_constants();
|
|
3293
3303
|
init_block_params();
|
|
3294
3304
|
globalInvocationCounter = 0;
|
|
3295
|
-
|
|
3305
|
+
GadgetCallParser = class {
|
|
3296
3306
|
buffer = "";
|
|
3297
|
-
|
|
3307
|
+
lastEmittedTextOffset = 0;
|
|
3298
3308
|
startPrefix;
|
|
3299
3309
|
endPrefix;
|
|
3300
3310
|
argPrefix;
|
|
@@ -3303,16 +3313,20 @@ var init_parser = __esm({
|
|
|
3303
3313
|
this.endPrefix = options.endPrefix ?? GADGET_END_PREFIX;
|
|
3304
3314
|
this.argPrefix = options.argPrefix ?? GADGET_ARG_PREFIX;
|
|
3305
3315
|
}
|
|
3306
|
-
|
|
3307
|
-
|
|
3316
|
+
/**
|
|
3317
|
+
* Extract and consume text up to the given index.
|
|
3318
|
+
* Returns undefined if no meaningful text to emit.
|
|
3319
|
+
*/
|
|
3320
|
+
extractTextSegment(index) {
|
|
3321
|
+
if (index <= this.lastEmittedTextOffset) {
|
|
3308
3322
|
return void 0;
|
|
3309
3323
|
}
|
|
3310
|
-
const segment = this.buffer.slice(this.
|
|
3311
|
-
this.
|
|
3324
|
+
const segment = this.buffer.slice(this.lastEmittedTextOffset, index);
|
|
3325
|
+
this.lastEmittedTextOffset = index;
|
|
3312
3326
|
return segment.trim().length > 0 ? segment : void 0;
|
|
3313
3327
|
}
|
|
3314
3328
|
/**
|
|
3315
|
-
* Parse gadget
|
|
3329
|
+
* Parse gadget invocation metadata from the header line.
|
|
3316
3330
|
*
|
|
3317
3331
|
* Supported formats:
|
|
3318
3332
|
* - `GadgetName` - Auto-generate ID, no dependencies
|
|
@@ -3321,24 +3335,24 @@ var init_parser = __esm({
|
|
|
3321
3335
|
*
|
|
3322
3336
|
* Dependencies must be comma-separated invocation IDs.
|
|
3323
3337
|
*/
|
|
3324
|
-
|
|
3325
|
-
const parts =
|
|
3338
|
+
parseInvocationMetadata(headerLine) {
|
|
3339
|
+
const parts = headerLine.split(":");
|
|
3326
3340
|
if (parts.length === 1) {
|
|
3327
3341
|
return {
|
|
3328
|
-
|
|
3342
|
+
gadgetName: parts[0],
|
|
3329
3343
|
invocationId: `gadget_${++globalInvocationCounter}`,
|
|
3330
3344
|
dependencies: []
|
|
3331
3345
|
};
|
|
3332
3346
|
} else if (parts.length === 2) {
|
|
3333
3347
|
return {
|
|
3334
|
-
|
|
3348
|
+
gadgetName: parts[0],
|
|
3335
3349
|
invocationId: parts[1].trim(),
|
|
3336
3350
|
dependencies: []
|
|
3337
3351
|
};
|
|
3338
3352
|
} else {
|
|
3339
3353
|
const deps = parts[2].split(",").map((d) => d.trim()).filter((d) => d.length > 0);
|
|
3340
3354
|
return {
|
|
3341
|
-
|
|
3355
|
+
gadgetName: parts[0],
|
|
3342
3356
|
invocationId: parts[1].trim(),
|
|
3343
3357
|
dependencies: deps
|
|
3344
3358
|
};
|
|
@@ -3370,19 +3384,15 @@ var init_parser = __esm({
|
|
|
3370
3384
|
while (true) {
|
|
3371
3385
|
const partStartIndex = this.buffer.indexOf(this.startPrefix, startIndex);
|
|
3372
3386
|
if (partStartIndex === -1) break;
|
|
3373
|
-
const textBefore = this.
|
|
3387
|
+
const textBefore = this.extractTextSegment(partStartIndex);
|
|
3374
3388
|
if (textBefore !== void 0) {
|
|
3375
3389
|
yield { type: "text", content: textBefore };
|
|
3376
3390
|
}
|
|
3377
3391
|
const metadataStartIndex = partStartIndex + this.startPrefix.length;
|
|
3378
3392
|
const metadataEndIndex = this.buffer.indexOf("\n", metadataStartIndex);
|
|
3379
3393
|
if (metadataEndIndex === -1) break;
|
|
3380
|
-
const
|
|
3381
|
-
const {
|
|
3382
|
-
actualName: actualGadgetName,
|
|
3383
|
-
invocationId,
|
|
3384
|
-
dependencies
|
|
3385
|
-
} = this.parseGadgetName(gadgetName);
|
|
3394
|
+
const headerLine = this.buffer.substring(metadataStartIndex, metadataEndIndex).trim();
|
|
3395
|
+
const { gadgetName, invocationId, dependencies } = this.parseInvocationMetadata(headerLine);
|
|
3386
3396
|
const contentStartIndex = metadataEndIndex + 1;
|
|
3387
3397
|
let partEndIndex;
|
|
3388
3398
|
let endMarkerLength = 0;
|
|
@@ -3402,7 +3412,7 @@ var init_parser = __esm({
|
|
|
3402
3412
|
yield {
|
|
3403
3413
|
type: "gadget_call",
|
|
3404
3414
|
call: {
|
|
3405
|
-
gadgetName
|
|
3415
|
+
gadgetName,
|
|
3406
3416
|
invocationId,
|
|
3407
3417
|
parametersRaw,
|
|
3408
3418
|
parameters,
|
|
@@ -3411,37 +3421,33 @@ var init_parser = __esm({
|
|
|
3411
3421
|
}
|
|
3412
3422
|
};
|
|
3413
3423
|
startIndex = partEndIndex + endMarkerLength;
|
|
3414
|
-
this.
|
|
3424
|
+
this.lastEmittedTextOffset = startIndex;
|
|
3415
3425
|
}
|
|
3416
3426
|
if (startIndex > 0) {
|
|
3417
3427
|
this.buffer = this.buffer.substring(startIndex);
|
|
3418
|
-
this.
|
|
3428
|
+
this.lastEmittedTextOffset = 0;
|
|
3419
3429
|
}
|
|
3420
3430
|
}
|
|
3421
3431
|
// Finalize parsing and return remaining text or incomplete gadgets
|
|
3422
3432
|
*finalize() {
|
|
3423
|
-
const startIndex = this.buffer.indexOf(this.startPrefix, this.
|
|
3433
|
+
const startIndex = this.buffer.indexOf(this.startPrefix, this.lastEmittedTextOffset);
|
|
3424
3434
|
if (startIndex !== -1) {
|
|
3425
|
-
const textBefore = this.
|
|
3435
|
+
const textBefore = this.extractTextSegment(startIndex);
|
|
3426
3436
|
if (textBefore !== void 0) {
|
|
3427
3437
|
yield { type: "text", content: textBefore };
|
|
3428
3438
|
}
|
|
3429
3439
|
const metadataStartIndex = startIndex + this.startPrefix.length;
|
|
3430
3440
|
const metadataEndIndex = this.buffer.indexOf("\n", metadataStartIndex);
|
|
3431
3441
|
if (metadataEndIndex !== -1) {
|
|
3432
|
-
const
|
|
3433
|
-
const {
|
|
3434
|
-
actualName: actualGadgetName,
|
|
3435
|
-
invocationId,
|
|
3436
|
-
dependencies
|
|
3437
|
-
} = this.parseGadgetName(gadgetName);
|
|
3442
|
+
const headerLine = this.buffer.substring(metadataStartIndex, metadataEndIndex).trim();
|
|
3443
|
+
const { gadgetName, invocationId, dependencies } = this.parseInvocationMetadata(headerLine);
|
|
3438
3444
|
const contentStartIndex = metadataEndIndex + 1;
|
|
3439
3445
|
const parametersRaw = this.buffer.substring(contentStartIndex).trim();
|
|
3440
3446
|
const { parameters, parseError } = this.parseParameters(parametersRaw);
|
|
3441
3447
|
yield {
|
|
3442
3448
|
type: "gadget_call",
|
|
3443
3449
|
call: {
|
|
3444
|
-
gadgetName
|
|
3450
|
+
gadgetName,
|
|
3445
3451
|
invocationId,
|
|
3446
3452
|
parametersRaw,
|
|
3447
3453
|
parameters,
|
|
@@ -3452,7 +3458,7 @@ var init_parser = __esm({
|
|
|
3452
3458
|
return;
|
|
3453
3459
|
}
|
|
3454
3460
|
}
|
|
3455
|
-
const remainingText = this.
|
|
3461
|
+
const remainingText = this.extractTextSegment(this.buffer.length);
|
|
3456
3462
|
if (remainingText !== void 0) {
|
|
3457
3463
|
yield { type: "text", content: remainingText };
|
|
3458
3464
|
}
|
|
@@ -3460,7 +3466,7 @@ var init_parser = __esm({
|
|
|
3460
3466
|
// Reset parser state (note: global invocation counter is NOT reset to ensure unique IDs)
|
|
3461
3467
|
reset() {
|
|
3462
3468
|
this.buffer = "";
|
|
3463
|
-
this.
|
|
3469
|
+
this.lastEmittedTextOffset = 0;
|
|
3464
3470
|
}
|
|
3465
3471
|
};
|
|
3466
3472
|
}
|
|
@@ -3479,14 +3485,17 @@ var init_executor = __esm({
|
|
|
3479
3485
|
init_exceptions();
|
|
3480
3486
|
init_parser();
|
|
3481
3487
|
GadgetExecutor = class {
|
|
3482
|
-
constructor(registry,
|
|
3488
|
+
constructor(registry, requestHumanInput, logger, defaultGadgetTimeoutMs, errorFormatterOptions, client, mediaStore, agentConfig, subagentConfig, onNestedEvent) {
|
|
3483
3489
|
this.registry = registry;
|
|
3484
|
-
this.
|
|
3490
|
+
this.requestHumanInput = requestHumanInput;
|
|
3485
3491
|
this.defaultGadgetTimeoutMs = defaultGadgetTimeoutMs;
|
|
3486
3492
|
this.client = client;
|
|
3487
3493
|
this.mediaStore = mediaStore;
|
|
3494
|
+
this.agentConfig = agentConfig;
|
|
3495
|
+
this.subagentConfig = subagentConfig;
|
|
3496
|
+
this.onNestedEvent = onNestedEvent;
|
|
3488
3497
|
this.logger = logger ?? createLogger({ name: "llmist:executor" });
|
|
3489
|
-
this.errorFormatter = new
|
|
3498
|
+
this.errorFormatter = new GadgetExecutionErrorFormatter(errorFormatterOptions);
|
|
3490
3499
|
this.argPrefix = errorFormatterOptions?.argPrefix ?? GADGET_ARG_PREFIX;
|
|
3491
3500
|
}
|
|
3492
3501
|
logger;
|
|
@@ -3506,11 +3515,11 @@ var init_executor = __esm({
|
|
|
3506
3515
|
});
|
|
3507
3516
|
}
|
|
3508
3517
|
/**
|
|
3509
|
-
*
|
|
3518
|
+
* Unify gadget execute result to consistent internal format.
|
|
3510
3519
|
* Handles string returns (backwards compat), object returns with cost,
|
|
3511
3520
|
* and object returns with media.
|
|
3512
3521
|
*/
|
|
3513
|
-
|
|
3522
|
+
unifyExecuteResult(raw) {
|
|
3514
3523
|
if (typeof raw === "string") {
|
|
3515
3524
|
return { result: raw, cost: 0 };
|
|
3516
3525
|
}
|
|
@@ -3628,7 +3637,11 @@ var init_executor = __esm({
|
|
|
3628
3637
|
const ctx = {
|
|
3629
3638
|
reportCost,
|
|
3630
3639
|
llmist: this.client ? new CostReportingLLMistWrapper(this.client, reportCost) : void 0,
|
|
3631
|
-
signal: abortController.signal
|
|
3640
|
+
signal: abortController.signal,
|
|
3641
|
+
agentConfig: this.agentConfig,
|
|
3642
|
+
subagentConfig: this.subagentConfig,
|
|
3643
|
+
invocationId: call.invocationId,
|
|
3644
|
+
onNestedEvent: this.onNestedEvent
|
|
3632
3645
|
};
|
|
3633
3646
|
let rawResult;
|
|
3634
3647
|
if (timeoutMs && timeoutMs > 0) {
|
|
@@ -3643,7 +3656,7 @@ var init_executor = __esm({
|
|
|
3643
3656
|
} else {
|
|
3644
3657
|
rawResult = await Promise.resolve(gadget.execute(validatedParameters, ctx));
|
|
3645
3658
|
}
|
|
3646
|
-
const { result, media, cost: returnCost } = this.
|
|
3659
|
+
const { result, media, cost: returnCost } = this.unifyExecuteResult(rawResult);
|
|
3647
3660
|
const totalCost = callbackCost + returnCost;
|
|
3648
3661
|
let mediaIds;
|
|
3649
3662
|
let storedMedia;
|
|
@@ -3689,7 +3702,7 @@ var init_executor = __esm({
|
|
|
3689
3702
|
storedMedia
|
|
3690
3703
|
};
|
|
3691
3704
|
} catch (error) {
|
|
3692
|
-
if (error instanceof
|
|
3705
|
+
if (error instanceof TaskCompletionSignal) {
|
|
3693
3706
|
this.logger.info("Gadget requested loop termination", {
|
|
3694
3707
|
gadgetName: call.gadgetName,
|
|
3695
3708
|
message: error.message
|
|
@@ -3717,7 +3730,7 @@ var init_executor = __esm({
|
|
|
3717
3730
|
executionTimeMs: Date.now() - startTime
|
|
3718
3731
|
};
|
|
3719
3732
|
}
|
|
3720
|
-
if (error instanceof
|
|
3733
|
+
if (error instanceof AbortException) {
|
|
3721
3734
|
this.logger.info("Gadget execution was aborted", {
|
|
3722
3735
|
gadgetName: call.gadgetName,
|
|
3723
3736
|
executionTimeMs: Date.now() - startTime
|
|
@@ -3730,14 +3743,14 @@ var init_executor = __esm({
|
|
|
3730
3743
|
executionTimeMs: Date.now() - startTime
|
|
3731
3744
|
};
|
|
3732
3745
|
}
|
|
3733
|
-
if (error instanceof
|
|
3746
|
+
if (error instanceof HumanInputRequiredException) {
|
|
3734
3747
|
this.logger.info("Gadget requested human input", {
|
|
3735
3748
|
gadgetName: call.gadgetName,
|
|
3736
3749
|
question: error.question
|
|
3737
3750
|
});
|
|
3738
|
-
if (this.
|
|
3751
|
+
if (this.requestHumanInput) {
|
|
3739
3752
|
try {
|
|
3740
|
-
const answer = await this.
|
|
3753
|
+
const answer = await this.requestHumanInput(error.question);
|
|
3741
3754
|
this.logger.debug("Human input received", {
|
|
3742
3755
|
gadgetName: call.gadgetName,
|
|
3743
3756
|
answerLength: answer.length
|
|
@@ -3835,13 +3848,13 @@ var init_stream_processor = __esm({
|
|
|
3835
3848
|
parser;
|
|
3836
3849
|
executor;
|
|
3837
3850
|
stopOnGadgetError;
|
|
3838
|
-
|
|
3839
|
-
|
|
3840
|
-
|
|
3851
|
+
canRecoverFromGadgetError;
|
|
3852
|
+
responseText = "";
|
|
3853
|
+
executionHalted = false;
|
|
3841
3854
|
observerFailureCount = 0;
|
|
3842
3855
|
// Dependency tracking for gadget execution DAG
|
|
3843
3856
|
/** Gadgets waiting for their dependencies to complete */
|
|
3844
|
-
|
|
3857
|
+
gadgetsAwaitingDependencies = /* @__PURE__ */ new Map();
|
|
3845
3858
|
/** Completed gadget results, keyed by invocation ID */
|
|
3846
3859
|
completedResults = /* @__PURE__ */ new Map();
|
|
3847
3860
|
/** Invocation IDs of gadgets that have failed (error or skipped due to dependency) */
|
|
@@ -3852,27 +3865,36 @@ var init_stream_processor = __esm({
|
|
|
3852
3865
|
this.hooks = options.hooks ?? {};
|
|
3853
3866
|
this.logger = options.logger ?? createLogger({ name: "llmist:stream-processor" });
|
|
3854
3867
|
this.stopOnGadgetError = options.stopOnGadgetError ?? true;
|
|
3855
|
-
this.
|
|
3856
|
-
this.parser = new
|
|
3868
|
+
this.canRecoverFromGadgetError = options.canRecoverFromGadgetError;
|
|
3869
|
+
this.parser = new GadgetCallParser({
|
|
3857
3870
|
startPrefix: options.gadgetStartPrefix,
|
|
3858
3871
|
endPrefix: options.gadgetEndPrefix,
|
|
3859
3872
|
argPrefix: options.gadgetArgPrefix
|
|
3860
3873
|
});
|
|
3861
3874
|
this.executor = new GadgetExecutor(
|
|
3862
3875
|
options.registry,
|
|
3863
|
-
options.
|
|
3876
|
+
options.requestHumanInput,
|
|
3864
3877
|
this.logger.getSubLogger({ name: "executor" }),
|
|
3865
3878
|
options.defaultGadgetTimeoutMs,
|
|
3866
3879
|
{ argPrefix: options.gadgetArgPrefix },
|
|
3867
3880
|
options.client,
|
|
3868
|
-
options.mediaStore
|
|
3881
|
+
options.mediaStore,
|
|
3882
|
+
options.agentConfig,
|
|
3883
|
+
options.subagentConfig,
|
|
3884
|
+
options.onNestedEvent
|
|
3869
3885
|
);
|
|
3870
3886
|
}
|
|
3871
3887
|
/**
|
|
3872
|
-
* Process an LLM stream and
|
|
3888
|
+
* Process an LLM stream and yield events in real-time.
|
|
3889
|
+
*
|
|
3890
|
+
* This is an async generator that yields events immediately as they occur:
|
|
3891
|
+
* - Text events are yielded as text is streamed from the LLM
|
|
3892
|
+
* - gadget_call events are yielded immediately when a gadget call is parsed
|
|
3893
|
+
* - gadget_result events are yielded when gadget execution completes
|
|
3894
|
+
*
|
|
3895
|
+
* The final event is always a StreamCompletionEvent containing metadata.
|
|
3873
3896
|
*/
|
|
3874
|
-
async process(stream2) {
|
|
3875
|
-
const outputs = [];
|
|
3897
|
+
async *process(stream2) {
|
|
3876
3898
|
let finishReason = null;
|
|
3877
3899
|
let usage;
|
|
3878
3900
|
let didExecuteGadgets = false;
|
|
@@ -3886,7 +3908,7 @@ var init_stream_processor = __esm({
|
|
|
3886
3908
|
if (this.hooks.interceptors?.interceptRawChunk) {
|
|
3887
3909
|
const context = {
|
|
3888
3910
|
iteration: this.iteration,
|
|
3889
|
-
accumulatedText: this.
|
|
3911
|
+
accumulatedText: this.responseText,
|
|
3890
3912
|
logger: this.logger
|
|
3891
3913
|
};
|
|
3892
3914
|
const intercepted = this.hooks.interceptors.interceptRawChunk(processedChunk, context);
|
|
@@ -3897,7 +3919,7 @@ var init_stream_processor = __esm({
|
|
|
3897
3919
|
}
|
|
3898
3920
|
}
|
|
3899
3921
|
if (processedChunk) {
|
|
3900
|
-
this.
|
|
3922
|
+
this.responseText += processedChunk;
|
|
3901
3923
|
}
|
|
3902
3924
|
}
|
|
3903
3925
|
if (this.hooks.observers?.onStreamChunk && (processedChunk || chunk.usage)) {
|
|
@@ -3906,7 +3928,7 @@ var init_stream_processor = __esm({
|
|
|
3906
3928
|
const context = {
|
|
3907
3929
|
iteration: this.iteration,
|
|
3908
3930
|
rawChunk: processedChunk,
|
|
3909
|
-
accumulatedText: this.
|
|
3931
|
+
accumulatedText: this.responseText,
|
|
3910
3932
|
usage,
|
|
3911
3933
|
logger: this.logger
|
|
3912
3934
|
};
|
|
@@ -3918,67 +3940,66 @@ var init_stream_processor = __esm({
|
|
|
3918
3940
|
continue;
|
|
3919
3941
|
}
|
|
3920
3942
|
for (const event of this.parser.feed(processedChunk)) {
|
|
3921
|
-
const
|
|
3922
|
-
|
|
3923
|
-
|
|
3924
|
-
|
|
3925
|
-
|
|
3926
|
-
|
|
3927
|
-
|
|
3928
|
-
shouldBreakLoop = true;
|
|
3943
|
+
for await (const processedEvent of this.processEventGenerator(event)) {
|
|
3944
|
+
yield processedEvent;
|
|
3945
|
+
if (processedEvent.type === "gadget_result") {
|
|
3946
|
+
didExecuteGadgets = true;
|
|
3947
|
+
if (processedEvent.result.breaksLoop) {
|
|
3948
|
+
shouldBreakLoop = true;
|
|
3949
|
+
}
|
|
3929
3950
|
}
|
|
3930
3951
|
}
|
|
3931
3952
|
}
|
|
3932
|
-
if (this.
|
|
3953
|
+
if (this.executionHalted) {
|
|
3933
3954
|
this.logger.info("Breaking from LLM stream due to gadget error");
|
|
3934
3955
|
break;
|
|
3935
3956
|
}
|
|
3936
3957
|
}
|
|
3937
|
-
if (!this.
|
|
3958
|
+
if (!this.executionHalted) {
|
|
3938
3959
|
for (const event of this.parser.finalize()) {
|
|
3939
|
-
const
|
|
3940
|
-
|
|
3941
|
-
|
|
3942
|
-
|
|
3943
|
-
|
|
3944
|
-
|
|
3945
|
-
|
|
3946
|
-
shouldBreakLoop = true;
|
|
3960
|
+
for await (const processedEvent of this.processEventGenerator(event)) {
|
|
3961
|
+
yield processedEvent;
|
|
3962
|
+
if (processedEvent.type === "gadget_result") {
|
|
3963
|
+
didExecuteGadgets = true;
|
|
3964
|
+
if (processedEvent.result.breaksLoop) {
|
|
3965
|
+
shouldBreakLoop = true;
|
|
3966
|
+
}
|
|
3947
3967
|
}
|
|
3948
3968
|
}
|
|
3949
3969
|
}
|
|
3950
|
-
const
|
|
3951
|
-
|
|
3952
|
-
|
|
3953
|
-
|
|
3954
|
-
|
|
3955
|
-
|
|
3956
|
-
|
|
3957
|
-
shouldBreakLoop = true;
|
|
3970
|
+
for await (const evt of this.processPendingGadgetsGenerator()) {
|
|
3971
|
+
yield evt;
|
|
3972
|
+
if (evt.type === "gadget_result") {
|
|
3973
|
+
didExecuteGadgets = true;
|
|
3974
|
+
if (evt.result.breaksLoop) {
|
|
3975
|
+
shouldBreakLoop = true;
|
|
3976
|
+
}
|
|
3958
3977
|
}
|
|
3959
3978
|
}
|
|
3960
3979
|
}
|
|
3961
|
-
let finalMessage = this.
|
|
3980
|
+
let finalMessage = this.responseText;
|
|
3962
3981
|
if (this.hooks.interceptors?.interceptAssistantMessage) {
|
|
3963
3982
|
const context = {
|
|
3964
3983
|
iteration: this.iteration,
|
|
3965
|
-
rawResponse: this.
|
|
3984
|
+
rawResponse: this.responseText,
|
|
3966
3985
|
logger: this.logger
|
|
3967
3986
|
};
|
|
3968
3987
|
finalMessage = this.hooks.interceptors.interceptAssistantMessage(finalMessage, context);
|
|
3969
3988
|
}
|
|
3970
|
-
|
|
3971
|
-
|
|
3989
|
+
const completionEvent = {
|
|
3990
|
+
type: "stream_complete",
|
|
3972
3991
|
shouldBreakLoop,
|
|
3973
3992
|
didExecuteGadgets,
|
|
3974
3993
|
finishReason,
|
|
3975
3994
|
usage,
|
|
3976
|
-
rawResponse: this.
|
|
3995
|
+
rawResponse: this.responseText,
|
|
3977
3996
|
finalMessage
|
|
3978
3997
|
};
|
|
3998
|
+
yield completionEvent;
|
|
3979
3999
|
}
|
|
3980
4000
|
/**
|
|
3981
4001
|
* Process a single parsed event (text or gadget call).
|
|
4002
|
+
* @deprecated Use processEventGenerator for real-time streaming
|
|
3982
4003
|
*/
|
|
3983
4004
|
async processEvent(event) {
|
|
3984
4005
|
if (event.type === "text") {
|
|
@@ -3988,6 +4009,23 @@ var init_stream_processor = __esm({
|
|
|
3988
4009
|
}
|
|
3989
4010
|
return [event];
|
|
3990
4011
|
}
|
|
4012
|
+
/**
|
|
4013
|
+
* Process a single parsed event, yielding events in real-time.
|
|
4014
|
+
* Generator version of processEvent for streaming support.
|
|
4015
|
+
*/
|
|
4016
|
+
async *processEventGenerator(event) {
|
|
4017
|
+
if (event.type === "text") {
|
|
4018
|
+
for (const e of await this.processTextEvent(event)) {
|
|
4019
|
+
yield e;
|
|
4020
|
+
}
|
|
4021
|
+
} else if (event.type === "gadget_call") {
|
|
4022
|
+
for await (const e of this.processGadgetCallGenerator(event.call)) {
|
|
4023
|
+
yield e;
|
|
4024
|
+
}
|
|
4025
|
+
} else {
|
|
4026
|
+
yield event;
|
|
4027
|
+
}
|
|
4028
|
+
}
|
|
3991
4029
|
/**
|
|
3992
4030
|
* Process a text event through interceptors.
|
|
3993
4031
|
*/
|
|
@@ -3996,7 +4034,7 @@ var init_stream_processor = __esm({
|
|
|
3996
4034
|
if (this.hooks.interceptors?.interceptTextChunk) {
|
|
3997
4035
|
const context = {
|
|
3998
4036
|
iteration: this.iteration,
|
|
3999
|
-
accumulatedText: this.
|
|
4037
|
+
accumulatedText: this.responseText,
|
|
4000
4038
|
logger: this.logger
|
|
4001
4039
|
};
|
|
4002
4040
|
const intercepted = this.hooks.interceptors.interceptTextChunk(content, context);
|
|
@@ -4015,7 +4053,7 @@ var init_stream_processor = __esm({
|
|
|
4015
4053
|
* After each execution, pending gadgets are checked to see if they can now run.
|
|
4016
4054
|
*/
|
|
4017
4055
|
async processGadgetCall(call) {
|
|
4018
|
-
if (this.
|
|
4056
|
+
if (this.executionHalted) {
|
|
4019
4057
|
this.logger.debug("Skipping gadget execution due to previous error", {
|
|
4020
4058
|
gadgetName: call.gadgetName
|
|
4021
4059
|
});
|
|
@@ -4054,7 +4092,7 @@ var init_stream_processor = __esm({
|
|
|
4054
4092
|
invocationId: call.invocationId,
|
|
4055
4093
|
waitingOn: unsatisfied
|
|
4056
4094
|
});
|
|
4057
|
-
this.
|
|
4095
|
+
this.gadgetsAwaitingDependencies.set(call.invocationId, call);
|
|
4058
4096
|
return events;
|
|
4059
4097
|
}
|
|
4060
4098
|
}
|
|
@@ -4064,9 +4102,68 @@ var init_stream_processor = __esm({
|
|
|
4064
4102
|
events.push(...triggeredEvents);
|
|
4065
4103
|
return events;
|
|
4066
4104
|
}
|
|
4105
|
+
/**
|
|
4106
|
+
* Process a gadget call, yielding events in real-time.
|
|
4107
|
+
*
|
|
4108
|
+
* Key difference from processGadgetCall: yields gadget_call event IMMEDIATELY
|
|
4109
|
+
* when parsed (before execution), enabling real-time UI feedback.
|
|
4110
|
+
*/
|
|
4111
|
+
async *processGadgetCallGenerator(call) {
|
|
4112
|
+
if (this.executionHalted) {
|
|
4113
|
+
this.logger.debug("Skipping gadget execution due to previous error", {
|
|
4114
|
+
gadgetName: call.gadgetName
|
|
4115
|
+
});
|
|
4116
|
+
return;
|
|
4117
|
+
}
|
|
4118
|
+
yield { type: "gadget_call", call };
|
|
4119
|
+
if (call.dependencies.length > 0) {
|
|
4120
|
+
if (call.dependencies.includes(call.invocationId)) {
|
|
4121
|
+
this.logger.warn("Gadget has self-referential dependency (depends on itself)", {
|
|
4122
|
+
gadgetName: call.gadgetName,
|
|
4123
|
+
invocationId: call.invocationId
|
|
4124
|
+
});
|
|
4125
|
+
this.failedInvocations.add(call.invocationId);
|
|
4126
|
+
const skipEvent = {
|
|
4127
|
+
type: "gadget_skipped",
|
|
4128
|
+
gadgetName: call.gadgetName,
|
|
4129
|
+
invocationId: call.invocationId,
|
|
4130
|
+
parameters: call.parameters ?? {},
|
|
4131
|
+
failedDependency: call.invocationId,
|
|
4132
|
+
failedDependencyError: `Gadget "${call.invocationId}" cannot depend on itself (self-referential dependency)`
|
|
4133
|
+
};
|
|
4134
|
+
yield skipEvent;
|
|
4135
|
+
return;
|
|
4136
|
+
}
|
|
4137
|
+
const failedDep = call.dependencies.find((dep) => this.failedInvocations.has(dep));
|
|
4138
|
+
if (failedDep) {
|
|
4139
|
+
const skipEvents = await this.handleFailedDependency(call, failedDep);
|
|
4140
|
+
for (const evt of skipEvents) {
|
|
4141
|
+
yield evt;
|
|
4142
|
+
}
|
|
4143
|
+
return;
|
|
4144
|
+
}
|
|
4145
|
+
const unsatisfied = call.dependencies.filter((dep) => !this.completedResults.has(dep));
|
|
4146
|
+
if (unsatisfied.length > 0) {
|
|
4147
|
+
this.logger.debug("Queueing gadget for later - waiting on dependencies", {
|
|
4148
|
+
gadgetName: call.gadgetName,
|
|
4149
|
+
invocationId: call.invocationId,
|
|
4150
|
+
waitingOn: unsatisfied
|
|
4151
|
+
});
|
|
4152
|
+
this.gadgetsAwaitingDependencies.set(call.invocationId, call);
|
|
4153
|
+
return;
|
|
4154
|
+
}
|
|
4155
|
+
}
|
|
4156
|
+
for await (const evt of this.executeGadgetGenerator(call)) {
|
|
4157
|
+
yield evt;
|
|
4158
|
+
}
|
|
4159
|
+
for await (const evt of this.processPendingGadgetsGenerator()) {
|
|
4160
|
+
yield evt;
|
|
4161
|
+
}
|
|
4162
|
+
}
|
|
4067
4163
|
/**
|
|
4068
4164
|
* Execute a gadget through the full hook lifecycle.
|
|
4069
4165
|
* This is the core execution logic, extracted from processGadgetCall.
|
|
4166
|
+
* @deprecated Use executeGadgetGenerator for real-time streaming
|
|
4070
4167
|
*/
|
|
4071
4168
|
async executeGadgetWithHooks(call) {
|
|
4072
4169
|
const events = [];
|
|
@@ -4076,14 +4173,14 @@ var init_stream_processor = __esm({
|
|
|
4076
4173
|
error: call.parseError,
|
|
4077
4174
|
rawParameters: call.parametersRaw
|
|
4078
4175
|
});
|
|
4079
|
-
const shouldContinue = await this.
|
|
4176
|
+
const shouldContinue = await this.checkCanRecoverFromError(
|
|
4080
4177
|
call.parseError,
|
|
4081
4178
|
call.gadgetName,
|
|
4082
4179
|
"parse",
|
|
4083
4180
|
call.parameters
|
|
4084
4181
|
);
|
|
4085
4182
|
if (!shouldContinue) {
|
|
4086
|
-
this.
|
|
4183
|
+
this.executionHalted = true;
|
|
4087
4184
|
}
|
|
4088
4185
|
}
|
|
4089
4186
|
let parameters = call.parameters ?? {};
|
|
@@ -4207,18 +4304,171 @@ var init_stream_processor = __esm({
|
|
|
4207
4304
|
events.push({ type: "gadget_result", result });
|
|
4208
4305
|
if (result.error) {
|
|
4209
4306
|
const errorType = this.determineErrorType(call, result);
|
|
4210
|
-
const shouldContinue = await this.
|
|
4307
|
+
const shouldContinue = await this.checkCanRecoverFromError(
|
|
4211
4308
|
result.error,
|
|
4212
4309
|
result.gadgetName,
|
|
4213
4310
|
errorType,
|
|
4214
4311
|
result.parameters
|
|
4215
4312
|
);
|
|
4216
4313
|
if (!shouldContinue) {
|
|
4217
|
-
this.
|
|
4314
|
+
this.executionHalted = true;
|
|
4218
4315
|
}
|
|
4219
4316
|
}
|
|
4220
4317
|
return events;
|
|
4221
4318
|
}
|
|
4319
|
+
/**
|
|
4320
|
+
* Execute a gadget and yield the result event.
|
|
4321
|
+
* Generator version that yields gadget_result immediately when execution completes.
|
|
4322
|
+
*/
|
|
4323
|
+
async *executeGadgetGenerator(call) {
|
|
4324
|
+
if (call.parseError) {
|
|
4325
|
+
this.logger.warn("Gadget has parse error", {
|
|
4326
|
+
gadgetName: call.gadgetName,
|
|
4327
|
+
error: call.parseError,
|
|
4328
|
+
rawParameters: call.parametersRaw
|
|
4329
|
+
});
|
|
4330
|
+
const shouldContinue = await this.checkCanRecoverFromError(
|
|
4331
|
+
call.parseError,
|
|
4332
|
+
call.gadgetName,
|
|
4333
|
+
"parse",
|
|
4334
|
+
call.parameters
|
|
4335
|
+
);
|
|
4336
|
+
if (!shouldContinue) {
|
|
4337
|
+
this.executionHalted = true;
|
|
4338
|
+
}
|
|
4339
|
+
}
|
|
4340
|
+
let parameters = call.parameters ?? {};
|
|
4341
|
+
if (this.hooks.interceptors?.interceptGadgetParameters) {
|
|
4342
|
+
const context = {
|
|
4343
|
+
iteration: this.iteration,
|
|
4344
|
+
gadgetName: call.gadgetName,
|
|
4345
|
+
invocationId: call.invocationId,
|
|
4346
|
+
logger: this.logger
|
|
4347
|
+
};
|
|
4348
|
+
parameters = this.hooks.interceptors.interceptGadgetParameters(parameters, context);
|
|
4349
|
+
}
|
|
4350
|
+
call.parameters = parameters;
|
|
4351
|
+
let shouldSkip = false;
|
|
4352
|
+
let syntheticResult;
|
|
4353
|
+
if (this.hooks.controllers?.beforeGadgetExecution) {
|
|
4354
|
+
const context = {
|
|
4355
|
+
iteration: this.iteration,
|
|
4356
|
+
gadgetName: call.gadgetName,
|
|
4357
|
+
invocationId: call.invocationId,
|
|
4358
|
+
parameters,
|
|
4359
|
+
logger: this.logger
|
|
4360
|
+
};
|
|
4361
|
+
const action = await this.hooks.controllers.beforeGadgetExecution(context);
|
|
4362
|
+
validateBeforeGadgetExecutionAction(action);
|
|
4363
|
+
if (action.action === "skip") {
|
|
4364
|
+
shouldSkip = true;
|
|
4365
|
+
syntheticResult = action.syntheticResult;
|
|
4366
|
+
this.logger.info("Controller skipped gadget execution", {
|
|
4367
|
+
gadgetName: call.gadgetName
|
|
4368
|
+
});
|
|
4369
|
+
}
|
|
4370
|
+
}
|
|
4371
|
+
const startObservers = [];
|
|
4372
|
+
if (this.hooks.observers?.onGadgetExecutionStart) {
|
|
4373
|
+
startObservers.push(async () => {
|
|
4374
|
+
const context = {
|
|
4375
|
+
iteration: this.iteration,
|
|
4376
|
+
gadgetName: call.gadgetName,
|
|
4377
|
+
invocationId: call.invocationId,
|
|
4378
|
+
parameters,
|
|
4379
|
+
logger: this.logger
|
|
4380
|
+
};
|
|
4381
|
+
await this.hooks.observers.onGadgetExecutionStart(context);
|
|
4382
|
+
});
|
|
4383
|
+
}
|
|
4384
|
+
await this.runObserversInParallel(startObservers);
|
|
4385
|
+
let result;
|
|
4386
|
+
if (shouldSkip) {
|
|
4387
|
+
result = {
|
|
4388
|
+
gadgetName: call.gadgetName,
|
|
4389
|
+
invocationId: call.invocationId,
|
|
4390
|
+
parameters,
|
|
4391
|
+
result: syntheticResult ?? "Execution skipped",
|
|
4392
|
+
executionTimeMs: 0
|
|
4393
|
+
};
|
|
4394
|
+
} else {
|
|
4395
|
+
result = await this.executor.execute(call);
|
|
4396
|
+
}
|
|
4397
|
+
const originalResult = result.result;
|
|
4398
|
+
if (result.result && this.hooks.interceptors?.interceptGadgetResult) {
|
|
4399
|
+
const context = {
|
|
4400
|
+
iteration: this.iteration,
|
|
4401
|
+
gadgetName: result.gadgetName,
|
|
4402
|
+
invocationId: result.invocationId,
|
|
4403
|
+
parameters,
|
|
4404
|
+
executionTimeMs: result.executionTimeMs,
|
|
4405
|
+
logger: this.logger
|
|
4406
|
+
};
|
|
4407
|
+
result.result = this.hooks.interceptors.interceptGadgetResult(result.result, context);
|
|
4408
|
+
}
|
|
4409
|
+
if (this.hooks.controllers?.afterGadgetExecution) {
|
|
4410
|
+
const context = {
|
|
4411
|
+
iteration: this.iteration,
|
|
4412
|
+
gadgetName: result.gadgetName,
|
|
4413
|
+
invocationId: result.invocationId,
|
|
4414
|
+
parameters,
|
|
4415
|
+
result: result.result,
|
|
4416
|
+
error: result.error,
|
|
4417
|
+
executionTimeMs: result.executionTimeMs,
|
|
4418
|
+
logger: this.logger
|
|
4419
|
+
};
|
|
4420
|
+
const action = await this.hooks.controllers.afterGadgetExecution(context);
|
|
4421
|
+
validateAfterGadgetExecutionAction(action);
|
|
4422
|
+
if (action.action === "recover" && result.error) {
|
|
4423
|
+
this.logger.info("Controller recovered from gadget error", {
|
|
4424
|
+
gadgetName: result.gadgetName,
|
|
4425
|
+
originalError: result.error
|
|
4426
|
+
});
|
|
4427
|
+
result = {
|
|
4428
|
+
...result,
|
|
4429
|
+
error: void 0,
|
|
4430
|
+
result: action.fallbackResult
|
|
4431
|
+
};
|
|
4432
|
+
}
|
|
4433
|
+
}
|
|
4434
|
+
const completeObservers = [];
|
|
4435
|
+
if (this.hooks.observers?.onGadgetExecutionComplete) {
|
|
4436
|
+
completeObservers.push(async () => {
|
|
4437
|
+
const context = {
|
|
4438
|
+
iteration: this.iteration,
|
|
4439
|
+
gadgetName: result.gadgetName,
|
|
4440
|
+
invocationId: result.invocationId,
|
|
4441
|
+
parameters,
|
|
4442
|
+
originalResult,
|
|
4443
|
+
finalResult: result.result,
|
|
4444
|
+
error: result.error,
|
|
4445
|
+
executionTimeMs: result.executionTimeMs,
|
|
4446
|
+
breaksLoop: result.breaksLoop,
|
|
4447
|
+
cost: result.cost,
|
|
4448
|
+
logger: this.logger
|
|
4449
|
+
};
|
|
4450
|
+
await this.hooks.observers.onGadgetExecutionComplete(context);
|
|
4451
|
+
});
|
|
4452
|
+
}
|
|
4453
|
+
await this.runObserversInParallel(completeObservers);
|
|
4454
|
+
this.completedResults.set(result.invocationId, result);
|
|
4455
|
+
if (result.error) {
|
|
4456
|
+
this.failedInvocations.add(result.invocationId);
|
|
4457
|
+
}
|
|
4458
|
+
yield { type: "gadget_result", result };
|
|
4459
|
+
if (result.error) {
|
|
4460
|
+
const errorType = this.determineErrorType(call, result);
|
|
4461
|
+
const shouldContinue = await this.checkCanRecoverFromError(
|
|
4462
|
+
result.error,
|
|
4463
|
+
result.gadgetName,
|
|
4464
|
+
errorType,
|
|
4465
|
+
result.parameters
|
|
4466
|
+
);
|
|
4467
|
+
if (!shouldContinue) {
|
|
4468
|
+
this.executionHalted = true;
|
|
4469
|
+
}
|
|
4470
|
+
}
|
|
4471
|
+
}
|
|
4222
4472
|
/**
|
|
4223
4473
|
* Handle a gadget that cannot execute because a dependency failed.
|
|
4224
4474
|
* Calls the onDependencySkipped controller to allow customization.
|
|
@@ -4301,11 +4551,11 @@ var init_stream_processor = __esm({
|
|
|
4301
4551
|
async processPendingGadgets() {
|
|
4302
4552
|
const events = [];
|
|
4303
4553
|
let progress = true;
|
|
4304
|
-
while (progress && this.
|
|
4554
|
+
while (progress && this.gadgetsAwaitingDependencies.size > 0) {
|
|
4305
4555
|
progress = false;
|
|
4306
4556
|
const readyToExecute = [];
|
|
4307
4557
|
const readyToSkip = [];
|
|
4308
|
-
for (const [invocationId, call] of this.
|
|
4558
|
+
for (const [invocationId, call] of this.gadgetsAwaitingDependencies) {
|
|
4309
4559
|
const failedDep = call.dependencies.find((dep) => this.failedInvocations.has(dep));
|
|
4310
4560
|
if (failedDep) {
|
|
4311
4561
|
readyToSkip.push({ call, failedDep });
|
|
@@ -4317,7 +4567,7 @@ var init_stream_processor = __esm({
|
|
|
4317
4567
|
}
|
|
4318
4568
|
}
|
|
4319
4569
|
for (const { call, failedDep } of readyToSkip) {
|
|
4320
|
-
this.
|
|
4570
|
+
this.gadgetsAwaitingDependencies.delete(call.invocationId);
|
|
4321
4571
|
const skipEvents = await this.handleFailedDependency(call, failedDep);
|
|
4322
4572
|
events.push(...skipEvents);
|
|
4323
4573
|
progress = true;
|
|
@@ -4328,7 +4578,7 @@ var init_stream_processor = __esm({
|
|
|
4328
4578
|
invocationIds: readyToExecute.map((c) => c.invocationId)
|
|
4329
4579
|
});
|
|
4330
4580
|
for (const call of readyToExecute) {
|
|
4331
|
-
this.
|
|
4581
|
+
this.gadgetsAwaitingDependencies.delete(call.invocationId);
|
|
4332
4582
|
}
|
|
4333
4583
|
const executePromises = readyToExecute.map((call) => this.executeGadgetWithHooks(call));
|
|
4334
4584
|
const results = await Promise.all(executePromises);
|
|
@@ -4338,9 +4588,9 @@ var init_stream_processor = __esm({
|
|
|
4338
4588
|
progress = true;
|
|
4339
4589
|
}
|
|
4340
4590
|
}
|
|
4341
|
-
if (this.
|
|
4342
|
-
const pendingIds = new Set(this.
|
|
4343
|
-
for (const [invocationId, call] of this.
|
|
4591
|
+
if (this.gadgetsAwaitingDependencies.size > 0) {
|
|
4592
|
+
const pendingIds = new Set(this.gadgetsAwaitingDependencies.keys());
|
|
4593
|
+
for (const [invocationId, call] of this.gadgetsAwaitingDependencies) {
|
|
4344
4594
|
const missingDeps = call.dependencies.filter((dep) => !this.completedResults.has(dep));
|
|
4345
4595
|
const circularDeps = missingDeps.filter((dep) => pendingIds.has(dep));
|
|
4346
4596
|
const trulyMissingDeps = missingDeps.filter((dep) => !pendingIds.has(dep));
|
|
@@ -4371,10 +4621,103 @@ var init_stream_processor = __esm({
|
|
|
4371
4621
|
};
|
|
4372
4622
|
events.push(skipEvent);
|
|
4373
4623
|
}
|
|
4374
|
-
this.
|
|
4624
|
+
this.gadgetsAwaitingDependencies.clear();
|
|
4375
4625
|
}
|
|
4376
4626
|
return events;
|
|
4377
4627
|
}
|
|
4628
|
+
/**
|
|
4629
|
+
* Process pending gadgets, yielding events in real-time.
|
|
4630
|
+
* Generator version that yields events as gadgets complete.
|
|
4631
|
+
*
|
|
4632
|
+
* Note: Gadgets are still executed in parallel for efficiency,
|
|
4633
|
+
* but results are yielded as they become available.
|
|
4634
|
+
*/
|
|
4635
|
+
async *processPendingGadgetsGenerator() {
|
|
4636
|
+
let progress = true;
|
|
4637
|
+
while (progress && this.gadgetsAwaitingDependencies.size > 0) {
|
|
4638
|
+
progress = false;
|
|
4639
|
+
const readyToExecute = [];
|
|
4640
|
+
const readyToSkip = [];
|
|
4641
|
+
for (const [_invocationId, call] of this.gadgetsAwaitingDependencies) {
|
|
4642
|
+
const failedDep = call.dependencies.find((dep) => this.failedInvocations.has(dep));
|
|
4643
|
+
if (failedDep) {
|
|
4644
|
+
readyToSkip.push({ call, failedDep });
|
|
4645
|
+
continue;
|
|
4646
|
+
}
|
|
4647
|
+
const allSatisfied = call.dependencies.every((dep) => this.completedResults.has(dep));
|
|
4648
|
+
if (allSatisfied) {
|
|
4649
|
+
readyToExecute.push(call);
|
|
4650
|
+
}
|
|
4651
|
+
}
|
|
4652
|
+
for (const { call, failedDep } of readyToSkip) {
|
|
4653
|
+
this.gadgetsAwaitingDependencies.delete(call.invocationId);
|
|
4654
|
+
const skipEvents = await this.handleFailedDependency(call, failedDep);
|
|
4655
|
+
for (const evt of skipEvents) {
|
|
4656
|
+
yield evt;
|
|
4657
|
+
}
|
|
4658
|
+
progress = true;
|
|
4659
|
+
}
|
|
4660
|
+
if (readyToExecute.length > 0) {
|
|
4661
|
+
this.logger.debug("Executing ready gadgets in parallel", {
|
|
4662
|
+
count: readyToExecute.length,
|
|
4663
|
+
invocationIds: readyToExecute.map((c) => c.invocationId)
|
|
4664
|
+
});
|
|
4665
|
+
for (const call of readyToExecute) {
|
|
4666
|
+
this.gadgetsAwaitingDependencies.delete(call.invocationId);
|
|
4667
|
+
}
|
|
4668
|
+
const eventSets = await Promise.all(
|
|
4669
|
+
readyToExecute.map(async (call) => {
|
|
4670
|
+
const events = [];
|
|
4671
|
+
for await (const evt of this.executeGadgetGenerator(call)) {
|
|
4672
|
+
events.push(evt);
|
|
4673
|
+
}
|
|
4674
|
+
return events;
|
|
4675
|
+
})
|
|
4676
|
+
);
|
|
4677
|
+
for (const events of eventSets) {
|
|
4678
|
+
for (const evt of events) {
|
|
4679
|
+
yield evt;
|
|
4680
|
+
}
|
|
4681
|
+
}
|
|
4682
|
+
progress = true;
|
|
4683
|
+
}
|
|
4684
|
+
}
|
|
4685
|
+
if (this.gadgetsAwaitingDependencies.size > 0) {
|
|
4686
|
+
const pendingIds = new Set(this.gadgetsAwaitingDependencies.keys());
|
|
4687
|
+
for (const [invocationId, call] of this.gadgetsAwaitingDependencies) {
|
|
4688
|
+
const missingDeps = call.dependencies.filter((dep) => !this.completedResults.has(dep));
|
|
4689
|
+
const circularDeps = missingDeps.filter((dep) => pendingIds.has(dep));
|
|
4690
|
+
const trulyMissingDeps = missingDeps.filter((dep) => !pendingIds.has(dep));
|
|
4691
|
+
let errorMessage;
|
|
4692
|
+
let logLevel = "warn";
|
|
4693
|
+
if (circularDeps.length > 0 && trulyMissingDeps.length > 0) {
|
|
4694
|
+
errorMessage = `Dependencies unresolvable: circular=[${circularDeps.join(", ")}], missing=[${trulyMissingDeps.join(", ")}]`;
|
|
4695
|
+
logLevel = "error";
|
|
4696
|
+
} else if (circularDeps.length > 0) {
|
|
4697
|
+
errorMessage = `Circular dependency detected: "${invocationId}" depends on "${circularDeps[0]}" which also depends on "${invocationId}" (directly or indirectly)`;
|
|
4698
|
+
} else {
|
|
4699
|
+
errorMessage = `Dependency "${missingDeps[0]}" was never executed - check that the invocation ID exists and is spelled correctly`;
|
|
4700
|
+
}
|
|
4701
|
+
this.logger[logLevel]("Gadget has unresolvable dependencies", {
|
|
4702
|
+
gadgetName: call.gadgetName,
|
|
4703
|
+
invocationId,
|
|
4704
|
+
circularDependencies: circularDeps,
|
|
4705
|
+
missingDependencies: trulyMissingDeps
|
|
4706
|
+
});
|
|
4707
|
+
this.failedInvocations.add(invocationId);
|
|
4708
|
+
const skipEvent = {
|
|
4709
|
+
type: "gadget_skipped",
|
|
4710
|
+
gadgetName: call.gadgetName,
|
|
4711
|
+
invocationId,
|
|
4712
|
+
parameters: call.parameters ?? {},
|
|
4713
|
+
failedDependency: missingDeps[0],
|
|
4714
|
+
failedDependencyError: errorMessage
|
|
4715
|
+
};
|
|
4716
|
+
yield skipEvent;
|
|
4717
|
+
}
|
|
4718
|
+
this.gadgetsAwaitingDependencies.clear();
|
|
4719
|
+
}
|
|
4720
|
+
}
|
|
4378
4721
|
/**
|
|
4379
4722
|
* Safely execute an observer, catching and logging any errors.
|
|
4380
4723
|
* Observers are non-critical, so errors are logged but don't crash the system.
|
|
@@ -4401,19 +4744,19 @@ var init_stream_processor = __esm({
|
|
|
4401
4744
|
);
|
|
4402
4745
|
}
|
|
4403
4746
|
/**
|
|
4404
|
-
* Check if execution
|
|
4747
|
+
* Check if execution can recover from an error.
|
|
4405
4748
|
*
|
|
4406
4749
|
* Returns true if we should continue processing subsequent gadgets, false if we should stop.
|
|
4407
4750
|
*
|
|
4408
4751
|
* Logic:
|
|
4409
|
-
* - If custom
|
|
4752
|
+
* - If custom canRecoverFromGadgetError is provided, use it
|
|
4410
4753
|
* - Otherwise, use stopOnGadgetError config:
|
|
4411
4754
|
* - stopOnGadgetError=true → return false (stop execution)
|
|
4412
4755
|
* - stopOnGadgetError=false → return true (continue execution)
|
|
4413
4756
|
*/
|
|
4414
|
-
async
|
|
4415
|
-
if (this.
|
|
4416
|
-
return await this.
|
|
4757
|
+
async checkCanRecoverFromError(error, gadgetName, errorType, parameters) {
|
|
4758
|
+
if (this.canRecoverFromGadgetError) {
|
|
4759
|
+
return await this.canRecoverFromGadgetError({
|
|
4417
4760
|
error,
|
|
4418
4761
|
gadgetName,
|
|
4419
4762
|
errorType,
|
|
@@ -4476,14 +4819,14 @@ var init_agent = __esm({
|
|
|
4476
4819
|
gadgetStartPrefix;
|
|
4477
4820
|
gadgetEndPrefix;
|
|
4478
4821
|
gadgetArgPrefix;
|
|
4479
|
-
|
|
4822
|
+
requestHumanInput;
|
|
4480
4823
|
textOnlyHandler;
|
|
4481
4824
|
textWithGadgetsHandler;
|
|
4482
4825
|
stopOnGadgetError;
|
|
4483
|
-
|
|
4826
|
+
canRecoverFromGadgetError;
|
|
4484
4827
|
defaultGadgetTimeoutMs;
|
|
4485
4828
|
defaultMaxTokens;
|
|
4486
|
-
|
|
4829
|
+
hasUserPrompt;
|
|
4487
4830
|
// Gadget output limiting
|
|
4488
4831
|
outputStore;
|
|
4489
4832
|
outputLimitEnabled;
|
|
@@ -4494,6 +4837,11 @@ var init_agent = __esm({
|
|
|
4494
4837
|
mediaStore;
|
|
4495
4838
|
// Cancellation
|
|
4496
4839
|
signal;
|
|
4840
|
+
// Subagent configuration
|
|
4841
|
+
agentContextConfig;
|
|
4842
|
+
subagentConfig;
|
|
4843
|
+
// Nested event callback for subagent gadgets
|
|
4844
|
+
onNestedEvent;
|
|
4497
4845
|
/**
|
|
4498
4846
|
* Creates a new Agent instance.
|
|
4499
4847
|
* @internal This constructor is private. Use LLMist.createAgent() or AgentBuilder instead.
|
|
@@ -4513,11 +4861,11 @@ var init_agent = __esm({
|
|
|
4513
4861
|
this.gadgetStartPrefix = options.gadgetStartPrefix;
|
|
4514
4862
|
this.gadgetEndPrefix = options.gadgetEndPrefix;
|
|
4515
4863
|
this.gadgetArgPrefix = options.gadgetArgPrefix;
|
|
4516
|
-
this.
|
|
4864
|
+
this.requestHumanInput = options.requestHumanInput;
|
|
4517
4865
|
this.textOnlyHandler = options.textOnlyHandler ?? "terminate";
|
|
4518
4866
|
this.textWithGadgetsHandler = options.textWithGadgetsHandler;
|
|
4519
4867
|
this.stopOnGadgetError = options.stopOnGadgetError ?? true;
|
|
4520
|
-
this.
|
|
4868
|
+
this.canRecoverFromGadgetError = options.canRecoverFromGadgetError;
|
|
4521
4869
|
this.defaultGadgetTimeoutMs = options.defaultGadgetTimeoutMs;
|
|
4522
4870
|
this.defaultMaxTokens = this.resolveMaxTokensFromCatalog(options.model);
|
|
4523
4871
|
this.outputLimitEnabled = options.gadgetOutputLimit ?? DEFAULT_GADGET_OUTPUT_LIMIT;
|
|
@@ -4533,7 +4881,7 @@ var init_agent = __esm({
|
|
|
4533
4881
|
createGadgetOutputViewer(this.outputStore, this.outputLimitCharLimit)
|
|
4534
4882
|
);
|
|
4535
4883
|
}
|
|
4536
|
-
this.hooks = this.
|
|
4884
|
+
this.hooks = this.chainOutputLimiterWithUserHooks(options.hooks);
|
|
4537
4885
|
const baseBuilder = new LLMMessageBuilder(options.promptConfig);
|
|
4538
4886
|
if (options.systemPrompt) {
|
|
4539
4887
|
baseBuilder.addSystem(options.systemPrompt);
|
|
@@ -4553,7 +4901,7 @@ var init_agent = __esm({
|
|
|
4553
4901
|
endPrefix: options.gadgetEndPrefix,
|
|
4554
4902
|
argPrefix: options.gadgetArgPrefix
|
|
4555
4903
|
});
|
|
4556
|
-
this.
|
|
4904
|
+
this.hasUserPrompt = !!options.userPrompt;
|
|
4557
4905
|
if (options.userPrompt) {
|
|
4558
4906
|
this.conversation.addUserMessage(options.userPrompt);
|
|
4559
4907
|
}
|
|
@@ -4566,6 +4914,12 @@ var init_agent = __esm({
|
|
|
4566
4914
|
);
|
|
4567
4915
|
}
|
|
4568
4916
|
this.signal = options.signal;
|
|
4917
|
+
this.agentContextConfig = {
|
|
4918
|
+
model: this.model,
|
|
4919
|
+
temperature: this.temperature
|
|
4920
|
+
};
|
|
4921
|
+
this.subagentConfig = options.subagentConfig;
|
|
4922
|
+
this.onNestedEvent = options.onNestedEvent;
|
|
4569
4923
|
}
|
|
4570
4924
|
/**
|
|
4571
4925
|
* Get the gadget registry for this agent.
|
|
@@ -4672,7 +5026,7 @@ var init_agent = __esm({
|
|
|
4672
5026
|
* @throws {Error} If no user prompt was provided (when using build() without ask())
|
|
4673
5027
|
*/
|
|
4674
5028
|
async *run() {
|
|
4675
|
-
if (!this.
|
|
5029
|
+
if (!this.hasUserPrompt) {
|
|
4676
5030
|
throw new Error(
|
|
4677
5031
|
"No user prompt provided. Use .ask(prompt) instead of .build(), or call agent.run() after providing a prompt."
|
|
4678
5032
|
);
|
|
@@ -4789,17 +5143,37 @@ var init_agent = __esm({
|
|
|
4789
5143
|
gadgetArgPrefix: this.gadgetArgPrefix,
|
|
4790
5144
|
hooks: this.hooks,
|
|
4791
5145
|
logger: this.logger.getSubLogger({ name: "stream-processor" }),
|
|
4792
|
-
|
|
5146
|
+
requestHumanInput: this.requestHumanInput,
|
|
4793
5147
|
stopOnGadgetError: this.stopOnGadgetError,
|
|
4794
|
-
|
|
5148
|
+
canRecoverFromGadgetError: this.canRecoverFromGadgetError,
|
|
4795
5149
|
defaultGadgetTimeoutMs: this.defaultGadgetTimeoutMs,
|
|
4796
5150
|
client: this.client,
|
|
4797
|
-
mediaStore: this.mediaStore
|
|
5151
|
+
mediaStore: this.mediaStore,
|
|
5152
|
+
agentConfig: this.agentContextConfig,
|
|
5153
|
+
subagentConfig: this.subagentConfig,
|
|
5154
|
+
onNestedEvent: this.onNestedEvent
|
|
4798
5155
|
});
|
|
4799
|
-
|
|
4800
|
-
|
|
4801
|
-
|
|
5156
|
+
let streamMetadata = null;
|
|
5157
|
+
let gadgetCallCount = 0;
|
|
5158
|
+
const textOutputs = [];
|
|
5159
|
+
const gadgetResults = [];
|
|
5160
|
+
for await (const event of processor.process(stream2)) {
|
|
5161
|
+
if (event.type === "stream_complete") {
|
|
5162
|
+
streamMetadata = event;
|
|
5163
|
+
continue;
|
|
5164
|
+
}
|
|
5165
|
+
if (event.type === "text") {
|
|
5166
|
+
textOutputs.push(event.content);
|
|
5167
|
+
} else if (event.type === "gadget_result") {
|
|
5168
|
+
gadgetCallCount++;
|
|
5169
|
+
gadgetResults.push(event);
|
|
5170
|
+
}
|
|
5171
|
+
yield event;
|
|
4802
5172
|
}
|
|
5173
|
+
if (!streamMetadata) {
|
|
5174
|
+
throw new Error("Stream processing completed without metadata event");
|
|
5175
|
+
}
|
|
5176
|
+
const result = streamMetadata;
|
|
4803
5177
|
this.logger.info("LLM response completed", {
|
|
4804
5178
|
finishReason: result.finishReason,
|
|
4805
5179
|
usage: result.usage,
|
|
@@ -4824,9 +5198,6 @@ var init_agent = __esm({
|
|
|
4824
5198
|
});
|
|
4825
5199
|
let finalMessage = result.finalMessage;
|
|
4826
5200
|
if (this.hooks.controllers?.afterLLMCall) {
|
|
4827
|
-
const gadgetCallCount = result.outputs.filter(
|
|
4828
|
-
(output) => output.type === "gadget_result"
|
|
4829
|
-
).length;
|
|
4830
5201
|
const context = {
|
|
4831
5202
|
iteration: currentIteration,
|
|
4832
5203
|
maxIterations: this.maxIterations,
|
|
@@ -4847,31 +5218,29 @@ var init_agent = __esm({
|
|
|
4847
5218
|
if (msg.role === "user") {
|
|
4848
5219
|
this.conversation.addUserMessage(msg.content);
|
|
4849
5220
|
} else if (msg.role === "assistant") {
|
|
4850
|
-
this.conversation.addAssistantMessage(
|
|
5221
|
+
this.conversation.addAssistantMessage(extractMessageText(msg.content));
|
|
4851
5222
|
} else if (msg.role === "system") {
|
|
4852
|
-
this.conversation.addUserMessage(`[System] ${
|
|
5223
|
+
this.conversation.addUserMessage(`[System] ${extractMessageText(msg.content)}`);
|
|
4853
5224
|
}
|
|
4854
5225
|
}
|
|
4855
5226
|
}
|
|
4856
5227
|
}
|
|
4857
5228
|
if (result.didExecuteGadgets) {
|
|
4858
5229
|
if (this.textWithGadgetsHandler) {
|
|
4859
|
-
const textContent =
|
|
4860
|
-
(output) => output.type === "text"
|
|
4861
|
-
).map((output) => output.content).join("");
|
|
5230
|
+
const textContent = textOutputs.join("");
|
|
4862
5231
|
if (textContent.trim()) {
|
|
4863
5232
|
const { gadgetName, parameterMapping, resultMapping } = this.textWithGadgetsHandler;
|
|
4864
|
-
this.conversation.
|
|
5233
|
+
this.conversation.addGadgetCallResult(
|
|
4865
5234
|
gadgetName,
|
|
4866
5235
|
parameterMapping(textContent),
|
|
4867
5236
|
resultMapping ? resultMapping(textContent) : textContent
|
|
4868
5237
|
);
|
|
4869
5238
|
}
|
|
4870
5239
|
}
|
|
4871
|
-
for (const output of
|
|
5240
|
+
for (const output of gadgetResults) {
|
|
4872
5241
|
if (output.type === "gadget_result") {
|
|
4873
5242
|
const gadgetResult = output.result;
|
|
4874
|
-
this.conversation.
|
|
5243
|
+
this.conversation.addGadgetCallResult(
|
|
4875
5244
|
gadgetResult.gadgetName,
|
|
4876
5245
|
gadgetResult.parameters,
|
|
4877
5246
|
gadgetResult.error ?? gadgetResult.result ?? "",
|
|
@@ -4882,7 +5251,7 @@ var init_agent = __esm({
|
|
|
4882
5251
|
}
|
|
4883
5252
|
} else {
|
|
4884
5253
|
if (finalMessage.trim()) {
|
|
4885
|
-
this.conversation.
|
|
5254
|
+
this.conversation.addGadgetCallResult(
|
|
4886
5255
|
"TellUser",
|
|
4887
5256
|
{ message: finalMessage, done: false, type: "info" },
|
|
4888
5257
|
`\u2139\uFE0F ${finalMessage}`
|
|
@@ -5008,10 +5377,10 @@ var init_agent = __esm({
|
|
|
5008
5377
|
return this.client.modelRegistry.getModelLimits(unprefixedModelId)?.maxOutputTokens;
|
|
5009
5378
|
}
|
|
5010
5379
|
/**
|
|
5011
|
-
*
|
|
5380
|
+
* Chain the output limiter interceptor with user-provided hooks.
|
|
5012
5381
|
* The limiter runs first, then chains to any user interceptor.
|
|
5013
5382
|
*/
|
|
5014
|
-
|
|
5383
|
+
chainOutputLimiterWithUserHooks(userHooks) {
|
|
5015
5384
|
if (!this.outputLimitEnabled) {
|
|
5016
5385
|
return userHooks ?? {};
|
|
5017
5386
|
}
|
|
@@ -5090,20 +5459,23 @@ var init_builder = __esm({
|
|
|
5090
5459
|
promptConfig;
|
|
5091
5460
|
gadgets = [];
|
|
5092
5461
|
initialMessages = [];
|
|
5093
|
-
|
|
5462
|
+
requestHumanInput;
|
|
5094
5463
|
gadgetStartPrefix;
|
|
5095
5464
|
gadgetEndPrefix;
|
|
5096
5465
|
gadgetArgPrefix;
|
|
5097
5466
|
textOnlyHandler;
|
|
5098
5467
|
textWithGadgetsHandler;
|
|
5099
5468
|
stopOnGadgetError;
|
|
5100
|
-
|
|
5469
|
+
canRecoverFromGadgetError;
|
|
5101
5470
|
defaultGadgetTimeoutMs;
|
|
5102
5471
|
gadgetOutputLimit;
|
|
5103
5472
|
gadgetOutputLimitPercent;
|
|
5104
5473
|
compactionConfig;
|
|
5105
5474
|
signal;
|
|
5106
5475
|
trailingMessage;
|
|
5476
|
+
subagentConfig;
|
|
5477
|
+
nestedEventCallback;
|
|
5478
|
+
parentContext;
|
|
5107
5479
|
constructor(client) {
|
|
5108
5480
|
this.client = client;
|
|
5109
5481
|
}
|
|
@@ -5194,13 +5566,13 @@ var init_builder = __esm({
|
|
|
5194
5566
|
*
|
|
5195
5567
|
* @example
|
|
5196
5568
|
* ```typescript
|
|
5197
|
-
* .
|
|
5569
|
+
* .withPromptTemplateConfig({
|
|
5198
5570
|
* mainInstruction: "Use the gadget markers below:",
|
|
5199
5571
|
* rules: ["Always use markers", "Never use function calling"]
|
|
5200
5572
|
* })
|
|
5201
5573
|
* ```
|
|
5202
5574
|
*/
|
|
5203
|
-
|
|
5575
|
+
withPromptTemplateConfig(config) {
|
|
5204
5576
|
this.promptConfig = config;
|
|
5205
5577
|
return this;
|
|
5206
5578
|
}
|
|
@@ -5280,7 +5652,7 @@ var init_builder = __esm({
|
|
|
5280
5652
|
* ```
|
|
5281
5653
|
*/
|
|
5282
5654
|
onHumanInput(handler) {
|
|
5283
|
-
this.
|
|
5655
|
+
this.requestHumanInput = handler;
|
|
5284
5656
|
return this;
|
|
5285
5657
|
}
|
|
5286
5658
|
/**
|
|
@@ -5415,9 +5787,9 @@ var init_builder = __esm({
|
|
|
5415
5787
|
* Provides fine-grained control over whether to continue after different types of errors.
|
|
5416
5788
|
* Overrides `stopOnGadgetError` when provided.
|
|
5417
5789
|
*
|
|
5418
|
-
* **Note:** This builder method configures the underlying `
|
|
5790
|
+
* **Note:** This builder method configures the underlying `canRecoverFromGadgetError` option
|
|
5419
5791
|
* in `AgentOptions`. The method is named `withErrorHandler` for better developer experience,
|
|
5420
|
-
* but maps to the `
|
|
5792
|
+
* but maps to the `canRecoverFromGadgetError` property internally.
|
|
5421
5793
|
*
|
|
5422
5794
|
* @param handler - Function that decides whether to continue after an error.
|
|
5423
5795
|
* Return `true` to continue execution, `false` to stop.
|
|
@@ -5438,7 +5810,7 @@ var init_builder = __esm({
|
|
|
5438
5810
|
* ```
|
|
5439
5811
|
*/
|
|
5440
5812
|
withErrorHandler(handler) {
|
|
5441
|
-
this.
|
|
5813
|
+
this.canRecoverFromGadgetError = handler;
|
|
5442
5814
|
return this;
|
|
5443
5815
|
}
|
|
5444
5816
|
/**
|
|
@@ -5579,6 +5951,95 @@ var init_builder = __esm({
|
|
|
5579
5951
|
this.signal = signal;
|
|
5580
5952
|
return this;
|
|
5581
5953
|
}
|
|
5954
|
+
/**
|
|
5955
|
+
* Set subagent configuration overrides.
|
|
5956
|
+
*
|
|
5957
|
+
* Subagent gadgets (like BrowseWeb) can read these settings from ExecutionContext
|
|
5958
|
+
* to inherit model and other options from the CLI configuration.
|
|
5959
|
+
*
|
|
5960
|
+
* @param config - Subagent configuration map keyed by gadget name
|
|
5961
|
+
* @returns This builder for chaining
|
|
5962
|
+
*
|
|
5963
|
+
* @example
|
|
5964
|
+
* ```typescript
|
|
5965
|
+
* .withSubagentConfig({
|
|
5966
|
+
* BrowseWeb: { model: "inherit", maxIterations: 20, headless: true },
|
|
5967
|
+
* CodeAnalyzer: { model: "sonnet", maxIterations: 10 }
|
|
5968
|
+
* })
|
|
5969
|
+
* ```
|
|
5970
|
+
*/
|
|
5971
|
+
withSubagentConfig(config) {
|
|
5972
|
+
this.subagentConfig = config;
|
|
5973
|
+
return this;
|
|
5974
|
+
}
|
|
5975
|
+
/**
|
|
5976
|
+
* Set the callback for nested subagent events.
|
|
5977
|
+
*
|
|
5978
|
+
* Subagent gadgets (like BrowseWeb) can use ExecutionContext.onNestedEvent
|
|
5979
|
+
* to report their internal LLM calls and gadget executions in real-time.
|
|
5980
|
+
* This callback receives those events, enabling hierarchical progress display.
|
|
5981
|
+
*
|
|
5982
|
+
* @param callback - Function to handle nested agent events
|
|
5983
|
+
* @returns This builder for chaining
|
|
5984
|
+
*
|
|
5985
|
+
* @example
|
|
5986
|
+
* ```typescript
|
|
5987
|
+
* .withNestedEventCallback((event) => {
|
|
5988
|
+
* if (event.type === "llm_call_start") {
|
|
5989
|
+
* console.log(` Nested LLM #${event.event.iteration} starting...`);
|
|
5990
|
+
* } else if (event.type === "gadget_call") {
|
|
5991
|
+
* console.log(` ⏵ ${event.event.call.gadgetName}...`);
|
|
5992
|
+
* }
|
|
5993
|
+
* })
|
|
5994
|
+
* ```
|
|
5995
|
+
*/
|
|
5996
|
+
withNestedEventCallback(callback) {
|
|
5997
|
+
this.nestedEventCallback = callback;
|
|
5998
|
+
return this;
|
|
5999
|
+
}
|
|
6000
|
+
/**
|
|
6001
|
+
* Enable automatic nested event forwarding to parent agent.
|
|
6002
|
+
*
|
|
6003
|
+
* When building a subagent inside a gadget, call this method to automatically
|
|
6004
|
+
* forward all LLM calls and gadget events to the parent agent. This enables
|
|
6005
|
+
* hierarchical progress display without any manual event handling.
|
|
6006
|
+
*
|
|
6007
|
+
* The method extracts `invocationId` and `onNestedEvent` from the execution
|
|
6008
|
+
* context and sets up automatic forwarding via hooks and event wrapping.
|
|
6009
|
+
*
|
|
6010
|
+
* @param ctx - ExecutionContext passed to the gadget's execute() method
|
|
6011
|
+
* @param depth - Nesting depth (default: 1 for direct child)
|
|
6012
|
+
* @returns This builder for chaining
|
|
6013
|
+
*
|
|
6014
|
+
* @example
|
|
6015
|
+
* ```typescript
|
|
6016
|
+
* // In a subagent gadget like BrowseWeb - ONE LINE enables auto-forwarding:
|
|
6017
|
+
* execute: async (params, ctx) => {
|
|
6018
|
+
* const agent = new AgentBuilder(client)
|
|
6019
|
+
* .withModel(model)
|
|
6020
|
+
* .withGadgets(Navigate, Click, Screenshot)
|
|
6021
|
+
* .withParentContext(ctx) // <-- This is all you need!
|
|
6022
|
+
* .ask(params.task);
|
|
6023
|
+
*
|
|
6024
|
+
* for await (const event of agent.run()) {
|
|
6025
|
+
* // Events automatically forwarded - just process normally
|
|
6026
|
+
* if (event.type === "text") {
|
|
6027
|
+
* result = event.content;
|
|
6028
|
+
* }
|
|
6029
|
+
* }
|
|
6030
|
+
* }
|
|
6031
|
+
* ```
|
|
6032
|
+
*/
|
|
6033
|
+
withParentContext(ctx, depth = 1) {
|
|
6034
|
+
if (ctx.onNestedEvent && ctx.invocationId) {
|
|
6035
|
+
this.parentContext = {
|
|
6036
|
+
invocationId: ctx.invocationId,
|
|
6037
|
+
onNestedEvent: ctx.onNestedEvent,
|
|
6038
|
+
depth
|
|
6039
|
+
};
|
|
6040
|
+
}
|
|
6041
|
+
return this;
|
|
6042
|
+
}
|
|
5582
6043
|
/**
|
|
5583
6044
|
* Add an ephemeral trailing message that appears at the end of each LLM request.
|
|
5584
6045
|
*
|
|
@@ -5646,14 +6107,58 @@ ${endPrefix}`
|
|
|
5646
6107
|
return this;
|
|
5647
6108
|
}
|
|
5648
6109
|
/**
|
|
5649
|
-
* Compose the final hooks, including
|
|
6110
|
+
* Compose the final hooks, including:
|
|
6111
|
+
* - Trailing message injection (if configured)
|
|
6112
|
+
* - Nested event forwarding for LLM calls (if parentContext is set)
|
|
5650
6113
|
*/
|
|
5651
6114
|
composeHooks() {
|
|
6115
|
+
let hooks = this.hooks;
|
|
6116
|
+
if (this.parentContext) {
|
|
6117
|
+
const { invocationId, onNestedEvent, depth } = this.parentContext;
|
|
6118
|
+
const existingOnLLMCallStart = hooks?.observers?.onLLMCallStart;
|
|
6119
|
+
const existingOnLLMCallComplete = hooks?.observers?.onLLMCallComplete;
|
|
6120
|
+
hooks = {
|
|
6121
|
+
...hooks,
|
|
6122
|
+
observers: {
|
|
6123
|
+
...hooks?.observers,
|
|
6124
|
+
onLLMCallStart: async (context) => {
|
|
6125
|
+
onNestedEvent({
|
|
6126
|
+
type: "llm_call_start",
|
|
6127
|
+
gadgetInvocationId: invocationId,
|
|
6128
|
+
depth,
|
|
6129
|
+
event: {
|
|
6130
|
+
iteration: context.iteration,
|
|
6131
|
+
model: context.options.model
|
|
6132
|
+
}
|
|
6133
|
+
});
|
|
6134
|
+
if (existingOnLLMCallStart) {
|
|
6135
|
+
await existingOnLLMCallStart(context);
|
|
6136
|
+
}
|
|
6137
|
+
},
|
|
6138
|
+
onLLMCallComplete: async (context) => {
|
|
6139
|
+
onNestedEvent({
|
|
6140
|
+
type: "llm_call_end",
|
|
6141
|
+
gadgetInvocationId: invocationId,
|
|
6142
|
+
depth,
|
|
6143
|
+
event: {
|
|
6144
|
+
iteration: context.iteration,
|
|
6145
|
+
model: context.options.model,
|
|
6146
|
+
outputTokens: context.usage?.outputTokens,
|
|
6147
|
+
finishReason: context.finishReason
|
|
6148
|
+
}
|
|
6149
|
+
});
|
|
6150
|
+
if (existingOnLLMCallComplete) {
|
|
6151
|
+
await existingOnLLMCallComplete(context);
|
|
6152
|
+
}
|
|
6153
|
+
}
|
|
6154
|
+
}
|
|
6155
|
+
};
|
|
6156
|
+
}
|
|
5652
6157
|
if (!this.trailingMessage) {
|
|
5653
|
-
return
|
|
6158
|
+
return hooks;
|
|
5654
6159
|
}
|
|
5655
6160
|
const trailingMsg = this.trailingMessage;
|
|
5656
|
-
const existingBeforeLLMCall =
|
|
6161
|
+
const existingBeforeLLMCall = hooks?.controllers?.beforeLLMCall;
|
|
5657
6162
|
const trailingMessageController = async (ctx) => {
|
|
5658
6163
|
const result = existingBeforeLLMCall ? await existingBeforeLLMCall(ctx) : { action: "proceed" };
|
|
5659
6164
|
if (result.action === "skip") {
|
|
@@ -5668,9 +6173,9 @@ ${endPrefix}`
|
|
|
5668
6173
|
};
|
|
5669
6174
|
};
|
|
5670
6175
|
return {
|
|
5671
|
-
...
|
|
6176
|
+
...hooks,
|
|
5672
6177
|
controllers: {
|
|
5673
|
-
...
|
|
6178
|
+
...hooks?.controllers,
|
|
5674
6179
|
beforeLLMCall: trailingMessageController
|
|
5675
6180
|
}
|
|
5676
6181
|
};
|
|
@@ -5731,6 +6236,19 @@ ${endPrefix}`
|
|
|
5731
6236
|
this.client = new LLMistClass();
|
|
5732
6237
|
}
|
|
5733
6238
|
const registry = GadgetRegistry.from(this.gadgets);
|
|
6239
|
+
let onNestedEvent = this.nestedEventCallback;
|
|
6240
|
+
if (this.parentContext) {
|
|
6241
|
+
const { invocationId, onNestedEvent: parentCallback, depth } = this.parentContext;
|
|
6242
|
+
const existingCallback = this.nestedEventCallback;
|
|
6243
|
+
onNestedEvent = (event) => {
|
|
6244
|
+
parentCallback({
|
|
6245
|
+
...event,
|
|
6246
|
+
gadgetInvocationId: invocationId,
|
|
6247
|
+
depth: event.depth + depth
|
|
6248
|
+
});
|
|
6249
|
+
existingCallback?.(event);
|
|
6250
|
+
};
|
|
6251
|
+
}
|
|
5734
6252
|
return {
|
|
5735
6253
|
client: this.client,
|
|
5736
6254
|
model: this.model ?? "openai:gpt-5-nano",
|
|
@@ -5743,19 +6261,21 @@ ${endPrefix}`
|
|
|
5743
6261
|
hooks: this.composeHooks(),
|
|
5744
6262
|
promptConfig: this.promptConfig,
|
|
5745
6263
|
initialMessages: this.initialMessages,
|
|
5746
|
-
|
|
6264
|
+
requestHumanInput: this.requestHumanInput,
|
|
5747
6265
|
gadgetStartPrefix: this.gadgetStartPrefix,
|
|
5748
6266
|
gadgetEndPrefix: this.gadgetEndPrefix,
|
|
5749
6267
|
gadgetArgPrefix: this.gadgetArgPrefix,
|
|
5750
6268
|
textOnlyHandler: this.textOnlyHandler,
|
|
5751
6269
|
textWithGadgetsHandler: this.textWithGadgetsHandler,
|
|
5752
6270
|
stopOnGadgetError: this.stopOnGadgetError,
|
|
5753
|
-
|
|
6271
|
+
canRecoverFromGadgetError: this.canRecoverFromGadgetError,
|
|
5754
6272
|
defaultGadgetTimeoutMs: this.defaultGadgetTimeoutMs,
|
|
5755
6273
|
gadgetOutputLimit: this.gadgetOutputLimit,
|
|
5756
6274
|
gadgetOutputLimitPercent: this.gadgetOutputLimitPercent,
|
|
5757
6275
|
compactionConfig: this.compactionConfig,
|
|
5758
|
-
signal: this.signal
|
|
6276
|
+
signal: this.signal,
|
|
6277
|
+
subagentConfig: this.subagentConfig,
|
|
6278
|
+
onNestedEvent
|
|
5759
6279
|
};
|
|
5760
6280
|
}
|
|
5761
6281
|
ask(userPrompt) {
|
|
@@ -5912,6 +6432,19 @@ ${endPrefix}`
|
|
|
5912
6432
|
this.client = new LLMistClass();
|
|
5913
6433
|
}
|
|
5914
6434
|
const registry = GadgetRegistry.from(this.gadgets);
|
|
6435
|
+
let onNestedEvent = this.nestedEventCallback;
|
|
6436
|
+
if (this.parentContext) {
|
|
6437
|
+
const { invocationId, onNestedEvent: parentCallback, depth } = this.parentContext;
|
|
6438
|
+
const existingCallback = this.nestedEventCallback;
|
|
6439
|
+
onNestedEvent = (event) => {
|
|
6440
|
+
parentCallback({
|
|
6441
|
+
...event,
|
|
6442
|
+
gadgetInvocationId: invocationId,
|
|
6443
|
+
depth: event.depth + depth
|
|
6444
|
+
});
|
|
6445
|
+
existingCallback?.(event);
|
|
6446
|
+
};
|
|
6447
|
+
}
|
|
5915
6448
|
const options = {
|
|
5916
6449
|
client: this.client,
|
|
5917
6450
|
model: this.model ?? "openai:gpt-5-nano",
|
|
@@ -5924,19 +6457,21 @@ ${endPrefix}`
|
|
|
5924
6457
|
hooks: this.composeHooks(),
|
|
5925
6458
|
promptConfig: this.promptConfig,
|
|
5926
6459
|
initialMessages: this.initialMessages,
|
|
5927
|
-
|
|
6460
|
+
requestHumanInput: this.requestHumanInput,
|
|
5928
6461
|
gadgetStartPrefix: this.gadgetStartPrefix,
|
|
5929
6462
|
gadgetEndPrefix: this.gadgetEndPrefix,
|
|
5930
6463
|
gadgetArgPrefix: this.gadgetArgPrefix,
|
|
5931
6464
|
textOnlyHandler: this.textOnlyHandler,
|
|
5932
6465
|
textWithGadgetsHandler: this.textWithGadgetsHandler,
|
|
5933
6466
|
stopOnGadgetError: this.stopOnGadgetError,
|
|
5934
|
-
|
|
6467
|
+
canRecoverFromGadgetError: this.canRecoverFromGadgetError,
|
|
5935
6468
|
defaultGadgetTimeoutMs: this.defaultGadgetTimeoutMs,
|
|
5936
6469
|
gadgetOutputLimit: this.gadgetOutputLimit,
|
|
5937
6470
|
gadgetOutputLimitPercent: this.gadgetOutputLimitPercent,
|
|
5938
6471
|
compactionConfig: this.compactionConfig,
|
|
5939
|
-
signal: this.signal
|
|
6472
|
+
signal: this.signal,
|
|
6473
|
+
subagentConfig: this.subagentConfig,
|
|
6474
|
+
onNestedEvent
|
|
5940
6475
|
};
|
|
5941
6476
|
return new Agent(AGENT_INTERNAL_KEY, options);
|
|
5942
6477
|
}
|
|
@@ -6242,9 +6777,9 @@ var init_base_provider = __esm({
|
|
|
6242
6777
|
*/
|
|
6243
6778
|
async *stream(options, descriptor, spec) {
|
|
6244
6779
|
const preparedMessages = this.prepareMessages(options.messages);
|
|
6245
|
-
const payload = this.
|
|
6780
|
+
const payload = this.buildApiRequest(options, descriptor, spec, preparedMessages);
|
|
6246
6781
|
const rawStream = await this.executeStreamRequest(payload, options.signal);
|
|
6247
|
-
yield* this.
|
|
6782
|
+
yield* this.normalizeProviderStream(rawStream);
|
|
6248
6783
|
}
|
|
6249
6784
|
/**
|
|
6250
6785
|
* Prepare messages for the request.
|
|
@@ -6344,11 +6879,11 @@ var init_anthropic = __esm({
|
|
|
6344
6879
|
"Anthropic does not support speech generation. Use OpenAI (TTS) or Google Gemini (TTS) instead."
|
|
6345
6880
|
);
|
|
6346
6881
|
}
|
|
6347
|
-
|
|
6882
|
+
buildApiRequest(options, descriptor, spec, messages) {
|
|
6348
6883
|
const systemMessages = messages.filter((message) => message.role === "system");
|
|
6349
6884
|
const system = systemMessages.length > 0 ? systemMessages.map((m, index) => ({
|
|
6350
6885
|
type: "text",
|
|
6351
|
-
text:
|
|
6886
|
+
text: extractMessageText(m.content),
|
|
6352
6887
|
// Add cache_control to the LAST system message block
|
|
6353
6888
|
...index === systemMessages.length - 1 ? { cache_control: { type: "ephemeral" } } : {}
|
|
6354
6889
|
})) : void 0;
|
|
@@ -6385,7 +6920,7 @@ var init_anthropic = __esm({
|
|
|
6385
6920
|
* Handles text, images (base64 only), and applies cache_control.
|
|
6386
6921
|
*/
|
|
6387
6922
|
convertToAnthropicContent(content, addCacheControl) {
|
|
6388
|
-
const parts =
|
|
6923
|
+
const parts = normalizeMessageContent(content);
|
|
6389
6924
|
return parts.map((part, index) => {
|
|
6390
6925
|
const isLastPart = index === parts.length - 1;
|
|
6391
6926
|
const cacheControl = addCacheControl && isLastPart ? { cache_control: { type: "ephemeral" } } : {};
|
|
@@ -6431,7 +6966,7 @@ var init_anthropic = __esm({
|
|
|
6431
6966
|
const stream2 = await client.messages.create(payload, signal ? { signal } : void 0);
|
|
6432
6967
|
return stream2;
|
|
6433
6968
|
}
|
|
6434
|
-
async *
|
|
6969
|
+
async *normalizeProviderStream(iterable) {
|
|
6435
6970
|
const stream2 = iterable;
|
|
6436
6971
|
let inputTokens = 0;
|
|
6437
6972
|
let cachedInputTokens = 0;
|
|
@@ -6508,7 +7043,7 @@ var init_anthropic = __esm({
|
|
|
6508
7043
|
async countTokens(messages, descriptor, _spec) {
|
|
6509
7044
|
const client = this.client;
|
|
6510
7045
|
const systemMessages = messages.filter((message) => message.role === "system");
|
|
6511
|
-
const system = systemMessages.length > 0 ? systemMessages.map((m) =>
|
|
7046
|
+
const system = systemMessages.length > 0 ? systemMessages.map((m) => extractMessageText(m.content)).join("\n\n") : void 0;
|
|
6512
7047
|
const conversation = messages.filter(
|
|
6513
7048
|
(message) => message.role !== "system"
|
|
6514
7049
|
).map((message) => ({
|
|
@@ -6530,7 +7065,7 @@ var init_anthropic = __esm({
|
|
|
6530
7065
|
let totalChars = 0;
|
|
6531
7066
|
let imageCount = 0;
|
|
6532
7067
|
for (const msg of messages) {
|
|
6533
|
-
const parts =
|
|
7068
|
+
const parts = normalizeMessageContent(msg.content);
|
|
6534
7069
|
for (const part of parts) {
|
|
6535
7070
|
if (part.type === "text") {
|
|
6536
7071
|
totalChars += part.text.length;
|
|
@@ -7221,7 +7756,7 @@ var init_gemini = __esm({
|
|
|
7221
7756
|
format: spec?.defaultFormat ?? "wav"
|
|
7222
7757
|
};
|
|
7223
7758
|
}
|
|
7224
|
-
|
|
7759
|
+
buildApiRequest(options, descriptor, _spec, messages) {
|
|
7225
7760
|
const contents = this.convertMessagesToContents(messages);
|
|
7226
7761
|
const generationConfig = this.buildGenerationConfig(options);
|
|
7227
7762
|
const config = {
|
|
@@ -7273,7 +7808,7 @@ var init_gemini = __esm({
|
|
|
7273
7808
|
if (message.role === "system") {
|
|
7274
7809
|
expandedMessages.push({
|
|
7275
7810
|
role: "user",
|
|
7276
|
-
content:
|
|
7811
|
+
content: extractMessageText(message.content)
|
|
7277
7812
|
});
|
|
7278
7813
|
expandedMessages.push({
|
|
7279
7814
|
role: "assistant",
|
|
@@ -7323,7 +7858,7 @@ var init_gemini = __esm({
|
|
|
7323
7858
|
* Handles text, images, and audio (Gemini supports all three).
|
|
7324
7859
|
*/
|
|
7325
7860
|
convertToGeminiParts(content) {
|
|
7326
|
-
const parts =
|
|
7861
|
+
const parts = normalizeMessageContent(content);
|
|
7327
7862
|
return parts.map((part) => {
|
|
7328
7863
|
if (part.type === "text") {
|
|
7329
7864
|
return { text: part.text };
|
|
@@ -7368,10 +7903,10 @@ var init_gemini = __esm({
|
|
|
7368
7903
|
}
|
|
7369
7904
|
return Object.keys(config).length > 0 ? config : null;
|
|
7370
7905
|
}
|
|
7371
|
-
async *
|
|
7906
|
+
async *normalizeProviderStream(iterable) {
|
|
7372
7907
|
const stream2 = iterable;
|
|
7373
7908
|
for await (const chunk of stream2) {
|
|
7374
|
-
const text3 = this.
|
|
7909
|
+
const text3 = this.extractMessageText(chunk);
|
|
7375
7910
|
if (text3) {
|
|
7376
7911
|
yield { text: text3, rawEvent: chunk };
|
|
7377
7912
|
}
|
|
@@ -7382,7 +7917,7 @@ var init_gemini = __esm({
|
|
|
7382
7917
|
}
|
|
7383
7918
|
}
|
|
7384
7919
|
}
|
|
7385
|
-
|
|
7920
|
+
extractMessageText(chunk) {
|
|
7386
7921
|
if (!chunk?.candidates) {
|
|
7387
7922
|
return "";
|
|
7388
7923
|
}
|
|
@@ -7447,7 +7982,7 @@ var init_gemini = __esm({
|
|
|
7447
7982
|
let totalChars = 0;
|
|
7448
7983
|
let mediaCount = 0;
|
|
7449
7984
|
for (const msg of messages) {
|
|
7450
|
-
const parts =
|
|
7985
|
+
const parts = normalizeMessageContent(msg.content);
|
|
7451
7986
|
for (const part of parts) {
|
|
7452
7987
|
if (part.type === "text") {
|
|
7453
7988
|
totalChars += part.text.length;
|
|
@@ -8193,7 +8728,7 @@ var init_openai = __esm({
|
|
|
8193
8728
|
format
|
|
8194
8729
|
};
|
|
8195
8730
|
}
|
|
8196
|
-
|
|
8731
|
+
buildApiRequest(options, descriptor, spec, messages) {
|
|
8197
8732
|
const { maxTokens, temperature, topP, stopSequences, extra } = options;
|
|
8198
8733
|
const supportsTemperature = spec?.metadata?.supportsTemperature !== false;
|
|
8199
8734
|
const shouldIncludeTemperature = typeof temperature === "number" && supportsTemperature;
|
|
@@ -8228,7 +8763,7 @@ var init_openai = __esm({
|
|
|
8228
8763
|
...message.name ? { name: message.name } : {}
|
|
8229
8764
|
};
|
|
8230
8765
|
}
|
|
8231
|
-
const textContent = typeof message.content === "string" ? message.content :
|
|
8766
|
+
const textContent = typeof message.content === "string" ? message.content : extractMessageText(message.content);
|
|
8232
8767
|
if (role === "system") {
|
|
8233
8768
|
return {
|
|
8234
8769
|
role: "system",
|
|
@@ -8288,7 +8823,7 @@ var init_openai = __esm({
|
|
|
8288
8823
|
const stream2 = await client.chat.completions.create(payload, signal ? { signal } : void 0);
|
|
8289
8824
|
return stream2;
|
|
8290
8825
|
}
|
|
8291
|
-
async *
|
|
8826
|
+
async *normalizeProviderStream(iterable) {
|
|
8292
8827
|
const stream2 = iterable;
|
|
8293
8828
|
for await (const chunk of stream2) {
|
|
8294
8829
|
const text3 = chunk.choices.map((choice) => choice.delta?.content ?? "").join("");
|
|
@@ -8346,9 +8881,9 @@ var init_openai = __esm({
|
|
|
8346
8881
|
tokenCount += OPENAI_MESSAGE_OVERHEAD_TOKENS;
|
|
8347
8882
|
const roleText = ROLE_MAP[message.role];
|
|
8348
8883
|
tokenCount += encoding.encode(roleText).length;
|
|
8349
|
-
const textContent =
|
|
8884
|
+
const textContent = extractMessageText(message.content);
|
|
8350
8885
|
tokenCount += encoding.encode(textContent).length;
|
|
8351
|
-
const parts =
|
|
8886
|
+
const parts = normalizeMessageContent(message.content);
|
|
8352
8887
|
for (const part of parts) {
|
|
8353
8888
|
if (part.type === "image") {
|
|
8354
8889
|
imageCount++;
|
|
@@ -8373,7 +8908,7 @@ var init_openai = __esm({
|
|
|
8373
8908
|
let totalChars = 0;
|
|
8374
8909
|
let imageCount = 0;
|
|
8375
8910
|
for (const msg of messages) {
|
|
8376
|
-
const parts =
|
|
8911
|
+
const parts = normalizeMessageContent(msg.content);
|
|
8377
8912
|
for (const part of parts) {
|
|
8378
8913
|
if (part.type === "text") {
|
|
8379
8914
|
totalChars += part.text.length;
|
|
@@ -10106,7 +10641,7 @@ var MockBuilder = class {
|
|
|
10106
10641
|
whenMessageContains(text3) {
|
|
10107
10642
|
this.matchers.push(
|
|
10108
10643
|
(ctx) => ctx.messages.some(
|
|
10109
|
-
(msg) =>
|
|
10644
|
+
(msg) => extractMessageText(msg.content).toLowerCase().includes(text3.toLowerCase())
|
|
10110
10645
|
)
|
|
10111
10646
|
);
|
|
10112
10647
|
return this;
|
|
@@ -10121,7 +10656,7 @@ var MockBuilder = class {
|
|
|
10121
10656
|
this.matchers.push((ctx) => {
|
|
10122
10657
|
const lastMsg = ctx.messages[ctx.messages.length - 1];
|
|
10123
10658
|
if (!lastMsg) return false;
|
|
10124
|
-
return
|
|
10659
|
+
return extractMessageText(lastMsg.content).toLowerCase().includes(text3.toLowerCase());
|
|
10125
10660
|
});
|
|
10126
10661
|
return this;
|
|
10127
10662
|
}
|
|
@@ -10132,7 +10667,7 @@ var MockBuilder = class {
|
|
|
10132
10667
|
* mockLLM().whenMessageMatches(/calculate \d+/)
|
|
10133
10668
|
*/
|
|
10134
10669
|
whenMessageMatches(regex) {
|
|
10135
|
-
this.matchers.push((ctx) => ctx.messages.some((msg) => regex.test(
|
|
10670
|
+
this.matchers.push((ctx) => ctx.messages.some((msg) => regex.test(extractMessageText(msg.content))));
|
|
10136
10671
|
return this;
|
|
10137
10672
|
}
|
|
10138
10673
|
/**
|
|
@@ -10144,7 +10679,7 @@ var MockBuilder = class {
|
|
|
10144
10679
|
whenRoleContains(role, text3) {
|
|
10145
10680
|
this.matchers.push(
|
|
10146
10681
|
(ctx) => ctx.messages.some(
|
|
10147
|
-
(msg) => msg.role === role &&
|
|
10682
|
+
(msg) => msg.role === role && extractMessageText(msg.content).toLowerCase().includes(text3.toLowerCase())
|
|
10148
10683
|
)
|
|
10149
10684
|
);
|
|
10150
10685
|
return this;
|
|
@@ -10566,7 +11101,7 @@ var MockConversationManager = class {
|
|
|
10566
11101
|
this.history.push(msg);
|
|
10567
11102
|
this.addedMessages.push(msg);
|
|
10568
11103
|
}
|
|
10569
|
-
|
|
11104
|
+
addGadgetCallResult(gadgetName, parameters, result) {
|
|
10570
11105
|
const assistantMsg = {
|
|
10571
11106
|
role: "assistant",
|
|
10572
11107
|
content: `!!!GADGET_START:${gadgetName}
|
|
@@ -10676,7 +11211,7 @@ function createMockConversationManager(turnCount, baseMessages = []) {
|
|
|
10676
11211
|
|
|
10677
11212
|
// src/testing/mock-gadget.ts
|
|
10678
11213
|
init_gadget();
|
|
10679
|
-
var MockGadgetImpl = class extends
|
|
11214
|
+
var MockGadgetImpl = class extends AbstractGadget {
|
|
10680
11215
|
name;
|
|
10681
11216
|
description;
|
|
10682
11217
|
parameterSchema;
|