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/cli.cjs
CHANGED
|
@@ -317,7 +317,7 @@ Example fixes:
|
|
|
317
317
|
);
|
|
318
318
|
}
|
|
319
319
|
}
|
|
320
|
-
function findUnknownTypes(schema,
|
|
320
|
+
function findUnknownTypes(schema, path6 = []) {
|
|
321
321
|
const issues = [];
|
|
322
322
|
if (!schema || typeof schema !== "object") {
|
|
323
323
|
return issues;
|
|
@@ -329,7 +329,7 @@ function findUnknownTypes(schema, path5 = []) {
|
|
|
329
329
|
}
|
|
330
330
|
if (schema.properties) {
|
|
331
331
|
for (const [propName, propSchema] of Object.entries(schema.properties)) {
|
|
332
|
-
const propPath = [...
|
|
332
|
+
const propPath = [...path6, propName];
|
|
333
333
|
if (hasNoType(propSchema)) {
|
|
334
334
|
issues.push(propPath.join(".") || propName);
|
|
335
335
|
}
|
|
@@ -337,7 +337,7 @@ function findUnknownTypes(schema, path5 = []) {
|
|
|
337
337
|
}
|
|
338
338
|
}
|
|
339
339
|
if (schema.items) {
|
|
340
|
-
const itemPath = [...
|
|
340
|
+
const itemPath = [...path6, "[]"];
|
|
341
341
|
if (hasNoType(schema.items)) {
|
|
342
342
|
issues.push(itemPath.join("."));
|
|
343
343
|
}
|
|
@@ -345,17 +345,17 @@ function findUnknownTypes(schema, path5 = []) {
|
|
|
345
345
|
}
|
|
346
346
|
if (schema.anyOf) {
|
|
347
347
|
schema.anyOf.forEach((subSchema, index) => {
|
|
348
|
-
issues.push(...findUnknownTypes(subSchema, [...
|
|
348
|
+
issues.push(...findUnknownTypes(subSchema, [...path6, `anyOf[${index}]`]));
|
|
349
349
|
});
|
|
350
350
|
}
|
|
351
351
|
if (schema.oneOf) {
|
|
352
352
|
schema.oneOf.forEach((subSchema, index) => {
|
|
353
|
-
issues.push(...findUnknownTypes(subSchema, [...
|
|
353
|
+
issues.push(...findUnknownTypes(subSchema, [...path6, `oneOf[${index}]`]));
|
|
354
354
|
});
|
|
355
355
|
}
|
|
356
356
|
if (schema.allOf) {
|
|
357
357
|
schema.allOf.forEach((subSchema, index) => {
|
|
358
|
-
issues.push(...findUnknownTypes(subSchema, [...
|
|
358
|
+
issues.push(...findUnknownTypes(subSchema, [...path6, `allOf[${index}]`]));
|
|
359
359
|
});
|
|
360
360
|
}
|
|
361
361
|
return issues;
|
|
@@ -530,13 +530,13 @@ var init_prompt_config = __esm({
|
|
|
530
530
|
});
|
|
531
531
|
|
|
532
532
|
// src/core/messages.ts
|
|
533
|
-
function
|
|
533
|
+
function normalizeMessageContent(content) {
|
|
534
534
|
if (typeof content === "string") {
|
|
535
535
|
return [{ type: "text", text: content }];
|
|
536
536
|
}
|
|
537
537
|
return content;
|
|
538
538
|
}
|
|
539
|
-
function
|
|
539
|
+
function extractMessageText(content) {
|
|
540
540
|
if (typeof content === "string") {
|
|
541
541
|
return content;
|
|
542
542
|
}
|
|
@@ -896,7 +896,17 @@ Produces: { "items": ["first", "second"] }`);
|
|
|
896
896
|
this.messages.push({ role: "user", content: parts });
|
|
897
897
|
return this;
|
|
898
898
|
}
|
|
899
|
-
|
|
899
|
+
/**
|
|
900
|
+
* Record a gadget execution result in the message history.
|
|
901
|
+
* Creates an assistant message with the gadget invocation and a user message with the result.
|
|
902
|
+
*
|
|
903
|
+
* @param gadget - Name of the gadget that was executed
|
|
904
|
+
* @param parameters - Parameters that were passed to the gadget
|
|
905
|
+
* @param result - Text result from the gadget execution
|
|
906
|
+
* @param media - Optional media outputs from the gadget
|
|
907
|
+
* @param mediaIds - Optional IDs for the media outputs
|
|
908
|
+
*/
|
|
909
|
+
addGadgetCallResult(gadget, parameters, result, media, mediaIds) {
|
|
900
910
|
const paramStr = this.formatBlockParameters(parameters, "");
|
|
901
911
|
this.messages.push({
|
|
902
912
|
role: "assistant",
|
|
@@ -1164,21 +1174,21 @@ var init_media_store = __esm({
|
|
|
1164
1174
|
});
|
|
1165
1175
|
|
|
1166
1176
|
// src/gadgets/exceptions.ts
|
|
1167
|
-
var
|
|
1177
|
+
var TaskCompletionSignal, HumanInputRequiredException, TimeoutException, AbortException;
|
|
1168
1178
|
var init_exceptions = __esm({
|
|
1169
1179
|
"src/gadgets/exceptions.ts"() {
|
|
1170
1180
|
"use strict";
|
|
1171
|
-
|
|
1181
|
+
TaskCompletionSignal = class extends Error {
|
|
1172
1182
|
constructor(message) {
|
|
1173
1183
|
super(message ?? "Agent loop terminated by gadget");
|
|
1174
|
-
this.name = "
|
|
1184
|
+
this.name = "TaskCompletionSignal";
|
|
1175
1185
|
}
|
|
1176
1186
|
};
|
|
1177
|
-
|
|
1187
|
+
HumanInputRequiredException = class extends Error {
|
|
1178
1188
|
question;
|
|
1179
1189
|
constructor(question) {
|
|
1180
1190
|
super(`Human input required: ${question}`);
|
|
1181
|
-
this.name = "
|
|
1191
|
+
this.name = "HumanInputRequiredException";
|
|
1182
1192
|
this.question = question;
|
|
1183
1193
|
}
|
|
1184
1194
|
};
|
|
@@ -1192,10 +1202,10 @@ var init_exceptions = __esm({
|
|
|
1192
1202
|
this.timeoutMs = timeoutMs;
|
|
1193
1203
|
}
|
|
1194
1204
|
};
|
|
1195
|
-
|
|
1205
|
+
AbortException = class extends Error {
|
|
1196
1206
|
constructor(message) {
|
|
1197
1207
|
super(message || "Gadget execution was aborted");
|
|
1198
|
-
this.name = "
|
|
1208
|
+
this.name = "AbortException";
|
|
1199
1209
|
}
|
|
1200
1210
|
};
|
|
1201
1211
|
}
|
|
@@ -1294,29 +1304,29 @@ function schemaToJSONSchema(schema, options) {
|
|
|
1294
1304
|
}
|
|
1295
1305
|
function detectDescriptionMismatch(schema, jsonSchema) {
|
|
1296
1306
|
const mismatches = [];
|
|
1297
|
-
function checkSchema(zodSchema, json,
|
|
1307
|
+
function checkSchema(zodSchema, json, path6) {
|
|
1298
1308
|
if (!zodSchema || typeof zodSchema !== "object") return;
|
|
1299
1309
|
const def = zodSchema._def;
|
|
1300
1310
|
const jsonObj = json;
|
|
1301
1311
|
if (def?.description && !jsonObj?.description) {
|
|
1302
|
-
mismatches.push(
|
|
1312
|
+
mismatches.push(path6 || "root");
|
|
1303
1313
|
}
|
|
1304
1314
|
if (def?.typeName === "ZodObject" && def?.shape) {
|
|
1305
1315
|
const shape = typeof def.shape === "function" ? def.shape() : def.shape;
|
|
1306
1316
|
for (const [key, fieldSchema] of Object.entries(shape)) {
|
|
1307
1317
|
const properties = jsonObj?.properties;
|
|
1308
1318
|
const jsonProp = properties?.[key];
|
|
1309
|
-
checkSchema(fieldSchema, jsonProp,
|
|
1319
|
+
checkSchema(fieldSchema, jsonProp, path6 ? `${path6}.${key}` : key);
|
|
1310
1320
|
}
|
|
1311
1321
|
}
|
|
1312
1322
|
if (def?.typeName === "ZodArray" && def?.type) {
|
|
1313
|
-
checkSchema(def.type, jsonObj?.items,
|
|
1323
|
+
checkSchema(def.type, jsonObj?.items, path6 ? `${path6}[]` : "[]");
|
|
1314
1324
|
}
|
|
1315
1325
|
if ((def?.typeName === "ZodOptional" || def?.typeName === "ZodNullable") && def?.innerType) {
|
|
1316
|
-
checkSchema(def.innerType, json,
|
|
1326
|
+
checkSchema(def.innerType, json, path6);
|
|
1317
1327
|
}
|
|
1318
1328
|
if (def?.typeName === "ZodDefault" && def?.innerType) {
|
|
1319
|
-
checkSchema(def.innerType, json,
|
|
1329
|
+
checkSchema(def.innerType, json, path6);
|
|
1320
1330
|
}
|
|
1321
1331
|
}
|
|
1322
1332
|
checkSchema(schema, jsonSchema, "");
|
|
@@ -1364,7 +1374,7 @@ var init_schema_to_json = __esm({
|
|
|
1364
1374
|
});
|
|
1365
1375
|
|
|
1366
1376
|
// src/gadgets/gadget.ts
|
|
1367
|
-
function
|
|
1377
|
+
function formatParamsForBlockExample(params, prefix = "", argPrefix = GADGET_ARG_PREFIX) {
|
|
1368
1378
|
const lines = [];
|
|
1369
1379
|
for (const [key, value] of Object.entries(params)) {
|
|
1370
1380
|
const fullPath = prefix ? `${prefix}/${key}` : key;
|
|
@@ -1372,14 +1382,14 @@ function formatParamsAsBlock(params, prefix = "", argPrefix = GADGET_ARG_PREFIX)
|
|
|
1372
1382
|
value.forEach((item, index) => {
|
|
1373
1383
|
const itemPath = `${fullPath}/${index}`;
|
|
1374
1384
|
if (typeof item === "object" && item !== null) {
|
|
1375
|
-
lines.push(
|
|
1385
|
+
lines.push(formatParamsForBlockExample(item, itemPath, argPrefix));
|
|
1376
1386
|
} else {
|
|
1377
1387
|
lines.push(`${argPrefix}${itemPath}`);
|
|
1378
1388
|
lines.push(String(item));
|
|
1379
1389
|
}
|
|
1380
1390
|
});
|
|
1381
1391
|
} else if (typeof value === "object" && value !== null) {
|
|
1382
|
-
lines.push(
|
|
1392
|
+
lines.push(formatParamsForBlockExample(value, fullPath, argPrefix));
|
|
1383
1393
|
} else {
|
|
1384
1394
|
lines.push(`${argPrefix}${fullPath}`);
|
|
1385
1395
|
lines.push(String(value));
|
|
@@ -1468,7 +1478,7 @@ function formatSchemaAsPlainText(schema, indent = "", atRoot = true) {
|
|
|
1468
1478
|
}
|
|
1469
1479
|
return lines.join("\n");
|
|
1470
1480
|
}
|
|
1471
|
-
var
|
|
1481
|
+
var AbstractGadget;
|
|
1472
1482
|
var init_gadget = __esm({
|
|
1473
1483
|
"src/gadgets/gadget.ts"() {
|
|
1474
1484
|
"use strict";
|
|
@@ -1476,7 +1486,7 @@ var init_gadget = __esm({
|
|
|
1476
1486
|
init_exceptions();
|
|
1477
1487
|
init_schema_to_json();
|
|
1478
1488
|
init_schema_validator();
|
|
1479
|
-
|
|
1489
|
+
AbstractGadget = class {
|
|
1480
1490
|
/**
|
|
1481
1491
|
* The name of the gadget. Used for identification when LLM calls it.
|
|
1482
1492
|
* If not provided, defaults to the class name.
|
|
@@ -1504,14 +1514,14 @@ var init_gadget = __esm({
|
|
|
1504
1514
|
*/
|
|
1505
1515
|
examples;
|
|
1506
1516
|
/**
|
|
1507
|
-
* Throws an
|
|
1517
|
+
* Throws an AbortException if the execution has been aborted.
|
|
1508
1518
|
*
|
|
1509
1519
|
* Call this at key checkpoints in long-running gadgets to allow early exit
|
|
1510
1520
|
* when the gadget has been cancelled (e.g., due to timeout). This enables
|
|
1511
1521
|
* resource cleanup and prevents unnecessary work after cancellation.
|
|
1512
1522
|
*
|
|
1513
1523
|
* @param ctx - The execution context containing the abort signal
|
|
1514
|
-
* @throws
|
|
1524
|
+
* @throws AbortException if ctx.signal.aborted is true
|
|
1515
1525
|
*
|
|
1516
1526
|
* @example
|
|
1517
1527
|
* ```typescript
|
|
@@ -1536,7 +1546,7 @@ var init_gadget = __esm({
|
|
|
1536
1546
|
*/
|
|
1537
1547
|
throwIfAborted(ctx) {
|
|
1538
1548
|
if (ctx?.signal?.aborted) {
|
|
1539
|
-
throw new
|
|
1549
|
+
throw new AbortException();
|
|
1540
1550
|
}
|
|
1541
1551
|
}
|
|
1542
1552
|
/**
|
|
@@ -1678,7 +1688,7 @@ var init_gadget = __esm({
|
|
|
1678
1688
|
}
|
|
1679
1689
|
parts.push(`${effectiveStartPrefix}${gadgetName}`);
|
|
1680
1690
|
parts.push(
|
|
1681
|
-
|
|
1691
|
+
formatParamsForBlockExample(example.params, "", effectiveArgPrefix)
|
|
1682
1692
|
);
|
|
1683
1693
|
parts.push(effectiveEndPrefix);
|
|
1684
1694
|
if (example.output !== void 0) {
|
|
@@ -1696,7 +1706,7 @@ var init_gadget = __esm({
|
|
|
1696
1706
|
|
|
1697
1707
|
// src/gadgets/create-gadget.ts
|
|
1698
1708
|
function createGadget(config) {
|
|
1699
|
-
class DynamicGadget extends
|
|
1709
|
+
class DynamicGadget extends AbstractGadget {
|
|
1700
1710
|
name = config.name;
|
|
1701
1711
|
description = config.description;
|
|
1702
1712
|
parameterSchema = config.schema;
|
|
@@ -2334,8 +2344,8 @@ var init_conversation_manager = __esm({
|
|
|
2334
2344
|
addAssistantMessage(content) {
|
|
2335
2345
|
this.historyBuilder.addAssistant(content);
|
|
2336
2346
|
}
|
|
2337
|
-
|
|
2338
|
-
this.historyBuilder.
|
|
2347
|
+
addGadgetCallResult(gadgetName, parameters, result, media, mediaIds) {
|
|
2348
|
+
this.historyBuilder.addGadgetCallResult(gadgetName, parameters, result, media, mediaIds);
|
|
2339
2349
|
}
|
|
2340
2350
|
getMessages() {
|
|
2341
2351
|
return [...this.baseMessages, ...this.initialMessages, ...this.historyBuilder.build()];
|
|
@@ -2355,7 +2365,7 @@ var init_conversation_manager = __esm({
|
|
|
2355
2365
|
if (msg.role === "user") {
|
|
2356
2366
|
this.historyBuilder.addUser(msg.content);
|
|
2357
2367
|
} else if (msg.role === "assistant") {
|
|
2358
|
-
this.historyBuilder.addAssistant(
|
|
2368
|
+
this.historyBuilder.addAssistant(extractMessageText(msg.content));
|
|
2359
2369
|
}
|
|
2360
2370
|
}
|
|
2361
2371
|
}
|
|
@@ -3205,12 +3215,12 @@ var init_cost_reporting_client = __esm({
|
|
|
3205
3215
|
});
|
|
3206
3216
|
|
|
3207
3217
|
// src/gadgets/error-formatter.ts
|
|
3208
|
-
var
|
|
3218
|
+
var GadgetExecutionErrorFormatter;
|
|
3209
3219
|
var init_error_formatter = __esm({
|
|
3210
3220
|
"src/gadgets/error-formatter.ts"() {
|
|
3211
3221
|
"use strict";
|
|
3212
3222
|
init_constants();
|
|
3213
|
-
|
|
3223
|
+
GadgetExecutionErrorFormatter = class {
|
|
3214
3224
|
argPrefix;
|
|
3215
3225
|
startPrefix;
|
|
3216
3226
|
endPrefix;
|
|
@@ -3231,8 +3241,8 @@ var init_error_formatter = __esm({
|
|
|
3231
3241
|
const parts = [];
|
|
3232
3242
|
parts.push(`Error: Invalid parameters for '${gadgetName}':`);
|
|
3233
3243
|
for (const issue of zodError.issues) {
|
|
3234
|
-
const
|
|
3235
|
-
parts.push(` - ${
|
|
3244
|
+
const path6 = issue.path.join(".") || "root";
|
|
3245
|
+
parts.push(` - ${path6}: ${issue.message}`);
|
|
3236
3246
|
}
|
|
3237
3247
|
parts.push("");
|
|
3238
3248
|
parts.push("Gadget Usage:");
|
|
@@ -3296,16 +3306,16 @@ function stripMarkdownFences(content) {
|
|
|
3296
3306
|
cleaned = cleaned.replace(closingFence, "");
|
|
3297
3307
|
return cleaned.trim();
|
|
3298
3308
|
}
|
|
3299
|
-
var globalInvocationCounter,
|
|
3309
|
+
var globalInvocationCounter, GadgetCallParser;
|
|
3300
3310
|
var init_parser = __esm({
|
|
3301
3311
|
"src/gadgets/parser.ts"() {
|
|
3302
3312
|
"use strict";
|
|
3303
3313
|
init_constants();
|
|
3304
3314
|
init_block_params();
|
|
3305
3315
|
globalInvocationCounter = 0;
|
|
3306
|
-
|
|
3316
|
+
GadgetCallParser = class {
|
|
3307
3317
|
buffer = "";
|
|
3308
|
-
|
|
3318
|
+
lastEmittedTextOffset = 0;
|
|
3309
3319
|
startPrefix;
|
|
3310
3320
|
endPrefix;
|
|
3311
3321
|
argPrefix;
|
|
@@ -3314,16 +3324,20 @@ var init_parser = __esm({
|
|
|
3314
3324
|
this.endPrefix = options.endPrefix ?? GADGET_END_PREFIX;
|
|
3315
3325
|
this.argPrefix = options.argPrefix ?? GADGET_ARG_PREFIX;
|
|
3316
3326
|
}
|
|
3317
|
-
|
|
3318
|
-
|
|
3327
|
+
/**
|
|
3328
|
+
* Extract and consume text up to the given index.
|
|
3329
|
+
* Returns undefined if no meaningful text to emit.
|
|
3330
|
+
*/
|
|
3331
|
+
extractTextSegment(index) {
|
|
3332
|
+
if (index <= this.lastEmittedTextOffset) {
|
|
3319
3333
|
return void 0;
|
|
3320
3334
|
}
|
|
3321
|
-
const segment = this.buffer.slice(this.
|
|
3322
|
-
this.
|
|
3335
|
+
const segment = this.buffer.slice(this.lastEmittedTextOffset, index);
|
|
3336
|
+
this.lastEmittedTextOffset = index;
|
|
3323
3337
|
return segment.trim().length > 0 ? segment : void 0;
|
|
3324
3338
|
}
|
|
3325
3339
|
/**
|
|
3326
|
-
* Parse gadget
|
|
3340
|
+
* Parse gadget invocation metadata from the header line.
|
|
3327
3341
|
*
|
|
3328
3342
|
* Supported formats:
|
|
3329
3343
|
* - `GadgetName` - Auto-generate ID, no dependencies
|
|
@@ -3332,24 +3346,24 @@ var init_parser = __esm({
|
|
|
3332
3346
|
*
|
|
3333
3347
|
* Dependencies must be comma-separated invocation IDs.
|
|
3334
3348
|
*/
|
|
3335
|
-
|
|
3336
|
-
const parts =
|
|
3349
|
+
parseInvocationMetadata(headerLine) {
|
|
3350
|
+
const parts = headerLine.split(":");
|
|
3337
3351
|
if (parts.length === 1) {
|
|
3338
3352
|
return {
|
|
3339
|
-
|
|
3353
|
+
gadgetName: parts[0],
|
|
3340
3354
|
invocationId: `gadget_${++globalInvocationCounter}`,
|
|
3341
3355
|
dependencies: []
|
|
3342
3356
|
};
|
|
3343
3357
|
} else if (parts.length === 2) {
|
|
3344
3358
|
return {
|
|
3345
|
-
|
|
3359
|
+
gadgetName: parts[0],
|
|
3346
3360
|
invocationId: parts[1].trim(),
|
|
3347
3361
|
dependencies: []
|
|
3348
3362
|
};
|
|
3349
3363
|
} else {
|
|
3350
3364
|
const deps = parts[2].split(",").map((d) => d.trim()).filter((d) => d.length > 0);
|
|
3351
3365
|
return {
|
|
3352
|
-
|
|
3366
|
+
gadgetName: parts[0],
|
|
3353
3367
|
invocationId: parts[1].trim(),
|
|
3354
3368
|
dependencies: deps
|
|
3355
3369
|
};
|
|
@@ -3381,19 +3395,15 @@ var init_parser = __esm({
|
|
|
3381
3395
|
while (true) {
|
|
3382
3396
|
const partStartIndex = this.buffer.indexOf(this.startPrefix, startIndex);
|
|
3383
3397
|
if (partStartIndex === -1) break;
|
|
3384
|
-
const textBefore = this.
|
|
3398
|
+
const textBefore = this.extractTextSegment(partStartIndex);
|
|
3385
3399
|
if (textBefore !== void 0) {
|
|
3386
3400
|
yield { type: "text", content: textBefore };
|
|
3387
3401
|
}
|
|
3388
3402
|
const metadataStartIndex = partStartIndex + this.startPrefix.length;
|
|
3389
3403
|
const metadataEndIndex = this.buffer.indexOf("\n", metadataStartIndex);
|
|
3390
3404
|
if (metadataEndIndex === -1) break;
|
|
3391
|
-
const
|
|
3392
|
-
const {
|
|
3393
|
-
actualName: actualGadgetName,
|
|
3394
|
-
invocationId,
|
|
3395
|
-
dependencies
|
|
3396
|
-
} = this.parseGadgetName(gadgetName);
|
|
3405
|
+
const headerLine = this.buffer.substring(metadataStartIndex, metadataEndIndex).trim();
|
|
3406
|
+
const { gadgetName, invocationId, dependencies } = this.parseInvocationMetadata(headerLine);
|
|
3397
3407
|
const contentStartIndex = metadataEndIndex + 1;
|
|
3398
3408
|
let partEndIndex;
|
|
3399
3409
|
let endMarkerLength = 0;
|
|
@@ -3413,7 +3423,7 @@ var init_parser = __esm({
|
|
|
3413
3423
|
yield {
|
|
3414
3424
|
type: "gadget_call",
|
|
3415
3425
|
call: {
|
|
3416
|
-
gadgetName
|
|
3426
|
+
gadgetName,
|
|
3417
3427
|
invocationId,
|
|
3418
3428
|
parametersRaw,
|
|
3419
3429
|
parameters,
|
|
@@ -3422,37 +3432,33 @@ var init_parser = __esm({
|
|
|
3422
3432
|
}
|
|
3423
3433
|
};
|
|
3424
3434
|
startIndex = partEndIndex + endMarkerLength;
|
|
3425
|
-
this.
|
|
3435
|
+
this.lastEmittedTextOffset = startIndex;
|
|
3426
3436
|
}
|
|
3427
3437
|
if (startIndex > 0) {
|
|
3428
3438
|
this.buffer = this.buffer.substring(startIndex);
|
|
3429
|
-
this.
|
|
3439
|
+
this.lastEmittedTextOffset = 0;
|
|
3430
3440
|
}
|
|
3431
3441
|
}
|
|
3432
3442
|
// Finalize parsing and return remaining text or incomplete gadgets
|
|
3433
3443
|
*finalize() {
|
|
3434
|
-
const startIndex = this.buffer.indexOf(this.startPrefix, this.
|
|
3444
|
+
const startIndex = this.buffer.indexOf(this.startPrefix, this.lastEmittedTextOffset);
|
|
3435
3445
|
if (startIndex !== -1) {
|
|
3436
|
-
const textBefore = this.
|
|
3446
|
+
const textBefore = this.extractTextSegment(startIndex);
|
|
3437
3447
|
if (textBefore !== void 0) {
|
|
3438
3448
|
yield { type: "text", content: textBefore };
|
|
3439
3449
|
}
|
|
3440
3450
|
const metadataStartIndex = startIndex + this.startPrefix.length;
|
|
3441
3451
|
const metadataEndIndex = this.buffer.indexOf("\n", metadataStartIndex);
|
|
3442
3452
|
if (metadataEndIndex !== -1) {
|
|
3443
|
-
const
|
|
3444
|
-
const {
|
|
3445
|
-
actualName: actualGadgetName,
|
|
3446
|
-
invocationId,
|
|
3447
|
-
dependencies
|
|
3448
|
-
} = this.parseGadgetName(gadgetName);
|
|
3453
|
+
const headerLine = this.buffer.substring(metadataStartIndex, metadataEndIndex).trim();
|
|
3454
|
+
const { gadgetName, invocationId, dependencies } = this.parseInvocationMetadata(headerLine);
|
|
3449
3455
|
const contentStartIndex = metadataEndIndex + 1;
|
|
3450
3456
|
const parametersRaw = this.buffer.substring(contentStartIndex).trim();
|
|
3451
3457
|
const { parameters, parseError } = this.parseParameters(parametersRaw);
|
|
3452
3458
|
yield {
|
|
3453
3459
|
type: "gadget_call",
|
|
3454
3460
|
call: {
|
|
3455
|
-
gadgetName
|
|
3461
|
+
gadgetName,
|
|
3456
3462
|
invocationId,
|
|
3457
3463
|
parametersRaw,
|
|
3458
3464
|
parameters,
|
|
@@ -3463,7 +3469,7 @@ var init_parser = __esm({
|
|
|
3463
3469
|
return;
|
|
3464
3470
|
}
|
|
3465
3471
|
}
|
|
3466
|
-
const remainingText = this.
|
|
3472
|
+
const remainingText = this.extractTextSegment(this.buffer.length);
|
|
3467
3473
|
if (remainingText !== void 0) {
|
|
3468
3474
|
yield { type: "text", content: remainingText };
|
|
3469
3475
|
}
|
|
@@ -3471,7 +3477,7 @@ var init_parser = __esm({
|
|
|
3471
3477
|
// Reset parser state (note: global invocation counter is NOT reset to ensure unique IDs)
|
|
3472
3478
|
reset() {
|
|
3473
3479
|
this.buffer = "";
|
|
3474
|
-
this.
|
|
3480
|
+
this.lastEmittedTextOffset = 0;
|
|
3475
3481
|
}
|
|
3476
3482
|
};
|
|
3477
3483
|
}
|
|
@@ -3490,14 +3496,17 @@ var init_executor = __esm({
|
|
|
3490
3496
|
init_exceptions();
|
|
3491
3497
|
init_parser();
|
|
3492
3498
|
GadgetExecutor = class {
|
|
3493
|
-
constructor(registry,
|
|
3499
|
+
constructor(registry, requestHumanInput, logger, defaultGadgetTimeoutMs, errorFormatterOptions, client, mediaStore, agentConfig, subagentConfig, onNestedEvent) {
|
|
3494
3500
|
this.registry = registry;
|
|
3495
|
-
this.
|
|
3501
|
+
this.requestHumanInput = requestHumanInput;
|
|
3496
3502
|
this.defaultGadgetTimeoutMs = defaultGadgetTimeoutMs;
|
|
3497
3503
|
this.client = client;
|
|
3498
3504
|
this.mediaStore = mediaStore;
|
|
3505
|
+
this.agentConfig = agentConfig;
|
|
3506
|
+
this.subagentConfig = subagentConfig;
|
|
3507
|
+
this.onNestedEvent = onNestedEvent;
|
|
3499
3508
|
this.logger = logger ?? createLogger({ name: "llmist:executor" });
|
|
3500
|
-
this.errorFormatter = new
|
|
3509
|
+
this.errorFormatter = new GadgetExecutionErrorFormatter(errorFormatterOptions);
|
|
3501
3510
|
this.argPrefix = errorFormatterOptions?.argPrefix ?? GADGET_ARG_PREFIX;
|
|
3502
3511
|
}
|
|
3503
3512
|
logger;
|
|
@@ -3517,11 +3526,11 @@ var init_executor = __esm({
|
|
|
3517
3526
|
});
|
|
3518
3527
|
}
|
|
3519
3528
|
/**
|
|
3520
|
-
*
|
|
3529
|
+
* Unify gadget execute result to consistent internal format.
|
|
3521
3530
|
* Handles string returns (backwards compat), object returns with cost,
|
|
3522
3531
|
* and object returns with media.
|
|
3523
3532
|
*/
|
|
3524
|
-
|
|
3533
|
+
unifyExecuteResult(raw) {
|
|
3525
3534
|
if (typeof raw === "string") {
|
|
3526
3535
|
return { result: raw, cost: 0 };
|
|
3527
3536
|
}
|
|
@@ -3639,7 +3648,11 @@ var init_executor = __esm({
|
|
|
3639
3648
|
const ctx = {
|
|
3640
3649
|
reportCost,
|
|
3641
3650
|
llmist: this.client ? new CostReportingLLMistWrapper(this.client, reportCost) : void 0,
|
|
3642
|
-
signal: abortController.signal
|
|
3651
|
+
signal: abortController.signal,
|
|
3652
|
+
agentConfig: this.agentConfig,
|
|
3653
|
+
subagentConfig: this.subagentConfig,
|
|
3654
|
+
invocationId: call.invocationId,
|
|
3655
|
+
onNestedEvent: this.onNestedEvent
|
|
3643
3656
|
};
|
|
3644
3657
|
let rawResult;
|
|
3645
3658
|
if (timeoutMs && timeoutMs > 0) {
|
|
@@ -3654,7 +3667,7 @@ var init_executor = __esm({
|
|
|
3654
3667
|
} else {
|
|
3655
3668
|
rawResult = await Promise.resolve(gadget.execute(validatedParameters, ctx));
|
|
3656
3669
|
}
|
|
3657
|
-
const { result, media, cost: returnCost } = this.
|
|
3670
|
+
const { result, media, cost: returnCost } = this.unifyExecuteResult(rawResult);
|
|
3658
3671
|
const totalCost = callbackCost + returnCost;
|
|
3659
3672
|
let mediaIds;
|
|
3660
3673
|
let storedMedia;
|
|
@@ -3700,7 +3713,7 @@ var init_executor = __esm({
|
|
|
3700
3713
|
storedMedia
|
|
3701
3714
|
};
|
|
3702
3715
|
} catch (error) {
|
|
3703
|
-
if (error instanceof
|
|
3716
|
+
if (error instanceof TaskCompletionSignal) {
|
|
3704
3717
|
this.logger.info("Gadget requested loop termination", {
|
|
3705
3718
|
gadgetName: call.gadgetName,
|
|
3706
3719
|
message: error.message
|
|
@@ -3728,7 +3741,7 @@ var init_executor = __esm({
|
|
|
3728
3741
|
executionTimeMs: Date.now() - startTime
|
|
3729
3742
|
};
|
|
3730
3743
|
}
|
|
3731
|
-
if (error instanceof
|
|
3744
|
+
if (error instanceof AbortException) {
|
|
3732
3745
|
this.logger.info("Gadget execution was aborted", {
|
|
3733
3746
|
gadgetName: call.gadgetName,
|
|
3734
3747
|
executionTimeMs: Date.now() - startTime
|
|
@@ -3741,14 +3754,14 @@ var init_executor = __esm({
|
|
|
3741
3754
|
executionTimeMs: Date.now() - startTime
|
|
3742
3755
|
};
|
|
3743
3756
|
}
|
|
3744
|
-
if (error instanceof
|
|
3757
|
+
if (error instanceof HumanInputRequiredException) {
|
|
3745
3758
|
this.logger.info("Gadget requested human input", {
|
|
3746
3759
|
gadgetName: call.gadgetName,
|
|
3747
3760
|
question: error.question
|
|
3748
3761
|
});
|
|
3749
|
-
if (this.
|
|
3762
|
+
if (this.requestHumanInput) {
|
|
3750
3763
|
try {
|
|
3751
|
-
const answer = await this.
|
|
3764
|
+
const answer = await this.requestHumanInput(error.question);
|
|
3752
3765
|
this.logger.debug("Human input received", {
|
|
3753
3766
|
gadgetName: call.gadgetName,
|
|
3754
3767
|
answerLength: answer.length
|
|
@@ -3846,13 +3859,13 @@ var init_stream_processor = __esm({
|
|
|
3846
3859
|
parser;
|
|
3847
3860
|
executor;
|
|
3848
3861
|
stopOnGadgetError;
|
|
3849
|
-
|
|
3850
|
-
|
|
3851
|
-
|
|
3862
|
+
canRecoverFromGadgetError;
|
|
3863
|
+
responseText = "";
|
|
3864
|
+
executionHalted = false;
|
|
3852
3865
|
observerFailureCount = 0;
|
|
3853
3866
|
// Dependency tracking for gadget execution DAG
|
|
3854
3867
|
/** Gadgets waiting for their dependencies to complete */
|
|
3855
|
-
|
|
3868
|
+
gadgetsAwaitingDependencies = /* @__PURE__ */ new Map();
|
|
3856
3869
|
/** Completed gadget results, keyed by invocation ID */
|
|
3857
3870
|
completedResults = /* @__PURE__ */ new Map();
|
|
3858
3871
|
/** Invocation IDs of gadgets that have failed (error or skipped due to dependency) */
|
|
@@ -3863,27 +3876,36 @@ var init_stream_processor = __esm({
|
|
|
3863
3876
|
this.hooks = options.hooks ?? {};
|
|
3864
3877
|
this.logger = options.logger ?? createLogger({ name: "llmist:stream-processor" });
|
|
3865
3878
|
this.stopOnGadgetError = options.stopOnGadgetError ?? true;
|
|
3866
|
-
this.
|
|
3867
|
-
this.parser = new
|
|
3879
|
+
this.canRecoverFromGadgetError = options.canRecoverFromGadgetError;
|
|
3880
|
+
this.parser = new GadgetCallParser({
|
|
3868
3881
|
startPrefix: options.gadgetStartPrefix,
|
|
3869
3882
|
endPrefix: options.gadgetEndPrefix,
|
|
3870
3883
|
argPrefix: options.gadgetArgPrefix
|
|
3871
3884
|
});
|
|
3872
3885
|
this.executor = new GadgetExecutor(
|
|
3873
3886
|
options.registry,
|
|
3874
|
-
options.
|
|
3887
|
+
options.requestHumanInput,
|
|
3875
3888
|
this.logger.getSubLogger({ name: "executor" }),
|
|
3876
3889
|
options.defaultGadgetTimeoutMs,
|
|
3877
3890
|
{ argPrefix: options.gadgetArgPrefix },
|
|
3878
3891
|
options.client,
|
|
3879
|
-
options.mediaStore
|
|
3892
|
+
options.mediaStore,
|
|
3893
|
+
options.agentConfig,
|
|
3894
|
+
options.subagentConfig,
|
|
3895
|
+
options.onNestedEvent
|
|
3880
3896
|
);
|
|
3881
3897
|
}
|
|
3882
3898
|
/**
|
|
3883
|
-
* Process an LLM stream and
|
|
3899
|
+
* Process an LLM stream and yield events in real-time.
|
|
3900
|
+
*
|
|
3901
|
+
* This is an async generator that yields events immediately as they occur:
|
|
3902
|
+
* - Text events are yielded as text is streamed from the LLM
|
|
3903
|
+
* - gadget_call events are yielded immediately when a gadget call is parsed
|
|
3904
|
+
* - gadget_result events are yielded when gadget execution completes
|
|
3905
|
+
*
|
|
3906
|
+
* The final event is always a StreamCompletionEvent containing metadata.
|
|
3884
3907
|
*/
|
|
3885
|
-
async process(stream2) {
|
|
3886
|
-
const outputs = [];
|
|
3908
|
+
async *process(stream2) {
|
|
3887
3909
|
let finishReason = null;
|
|
3888
3910
|
let usage;
|
|
3889
3911
|
let didExecuteGadgets = false;
|
|
@@ -3897,7 +3919,7 @@ var init_stream_processor = __esm({
|
|
|
3897
3919
|
if (this.hooks.interceptors?.interceptRawChunk) {
|
|
3898
3920
|
const context = {
|
|
3899
3921
|
iteration: this.iteration,
|
|
3900
|
-
accumulatedText: this.
|
|
3922
|
+
accumulatedText: this.responseText,
|
|
3901
3923
|
logger: this.logger
|
|
3902
3924
|
};
|
|
3903
3925
|
const intercepted = this.hooks.interceptors.interceptRawChunk(processedChunk, context);
|
|
@@ -3908,7 +3930,7 @@ var init_stream_processor = __esm({
|
|
|
3908
3930
|
}
|
|
3909
3931
|
}
|
|
3910
3932
|
if (processedChunk) {
|
|
3911
|
-
this.
|
|
3933
|
+
this.responseText += processedChunk;
|
|
3912
3934
|
}
|
|
3913
3935
|
}
|
|
3914
3936
|
if (this.hooks.observers?.onStreamChunk && (processedChunk || chunk.usage)) {
|
|
@@ -3917,7 +3939,7 @@ var init_stream_processor = __esm({
|
|
|
3917
3939
|
const context = {
|
|
3918
3940
|
iteration: this.iteration,
|
|
3919
3941
|
rawChunk: processedChunk,
|
|
3920
|
-
accumulatedText: this.
|
|
3942
|
+
accumulatedText: this.responseText,
|
|
3921
3943
|
usage,
|
|
3922
3944
|
logger: this.logger
|
|
3923
3945
|
};
|
|
@@ -3929,67 +3951,66 @@ var init_stream_processor = __esm({
|
|
|
3929
3951
|
continue;
|
|
3930
3952
|
}
|
|
3931
3953
|
for (const event of this.parser.feed(processedChunk)) {
|
|
3932
|
-
const
|
|
3933
|
-
|
|
3934
|
-
|
|
3935
|
-
|
|
3936
|
-
|
|
3937
|
-
|
|
3938
|
-
|
|
3939
|
-
shouldBreakLoop = true;
|
|
3954
|
+
for await (const processedEvent of this.processEventGenerator(event)) {
|
|
3955
|
+
yield processedEvent;
|
|
3956
|
+
if (processedEvent.type === "gadget_result") {
|
|
3957
|
+
didExecuteGadgets = true;
|
|
3958
|
+
if (processedEvent.result.breaksLoop) {
|
|
3959
|
+
shouldBreakLoop = true;
|
|
3960
|
+
}
|
|
3940
3961
|
}
|
|
3941
3962
|
}
|
|
3942
3963
|
}
|
|
3943
|
-
if (this.
|
|
3964
|
+
if (this.executionHalted) {
|
|
3944
3965
|
this.logger.info("Breaking from LLM stream due to gadget error");
|
|
3945
3966
|
break;
|
|
3946
3967
|
}
|
|
3947
3968
|
}
|
|
3948
|
-
if (!this.
|
|
3969
|
+
if (!this.executionHalted) {
|
|
3949
3970
|
for (const event of this.parser.finalize()) {
|
|
3950
|
-
const
|
|
3951
|
-
|
|
3952
|
-
|
|
3953
|
-
|
|
3954
|
-
|
|
3955
|
-
|
|
3956
|
-
|
|
3957
|
-
shouldBreakLoop = true;
|
|
3971
|
+
for await (const processedEvent of this.processEventGenerator(event)) {
|
|
3972
|
+
yield processedEvent;
|
|
3973
|
+
if (processedEvent.type === "gadget_result") {
|
|
3974
|
+
didExecuteGadgets = true;
|
|
3975
|
+
if (processedEvent.result.breaksLoop) {
|
|
3976
|
+
shouldBreakLoop = true;
|
|
3977
|
+
}
|
|
3958
3978
|
}
|
|
3959
3979
|
}
|
|
3960
3980
|
}
|
|
3961
|
-
const
|
|
3962
|
-
|
|
3963
|
-
|
|
3964
|
-
|
|
3965
|
-
|
|
3966
|
-
|
|
3967
|
-
|
|
3968
|
-
shouldBreakLoop = true;
|
|
3981
|
+
for await (const evt of this.processPendingGadgetsGenerator()) {
|
|
3982
|
+
yield evt;
|
|
3983
|
+
if (evt.type === "gadget_result") {
|
|
3984
|
+
didExecuteGadgets = true;
|
|
3985
|
+
if (evt.result.breaksLoop) {
|
|
3986
|
+
shouldBreakLoop = true;
|
|
3987
|
+
}
|
|
3969
3988
|
}
|
|
3970
3989
|
}
|
|
3971
3990
|
}
|
|
3972
|
-
let finalMessage = this.
|
|
3991
|
+
let finalMessage = this.responseText;
|
|
3973
3992
|
if (this.hooks.interceptors?.interceptAssistantMessage) {
|
|
3974
3993
|
const context = {
|
|
3975
3994
|
iteration: this.iteration,
|
|
3976
|
-
rawResponse: this.
|
|
3995
|
+
rawResponse: this.responseText,
|
|
3977
3996
|
logger: this.logger
|
|
3978
3997
|
};
|
|
3979
3998
|
finalMessage = this.hooks.interceptors.interceptAssistantMessage(finalMessage, context);
|
|
3980
3999
|
}
|
|
3981
|
-
|
|
3982
|
-
|
|
4000
|
+
const completionEvent = {
|
|
4001
|
+
type: "stream_complete",
|
|
3983
4002
|
shouldBreakLoop,
|
|
3984
4003
|
didExecuteGadgets,
|
|
3985
4004
|
finishReason,
|
|
3986
4005
|
usage,
|
|
3987
|
-
rawResponse: this.
|
|
4006
|
+
rawResponse: this.responseText,
|
|
3988
4007
|
finalMessage
|
|
3989
4008
|
};
|
|
4009
|
+
yield completionEvent;
|
|
3990
4010
|
}
|
|
3991
4011
|
/**
|
|
3992
4012
|
* Process a single parsed event (text or gadget call).
|
|
4013
|
+
* @deprecated Use processEventGenerator for real-time streaming
|
|
3993
4014
|
*/
|
|
3994
4015
|
async processEvent(event) {
|
|
3995
4016
|
if (event.type === "text") {
|
|
@@ -3999,6 +4020,23 @@ var init_stream_processor = __esm({
|
|
|
3999
4020
|
}
|
|
4000
4021
|
return [event];
|
|
4001
4022
|
}
|
|
4023
|
+
/**
|
|
4024
|
+
* Process a single parsed event, yielding events in real-time.
|
|
4025
|
+
* Generator version of processEvent for streaming support.
|
|
4026
|
+
*/
|
|
4027
|
+
async *processEventGenerator(event) {
|
|
4028
|
+
if (event.type === "text") {
|
|
4029
|
+
for (const e of await this.processTextEvent(event)) {
|
|
4030
|
+
yield e;
|
|
4031
|
+
}
|
|
4032
|
+
} else if (event.type === "gadget_call") {
|
|
4033
|
+
for await (const e of this.processGadgetCallGenerator(event.call)) {
|
|
4034
|
+
yield e;
|
|
4035
|
+
}
|
|
4036
|
+
} else {
|
|
4037
|
+
yield event;
|
|
4038
|
+
}
|
|
4039
|
+
}
|
|
4002
4040
|
/**
|
|
4003
4041
|
* Process a text event through interceptors.
|
|
4004
4042
|
*/
|
|
@@ -4007,7 +4045,7 @@ var init_stream_processor = __esm({
|
|
|
4007
4045
|
if (this.hooks.interceptors?.interceptTextChunk) {
|
|
4008
4046
|
const context = {
|
|
4009
4047
|
iteration: this.iteration,
|
|
4010
|
-
accumulatedText: this.
|
|
4048
|
+
accumulatedText: this.responseText,
|
|
4011
4049
|
logger: this.logger
|
|
4012
4050
|
};
|
|
4013
4051
|
const intercepted = this.hooks.interceptors.interceptTextChunk(content, context);
|
|
@@ -4026,7 +4064,7 @@ var init_stream_processor = __esm({
|
|
|
4026
4064
|
* After each execution, pending gadgets are checked to see if they can now run.
|
|
4027
4065
|
*/
|
|
4028
4066
|
async processGadgetCall(call) {
|
|
4029
|
-
if (this.
|
|
4067
|
+
if (this.executionHalted) {
|
|
4030
4068
|
this.logger.debug("Skipping gadget execution due to previous error", {
|
|
4031
4069
|
gadgetName: call.gadgetName
|
|
4032
4070
|
});
|
|
@@ -4065,7 +4103,7 @@ var init_stream_processor = __esm({
|
|
|
4065
4103
|
invocationId: call.invocationId,
|
|
4066
4104
|
waitingOn: unsatisfied
|
|
4067
4105
|
});
|
|
4068
|
-
this.
|
|
4106
|
+
this.gadgetsAwaitingDependencies.set(call.invocationId, call);
|
|
4069
4107
|
return events;
|
|
4070
4108
|
}
|
|
4071
4109
|
}
|
|
@@ -4075,9 +4113,68 @@ var init_stream_processor = __esm({
|
|
|
4075
4113
|
events.push(...triggeredEvents);
|
|
4076
4114
|
return events;
|
|
4077
4115
|
}
|
|
4116
|
+
/**
|
|
4117
|
+
* Process a gadget call, yielding events in real-time.
|
|
4118
|
+
*
|
|
4119
|
+
* Key difference from processGadgetCall: yields gadget_call event IMMEDIATELY
|
|
4120
|
+
* when parsed (before execution), enabling real-time UI feedback.
|
|
4121
|
+
*/
|
|
4122
|
+
async *processGadgetCallGenerator(call) {
|
|
4123
|
+
if (this.executionHalted) {
|
|
4124
|
+
this.logger.debug("Skipping gadget execution due to previous error", {
|
|
4125
|
+
gadgetName: call.gadgetName
|
|
4126
|
+
});
|
|
4127
|
+
return;
|
|
4128
|
+
}
|
|
4129
|
+
yield { type: "gadget_call", call };
|
|
4130
|
+
if (call.dependencies.length > 0) {
|
|
4131
|
+
if (call.dependencies.includes(call.invocationId)) {
|
|
4132
|
+
this.logger.warn("Gadget has self-referential dependency (depends on itself)", {
|
|
4133
|
+
gadgetName: call.gadgetName,
|
|
4134
|
+
invocationId: call.invocationId
|
|
4135
|
+
});
|
|
4136
|
+
this.failedInvocations.add(call.invocationId);
|
|
4137
|
+
const skipEvent = {
|
|
4138
|
+
type: "gadget_skipped",
|
|
4139
|
+
gadgetName: call.gadgetName,
|
|
4140
|
+
invocationId: call.invocationId,
|
|
4141
|
+
parameters: call.parameters ?? {},
|
|
4142
|
+
failedDependency: call.invocationId,
|
|
4143
|
+
failedDependencyError: `Gadget "${call.invocationId}" cannot depend on itself (self-referential dependency)`
|
|
4144
|
+
};
|
|
4145
|
+
yield skipEvent;
|
|
4146
|
+
return;
|
|
4147
|
+
}
|
|
4148
|
+
const failedDep = call.dependencies.find((dep) => this.failedInvocations.has(dep));
|
|
4149
|
+
if (failedDep) {
|
|
4150
|
+
const skipEvents = await this.handleFailedDependency(call, failedDep);
|
|
4151
|
+
for (const evt of skipEvents) {
|
|
4152
|
+
yield evt;
|
|
4153
|
+
}
|
|
4154
|
+
return;
|
|
4155
|
+
}
|
|
4156
|
+
const unsatisfied = call.dependencies.filter((dep) => !this.completedResults.has(dep));
|
|
4157
|
+
if (unsatisfied.length > 0) {
|
|
4158
|
+
this.logger.debug("Queueing gadget for later - waiting on dependencies", {
|
|
4159
|
+
gadgetName: call.gadgetName,
|
|
4160
|
+
invocationId: call.invocationId,
|
|
4161
|
+
waitingOn: unsatisfied
|
|
4162
|
+
});
|
|
4163
|
+
this.gadgetsAwaitingDependencies.set(call.invocationId, call);
|
|
4164
|
+
return;
|
|
4165
|
+
}
|
|
4166
|
+
}
|
|
4167
|
+
for await (const evt of this.executeGadgetGenerator(call)) {
|
|
4168
|
+
yield evt;
|
|
4169
|
+
}
|
|
4170
|
+
for await (const evt of this.processPendingGadgetsGenerator()) {
|
|
4171
|
+
yield evt;
|
|
4172
|
+
}
|
|
4173
|
+
}
|
|
4078
4174
|
/**
|
|
4079
4175
|
* Execute a gadget through the full hook lifecycle.
|
|
4080
4176
|
* This is the core execution logic, extracted from processGadgetCall.
|
|
4177
|
+
* @deprecated Use executeGadgetGenerator for real-time streaming
|
|
4081
4178
|
*/
|
|
4082
4179
|
async executeGadgetWithHooks(call) {
|
|
4083
4180
|
const events = [];
|
|
@@ -4087,14 +4184,14 @@ var init_stream_processor = __esm({
|
|
|
4087
4184
|
error: call.parseError,
|
|
4088
4185
|
rawParameters: call.parametersRaw
|
|
4089
4186
|
});
|
|
4090
|
-
const shouldContinue = await this.
|
|
4187
|
+
const shouldContinue = await this.checkCanRecoverFromError(
|
|
4091
4188
|
call.parseError,
|
|
4092
4189
|
call.gadgetName,
|
|
4093
4190
|
"parse",
|
|
4094
4191
|
call.parameters
|
|
4095
4192
|
);
|
|
4096
4193
|
if (!shouldContinue) {
|
|
4097
|
-
this.
|
|
4194
|
+
this.executionHalted = true;
|
|
4098
4195
|
}
|
|
4099
4196
|
}
|
|
4100
4197
|
let parameters = call.parameters ?? {};
|
|
@@ -4218,18 +4315,171 @@ var init_stream_processor = __esm({
|
|
|
4218
4315
|
events.push({ type: "gadget_result", result });
|
|
4219
4316
|
if (result.error) {
|
|
4220
4317
|
const errorType = this.determineErrorType(call, result);
|
|
4221
|
-
const shouldContinue = await this.
|
|
4318
|
+
const shouldContinue = await this.checkCanRecoverFromError(
|
|
4222
4319
|
result.error,
|
|
4223
4320
|
result.gadgetName,
|
|
4224
4321
|
errorType,
|
|
4225
4322
|
result.parameters
|
|
4226
4323
|
);
|
|
4227
4324
|
if (!shouldContinue) {
|
|
4228
|
-
this.
|
|
4325
|
+
this.executionHalted = true;
|
|
4229
4326
|
}
|
|
4230
4327
|
}
|
|
4231
4328
|
return events;
|
|
4232
4329
|
}
|
|
4330
|
+
/**
|
|
4331
|
+
* Execute a gadget and yield the result event.
|
|
4332
|
+
* Generator version that yields gadget_result immediately when execution completes.
|
|
4333
|
+
*/
|
|
4334
|
+
async *executeGadgetGenerator(call) {
|
|
4335
|
+
if (call.parseError) {
|
|
4336
|
+
this.logger.warn("Gadget has parse error", {
|
|
4337
|
+
gadgetName: call.gadgetName,
|
|
4338
|
+
error: call.parseError,
|
|
4339
|
+
rawParameters: call.parametersRaw
|
|
4340
|
+
});
|
|
4341
|
+
const shouldContinue = await this.checkCanRecoverFromError(
|
|
4342
|
+
call.parseError,
|
|
4343
|
+
call.gadgetName,
|
|
4344
|
+
"parse",
|
|
4345
|
+
call.parameters
|
|
4346
|
+
);
|
|
4347
|
+
if (!shouldContinue) {
|
|
4348
|
+
this.executionHalted = true;
|
|
4349
|
+
}
|
|
4350
|
+
}
|
|
4351
|
+
let parameters = call.parameters ?? {};
|
|
4352
|
+
if (this.hooks.interceptors?.interceptGadgetParameters) {
|
|
4353
|
+
const context = {
|
|
4354
|
+
iteration: this.iteration,
|
|
4355
|
+
gadgetName: call.gadgetName,
|
|
4356
|
+
invocationId: call.invocationId,
|
|
4357
|
+
logger: this.logger
|
|
4358
|
+
};
|
|
4359
|
+
parameters = this.hooks.interceptors.interceptGadgetParameters(parameters, context);
|
|
4360
|
+
}
|
|
4361
|
+
call.parameters = parameters;
|
|
4362
|
+
let shouldSkip = false;
|
|
4363
|
+
let syntheticResult;
|
|
4364
|
+
if (this.hooks.controllers?.beforeGadgetExecution) {
|
|
4365
|
+
const context = {
|
|
4366
|
+
iteration: this.iteration,
|
|
4367
|
+
gadgetName: call.gadgetName,
|
|
4368
|
+
invocationId: call.invocationId,
|
|
4369
|
+
parameters,
|
|
4370
|
+
logger: this.logger
|
|
4371
|
+
};
|
|
4372
|
+
const action = await this.hooks.controllers.beforeGadgetExecution(context);
|
|
4373
|
+
validateBeforeGadgetExecutionAction(action);
|
|
4374
|
+
if (action.action === "skip") {
|
|
4375
|
+
shouldSkip = true;
|
|
4376
|
+
syntheticResult = action.syntheticResult;
|
|
4377
|
+
this.logger.info("Controller skipped gadget execution", {
|
|
4378
|
+
gadgetName: call.gadgetName
|
|
4379
|
+
});
|
|
4380
|
+
}
|
|
4381
|
+
}
|
|
4382
|
+
const startObservers = [];
|
|
4383
|
+
if (this.hooks.observers?.onGadgetExecutionStart) {
|
|
4384
|
+
startObservers.push(async () => {
|
|
4385
|
+
const context = {
|
|
4386
|
+
iteration: this.iteration,
|
|
4387
|
+
gadgetName: call.gadgetName,
|
|
4388
|
+
invocationId: call.invocationId,
|
|
4389
|
+
parameters,
|
|
4390
|
+
logger: this.logger
|
|
4391
|
+
};
|
|
4392
|
+
await this.hooks.observers.onGadgetExecutionStart(context);
|
|
4393
|
+
});
|
|
4394
|
+
}
|
|
4395
|
+
await this.runObserversInParallel(startObservers);
|
|
4396
|
+
let result;
|
|
4397
|
+
if (shouldSkip) {
|
|
4398
|
+
result = {
|
|
4399
|
+
gadgetName: call.gadgetName,
|
|
4400
|
+
invocationId: call.invocationId,
|
|
4401
|
+
parameters,
|
|
4402
|
+
result: syntheticResult ?? "Execution skipped",
|
|
4403
|
+
executionTimeMs: 0
|
|
4404
|
+
};
|
|
4405
|
+
} else {
|
|
4406
|
+
result = await this.executor.execute(call);
|
|
4407
|
+
}
|
|
4408
|
+
const originalResult = result.result;
|
|
4409
|
+
if (result.result && this.hooks.interceptors?.interceptGadgetResult) {
|
|
4410
|
+
const context = {
|
|
4411
|
+
iteration: this.iteration,
|
|
4412
|
+
gadgetName: result.gadgetName,
|
|
4413
|
+
invocationId: result.invocationId,
|
|
4414
|
+
parameters,
|
|
4415
|
+
executionTimeMs: result.executionTimeMs,
|
|
4416
|
+
logger: this.logger
|
|
4417
|
+
};
|
|
4418
|
+
result.result = this.hooks.interceptors.interceptGadgetResult(result.result, context);
|
|
4419
|
+
}
|
|
4420
|
+
if (this.hooks.controllers?.afterGadgetExecution) {
|
|
4421
|
+
const context = {
|
|
4422
|
+
iteration: this.iteration,
|
|
4423
|
+
gadgetName: result.gadgetName,
|
|
4424
|
+
invocationId: result.invocationId,
|
|
4425
|
+
parameters,
|
|
4426
|
+
result: result.result,
|
|
4427
|
+
error: result.error,
|
|
4428
|
+
executionTimeMs: result.executionTimeMs,
|
|
4429
|
+
logger: this.logger
|
|
4430
|
+
};
|
|
4431
|
+
const action = await this.hooks.controllers.afterGadgetExecution(context);
|
|
4432
|
+
validateAfterGadgetExecutionAction(action);
|
|
4433
|
+
if (action.action === "recover" && result.error) {
|
|
4434
|
+
this.logger.info("Controller recovered from gadget error", {
|
|
4435
|
+
gadgetName: result.gadgetName,
|
|
4436
|
+
originalError: result.error
|
|
4437
|
+
});
|
|
4438
|
+
result = {
|
|
4439
|
+
...result,
|
|
4440
|
+
error: void 0,
|
|
4441
|
+
result: action.fallbackResult
|
|
4442
|
+
};
|
|
4443
|
+
}
|
|
4444
|
+
}
|
|
4445
|
+
const completeObservers = [];
|
|
4446
|
+
if (this.hooks.observers?.onGadgetExecutionComplete) {
|
|
4447
|
+
completeObservers.push(async () => {
|
|
4448
|
+
const context = {
|
|
4449
|
+
iteration: this.iteration,
|
|
4450
|
+
gadgetName: result.gadgetName,
|
|
4451
|
+
invocationId: result.invocationId,
|
|
4452
|
+
parameters,
|
|
4453
|
+
originalResult,
|
|
4454
|
+
finalResult: result.result,
|
|
4455
|
+
error: result.error,
|
|
4456
|
+
executionTimeMs: result.executionTimeMs,
|
|
4457
|
+
breaksLoop: result.breaksLoop,
|
|
4458
|
+
cost: result.cost,
|
|
4459
|
+
logger: this.logger
|
|
4460
|
+
};
|
|
4461
|
+
await this.hooks.observers.onGadgetExecutionComplete(context);
|
|
4462
|
+
});
|
|
4463
|
+
}
|
|
4464
|
+
await this.runObserversInParallel(completeObservers);
|
|
4465
|
+
this.completedResults.set(result.invocationId, result);
|
|
4466
|
+
if (result.error) {
|
|
4467
|
+
this.failedInvocations.add(result.invocationId);
|
|
4468
|
+
}
|
|
4469
|
+
yield { type: "gadget_result", result };
|
|
4470
|
+
if (result.error) {
|
|
4471
|
+
const errorType = this.determineErrorType(call, result);
|
|
4472
|
+
const shouldContinue = await this.checkCanRecoverFromError(
|
|
4473
|
+
result.error,
|
|
4474
|
+
result.gadgetName,
|
|
4475
|
+
errorType,
|
|
4476
|
+
result.parameters
|
|
4477
|
+
);
|
|
4478
|
+
if (!shouldContinue) {
|
|
4479
|
+
this.executionHalted = true;
|
|
4480
|
+
}
|
|
4481
|
+
}
|
|
4482
|
+
}
|
|
4233
4483
|
/**
|
|
4234
4484
|
* Handle a gadget that cannot execute because a dependency failed.
|
|
4235
4485
|
* Calls the onDependencySkipped controller to allow customization.
|
|
@@ -4312,11 +4562,11 @@ var init_stream_processor = __esm({
|
|
|
4312
4562
|
async processPendingGadgets() {
|
|
4313
4563
|
const events = [];
|
|
4314
4564
|
let progress = true;
|
|
4315
|
-
while (progress && this.
|
|
4565
|
+
while (progress && this.gadgetsAwaitingDependencies.size > 0) {
|
|
4316
4566
|
progress = false;
|
|
4317
4567
|
const readyToExecute = [];
|
|
4318
4568
|
const readyToSkip = [];
|
|
4319
|
-
for (const [invocationId, call] of this.
|
|
4569
|
+
for (const [invocationId, call] of this.gadgetsAwaitingDependencies) {
|
|
4320
4570
|
const failedDep = call.dependencies.find((dep) => this.failedInvocations.has(dep));
|
|
4321
4571
|
if (failedDep) {
|
|
4322
4572
|
readyToSkip.push({ call, failedDep });
|
|
@@ -4328,7 +4578,7 @@ var init_stream_processor = __esm({
|
|
|
4328
4578
|
}
|
|
4329
4579
|
}
|
|
4330
4580
|
for (const { call, failedDep } of readyToSkip) {
|
|
4331
|
-
this.
|
|
4581
|
+
this.gadgetsAwaitingDependencies.delete(call.invocationId);
|
|
4332
4582
|
const skipEvents = await this.handleFailedDependency(call, failedDep);
|
|
4333
4583
|
events.push(...skipEvents);
|
|
4334
4584
|
progress = true;
|
|
@@ -4339,7 +4589,7 @@ var init_stream_processor = __esm({
|
|
|
4339
4589
|
invocationIds: readyToExecute.map((c) => c.invocationId)
|
|
4340
4590
|
});
|
|
4341
4591
|
for (const call of readyToExecute) {
|
|
4342
|
-
this.
|
|
4592
|
+
this.gadgetsAwaitingDependencies.delete(call.invocationId);
|
|
4343
4593
|
}
|
|
4344
4594
|
const executePromises = readyToExecute.map((call) => this.executeGadgetWithHooks(call));
|
|
4345
4595
|
const results = await Promise.all(executePromises);
|
|
@@ -4349,9 +4599,9 @@ var init_stream_processor = __esm({
|
|
|
4349
4599
|
progress = true;
|
|
4350
4600
|
}
|
|
4351
4601
|
}
|
|
4352
|
-
if (this.
|
|
4353
|
-
const pendingIds = new Set(this.
|
|
4354
|
-
for (const [invocationId, call] of this.
|
|
4602
|
+
if (this.gadgetsAwaitingDependencies.size > 0) {
|
|
4603
|
+
const pendingIds = new Set(this.gadgetsAwaitingDependencies.keys());
|
|
4604
|
+
for (const [invocationId, call] of this.gadgetsAwaitingDependencies) {
|
|
4355
4605
|
const missingDeps = call.dependencies.filter((dep) => !this.completedResults.has(dep));
|
|
4356
4606
|
const circularDeps = missingDeps.filter((dep) => pendingIds.has(dep));
|
|
4357
4607
|
const trulyMissingDeps = missingDeps.filter((dep) => !pendingIds.has(dep));
|
|
@@ -4382,10 +4632,103 @@ var init_stream_processor = __esm({
|
|
|
4382
4632
|
};
|
|
4383
4633
|
events.push(skipEvent);
|
|
4384
4634
|
}
|
|
4385
|
-
this.
|
|
4635
|
+
this.gadgetsAwaitingDependencies.clear();
|
|
4386
4636
|
}
|
|
4387
4637
|
return events;
|
|
4388
4638
|
}
|
|
4639
|
+
/**
|
|
4640
|
+
* Process pending gadgets, yielding events in real-time.
|
|
4641
|
+
* Generator version that yields events as gadgets complete.
|
|
4642
|
+
*
|
|
4643
|
+
* Note: Gadgets are still executed in parallel for efficiency,
|
|
4644
|
+
* but results are yielded as they become available.
|
|
4645
|
+
*/
|
|
4646
|
+
async *processPendingGadgetsGenerator() {
|
|
4647
|
+
let progress = true;
|
|
4648
|
+
while (progress && this.gadgetsAwaitingDependencies.size > 0) {
|
|
4649
|
+
progress = false;
|
|
4650
|
+
const readyToExecute = [];
|
|
4651
|
+
const readyToSkip = [];
|
|
4652
|
+
for (const [_invocationId, call] of this.gadgetsAwaitingDependencies) {
|
|
4653
|
+
const failedDep = call.dependencies.find((dep) => this.failedInvocations.has(dep));
|
|
4654
|
+
if (failedDep) {
|
|
4655
|
+
readyToSkip.push({ call, failedDep });
|
|
4656
|
+
continue;
|
|
4657
|
+
}
|
|
4658
|
+
const allSatisfied = call.dependencies.every((dep) => this.completedResults.has(dep));
|
|
4659
|
+
if (allSatisfied) {
|
|
4660
|
+
readyToExecute.push(call);
|
|
4661
|
+
}
|
|
4662
|
+
}
|
|
4663
|
+
for (const { call, failedDep } of readyToSkip) {
|
|
4664
|
+
this.gadgetsAwaitingDependencies.delete(call.invocationId);
|
|
4665
|
+
const skipEvents = await this.handleFailedDependency(call, failedDep);
|
|
4666
|
+
for (const evt of skipEvents) {
|
|
4667
|
+
yield evt;
|
|
4668
|
+
}
|
|
4669
|
+
progress = true;
|
|
4670
|
+
}
|
|
4671
|
+
if (readyToExecute.length > 0) {
|
|
4672
|
+
this.logger.debug("Executing ready gadgets in parallel", {
|
|
4673
|
+
count: readyToExecute.length,
|
|
4674
|
+
invocationIds: readyToExecute.map((c) => c.invocationId)
|
|
4675
|
+
});
|
|
4676
|
+
for (const call of readyToExecute) {
|
|
4677
|
+
this.gadgetsAwaitingDependencies.delete(call.invocationId);
|
|
4678
|
+
}
|
|
4679
|
+
const eventSets = await Promise.all(
|
|
4680
|
+
readyToExecute.map(async (call) => {
|
|
4681
|
+
const events = [];
|
|
4682
|
+
for await (const evt of this.executeGadgetGenerator(call)) {
|
|
4683
|
+
events.push(evt);
|
|
4684
|
+
}
|
|
4685
|
+
return events;
|
|
4686
|
+
})
|
|
4687
|
+
);
|
|
4688
|
+
for (const events of eventSets) {
|
|
4689
|
+
for (const evt of events) {
|
|
4690
|
+
yield evt;
|
|
4691
|
+
}
|
|
4692
|
+
}
|
|
4693
|
+
progress = true;
|
|
4694
|
+
}
|
|
4695
|
+
}
|
|
4696
|
+
if (this.gadgetsAwaitingDependencies.size > 0) {
|
|
4697
|
+
const pendingIds = new Set(this.gadgetsAwaitingDependencies.keys());
|
|
4698
|
+
for (const [invocationId, call] of this.gadgetsAwaitingDependencies) {
|
|
4699
|
+
const missingDeps = call.dependencies.filter((dep) => !this.completedResults.has(dep));
|
|
4700
|
+
const circularDeps = missingDeps.filter((dep) => pendingIds.has(dep));
|
|
4701
|
+
const trulyMissingDeps = missingDeps.filter((dep) => !pendingIds.has(dep));
|
|
4702
|
+
let errorMessage;
|
|
4703
|
+
let logLevel = "warn";
|
|
4704
|
+
if (circularDeps.length > 0 && trulyMissingDeps.length > 0) {
|
|
4705
|
+
errorMessage = `Dependencies unresolvable: circular=[${circularDeps.join(", ")}], missing=[${trulyMissingDeps.join(", ")}]`;
|
|
4706
|
+
logLevel = "error";
|
|
4707
|
+
} else if (circularDeps.length > 0) {
|
|
4708
|
+
errorMessage = `Circular dependency detected: "${invocationId}" depends on "${circularDeps[0]}" which also depends on "${invocationId}" (directly or indirectly)`;
|
|
4709
|
+
} else {
|
|
4710
|
+
errorMessage = `Dependency "${missingDeps[0]}" was never executed - check that the invocation ID exists and is spelled correctly`;
|
|
4711
|
+
}
|
|
4712
|
+
this.logger[logLevel]("Gadget has unresolvable dependencies", {
|
|
4713
|
+
gadgetName: call.gadgetName,
|
|
4714
|
+
invocationId,
|
|
4715
|
+
circularDependencies: circularDeps,
|
|
4716
|
+
missingDependencies: trulyMissingDeps
|
|
4717
|
+
});
|
|
4718
|
+
this.failedInvocations.add(invocationId);
|
|
4719
|
+
const skipEvent = {
|
|
4720
|
+
type: "gadget_skipped",
|
|
4721
|
+
gadgetName: call.gadgetName,
|
|
4722
|
+
invocationId,
|
|
4723
|
+
parameters: call.parameters ?? {},
|
|
4724
|
+
failedDependency: missingDeps[0],
|
|
4725
|
+
failedDependencyError: errorMessage
|
|
4726
|
+
};
|
|
4727
|
+
yield skipEvent;
|
|
4728
|
+
}
|
|
4729
|
+
this.gadgetsAwaitingDependencies.clear();
|
|
4730
|
+
}
|
|
4731
|
+
}
|
|
4389
4732
|
/**
|
|
4390
4733
|
* Safely execute an observer, catching and logging any errors.
|
|
4391
4734
|
* Observers are non-critical, so errors are logged but don't crash the system.
|
|
@@ -4412,19 +4755,19 @@ var init_stream_processor = __esm({
|
|
|
4412
4755
|
);
|
|
4413
4756
|
}
|
|
4414
4757
|
/**
|
|
4415
|
-
* Check if execution
|
|
4758
|
+
* Check if execution can recover from an error.
|
|
4416
4759
|
*
|
|
4417
4760
|
* Returns true if we should continue processing subsequent gadgets, false if we should stop.
|
|
4418
4761
|
*
|
|
4419
4762
|
* Logic:
|
|
4420
|
-
* - If custom
|
|
4763
|
+
* - If custom canRecoverFromGadgetError is provided, use it
|
|
4421
4764
|
* - Otherwise, use stopOnGadgetError config:
|
|
4422
4765
|
* - stopOnGadgetError=true → return false (stop execution)
|
|
4423
4766
|
* - stopOnGadgetError=false → return true (continue execution)
|
|
4424
4767
|
*/
|
|
4425
|
-
async
|
|
4426
|
-
if (this.
|
|
4427
|
-
return await this.
|
|
4768
|
+
async checkCanRecoverFromError(error, gadgetName, errorType, parameters) {
|
|
4769
|
+
if (this.canRecoverFromGadgetError) {
|
|
4770
|
+
return await this.canRecoverFromGadgetError({
|
|
4428
4771
|
error,
|
|
4429
4772
|
gadgetName,
|
|
4430
4773
|
errorType,
|
|
@@ -4487,14 +4830,14 @@ var init_agent = __esm({
|
|
|
4487
4830
|
gadgetStartPrefix;
|
|
4488
4831
|
gadgetEndPrefix;
|
|
4489
4832
|
gadgetArgPrefix;
|
|
4490
|
-
|
|
4833
|
+
requestHumanInput;
|
|
4491
4834
|
textOnlyHandler;
|
|
4492
4835
|
textWithGadgetsHandler;
|
|
4493
4836
|
stopOnGadgetError;
|
|
4494
|
-
|
|
4837
|
+
canRecoverFromGadgetError;
|
|
4495
4838
|
defaultGadgetTimeoutMs;
|
|
4496
4839
|
defaultMaxTokens;
|
|
4497
|
-
|
|
4840
|
+
hasUserPrompt;
|
|
4498
4841
|
// Gadget output limiting
|
|
4499
4842
|
outputStore;
|
|
4500
4843
|
outputLimitEnabled;
|
|
@@ -4505,6 +4848,11 @@ var init_agent = __esm({
|
|
|
4505
4848
|
mediaStore;
|
|
4506
4849
|
// Cancellation
|
|
4507
4850
|
signal;
|
|
4851
|
+
// Subagent configuration
|
|
4852
|
+
agentContextConfig;
|
|
4853
|
+
subagentConfig;
|
|
4854
|
+
// Nested event callback for subagent gadgets
|
|
4855
|
+
onNestedEvent;
|
|
4508
4856
|
/**
|
|
4509
4857
|
* Creates a new Agent instance.
|
|
4510
4858
|
* @internal This constructor is private. Use LLMist.createAgent() or AgentBuilder instead.
|
|
@@ -4524,11 +4872,11 @@ var init_agent = __esm({
|
|
|
4524
4872
|
this.gadgetStartPrefix = options.gadgetStartPrefix;
|
|
4525
4873
|
this.gadgetEndPrefix = options.gadgetEndPrefix;
|
|
4526
4874
|
this.gadgetArgPrefix = options.gadgetArgPrefix;
|
|
4527
|
-
this.
|
|
4875
|
+
this.requestHumanInput = options.requestHumanInput;
|
|
4528
4876
|
this.textOnlyHandler = options.textOnlyHandler ?? "terminate";
|
|
4529
4877
|
this.textWithGadgetsHandler = options.textWithGadgetsHandler;
|
|
4530
4878
|
this.stopOnGadgetError = options.stopOnGadgetError ?? true;
|
|
4531
|
-
this.
|
|
4879
|
+
this.canRecoverFromGadgetError = options.canRecoverFromGadgetError;
|
|
4532
4880
|
this.defaultGadgetTimeoutMs = options.defaultGadgetTimeoutMs;
|
|
4533
4881
|
this.defaultMaxTokens = this.resolveMaxTokensFromCatalog(options.model);
|
|
4534
4882
|
this.outputLimitEnabled = options.gadgetOutputLimit ?? DEFAULT_GADGET_OUTPUT_LIMIT;
|
|
@@ -4544,7 +4892,7 @@ var init_agent = __esm({
|
|
|
4544
4892
|
createGadgetOutputViewer(this.outputStore, this.outputLimitCharLimit)
|
|
4545
4893
|
);
|
|
4546
4894
|
}
|
|
4547
|
-
this.hooks = this.
|
|
4895
|
+
this.hooks = this.chainOutputLimiterWithUserHooks(options.hooks);
|
|
4548
4896
|
const baseBuilder = new LLMMessageBuilder(options.promptConfig);
|
|
4549
4897
|
if (options.systemPrompt) {
|
|
4550
4898
|
baseBuilder.addSystem(options.systemPrompt);
|
|
@@ -4564,7 +4912,7 @@ var init_agent = __esm({
|
|
|
4564
4912
|
endPrefix: options.gadgetEndPrefix,
|
|
4565
4913
|
argPrefix: options.gadgetArgPrefix
|
|
4566
4914
|
});
|
|
4567
|
-
this.
|
|
4915
|
+
this.hasUserPrompt = !!options.userPrompt;
|
|
4568
4916
|
if (options.userPrompt) {
|
|
4569
4917
|
this.conversation.addUserMessage(options.userPrompt);
|
|
4570
4918
|
}
|
|
@@ -4577,6 +4925,12 @@ var init_agent = __esm({
|
|
|
4577
4925
|
);
|
|
4578
4926
|
}
|
|
4579
4927
|
this.signal = options.signal;
|
|
4928
|
+
this.agentContextConfig = {
|
|
4929
|
+
model: this.model,
|
|
4930
|
+
temperature: this.temperature
|
|
4931
|
+
};
|
|
4932
|
+
this.subagentConfig = options.subagentConfig;
|
|
4933
|
+
this.onNestedEvent = options.onNestedEvent;
|
|
4580
4934
|
}
|
|
4581
4935
|
/**
|
|
4582
4936
|
* Get the gadget registry for this agent.
|
|
@@ -4683,7 +5037,7 @@ var init_agent = __esm({
|
|
|
4683
5037
|
* @throws {Error} If no user prompt was provided (when using build() without ask())
|
|
4684
5038
|
*/
|
|
4685
5039
|
async *run() {
|
|
4686
|
-
if (!this.
|
|
5040
|
+
if (!this.hasUserPrompt) {
|
|
4687
5041
|
throw new Error(
|
|
4688
5042
|
"No user prompt provided. Use .ask(prompt) instead of .build(), or call agent.run() after providing a prompt."
|
|
4689
5043
|
);
|
|
@@ -4800,17 +5154,37 @@ var init_agent = __esm({
|
|
|
4800
5154
|
gadgetArgPrefix: this.gadgetArgPrefix,
|
|
4801
5155
|
hooks: this.hooks,
|
|
4802
5156
|
logger: this.logger.getSubLogger({ name: "stream-processor" }),
|
|
4803
|
-
|
|
5157
|
+
requestHumanInput: this.requestHumanInput,
|
|
4804
5158
|
stopOnGadgetError: this.stopOnGadgetError,
|
|
4805
|
-
|
|
5159
|
+
canRecoverFromGadgetError: this.canRecoverFromGadgetError,
|
|
4806
5160
|
defaultGadgetTimeoutMs: this.defaultGadgetTimeoutMs,
|
|
4807
5161
|
client: this.client,
|
|
4808
|
-
mediaStore: this.mediaStore
|
|
5162
|
+
mediaStore: this.mediaStore,
|
|
5163
|
+
agentConfig: this.agentContextConfig,
|
|
5164
|
+
subagentConfig: this.subagentConfig,
|
|
5165
|
+
onNestedEvent: this.onNestedEvent
|
|
4809
5166
|
});
|
|
4810
|
-
|
|
4811
|
-
|
|
4812
|
-
|
|
5167
|
+
let streamMetadata = null;
|
|
5168
|
+
let gadgetCallCount = 0;
|
|
5169
|
+
const textOutputs = [];
|
|
5170
|
+
const gadgetResults = [];
|
|
5171
|
+
for await (const event of processor.process(stream2)) {
|
|
5172
|
+
if (event.type === "stream_complete") {
|
|
5173
|
+
streamMetadata = event;
|
|
5174
|
+
continue;
|
|
5175
|
+
}
|
|
5176
|
+
if (event.type === "text") {
|
|
5177
|
+
textOutputs.push(event.content);
|
|
5178
|
+
} else if (event.type === "gadget_result") {
|
|
5179
|
+
gadgetCallCount++;
|
|
5180
|
+
gadgetResults.push(event);
|
|
5181
|
+
}
|
|
5182
|
+
yield event;
|
|
4813
5183
|
}
|
|
5184
|
+
if (!streamMetadata) {
|
|
5185
|
+
throw new Error("Stream processing completed without metadata event");
|
|
5186
|
+
}
|
|
5187
|
+
const result = streamMetadata;
|
|
4814
5188
|
this.logger.info("LLM response completed", {
|
|
4815
5189
|
finishReason: result.finishReason,
|
|
4816
5190
|
usage: result.usage,
|
|
@@ -4835,9 +5209,6 @@ var init_agent = __esm({
|
|
|
4835
5209
|
});
|
|
4836
5210
|
let finalMessage = result.finalMessage;
|
|
4837
5211
|
if (this.hooks.controllers?.afterLLMCall) {
|
|
4838
|
-
const gadgetCallCount = result.outputs.filter(
|
|
4839
|
-
(output) => output.type === "gadget_result"
|
|
4840
|
-
).length;
|
|
4841
5212
|
const context = {
|
|
4842
5213
|
iteration: currentIteration,
|
|
4843
5214
|
maxIterations: this.maxIterations,
|
|
@@ -4858,31 +5229,29 @@ var init_agent = __esm({
|
|
|
4858
5229
|
if (msg.role === "user") {
|
|
4859
5230
|
this.conversation.addUserMessage(msg.content);
|
|
4860
5231
|
} else if (msg.role === "assistant") {
|
|
4861
|
-
this.conversation.addAssistantMessage(
|
|
5232
|
+
this.conversation.addAssistantMessage(extractMessageText(msg.content));
|
|
4862
5233
|
} else if (msg.role === "system") {
|
|
4863
|
-
this.conversation.addUserMessage(`[System] ${
|
|
5234
|
+
this.conversation.addUserMessage(`[System] ${extractMessageText(msg.content)}`);
|
|
4864
5235
|
}
|
|
4865
5236
|
}
|
|
4866
5237
|
}
|
|
4867
5238
|
}
|
|
4868
5239
|
if (result.didExecuteGadgets) {
|
|
4869
5240
|
if (this.textWithGadgetsHandler) {
|
|
4870
|
-
const textContent =
|
|
4871
|
-
(output) => output.type === "text"
|
|
4872
|
-
).map((output) => output.content).join("");
|
|
5241
|
+
const textContent = textOutputs.join("");
|
|
4873
5242
|
if (textContent.trim()) {
|
|
4874
5243
|
const { gadgetName, parameterMapping, resultMapping } = this.textWithGadgetsHandler;
|
|
4875
|
-
this.conversation.
|
|
5244
|
+
this.conversation.addGadgetCallResult(
|
|
4876
5245
|
gadgetName,
|
|
4877
5246
|
parameterMapping(textContent),
|
|
4878
5247
|
resultMapping ? resultMapping(textContent) : textContent
|
|
4879
5248
|
);
|
|
4880
5249
|
}
|
|
4881
5250
|
}
|
|
4882
|
-
for (const output of
|
|
5251
|
+
for (const output of gadgetResults) {
|
|
4883
5252
|
if (output.type === "gadget_result") {
|
|
4884
5253
|
const gadgetResult = output.result;
|
|
4885
|
-
this.conversation.
|
|
5254
|
+
this.conversation.addGadgetCallResult(
|
|
4886
5255
|
gadgetResult.gadgetName,
|
|
4887
5256
|
gadgetResult.parameters,
|
|
4888
5257
|
gadgetResult.error ?? gadgetResult.result ?? "",
|
|
@@ -4893,7 +5262,7 @@ var init_agent = __esm({
|
|
|
4893
5262
|
}
|
|
4894
5263
|
} else {
|
|
4895
5264
|
if (finalMessage.trim()) {
|
|
4896
|
-
this.conversation.
|
|
5265
|
+
this.conversation.addGadgetCallResult(
|
|
4897
5266
|
"TellUser",
|
|
4898
5267
|
{ message: finalMessage, done: false, type: "info" },
|
|
4899
5268
|
`\u2139\uFE0F ${finalMessage}`
|
|
@@ -5019,10 +5388,10 @@ var init_agent = __esm({
|
|
|
5019
5388
|
return this.client.modelRegistry.getModelLimits(unprefixedModelId)?.maxOutputTokens;
|
|
5020
5389
|
}
|
|
5021
5390
|
/**
|
|
5022
|
-
*
|
|
5391
|
+
* Chain the output limiter interceptor with user-provided hooks.
|
|
5023
5392
|
* The limiter runs first, then chains to any user interceptor.
|
|
5024
5393
|
*/
|
|
5025
|
-
|
|
5394
|
+
chainOutputLimiterWithUserHooks(userHooks) {
|
|
5026
5395
|
if (!this.outputLimitEnabled) {
|
|
5027
5396
|
return userHooks ?? {};
|
|
5028
5397
|
}
|
|
@@ -5376,9 +5745,9 @@ var init_base_provider = __esm({
|
|
|
5376
5745
|
*/
|
|
5377
5746
|
async *stream(options, descriptor, spec) {
|
|
5378
5747
|
const preparedMessages = this.prepareMessages(options.messages);
|
|
5379
|
-
const payload = this.
|
|
5748
|
+
const payload = this.buildApiRequest(options, descriptor, spec, preparedMessages);
|
|
5380
5749
|
const rawStream = await this.executeStreamRequest(payload, options.signal);
|
|
5381
|
-
yield* this.
|
|
5750
|
+
yield* this.normalizeProviderStream(rawStream);
|
|
5382
5751
|
}
|
|
5383
5752
|
/**
|
|
5384
5753
|
* Prepare messages for the request.
|
|
@@ -5478,11 +5847,11 @@ var init_anthropic = __esm({
|
|
|
5478
5847
|
"Anthropic does not support speech generation. Use OpenAI (TTS) or Google Gemini (TTS) instead."
|
|
5479
5848
|
);
|
|
5480
5849
|
}
|
|
5481
|
-
|
|
5850
|
+
buildApiRequest(options, descriptor, spec, messages) {
|
|
5482
5851
|
const systemMessages = messages.filter((message) => message.role === "system");
|
|
5483
5852
|
const system = systemMessages.length > 0 ? systemMessages.map((m, index) => ({
|
|
5484
5853
|
type: "text",
|
|
5485
|
-
text:
|
|
5854
|
+
text: extractMessageText(m.content),
|
|
5486
5855
|
// Add cache_control to the LAST system message block
|
|
5487
5856
|
...index === systemMessages.length - 1 ? { cache_control: { type: "ephemeral" } } : {}
|
|
5488
5857
|
})) : void 0;
|
|
@@ -5519,7 +5888,7 @@ var init_anthropic = __esm({
|
|
|
5519
5888
|
* Handles text, images (base64 only), and applies cache_control.
|
|
5520
5889
|
*/
|
|
5521
5890
|
convertToAnthropicContent(content, addCacheControl) {
|
|
5522
|
-
const parts =
|
|
5891
|
+
const parts = normalizeMessageContent(content);
|
|
5523
5892
|
return parts.map((part, index) => {
|
|
5524
5893
|
const isLastPart = index === parts.length - 1;
|
|
5525
5894
|
const cacheControl = addCacheControl && isLastPart ? { cache_control: { type: "ephemeral" } } : {};
|
|
@@ -5565,7 +5934,7 @@ var init_anthropic = __esm({
|
|
|
5565
5934
|
const stream2 = await client.messages.create(payload, signal ? { signal } : void 0);
|
|
5566
5935
|
return stream2;
|
|
5567
5936
|
}
|
|
5568
|
-
async *
|
|
5937
|
+
async *normalizeProviderStream(iterable) {
|
|
5569
5938
|
const stream2 = iterable;
|
|
5570
5939
|
let inputTokens = 0;
|
|
5571
5940
|
let cachedInputTokens = 0;
|
|
@@ -5642,7 +6011,7 @@ var init_anthropic = __esm({
|
|
|
5642
6011
|
async countTokens(messages, descriptor, _spec) {
|
|
5643
6012
|
const client = this.client;
|
|
5644
6013
|
const systemMessages = messages.filter((message) => message.role === "system");
|
|
5645
|
-
const system = systemMessages.length > 0 ? systemMessages.map((m) =>
|
|
6014
|
+
const system = systemMessages.length > 0 ? systemMessages.map((m) => extractMessageText(m.content)).join("\n\n") : void 0;
|
|
5646
6015
|
const conversation = messages.filter(
|
|
5647
6016
|
(message) => message.role !== "system"
|
|
5648
6017
|
).map((message) => ({
|
|
@@ -5664,7 +6033,7 @@ var init_anthropic = __esm({
|
|
|
5664
6033
|
let totalChars = 0;
|
|
5665
6034
|
let imageCount = 0;
|
|
5666
6035
|
for (const msg of messages) {
|
|
5667
|
-
const parts =
|
|
6036
|
+
const parts = normalizeMessageContent(msg.content);
|
|
5668
6037
|
for (const part of parts) {
|
|
5669
6038
|
if (part.type === "text") {
|
|
5670
6039
|
totalChars += part.text.length;
|
|
@@ -6355,7 +6724,7 @@ var init_gemini = __esm({
|
|
|
6355
6724
|
format: spec?.defaultFormat ?? "wav"
|
|
6356
6725
|
};
|
|
6357
6726
|
}
|
|
6358
|
-
|
|
6727
|
+
buildApiRequest(options, descriptor, _spec, messages) {
|
|
6359
6728
|
const contents = this.convertMessagesToContents(messages);
|
|
6360
6729
|
const generationConfig = this.buildGenerationConfig(options);
|
|
6361
6730
|
const config = {
|
|
@@ -6407,7 +6776,7 @@ var init_gemini = __esm({
|
|
|
6407
6776
|
if (message.role === "system") {
|
|
6408
6777
|
expandedMessages.push({
|
|
6409
6778
|
role: "user",
|
|
6410
|
-
content:
|
|
6779
|
+
content: extractMessageText(message.content)
|
|
6411
6780
|
});
|
|
6412
6781
|
expandedMessages.push({
|
|
6413
6782
|
role: "assistant",
|
|
@@ -6457,7 +6826,7 @@ var init_gemini = __esm({
|
|
|
6457
6826
|
* Handles text, images, and audio (Gemini supports all three).
|
|
6458
6827
|
*/
|
|
6459
6828
|
convertToGeminiParts(content) {
|
|
6460
|
-
const parts =
|
|
6829
|
+
const parts = normalizeMessageContent(content);
|
|
6461
6830
|
return parts.map((part) => {
|
|
6462
6831
|
if (part.type === "text") {
|
|
6463
6832
|
return { text: part.text };
|
|
@@ -6502,10 +6871,10 @@ var init_gemini = __esm({
|
|
|
6502
6871
|
}
|
|
6503
6872
|
return Object.keys(config).length > 0 ? config : null;
|
|
6504
6873
|
}
|
|
6505
|
-
async *
|
|
6874
|
+
async *normalizeProviderStream(iterable) {
|
|
6506
6875
|
const stream2 = iterable;
|
|
6507
6876
|
for await (const chunk of stream2) {
|
|
6508
|
-
const text3 = this.
|
|
6877
|
+
const text3 = this.extractMessageText(chunk);
|
|
6509
6878
|
if (text3) {
|
|
6510
6879
|
yield { text: text3, rawEvent: chunk };
|
|
6511
6880
|
}
|
|
@@ -6516,7 +6885,7 @@ var init_gemini = __esm({
|
|
|
6516
6885
|
}
|
|
6517
6886
|
}
|
|
6518
6887
|
}
|
|
6519
|
-
|
|
6888
|
+
extractMessageText(chunk) {
|
|
6520
6889
|
if (!chunk?.candidates) {
|
|
6521
6890
|
return "";
|
|
6522
6891
|
}
|
|
@@ -6581,7 +6950,7 @@ var init_gemini = __esm({
|
|
|
6581
6950
|
let totalChars = 0;
|
|
6582
6951
|
let mediaCount = 0;
|
|
6583
6952
|
for (const msg of messages) {
|
|
6584
|
-
const parts =
|
|
6953
|
+
const parts = normalizeMessageContent(msg.content);
|
|
6585
6954
|
for (const part of parts) {
|
|
6586
6955
|
if (part.type === "text") {
|
|
6587
6956
|
totalChars += part.text.length;
|
|
@@ -7327,7 +7696,7 @@ var init_openai = __esm({
|
|
|
7327
7696
|
format
|
|
7328
7697
|
};
|
|
7329
7698
|
}
|
|
7330
|
-
|
|
7699
|
+
buildApiRequest(options, descriptor, spec, messages) {
|
|
7331
7700
|
const { maxTokens, temperature, topP, stopSequences, extra } = options;
|
|
7332
7701
|
const supportsTemperature = spec?.metadata?.supportsTemperature !== false;
|
|
7333
7702
|
const shouldIncludeTemperature = typeof temperature === "number" && supportsTemperature;
|
|
@@ -7362,7 +7731,7 @@ var init_openai = __esm({
|
|
|
7362
7731
|
...message.name ? { name: message.name } : {}
|
|
7363
7732
|
};
|
|
7364
7733
|
}
|
|
7365
|
-
const textContent = typeof message.content === "string" ? message.content :
|
|
7734
|
+
const textContent = typeof message.content === "string" ? message.content : extractMessageText(message.content);
|
|
7366
7735
|
if (role === "system") {
|
|
7367
7736
|
return {
|
|
7368
7737
|
role: "system",
|
|
@@ -7422,7 +7791,7 @@ var init_openai = __esm({
|
|
|
7422
7791
|
const stream2 = await client.chat.completions.create(payload, signal ? { signal } : void 0);
|
|
7423
7792
|
return stream2;
|
|
7424
7793
|
}
|
|
7425
|
-
async *
|
|
7794
|
+
async *normalizeProviderStream(iterable) {
|
|
7426
7795
|
const stream2 = iterable;
|
|
7427
7796
|
for await (const chunk of stream2) {
|
|
7428
7797
|
const text3 = chunk.choices.map((choice) => choice.delta?.content ?? "").join("");
|
|
@@ -7480,9 +7849,9 @@ var init_openai = __esm({
|
|
|
7480
7849
|
tokenCount += OPENAI_MESSAGE_OVERHEAD_TOKENS;
|
|
7481
7850
|
const roleText = ROLE_MAP[message.role];
|
|
7482
7851
|
tokenCount += encoding.encode(roleText).length;
|
|
7483
|
-
const textContent =
|
|
7852
|
+
const textContent = extractMessageText(message.content);
|
|
7484
7853
|
tokenCount += encoding.encode(textContent).length;
|
|
7485
|
-
const parts =
|
|
7854
|
+
const parts = normalizeMessageContent(message.content);
|
|
7486
7855
|
for (const part of parts) {
|
|
7487
7856
|
if (part.type === "image") {
|
|
7488
7857
|
imageCount++;
|
|
@@ -7507,7 +7876,7 @@ var init_openai = __esm({
|
|
|
7507
7876
|
let totalChars = 0;
|
|
7508
7877
|
let imageCount = 0;
|
|
7509
7878
|
for (const msg of messages) {
|
|
7510
|
-
const parts =
|
|
7879
|
+
const parts = normalizeMessageContent(msg.content);
|
|
7511
7880
|
for (const part of parts) {
|
|
7512
7881
|
if (part.type === "text") {
|
|
7513
7882
|
totalChars += part.text.length;
|
|
@@ -8372,20 +8741,23 @@ var init_builder = __esm({
|
|
|
8372
8741
|
promptConfig;
|
|
8373
8742
|
gadgets = [];
|
|
8374
8743
|
initialMessages = [];
|
|
8375
|
-
|
|
8744
|
+
requestHumanInput;
|
|
8376
8745
|
gadgetStartPrefix;
|
|
8377
8746
|
gadgetEndPrefix;
|
|
8378
8747
|
gadgetArgPrefix;
|
|
8379
8748
|
textOnlyHandler;
|
|
8380
8749
|
textWithGadgetsHandler;
|
|
8381
8750
|
stopOnGadgetError;
|
|
8382
|
-
|
|
8751
|
+
canRecoverFromGadgetError;
|
|
8383
8752
|
defaultGadgetTimeoutMs;
|
|
8384
8753
|
gadgetOutputLimit;
|
|
8385
8754
|
gadgetOutputLimitPercent;
|
|
8386
8755
|
compactionConfig;
|
|
8387
8756
|
signal;
|
|
8388
8757
|
trailingMessage;
|
|
8758
|
+
subagentConfig;
|
|
8759
|
+
nestedEventCallback;
|
|
8760
|
+
parentContext;
|
|
8389
8761
|
constructor(client) {
|
|
8390
8762
|
this.client = client;
|
|
8391
8763
|
}
|
|
@@ -8476,13 +8848,13 @@ var init_builder = __esm({
|
|
|
8476
8848
|
*
|
|
8477
8849
|
* @example
|
|
8478
8850
|
* ```typescript
|
|
8479
|
-
* .
|
|
8851
|
+
* .withPromptTemplateConfig({
|
|
8480
8852
|
* mainInstruction: "Use the gadget markers below:",
|
|
8481
8853
|
* rules: ["Always use markers", "Never use function calling"]
|
|
8482
8854
|
* })
|
|
8483
8855
|
* ```
|
|
8484
8856
|
*/
|
|
8485
|
-
|
|
8857
|
+
withPromptTemplateConfig(config) {
|
|
8486
8858
|
this.promptConfig = config;
|
|
8487
8859
|
return this;
|
|
8488
8860
|
}
|
|
@@ -8562,7 +8934,7 @@ var init_builder = __esm({
|
|
|
8562
8934
|
* ```
|
|
8563
8935
|
*/
|
|
8564
8936
|
onHumanInput(handler) {
|
|
8565
|
-
this.
|
|
8937
|
+
this.requestHumanInput = handler;
|
|
8566
8938
|
return this;
|
|
8567
8939
|
}
|
|
8568
8940
|
/**
|
|
@@ -8697,9 +9069,9 @@ var init_builder = __esm({
|
|
|
8697
9069
|
* Provides fine-grained control over whether to continue after different types of errors.
|
|
8698
9070
|
* Overrides `stopOnGadgetError` when provided.
|
|
8699
9071
|
*
|
|
8700
|
-
* **Note:** This builder method configures the underlying `
|
|
9072
|
+
* **Note:** This builder method configures the underlying `canRecoverFromGadgetError` option
|
|
8701
9073
|
* in `AgentOptions`. The method is named `withErrorHandler` for better developer experience,
|
|
8702
|
-
* but maps to the `
|
|
9074
|
+
* but maps to the `canRecoverFromGadgetError` property internally.
|
|
8703
9075
|
*
|
|
8704
9076
|
* @param handler - Function that decides whether to continue after an error.
|
|
8705
9077
|
* Return `true` to continue execution, `false` to stop.
|
|
@@ -8720,7 +9092,7 @@ var init_builder = __esm({
|
|
|
8720
9092
|
* ```
|
|
8721
9093
|
*/
|
|
8722
9094
|
withErrorHandler(handler) {
|
|
8723
|
-
this.
|
|
9095
|
+
this.canRecoverFromGadgetError = handler;
|
|
8724
9096
|
return this;
|
|
8725
9097
|
}
|
|
8726
9098
|
/**
|
|
@@ -8857,8 +9229,97 @@ var init_builder = __esm({
|
|
|
8857
9229
|
* document.getElementById("cancel").onclick = () => controller.abort();
|
|
8858
9230
|
* ```
|
|
8859
9231
|
*/
|
|
8860
|
-
withSignal(signal) {
|
|
8861
|
-
this.signal = signal;
|
|
9232
|
+
withSignal(signal) {
|
|
9233
|
+
this.signal = signal;
|
|
9234
|
+
return this;
|
|
9235
|
+
}
|
|
9236
|
+
/**
|
|
9237
|
+
* Set subagent configuration overrides.
|
|
9238
|
+
*
|
|
9239
|
+
* Subagent gadgets (like BrowseWeb) can read these settings from ExecutionContext
|
|
9240
|
+
* to inherit model and other options from the CLI configuration.
|
|
9241
|
+
*
|
|
9242
|
+
* @param config - Subagent configuration map keyed by gadget name
|
|
9243
|
+
* @returns This builder for chaining
|
|
9244
|
+
*
|
|
9245
|
+
* @example
|
|
9246
|
+
* ```typescript
|
|
9247
|
+
* .withSubagentConfig({
|
|
9248
|
+
* BrowseWeb: { model: "inherit", maxIterations: 20, headless: true },
|
|
9249
|
+
* CodeAnalyzer: { model: "sonnet", maxIterations: 10 }
|
|
9250
|
+
* })
|
|
9251
|
+
* ```
|
|
9252
|
+
*/
|
|
9253
|
+
withSubagentConfig(config) {
|
|
9254
|
+
this.subagentConfig = config;
|
|
9255
|
+
return this;
|
|
9256
|
+
}
|
|
9257
|
+
/**
|
|
9258
|
+
* Set the callback for nested subagent events.
|
|
9259
|
+
*
|
|
9260
|
+
* Subagent gadgets (like BrowseWeb) can use ExecutionContext.onNestedEvent
|
|
9261
|
+
* to report their internal LLM calls and gadget executions in real-time.
|
|
9262
|
+
* This callback receives those events, enabling hierarchical progress display.
|
|
9263
|
+
*
|
|
9264
|
+
* @param callback - Function to handle nested agent events
|
|
9265
|
+
* @returns This builder for chaining
|
|
9266
|
+
*
|
|
9267
|
+
* @example
|
|
9268
|
+
* ```typescript
|
|
9269
|
+
* .withNestedEventCallback((event) => {
|
|
9270
|
+
* if (event.type === "llm_call_start") {
|
|
9271
|
+
* console.log(` Nested LLM #${event.event.iteration} starting...`);
|
|
9272
|
+
* } else if (event.type === "gadget_call") {
|
|
9273
|
+
* console.log(` ⏵ ${event.event.call.gadgetName}...`);
|
|
9274
|
+
* }
|
|
9275
|
+
* })
|
|
9276
|
+
* ```
|
|
9277
|
+
*/
|
|
9278
|
+
withNestedEventCallback(callback) {
|
|
9279
|
+
this.nestedEventCallback = callback;
|
|
9280
|
+
return this;
|
|
9281
|
+
}
|
|
9282
|
+
/**
|
|
9283
|
+
* Enable automatic nested event forwarding to parent agent.
|
|
9284
|
+
*
|
|
9285
|
+
* When building a subagent inside a gadget, call this method to automatically
|
|
9286
|
+
* forward all LLM calls and gadget events to the parent agent. This enables
|
|
9287
|
+
* hierarchical progress display without any manual event handling.
|
|
9288
|
+
*
|
|
9289
|
+
* The method extracts `invocationId` and `onNestedEvent` from the execution
|
|
9290
|
+
* context and sets up automatic forwarding via hooks and event wrapping.
|
|
9291
|
+
*
|
|
9292
|
+
* @param ctx - ExecutionContext passed to the gadget's execute() method
|
|
9293
|
+
* @param depth - Nesting depth (default: 1 for direct child)
|
|
9294
|
+
* @returns This builder for chaining
|
|
9295
|
+
*
|
|
9296
|
+
* @example
|
|
9297
|
+
* ```typescript
|
|
9298
|
+
* // In a subagent gadget like BrowseWeb - ONE LINE enables auto-forwarding:
|
|
9299
|
+
* execute: async (params, ctx) => {
|
|
9300
|
+
* const agent = new AgentBuilder(client)
|
|
9301
|
+
* .withModel(model)
|
|
9302
|
+
* .withGadgets(Navigate, Click, Screenshot)
|
|
9303
|
+
* .withParentContext(ctx) // <-- This is all you need!
|
|
9304
|
+
* .ask(params.task);
|
|
9305
|
+
*
|
|
9306
|
+
* for await (const event of agent.run()) {
|
|
9307
|
+
* // Events automatically forwarded - just process normally
|
|
9308
|
+
* if (event.type === "text") {
|
|
9309
|
+
* result = event.content;
|
|
9310
|
+
* }
|
|
9311
|
+
* }
|
|
9312
|
+
* }
|
|
9313
|
+
* ```
|
|
9314
|
+
*/
|
|
9315
|
+
withParentContext(ctx, depth = 1) {
|
|
9316
|
+
if (ctx.onNestedEvent && ctx.invocationId) {
|
|
9317
|
+
this.parentContext = {
|
|
9318
|
+
invocationId: ctx.invocationId,
|
|
9319
|
+
onNestedEvent: ctx.onNestedEvent,
|
|
9320
|
+
depth
|
|
9321
|
+
};
|
|
9322
|
+
}
|
|
8862
9323
|
return this;
|
|
8863
9324
|
}
|
|
8864
9325
|
/**
|
|
@@ -8928,14 +9389,58 @@ ${endPrefix}`
|
|
|
8928
9389
|
return this;
|
|
8929
9390
|
}
|
|
8930
9391
|
/**
|
|
8931
|
-
* Compose the final hooks, including
|
|
9392
|
+
* Compose the final hooks, including:
|
|
9393
|
+
* - Trailing message injection (if configured)
|
|
9394
|
+
* - Nested event forwarding for LLM calls (if parentContext is set)
|
|
8932
9395
|
*/
|
|
8933
9396
|
composeHooks() {
|
|
9397
|
+
let hooks = this.hooks;
|
|
9398
|
+
if (this.parentContext) {
|
|
9399
|
+
const { invocationId, onNestedEvent, depth } = this.parentContext;
|
|
9400
|
+
const existingOnLLMCallStart = hooks?.observers?.onLLMCallStart;
|
|
9401
|
+
const existingOnLLMCallComplete = hooks?.observers?.onLLMCallComplete;
|
|
9402
|
+
hooks = {
|
|
9403
|
+
...hooks,
|
|
9404
|
+
observers: {
|
|
9405
|
+
...hooks?.observers,
|
|
9406
|
+
onLLMCallStart: async (context) => {
|
|
9407
|
+
onNestedEvent({
|
|
9408
|
+
type: "llm_call_start",
|
|
9409
|
+
gadgetInvocationId: invocationId,
|
|
9410
|
+
depth,
|
|
9411
|
+
event: {
|
|
9412
|
+
iteration: context.iteration,
|
|
9413
|
+
model: context.options.model
|
|
9414
|
+
}
|
|
9415
|
+
});
|
|
9416
|
+
if (existingOnLLMCallStart) {
|
|
9417
|
+
await existingOnLLMCallStart(context);
|
|
9418
|
+
}
|
|
9419
|
+
},
|
|
9420
|
+
onLLMCallComplete: async (context) => {
|
|
9421
|
+
onNestedEvent({
|
|
9422
|
+
type: "llm_call_end",
|
|
9423
|
+
gadgetInvocationId: invocationId,
|
|
9424
|
+
depth,
|
|
9425
|
+
event: {
|
|
9426
|
+
iteration: context.iteration,
|
|
9427
|
+
model: context.options.model,
|
|
9428
|
+
outputTokens: context.usage?.outputTokens,
|
|
9429
|
+
finishReason: context.finishReason
|
|
9430
|
+
}
|
|
9431
|
+
});
|
|
9432
|
+
if (existingOnLLMCallComplete) {
|
|
9433
|
+
await existingOnLLMCallComplete(context);
|
|
9434
|
+
}
|
|
9435
|
+
}
|
|
9436
|
+
}
|
|
9437
|
+
};
|
|
9438
|
+
}
|
|
8934
9439
|
if (!this.trailingMessage) {
|
|
8935
|
-
return
|
|
9440
|
+
return hooks;
|
|
8936
9441
|
}
|
|
8937
9442
|
const trailingMsg = this.trailingMessage;
|
|
8938
|
-
const existingBeforeLLMCall =
|
|
9443
|
+
const existingBeforeLLMCall = hooks?.controllers?.beforeLLMCall;
|
|
8939
9444
|
const trailingMessageController = async (ctx) => {
|
|
8940
9445
|
const result = existingBeforeLLMCall ? await existingBeforeLLMCall(ctx) : { action: "proceed" };
|
|
8941
9446
|
if (result.action === "skip") {
|
|
@@ -8950,9 +9455,9 @@ ${endPrefix}`
|
|
|
8950
9455
|
};
|
|
8951
9456
|
};
|
|
8952
9457
|
return {
|
|
8953
|
-
...
|
|
9458
|
+
...hooks,
|
|
8954
9459
|
controllers: {
|
|
8955
|
-
...
|
|
9460
|
+
...hooks?.controllers,
|
|
8956
9461
|
beforeLLMCall: trailingMessageController
|
|
8957
9462
|
}
|
|
8958
9463
|
};
|
|
@@ -9013,6 +9518,19 @@ ${endPrefix}`
|
|
|
9013
9518
|
this.client = new LLMistClass();
|
|
9014
9519
|
}
|
|
9015
9520
|
const registry = GadgetRegistry.from(this.gadgets);
|
|
9521
|
+
let onNestedEvent = this.nestedEventCallback;
|
|
9522
|
+
if (this.parentContext) {
|
|
9523
|
+
const { invocationId, onNestedEvent: parentCallback, depth } = this.parentContext;
|
|
9524
|
+
const existingCallback = this.nestedEventCallback;
|
|
9525
|
+
onNestedEvent = (event) => {
|
|
9526
|
+
parentCallback({
|
|
9527
|
+
...event,
|
|
9528
|
+
gadgetInvocationId: invocationId,
|
|
9529
|
+
depth: event.depth + depth
|
|
9530
|
+
});
|
|
9531
|
+
existingCallback?.(event);
|
|
9532
|
+
};
|
|
9533
|
+
}
|
|
9016
9534
|
return {
|
|
9017
9535
|
client: this.client,
|
|
9018
9536
|
model: this.model ?? "openai:gpt-5-nano",
|
|
@@ -9025,19 +9543,21 @@ ${endPrefix}`
|
|
|
9025
9543
|
hooks: this.composeHooks(),
|
|
9026
9544
|
promptConfig: this.promptConfig,
|
|
9027
9545
|
initialMessages: this.initialMessages,
|
|
9028
|
-
|
|
9546
|
+
requestHumanInput: this.requestHumanInput,
|
|
9029
9547
|
gadgetStartPrefix: this.gadgetStartPrefix,
|
|
9030
9548
|
gadgetEndPrefix: this.gadgetEndPrefix,
|
|
9031
9549
|
gadgetArgPrefix: this.gadgetArgPrefix,
|
|
9032
9550
|
textOnlyHandler: this.textOnlyHandler,
|
|
9033
9551
|
textWithGadgetsHandler: this.textWithGadgetsHandler,
|
|
9034
9552
|
stopOnGadgetError: this.stopOnGadgetError,
|
|
9035
|
-
|
|
9553
|
+
canRecoverFromGadgetError: this.canRecoverFromGadgetError,
|
|
9036
9554
|
defaultGadgetTimeoutMs: this.defaultGadgetTimeoutMs,
|
|
9037
9555
|
gadgetOutputLimit: this.gadgetOutputLimit,
|
|
9038
9556
|
gadgetOutputLimitPercent: this.gadgetOutputLimitPercent,
|
|
9039
9557
|
compactionConfig: this.compactionConfig,
|
|
9040
|
-
signal: this.signal
|
|
9558
|
+
signal: this.signal,
|
|
9559
|
+
subagentConfig: this.subagentConfig,
|
|
9560
|
+
onNestedEvent
|
|
9041
9561
|
};
|
|
9042
9562
|
}
|
|
9043
9563
|
ask(userPrompt) {
|
|
@@ -9194,6 +9714,19 @@ ${endPrefix}`
|
|
|
9194
9714
|
this.client = new LLMistClass();
|
|
9195
9715
|
}
|
|
9196
9716
|
const registry = GadgetRegistry.from(this.gadgets);
|
|
9717
|
+
let onNestedEvent = this.nestedEventCallback;
|
|
9718
|
+
if (this.parentContext) {
|
|
9719
|
+
const { invocationId, onNestedEvent: parentCallback, depth } = this.parentContext;
|
|
9720
|
+
const existingCallback = this.nestedEventCallback;
|
|
9721
|
+
onNestedEvent = (event) => {
|
|
9722
|
+
parentCallback({
|
|
9723
|
+
...event,
|
|
9724
|
+
gadgetInvocationId: invocationId,
|
|
9725
|
+
depth: event.depth + depth
|
|
9726
|
+
});
|
|
9727
|
+
existingCallback?.(event);
|
|
9728
|
+
};
|
|
9729
|
+
}
|
|
9197
9730
|
const options = {
|
|
9198
9731
|
client: this.client,
|
|
9199
9732
|
model: this.model ?? "openai:gpt-5-nano",
|
|
@@ -9206,19 +9739,21 @@ ${endPrefix}`
|
|
|
9206
9739
|
hooks: this.composeHooks(),
|
|
9207
9740
|
promptConfig: this.promptConfig,
|
|
9208
9741
|
initialMessages: this.initialMessages,
|
|
9209
|
-
|
|
9742
|
+
requestHumanInput: this.requestHumanInput,
|
|
9210
9743
|
gadgetStartPrefix: this.gadgetStartPrefix,
|
|
9211
9744
|
gadgetEndPrefix: this.gadgetEndPrefix,
|
|
9212
9745
|
gadgetArgPrefix: this.gadgetArgPrefix,
|
|
9213
9746
|
textOnlyHandler: this.textOnlyHandler,
|
|
9214
9747
|
textWithGadgetsHandler: this.textWithGadgetsHandler,
|
|
9215
9748
|
stopOnGadgetError: this.stopOnGadgetError,
|
|
9216
|
-
|
|
9749
|
+
canRecoverFromGadgetError: this.canRecoverFromGadgetError,
|
|
9217
9750
|
defaultGadgetTimeoutMs: this.defaultGadgetTimeoutMs,
|
|
9218
9751
|
gadgetOutputLimit: this.gadgetOutputLimit,
|
|
9219
9752
|
gadgetOutputLimitPercent: this.gadgetOutputLimitPercent,
|
|
9220
9753
|
compactionConfig: this.compactionConfig,
|
|
9221
|
-
signal: this.signal
|
|
9754
|
+
signal: this.signal,
|
|
9755
|
+
subagentConfig: this.subagentConfig,
|
|
9756
|
+
onNestedEvent
|
|
9222
9757
|
};
|
|
9223
9758
|
return new Agent(AGENT_INTERNAL_KEY, options);
|
|
9224
9759
|
}
|
|
@@ -9236,7 +9771,8 @@ var COMMANDS = {
|
|
|
9236
9771
|
gadget: "gadget",
|
|
9237
9772
|
image: "image",
|
|
9238
9773
|
speech: "speech",
|
|
9239
|
-
vision: "vision"
|
|
9774
|
+
vision: "vision",
|
|
9775
|
+
init: "init"
|
|
9240
9776
|
};
|
|
9241
9777
|
var LOG_LEVELS = ["silly", "trace", "debug", "info", "warn", "error", "fatal"];
|
|
9242
9778
|
var DEFAULT_MODEL = "openai:gpt-5-nano";
|
|
@@ -9312,7 +9848,7 @@ var import_commander2 = require("commander");
|
|
|
9312
9848
|
// package.json
|
|
9313
9849
|
var package_default = {
|
|
9314
9850
|
name: "llmist",
|
|
9315
|
-
version: "
|
|
9851
|
+
version: "3.0.0",
|
|
9316
9852
|
description: "TypeScript LLM client with streaming tool execution. Tools fire mid-stream. Built-in function calling works with any model\u2014no structured outputs or native tool support required.",
|
|
9317
9853
|
type: "module",
|
|
9318
9854
|
main: "dist/index.cjs",
|
|
@@ -9720,7 +10256,7 @@ var askUser = createGadget({
|
|
|
9720
10256
|
}
|
|
9721
10257
|
],
|
|
9722
10258
|
execute: ({ question }) => {
|
|
9723
|
-
throw new
|
|
10259
|
+
throw new HumanInputRequiredException(question);
|
|
9724
10260
|
}
|
|
9725
10261
|
});
|
|
9726
10262
|
var tellUser = createGadget({
|
|
@@ -9770,11 +10306,59 @@ var finish = createGadget({
|
|
|
9770
10306
|
}
|
|
9771
10307
|
],
|
|
9772
10308
|
execute: () => {
|
|
9773
|
-
throw new
|
|
10309
|
+
throw new TaskCompletionSignal("Task completed");
|
|
9774
10310
|
}
|
|
9775
10311
|
});
|
|
9776
10312
|
var builtinGadgets = [askUser, tellUser, finish];
|
|
9777
10313
|
|
|
10314
|
+
// src/cli/subagent-config.ts
|
|
10315
|
+
var INHERIT_MODEL = "inherit";
|
|
10316
|
+
function resolveSubagentConfig(subagentName, parentModel, profileConfig, globalConfig) {
|
|
10317
|
+
const resolved = {};
|
|
10318
|
+
const globalDefaultModel = globalConfig?.["default-model"];
|
|
10319
|
+
const globalSubagent = extractSubagentConfig(globalConfig, subagentName);
|
|
10320
|
+
const profileSubagent = profileConfig?.[subagentName] ?? {};
|
|
10321
|
+
const merged = { ...globalSubagent, ...profileSubagent };
|
|
10322
|
+
const configModel = merged.model ?? globalDefaultModel ?? INHERIT_MODEL;
|
|
10323
|
+
resolved.model = configModel === INHERIT_MODEL ? parentModel : configModel;
|
|
10324
|
+
for (const [key, value] of Object.entries(merged)) {
|
|
10325
|
+
if (key !== "model") {
|
|
10326
|
+
resolved[key] = value;
|
|
10327
|
+
}
|
|
10328
|
+
}
|
|
10329
|
+
return resolved;
|
|
10330
|
+
}
|
|
10331
|
+
function buildSubagentConfigMap(parentModel, profileConfig, globalConfig) {
|
|
10332
|
+
const subagentNames = /* @__PURE__ */ new Set();
|
|
10333
|
+
if (globalConfig) {
|
|
10334
|
+
for (const key of Object.keys(globalConfig)) {
|
|
10335
|
+
if (key !== "default-model" && typeof globalConfig[key] === "object") {
|
|
10336
|
+
subagentNames.add(key);
|
|
10337
|
+
}
|
|
10338
|
+
}
|
|
10339
|
+
}
|
|
10340
|
+
if (profileConfig) {
|
|
10341
|
+
for (const key of Object.keys(profileConfig)) {
|
|
10342
|
+
subagentNames.add(key);
|
|
10343
|
+
}
|
|
10344
|
+
}
|
|
10345
|
+
const result = {};
|
|
10346
|
+
for (const name of subagentNames) {
|
|
10347
|
+
result[name] = resolveSubagentConfig(name, parentModel, profileConfig, globalConfig);
|
|
10348
|
+
}
|
|
10349
|
+
return result;
|
|
10350
|
+
}
|
|
10351
|
+
function extractSubagentConfig(globalConfig, subagentName) {
|
|
10352
|
+
if (!globalConfig) {
|
|
10353
|
+
return {};
|
|
10354
|
+
}
|
|
10355
|
+
const value = globalConfig[subagentName];
|
|
10356
|
+
if (typeof value === "object" && value !== null) {
|
|
10357
|
+
return value;
|
|
10358
|
+
}
|
|
10359
|
+
return {};
|
|
10360
|
+
}
|
|
10361
|
+
|
|
9778
10362
|
// src/cli/config.ts
|
|
9779
10363
|
var import_node_fs3 = require("fs");
|
|
9780
10364
|
var import_node_os2 = require("os");
|
|
@@ -9863,7 +10447,7 @@ function hasTemplateSyntax(str) {
|
|
|
9863
10447
|
}
|
|
9864
10448
|
|
|
9865
10449
|
// src/cli/config.ts
|
|
9866
|
-
var
|
|
10450
|
+
var VALID_PERMISSION_LEVELS = ["allowed", "denied", "approval-required"];
|
|
9867
10451
|
var GLOBAL_CONFIG_KEYS = /* @__PURE__ */ new Set(["log-level", "log-file", "log-reset"]);
|
|
9868
10452
|
var VALID_LOG_LEVELS = ["silly", "trace", "debug", "info", "warn", "error", "fatal"];
|
|
9869
10453
|
var COMPLETE_CONFIG_KEYS = /* @__PURE__ */ new Set([
|
|
@@ -9903,6 +10487,8 @@ var AGENT_CONFIG_KEYS = /* @__PURE__ */ new Set([
|
|
|
9903
10487
|
"gadget-end-prefix",
|
|
9904
10488
|
"gadget-arg-prefix",
|
|
9905
10489
|
"gadget-approval",
|
|
10490
|
+
"subagents",
|
|
10491
|
+
// Per-subagent configuration overrides
|
|
9906
10492
|
"quiet",
|
|
9907
10493
|
"inherits",
|
|
9908
10494
|
"log-level",
|
|
@@ -9928,9 +10514,9 @@ function getConfigPath() {
|
|
|
9928
10514
|
return (0, import_node_path4.join)((0, import_node_os2.homedir)(), ".llmist", "cli.toml");
|
|
9929
10515
|
}
|
|
9930
10516
|
var ConfigError = class extends Error {
|
|
9931
|
-
constructor(message,
|
|
9932
|
-
super(
|
|
9933
|
-
this.path =
|
|
10517
|
+
constructor(message, path6) {
|
|
10518
|
+
super(path6 ? `${path6}: ${message}` : message);
|
|
10519
|
+
this.path = path6;
|
|
9934
10520
|
this.name = "ConfigError";
|
|
9935
10521
|
}
|
|
9936
10522
|
};
|
|
@@ -9986,6 +10572,63 @@ function validateInherits(value, section) {
|
|
|
9986
10572
|
}
|
|
9987
10573
|
throw new ConfigError(`[${section}].inherits must be a string or array of strings`);
|
|
9988
10574
|
}
|
|
10575
|
+
function validateSingleSubagentConfig(value, subagentName, section) {
|
|
10576
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
10577
|
+
throw new ConfigError(
|
|
10578
|
+
`[${section}].${subagentName} must be a table (e.g., { model = "inherit", maxIterations = 20 })`
|
|
10579
|
+
);
|
|
10580
|
+
}
|
|
10581
|
+
const result = {};
|
|
10582
|
+
const rawObj = value;
|
|
10583
|
+
for (const [key, val] of Object.entries(rawObj)) {
|
|
10584
|
+
if (key === "model") {
|
|
10585
|
+
if (typeof val !== "string") {
|
|
10586
|
+
throw new ConfigError(`[${section}].${subagentName}.model must be a string`);
|
|
10587
|
+
}
|
|
10588
|
+
result.model = val;
|
|
10589
|
+
} else if (key === "maxIterations") {
|
|
10590
|
+
if (typeof val !== "number" || !Number.isInteger(val) || val < 1) {
|
|
10591
|
+
throw new ConfigError(
|
|
10592
|
+
`[${section}].${subagentName}.maxIterations must be a positive integer`
|
|
10593
|
+
);
|
|
10594
|
+
}
|
|
10595
|
+
result.maxIterations = val;
|
|
10596
|
+
} else {
|
|
10597
|
+
result[key] = val;
|
|
10598
|
+
}
|
|
10599
|
+
}
|
|
10600
|
+
return result;
|
|
10601
|
+
}
|
|
10602
|
+
function validateSubagentConfigMap(value, section) {
|
|
10603
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
10604
|
+
throw new ConfigError(
|
|
10605
|
+
`[${section}].subagents must be a table (e.g., { BrowseWeb = { model = "inherit" } })`
|
|
10606
|
+
);
|
|
10607
|
+
}
|
|
10608
|
+
const result = {};
|
|
10609
|
+
for (const [subagentName, config] of Object.entries(value)) {
|
|
10610
|
+
result[subagentName] = validateSingleSubagentConfig(config, subagentName, `${section}.subagents`);
|
|
10611
|
+
}
|
|
10612
|
+
return result;
|
|
10613
|
+
}
|
|
10614
|
+
function validateGlobalSubagentConfig(value, section) {
|
|
10615
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
10616
|
+
throw new ConfigError(`[${section}] must be a table`);
|
|
10617
|
+
}
|
|
10618
|
+
const result = {};
|
|
10619
|
+
const rawObj = value;
|
|
10620
|
+
for (const [key, val] of Object.entries(rawObj)) {
|
|
10621
|
+
if (key === "default-model") {
|
|
10622
|
+
if (typeof val !== "string") {
|
|
10623
|
+
throw new ConfigError(`[${section}].default-model must be a string`);
|
|
10624
|
+
}
|
|
10625
|
+
result["default-model"] = val;
|
|
10626
|
+
} else {
|
|
10627
|
+
result[key] = validateSingleSubagentConfig(val, key, section);
|
|
10628
|
+
}
|
|
10629
|
+
}
|
|
10630
|
+
return result;
|
|
10631
|
+
}
|
|
9989
10632
|
function validateGadgetApproval(value, section) {
|
|
9990
10633
|
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
9991
10634
|
throw new ConfigError(
|
|
@@ -9997,9 +10640,9 @@ function validateGadgetApproval(value, section) {
|
|
|
9997
10640
|
if (typeof mode !== "string") {
|
|
9998
10641
|
throw new ConfigError(`[${section}].gadget-approval.${gadgetName} must be a string`);
|
|
9999
10642
|
}
|
|
10000
|
-
if (!
|
|
10643
|
+
if (!VALID_PERMISSION_LEVELS.includes(mode)) {
|
|
10001
10644
|
throw new ConfigError(
|
|
10002
|
-
`[${section}].gadget-approval.${gadgetName} must be one of: ${
|
|
10645
|
+
`[${section}].gadget-approval.${gadgetName} must be one of: ${VALID_PERMISSION_LEVELS.join(", ")}`
|
|
10003
10646
|
);
|
|
10004
10647
|
}
|
|
10005
10648
|
result[gadgetName] = mode;
|
|
@@ -10168,6 +10811,9 @@ function validateAgentConfig(raw, section) {
|
|
|
10168
10811
|
if ("gadget-approval" in rawObj) {
|
|
10169
10812
|
result["gadget-approval"] = validateGadgetApproval(rawObj["gadget-approval"], section);
|
|
10170
10813
|
}
|
|
10814
|
+
if ("subagents" in rawObj) {
|
|
10815
|
+
result.subagents = validateSubagentConfigMap(rawObj.subagents, section);
|
|
10816
|
+
}
|
|
10171
10817
|
if ("quiet" in rawObj) {
|
|
10172
10818
|
result.quiet = validateBoolean(rawObj.quiet, "quiet", section);
|
|
10173
10819
|
}
|
|
@@ -10336,6 +10982,9 @@ function validateCustomConfig(raw, section) {
|
|
|
10336
10982
|
if ("gadget-approval" in rawObj) {
|
|
10337
10983
|
result["gadget-approval"] = validateGadgetApproval(rawObj["gadget-approval"], section);
|
|
10338
10984
|
}
|
|
10985
|
+
if ("subagents" in rawObj) {
|
|
10986
|
+
result.subagents = validateSubagentConfigMap(rawObj.subagents, section);
|
|
10987
|
+
}
|
|
10339
10988
|
if ("max-tokens" in rawObj) {
|
|
10340
10989
|
result["max-tokens"] = validateNumber(rawObj["max-tokens"], "max-tokens", section, {
|
|
10341
10990
|
integer: true,
|
|
@@ -10383,6 +11032,8 @@ function validateConfig(raw, configPath) {
|
|
|
10383
11032
|
result.prompts = validatePromptsConfig(value, key);
|
|
10384
11033
|
} else if (key === "docker") {
|
|
10385
11034
|
result.docker = validateDockerConfig(value, key);
|
|
11035
|
+
} else if (key === "subagents") {
|
|
11036
|
+
result.subagents = validateGlobalSubagentConfig(value, key);
|
|
10386
11037
|
} else {
|
|
10387
11038
|
result[key] = validateCustomConfig(value, key);
|
|
10388
11039
|
}
|
|
@@ -10423,7 +11074,16 @@ function loadConfig() {
|
|
|
10423
11074
|
return resolveTemplatesInConfig(inherited, configPath);
|
|
10424
11075
|
}
|
|
10425
11076
|
function getCustomCommandNames(config) {
|
|
10426
|
-
const reserved = /* @__PURE__ */ new Set([
|
|
11077
|
+
const reserved = /* @__PURE__ */ new Set([
|
|
11078
|
+
"global",
|
|
11079
|
+
"complete",
|
|
11080
|
+
"agent",
|
|
11081
|
+
"image",
|
|
11082
|
+
"speech",
|
|
11083
|
+
"prompts",
|
|
11084
|
+
"docker",
|
|
11085
|
+
"subagents"
|
|
11086
|
+
]);
|
|
10427
11087
|
return Object.keys(config).filter((key) => !reserved.has(key));
|
|
10428
11088
|
}
|
|
10429
11089
|
function resolveTemplatesInConfig(config, configPath) {
|
|
@@ -10957,11 +11617,11 @@ function resolveDevMode(config, cliDevMode) {
|
|
|
10957
11617
|
}
|
|
10958
11618
|
return { enabled: true, sourcePath };
|
|
10959
11619
|
}
|
|
10960
|
-
function expandHome(
|
|
10961
|
-
if (
|
|
10962
|
-
return
|
|
11620
|
+
function expandHome(path6) {
|
|
11621
|
+
if (path6.startsWith("~")) {
|
|
11622
|
+
return path6.replace(/^~/, (0, import_node_os4.homedir)());
|
|
10963
11623
|
}
|
|
10964
|
-
return
|
|
11624
|
+
return path6;
|
|
10965
11625
|
}
|
|
10966
11626
|
function buildDockerRunArgs(ctx, imageName, devMode) {
|
|
10967
11627
|
const args = ["run", "--rm"];
|
|
@@ -11138,9 +11798,9 @@ async function readFileBuffer(filePath, options = {}) {
|
|
|
11138
11798
|
}
|
|
11139
11799
|
|
|
11140
11800
|
// src/cli/gadgets.ts
|
|
11141
|
-
var
|
|
11142
|
-
var
|
|
11143
|
-
var
|
|
11801
|
+
var import_node_fs11 = __toESM(require("fs"), 1);
|
|
11802
|
+
var import_node_path12 = __toESM(require("path"), 1);
|
|
11803
|
+
var import_node_url2 = require("url");
|
|
11144
11804
|
init_gadget();
|
|
11145
11805
|
|
|
11146
11806
|
// src/cli/builtins/filesystem/edit-file.ts
|
|
@@ -11665,6 +12325,208 @@ function isBuiltinGadgetName(name) {
|
|
|
11665
12325
|
return name in builtinGadgetRegistry;
|
|
11666
12326
|
}
|
|
11667
12327
|
|
|
12328
|
+
// src/cli/external-gadgets.ts
|
|
12329
|
+
var import_node_child_process = require("child_process");
|
|
12330
|
+
var import_node_fs10 = __toESM(require("fs"), 1);
|
|
12331
|
+
var import_node_path11 = __toESM(require("path"), 1);
|
|
12332
|
+
var import_node_os5 = __toESM(require("os"), 1);
|
|
12333
|
+
var import_node_url = require("url");
|
|
12334
|
+
var CACHE_DIR2 = import_node_path11.default.join(import_node_os5.default.homedir(), ".llmist", "gadget-cache");
|
|
12335
|
+
function isExternalPackageSpecifier(specifier) {
|
|
12336
|
+
if (/^@?[a-z0-9][\w.-]*(?:@[\w.-]+)?(?::[a-z]+)?(?:\/\w+)?$/i.test(specifier)) {
|
|
12337
|
+
return true;
|
|
12338
|
+
}
|
|
12339
|
+
if (specifier.startsWith("git+")) {
|
|
12340
|
+
return true;
|
|
12341
|
+
}
|
|
12342
|
+
return false;
|
|
12343
|
+
}
|
|
12344
|
+
function parseGadgetSpecifier(specifier) {
|
|
12345
|
+
if (specifier.startsWith("git+")) {
|
|
12346
|
+
const url = specifier.slice(4);
|
|
12347
|
+
const [baseUrl, ref] = url.split("#");
|
|
12348
|
+
return {
|
|
12349
|
+
type: "git",
|
|
12350
|
+
package: baseUrl,
|
|
12351
|
+
version: ref
|
|
12352
|
+
};
|
|
12353
|
+
}
|
|
12354
|
+
const npmMatch = specifier.match(
|
|
12355
|
+
/^(@?[a-z0-9][\w.-]*)(?:@([\w.-]+))?(?::([a-z]+))?(?:\/(\w+))?$/i
|
|
12356
|
+
);
|
|
12357
|
+
if (npmMatch) {
|
|
12358
|
+
const [, pkg, version, preset, gadgetName] = npmMatch;
|
|
12359
|
+
return {
|
|
12360
|
+
type: "npm",
|
|
12361
|
+
package: pkg,
|
|
12362
|
+
version,
|
|
12363
|
+
preset,
|
|
12364
|
+
gadgetName
|
|
12365
|
+
};
|
|
12366
|
+
}
|
|
12367
|
+
return null;
|
|
12368
|
+
}
|
|
12369
|
+
function getCacheDir(spec) {
|
|
12370
|
+
const versionSuffix = spec.version ? `@${spec.version}` : "@latest";
|
|
12371
|
+
if (spec.type === "npm") {
|
|
12372
|
+
return import_node_path11.default.join(CACHE_DIR2, "npm", `${spec.package}${versionSuffix}`);
|
|
12373
|
+
}
|
|
12374
|
+
const sanitizedUrl = spec.package.replace(/[/:]/g, "-").replace(/^-+|-+$/g, "");
|
|
12375
|
+
return import_node_path11.default.join(CACHE_DIR2, "git", `${sanitizedUrl}${versionSuffix}`);
|
|
12376
|
+
}
|
|
12377
|
+
function isCached(cacheDir) {
|
|
12378
|
+
const packageJsonPath = import_node_path11.default.join(cacheDir, "package.json");
|
|
12379
|
+
return import_node_fs10.default.existsSync(packageJsonPath);
|
|
12380
|
+
}
|
|
12381
|
+
async function installNpmPackage(spec, cacheDir) {
|
|
12382
|
+
import_node_fs10.default.mkdirSync(cacheDir, { recursive: true });
|
|
12383
|
+
const packageJson = {
|
|
12384
|
+
name: "llmist-gadget-cache",
|
|
12385
|
+
private: true,
|
|
12386
|
+
type: "module"
|
|
12387
|
+
};
|
|
12388
|
+
import_node_fs10.default.writeFileSync(import_node_path11.default.join(cacheDir, "package.json"), JSON.stringify(packageJson, null, 2));
|
|
12389
|
+
const packageSpec = spec.version ? `${spec.package}@${spec.version}` : spec.package;
|
|
12390
|
+
try {
|
|
12391
|
+
(0, import_node_child_process.execSync)(`npm install --prefix "${cacheDir}" "${packageSpec}" --save`, {
|
|
12392
|
+
stdio: "pipe",
|
|
12393
|
+
cwd: cacheDir
|
|
12394
|
+
});
|
|
12395
|
+
} catch (error) {
|
|
12396
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
12397
|
+
throw new Error(`Failed to install npm package '${packageSpec}': ${message}`);
|
|
12398
|
+
}
|
|
12399
|
+
}
|
|
12400
|
+
async function installGitPackage(spec, cacheDir) {
|
|
12401
|
+
import_node_fs10.default.mkdirSync(import_node_path11.default.dirname(cacheDir), { recursive: true });
|
|
12402
|
+
if (import_node_fs10.default.existsSync(cacheDir)) {
|
|
12403
|
+
try {
|
|
12404
|
+
(0, import_node_child_process.execSync)("git fetch", { cwd: cacheDir, stdio: "pipe" });
|
|
12405
|
+
if (spec.version) {
|
|
12406
|
+
(0, import_node_child_process.execSync)(`git checkout ${spec.version}`, { cwd: cacheDir, stdio: "pipe" });
|
|
12407
|
+
}
|
|
12408
|
+
} catch (error) {
|
|
12409
|
+
import_node_fs10.default.rmSync(cacheDir, { recursive: true, force: true });
|
|
12410
|
+
}
|
|
12411
|
+
}
|
|
12412
|
+
if (!import_node_fs10.default.existsSync(cacheDir)) {
|
|
12413
|
+
try {
|
|
12414
|
+
const cloneCmd = spec.version ? `git clone --branch ${spec.version} "${spec.package}" "${cacheDir}"` : `git clone "${spec.package}" "${cacheDir}"`;
|
|
12415
|
+
(0, import_node_child_process.execSync)(cloneCmd, { stdio: "pipe" });
|
|
12416
|
+
} catch (error) {
|
|
12417
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
12418
|
+
throw new Error(`Failed to clone git repository '${spec.package}': ${message}`);
|
|
12419
|
+
}
|
|
12420
|
+
if (import_node_fs10.default.existsSync(import_node_path11.default.join(cacheDir, "package.json"))) {
|
|
12421
|
+
try {
|
|
12422
|
+
(0, import_node_child_process.execSync)("npm install", { cwd: cacheDir, stdio: "pipe" });
|
|
12423
|
+
} catch (error) {
|
|
12424
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
12425
|
+
throw new Error(`Failed to install dependencies for '${spec.package}': ${message}`);
|
|
12426
|
+
}
|
|
12427
|
+
try {
|
|
12428
|
+
const packageJson = JSON.parse(import_node_fs10.default.readFileSync(import_node_path11.default.join(cacheDir, "package.json"), "utf-8"));
|
|
12429
|
+
if (packageJson.scripts?.build) {
|
|
12430
|
+
(0, import_node_child_process.execSync)("npm run build", { cwd: cacheDir, stdio: "pipe" });
|
|
12431
|
+
}
|
|
12432
|
+
} catch (error) {
|
|
12433
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
12434
|
+
throw new Error(`Failed to build package '${spec.package}': ${message}`);
|
|
12435
|
+
}
|
|
12436
|
+
}
|
|
12437
|
+
}
|
|
12438
|
+
}
|
|
12439
|
+
function readManifest(packageDir) {
|
|
12440
|
+
const packageJsonPath = import_node_path11.default.join(packageDir, "package.json");
|
|
12441
|
+
if (!import_node_fs10.default.existsSync(packageJsonPath)) {
|
|
12442
|
+
return null;
|
|
12443
|
+
}
|
|
12444
|
+
try {
|
|
12445
|
+
const packageJson = JSON.parse(import_node_fs10.default.readFileSync(packageJsonPath, "utf-8"));
|
|
12446
|
+
return packageJson.llmist || null;
|
|
12447
|
+
} catch {
|
|
12448
|
+
return null;
|
|
12449
|
+
}
|
|
12450
|
+
}
|
|
12451
|
+
function getPackagePath(cacheDir, packageName) {
|
|
12452
|
+
const nodeModulesPath = import_node_path11.default.join(cacheDir, "node_modules", packageName);
|
|
12453
|
+
if (import_node_fs10.default.existsSync(nodeModulesPath)) {
|
|
12454
|
+
return nodeModulesPath;
|
|
12455
|
+
}
|
|
12456
|
+
return cacheDir;
|
|
12457
|
+
}
|
|
12458
|
+
async function loadExternalGadgets(specifier, forceInstall = false) {
|
|
12459
|
+
const spec = parseGadgetSpecifier(specifier);
|
|
12460
|
+
if (!spec) {
|
|
12461
|
+
throw new Error(`Invalid external package specifier: ${specifier}`);
|
|
12462
|
+
}
|
|
12463
|
+
const cacheDir = getCacheDir(spec);
|
|
12464
|
+
if (!isCached(cacheDir) || forceInstall) {
|
|
12465
|
+
if (spec.type === "npm") {
|
|
12466
|
+
await installNpmPackage(spec, cacheDir);
|
|
12467
|
+
} else {
|
|
12468
|
+
await installGitPackage(spec, cacheDir);
|
|
12469
|
+
}
|
|
12470
|
+
}
|
|
12471
|
+
const packagePath = getPackagePath(cacheDir, spec.package);
|
|
12472
|
+
const manifest = readManifest(packagePath);
|
|
12473
|
+
let entryPoint;
|
|
12474
|
+
let gadgetNames = null;
|
|
12475
|
+
if (spec.gadgetName) {
|
|
12476
|
+
gadgetNames = [spec.gadgetName];
|
|
12477
|
+
if (manifest?.subagents?.[spec.gadgetName]) {
|
|
12478
|
+
entryPoint = manifest.subagents[spec.gadgetName].entryPoint;
|
|
12479
|
+
} else {
|
|
12480
|
+
entryPoint = manifest?.gadgets || "./dist/index.js";
|
|
12481
|
+
}
|
|
12482
|
+
} else if (spec.preset) {
|
|
12483
|
+
if (!manifest?.presets?.[spec.preset]) {
|
|
12484
|
+
throw new Error(`Unknown preset '${spec.preset}' in package '${spec.package}'`);
|
|
12485
|
+
}
|
|
12486
|
+
const preset = manifest.presets[spec.preset];
|
|
12487
|
+
if (preset === "*") {
|
|
12488
|
+
gadgetNames = null;
|
|
12489
|
+
} else {
|
|
12490
|
+
gadgetNames = preset;
|
|
12491
|
+
}
|
|
12492
|
+
entryPoint = manifest?.gadgets || "./dist/index.js";
|
|
12493
|
+
} else {
|
|
12494
|
+
entryPoint = manifest?.gadgets || "./dist/index.js";
|
|
12495
|
+
}
|
|
12496
|
+
const resolvedEntryPoint = import_node_path11.default.resolve(packagePath, entryPoint);
|
|
12497
|
+
if (!import_node_fs10.default.existsSync(resolvedEntryPoint)) {
|
|
12498
|
+
throw new Error(
|
|
12499
|
+
`Entry point not found: ${resolvedEntryPoint}. Make sure the package is built (run 'npm run build' in the package directory).`
|
|
12500
|
+
);
|
|
12501
|
+
}
|
|
12502
|
+
const moduleUrl = (0, import_node_url.pathToFileURL)(resolvedEntryPoint).href;
|
|
12503
|
+
let exports2;
|
|
12504
|
+
try {
|
|
12505
|
+
exports2 = await import(moduleUrl);
|
|
12506
|
+
} catch (error) {
|
|
12507
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
12508
|
+
throw new Error(`Failed to import '${specifier}': ${message}`);
|
|
12509
|
+
}
|
|
12510
|
+
let gadgets = extractGadgetsFromModule(exports2);
|
|
12511
|
+
if (gadgetNames) {
|
|
12512
|
+
const gadgetSet = new Set(gadgetNames.map((n) => n.toLowerCase()));
|
|
12513
|
+
gadgets = gadgets.filter((g) => {
|
|
12514
|
+
const name = g.name?.toLowerCase() || "";
|
|
12515
|
+
return gadgetSet.has(name);
|
|
12516
|
+
});
|
|
12517
|
+
const foundNames = new Set(gadgets.map((g) => g.name?.toLowerCase() || ""));
|
|
12518
|
+
for (const requested of gadgetNames) {
|
|
12519
|
+
if (!foundNames.has(requested.toLowerCase())) {
|
|
12520
|
+
throw new Error(`Gadget '${requested}' not found in package '${spec.package}'`);
|
|
12521
|
+
}
|
|
12522
|
+
}
|
|
12523
|
+
}
|
|
12524
|
+
if (gadgets.length === 0) {
|
|
12525
|
+
throw new Error(`No gadgets found in package '${spec.package}'`);
|
|
12526
|
+
}
|
|
12527
|
+
return gadgets;
|
|
12528
|
+
}
|
|
12529
|
+
|
|
11668
12530
|
// src/cli/gadgets.ts
|
|
11669
12531
|
var PATH_PREFIXES = [".", "/", "~"];
|
|
11670
12532
|
var BUILTIN_PREFIX = "builtin:";
|
|
@@ -11680,7 +12542,17 @@ function isGadgetConstructor(value) {
|
|
|
11680
12542
|
return false;
|
|
11681
12543
|
}
|
|
11682
12544
|
const prototype = value.prototype;
|
|
11683
|
-
|
|
12545
|
+
if (!prototype) {
|
|
12546
|
+
return false;
|
|
12547
|
+
}
|
|
12548
|
+
if (prototype instanceof AbstractGadget) {
|
|
12549
|
+
return true;
|
|
12550
|
+
}
|
|
12551
|
+
const proto = prototype;
|
|
12552
|
+
if (typeof proto.execute === "function") {
|
|
12553
|
+
return true;
|
|
12554
|
+
}
|
|
12555
|
+
return isGadgetLike(prototype);
|
|
11684
12556
|
}
|
|
11685
12557
|
function expandHomePath(input) {
|
|
11686
12558
|
if (!input.startsWith("~")) {
|
|
@@ -11690,10 +12562,10 @@ function expandHomePath(input) {
|
|
|
11690
12562
|
if (!home) {
|
|
11691
12563
|
return input;
|
|
11692
12564
|
}
|
|
11693
|
-
return
|
|
12565
|
+
return import_node_path12.default.join(home, input.slice(1));
|
|
11694
12566
|
}
|
|
11695
12567
|
function isFileLikeSpecifier(specifier) {
|
|
11696
|
-
return PATH_PREFIXES.some((prefix) => specifier.startsWith(prefix)) || specifier.includes(
|
|
12568
|
+
return PATH_PREFIXES.some((prefix) => specifier.startsWith(prefix)) || specifier.includes(import_node_path12.default.sep);
|
|
11697
12569
|
}
|
|
11698
12570
|
function tryResolveBuiltin(specifier) {
|
|
11699
12571
|
if (specifier.startsWith(BUILTIN_PREFIX)) {
|
|
@@ -11716,11 +12588,11 @@ function resolveGadgetSpecifier(specifier, cwd) {
|
|
|
11716
12588
|
return specifier;
|
|
11717
12589
|
}
|
|
11718
12590
|
const expanded = expandHomePath(specifier);
|
|
11719
|
-
const resolvedPath =
|
|
11720
|
-
if (!
|
|
12591
|
+
const resolvedPath = import_node_path12.default.resolve(cwd, expanded);
|
|
12592
|
+
if (!import_node_fs11.default.existsSync(resolvedPath)) {
|
|
11721
12593
|
throw new Error(`Gadget module not found at ${resolvedPath}`);
|
|
11722
12594
|
}
|
|
11723
|
-
return (0,
|
|
12595
|
+
return (0, import_node_url2.pathToFileURL)(resolvedPath).href;
|
|
11724
12596
|
}
|
|
11725
12597
|
function extractGadgetsFromModule(moduleExports) {
|
|
11726
12598
|
const results = [];
|
|
@@ -11733,7 +12605,7 @@ function extractGadgetsFromModule(moduleExports) {
|
|
|
11733
12605
|
return;
|
|
11734
12606
|
}
|
|
11735
12607
|
visited.add(value);
|
|
11736
|
-
if (value instanceof
|
|
12608
|
+
if (value instanceof AbstractGadget || isGadgetLike(value)) {
|
|
11737
12609
|
results.push(value);
|
|
11738
12610
|
return;
|
|
11739
12611
|
}
|
|
@@ -11758,12 +12630,23 @@ function extractGadgetsFromModule(moduleExports) {
|
|
|
11758
12630
|
}
|
|
11759
12631
|
async function loadGadgets(specifiers, cwd, importer = (specifier) => import(specifier)) {
|
|
11760
12632
|
const gadgets = [];
|
|
12633
|
+
const usingDefaultImporter = importer.toString().includes("import(specifier)");
|
|
11761
12634
|
for (const specifier of specifiers) {
|
|
11762
12635
|
const builtin = tryResolveBuiltin(specifier);
|
|
11763
12636
|
if (builtin) {
|
|
11764
12637
|
gadgets.push(builtin);
|
|
11765
12638
|
continue;
|
|
11766
12639
|
}
|
|
12640
|
+
if (usingDefaultImporter && isExternalPackageSpecifier(specifier)) {
|
|
12641
|
+
try {
|
|
12642
|
+
const externalGadgets = await loadExternalGadgets(specifier);
|
|
12643
|
+
gadgets.push(...externalGadgets);
|
|
12644
|
+
continue;
|
|
12645
|
+
} catch (error) {
|
|
12646
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
12647
|
+
throw new Error(`Failed to load external package '${specifier}': ${message}`);
|
|
12648
|
+
}
|
|
12649
|
+
}
|
|
11767
12650
|
const resolved = resolveGadgetSpecifier(specifier, cwd);
|
|
11768
12651
|
let exports2;
|
|
11769
12652
|
try {
|
|
@@ -11789,13 +12672,13 @@ async function loadGadgets(specifiers, cwd, importer = (specifier) => import(spe
|
|
|
11789
12672
|
|
|
11790
12673
|
// src/cli/llm-logging.ts
|
|
11791
12674
|
var import_promises4 = require("fs/promises");
|
|
11792
|
-
var
|
|
11793
|
-
var
|
|
12675
|
+
var import_node_os6 = require("os");
|
|
12676
|
+
var import_node_path13 = require("path");
|
|
11794
12677
|
init_messages();
|
|
11795
|
-
var DEFAULT_LLM_LOG_DIR = (0,
|
|
12678
|
+
var DEFAULT_LLM_LOG_DIR = (0, import_node_path13.join)((0, import_node_os6.homedir)(), ".llmist", "logs");
|
|
11796
12679
|
function resolveLogDir(option, subdir) {
|
|
11797
12680
|
if (option === true) {
|
|
11798
|
-
return (0,
|
|
12681
|
+
return (0, import_node_path13.join)(DEFAULT_LLM_LOG_DIR, subdir);
|
|
11799
12682
|
}
|
|
11800
12683
|
if (typeof option === "string") {
|
|
11801
12684
|
return option;
|
|
@@ -11806,14 +12689,14 @@ function formatLlmRequest(messages) {
|
|
|
11806
12689
|
const lines = [];
|
|
11807
12690
|
for (const msg of messages) {
|
|
11808
12691
|
lines.push(`=== ${msg.role.toUpperCase()} ===`);
|
|
11809
|
-
lines.push(msg.content ?
|
|
12692
|
+
lines.push(msg.content ? extractMessageText(msg.content) : "");
|
|
11810
12693
|
lines.push("");
|
|
11811
12694
|
}
|
|
11812
12695
|
return lines.join("\n");
|
|
11813
12696
|
}
|
|
11814
12697
|
async function writeLogFile(dir, filename, content) {
|
|
11815
12698
|
await (0, import_promises4.mkdir)(dir, { recursive: true });
|
|
11816
|
-
await (0, import_promises4.writeFile)((0,
|
|
12699
|
+
await (0, import_promises4.writeFile)((0, import_node_path13.join)(dir, filename), content, "utf-8");
|
|
11817
12700
|
}
|
|
11818
12701
|
function formatSessionTimestamp(date = /* @__PURE__ */ new Date()) {
|
|
11819
12702
|
const pad = (n) => n.toString().padStart(2, "0");
|
|
@@ -11827,7 +12710,7 @@ function formatSessionTimestamp(date = /* @__PURE__ */ new Date()) {
|
|
|
11827
12710
|
}
|
|
11828
12711
|
async function createSessionDir(baseDir) {
|
|
11829
12712
|
const timestamp = formatSessionTimestamp();
|
|
11830
|
-
const sessionDir = (0,
|
|
12713
|
+
const sessionDir = (0, import_node_path13.join)(baseDir, timestamp);
|
|
11831
12714
|
try {
|
|
11832
12715
|
await (0, import_promises4.mkdir)(sessionDir, { recursive: true });
|
|
11833
12716
|
return sessionDir;
|
|
@@ -12021,12 +12904,14 @@ function formatMediaLine(media) {
|
|
|
12021
12904
|
const id = import_chalk3.default.cyan(media.id);
|
|
12022
12905
|
const mimeType = import_chalk3.default.dim(media.mimeType);
|
|
12023
12906
|
const size = import_chalk3.default.yellow(formatBytes(media.sizeBytes));
|
|
12024
|
-
const
|
|
12025
|
-
return `${import_chalk3.default.dim("[")}${icon} ${id} ${mimeType} ${size}${import_chalk3.default.dim("]")} ${import_chalk3.default.dim("\u2192")} ${
|
|
12907
|
+
const path6 = import_chalk3.default.dim(media.path);
|
|
12908
|
+
return `${import_chalk3.default.dim("[")}${icon} ${id} ${mimeType} ${size}${import_chalk3.default.dim("]")} ${import_chalk3.default.dim("\u2192")} ${path6}`;
|
|
12026
12909
|
}
|
|
12027
12910
|
function formatGadgetSummary2(result) {
|
|
12028
12911
|
const gadgetLabel = import_chalk3.default.magenta.bold(result.gadgetName);
|
|
12029
|
-
const timeLabel = import_chalk3.default.dim(
|
|
12912
|
+
const timeLabel = import_chalk3.default.dim(
|
|
12913
|
+
result.executionTimeMs >= 1e3 ? `${(result.executionTimeMs / 1e3).toFixed(1)}s` : `${Math.round(result.executionTimeMs)}ms`
|
|
12914
|
+
);
|
|
12030
12915
|
const paramsStr = formatParametersInline(result.parameters);
|
|
12031
12916
|
const paramsLabel = paramsStr ? `${import_chalk3.default.dim("(")}${paramsStr}${import_chalk3.default.dim(")")}` : "";
|
|
12032
12917
|
if (result.error) {
|
|
@@ -12194,6 +13079,8 @@ var StreamProgress = class {
|
|
|
12194
13079
|
delayTimeout = null;
|
|
12195
13080
|
isRunning = false;
|
|
12196
13081
|
hasRendered = false;
|
|
13082
|
+
lastRenderLineCount = 0;
|
|
13083
|
+
// Track lines rendered for multi-line clearing
|
|
12197
13084
|
// Current call stats (streaming mode)
|
|
12198
13085
|
mode = "cumulative";
|
|
12199
13086
|
model = "";
|
|
@@ -12213,6 +13100,99 @@ var StreamProgress = class {
|
|
|
12213
13100
|
totalCost = 0;
|
|
12214
13101
|
iterations = 0;
|
|
12215
13102
|
currentIteration = 0;
|
|
13103
|
+
// In-flight gadget tracking for concurrent status display
|
|
13104
|
+
inFlightGadgets = /* @__PURE__ */ new Map();
|
|
13105
|
+
// Nested agent tracking for hierarchical subagent display
|
|
13106
|
+
nestedAgents = /* @__PURE__ */ new Map();
|
|
13107
|
+
// Nested gadget tracking for hierarchical subagent display
|
|
13108
|
+
nestedGadgets = /* @__PURE__ */ new Map();
|
|
13109
|
+
/**
|
|
13110
|
+
* Add a gadget to the in-flight tracking (called when gadget_call event received).
|
|
13111
|
+
* Triggers re-render to show the gadget in the status display.
|
|
13112
|
+
*/
|
|
13113
|
+
addGadget(invocationId, name, params) {
|
|
13114
|
+
this.inFlightGadgets.set(invocationId, { name, params, startTime: Date.now() });
|
|
13115
|
+
if (this.isRunning && this.isTTY) {
|
|
13116
|
+
this.render();
|
|
13117
|
+
}
|
|
13118
|
+
}
|
|
13119
|
+
/**
|
|
13120
|
+
* Remove a gadget from in-flight tracking (called when gadget_result event received).
|
|
13121
|
+
* Triggers re-render to update the status display.
|
|
13122
|
+
*/
|
|
13123
|
+
removeGadget(invocationId) {
|
|
13124
|
+
this.inFlightGadgets.delete(invocationId);
|
|
13125
|
+
if (this.isRunning && this.isTTY) {
|
|
13126
|
+
this.render();
|
|
13127
|
+
}
|
|
13128
|
+
}
|
|
13129
|
+
/**
|
|
13130
|
+
* Check if there are any gadgets currently in flight.
|
|
13131
|
+
*/
|
|
13132
|
+
hasInFlightGadgets() {
|
|
13133
|
+
return this.inFlightGadgets.size > 0;
|
|
13134
|
+
}
|
|
13135
|
+
/**
|
|
13136
|
+
* Add a nested agent LLM call (called when nested llm_call_start event received).
|
|
13137
|
+
* Used to display hierarchical progress for subagent gadgets.
|
|
13138
|
+
*/
|
|
13139
|
+
addNestedAgent(id, parentInvocationId, depth, model, iteration, inputTokens) {
|
|
13140
|
+
this.nestedAgents.set(id, {
|
|
13141
|
+
parentInvocationId,
|
|
13142
|
+
depth,
|
|
13143
|
+
model,
|
|
13144
|
+
iteration,
|
|
13145
|
+
startTime: Date.now(),
|
|
13146
|
+
inputTokens
|
|
13147
|
+
});
|
|
13148
|
+
if (this.isRunning && this.isTTY) {
|
|
13149
|
+
this.render();
|
|
13150
|
+
}
|
|
13151
|
+
}
|
|
13152
|
+
/**
|
|
13153
|
+
* Update a nested agent with completion info (called when nested llm_call_end event received).
|
|
13154
|
+
*/
|
|
13155
|
+
updateNestedAgent(id, outputTokens) {
|
|
13156
|
+
const agent = this.nestedAgents.get(id);
|
|
13157
|
+
if (agent) {
|
|
13158
|
+
agent.outputTokens = outputTokens;
|
|
13159
|
+
if (this.isRunning && this.isTTY) {
|
|
13160
|
+
this.render();
|
|
13161
|
+
}
|
|
13162
|
+
}
|
|
13163
|
+
}
|
|
13164
|
+
/**
|
|
13165
|
+
* Remove a nested agent (called when the nested LLM call completes).
|
|
13166
|
+
*/
|
|
13167
|
+
removeNestedAgent(id) {
|
|
13168
|
+
this.nestedAgents.delete(id);
|
|
13169
|
+
if (this.isRunning && this.isTTY) {
|
|
13170
|
+
this.render();
|
|
13171
|
+
}
|
|
13172
|
+
}
|
|
13173
|
+
/**
|
|
13174
|
+
* Add a nested gadget call (called when nested gadget_call event received).
|
|
13175
|
+
*/
|
|
13176
|
+
addNestedGadget(id, depth, parentInvocationId, name) {
|
|
13177
|
+
this.nestedGadgets.set(id, {
|
|
13178
|
+
depth,
|
|
13179
|
+
parentInvocationId,
|
|
13180
|
+
name,
|
|
13181
|
+
startTime: Date.now()
|
|
13182
|
+
});
|
|
13183
|
+
if (this.isRunning && this.isTTY) {
|
|
13184
|
+
this.render();
|
|
13185
|
+
}
|
|
13186
|
+
}
|
|
13187
|
+
/**
|
|
13188
|
+
* Remove a nested gadget (called when nested gadget_result event received).
|
|
13189
|
+
*/
|
|
13190
|
+
removeNestedGadget(id) {
|
|
13191
|
+
this.nestedGadgets.delete(id);
|
|
13192
|
+
if (this.isRunning && this.isTTY) {
|
|
13193
|
+
this.render();
|
|
13194
|
+
}
|
|
13195
|
+
}
|
|
12216
13196
|
/**
|
|
12217
13197
|
* Starts a new LLM call. Switches to streaming mode.
|
|
12218
13198
|
* @param model - Model name being used
|
|
@@ -12339,15 +13319,57 @@ var StreamProgress = class {
|
|
|
12339
13319
|
this.isStreaming = true;
|
|
12340
13320
|
}
|
|
12341
13321
|
render() {
|
|
13322
|
+
this.clearRenderedLines();
|
|
12342
13323
|
const spinner = SPINNER_FRAMES[this.frameIndex++ % SPINNER_FRAMES.length];
|
|
13324
|
+
const lines = [];
|
|
12343
13325
|
if (this.mode === "streaming") {
|
|
12344
|
-
this.
|
|
13326
|
+
lines.push(this.formatStreamingLine(spinner));
|
|
12345
13327
|
} else {
|
|
12346
|
-
this.
|
|
12347
|
-
}
|
|
13328
|
+
lines.push(this.formatCumulativeLine(spinner));
|
|
13329
|
+
}
|
|
13330
|
+
if (this.isTTY) {
|
|
13331
|
+
for (const [gadgetId, gadget] of this.inFlightGadgets) {
|
|
13332
|
+
const elapsed = ((Date.now() - gadget.startTime) / 1e3).toFixed(1);
|
|
13333
|
+
const gadgetLine = ` ${import_chalk4.default.blue("\u23F5")} ${import_chalk4.default.magenta.bold(gadget.name)}${import_chalk4.default.dim("(...)")} ${import_chalk4.default.dim(elapsed + "s")}`;
|
|
13334
|
+
lines.push(gadgetLine);
|
|
13335
|
+
for (const [_agentId, nested] of this.nestedAgents) {
|
|
13336
|
+
if (nested.parentInvocationId !== gadgetId) continue;
|
|
13337
|
+
const indent = " ".repeat(nested.depth + 1);
|
|
13338
|
+
const nestedElapsed = ((Date.now() - nested.startTime) / 1e3).toFixed(1);
|
|
13339
|
+
const tokens = nested.inputTokens ? ` ${import_chalk4.default.dim("\u2191")}${import_chalk4.default.yellow(formatTokens(nested.inputTokens))}` : "";
|
|
13340
|
+
const outTokens = nested.outputTokens ? ` ${import_chalk4.default.dim("\u2193")}${import_chalk4.default.green(formatTokens(nested.outputTokens))}` : "";
|
|
13341
|
+
const nestedLine = `${indent}${import_chalk4.default.cyan(`#${nested.iteration}`)} ${import_chalk4.default.dim(nested.model)}${tokens}${outTokens} ${import_chalk4.default.dim(nestedElapsed + "s")} ${import_chalk4.default.cyan(spinner)}`;
|
|
13342
|
+
lines.push(nestedLine);
|
|
13343
|
+
}
|
|
13344
|
+
for (const [_nestedId, nestedGadget] of this.nestedGadgets) {
|
|
13345
|
+
if (nestedGadget.parentInvocationId === gadgetId) {
|
|
13346
|
+
const indent = " ".repeat(nestedGadget.depth + 1);
|
|
13347
|
+
const nestedElapsed = ((Date.now() - nestedGadget.startTime) / 1e3).toFixed(1);
|
|
13348
|
+
const nestedGadgetLine = `${indent}${import_chalk4.default.blue("\u23F5")} ${import_chalk4.default.dim(nestedGadget.name + "(...)")} ${import_chalk4.default.dim(nestedElapsed + "s")}`;
|
|
13349
|
+
lines.push(nestedGadgetLine);
|
|
13350
|
+
}
|
|
13351
|
+
}
|
|
13352
|
+
}
|
|
13353
|
+
}
|
|
13354
|
+
this.lastRenderLineCount = lines.length;
|
|
13355
|
+
this.target.write("\r" + lines.join("\n"));
|
|
12348
13356
|
this.hasRendered = true;
|
|
12349
13357
|
}
|
|
12350
|
-
|
|
13358
|
+
/**
|
|
13359
|
+
* Clears the previously rendered lines (for multi-line status display).
|
|
13360
|
+
*/
|
|
13361
|
+
clearRenderedLines() {
|
|
13362
|
+
if (!this.hasRendered || this.lastRenderLineCount === 0) return;
|
|
13363
|
+
this.target.write("\r\x1B[K");
|
|
13364
|
+
for (let i = 1; i < this.lastRenderLineCount; i++) {
|
|
13365
|
+
this.target.write("\x1B[1A\x1B[K");
|
|
13366
|
+
}
|
|
13367
|
+
this.target.write("\r");
|
|
13368
|
+
}
|
|
13369
|
+
/**
|
|
13370
|
+
* Format the streaming mode progress line (returns string, doesn't write).
|
|
13371
|
+
*/
|
|
13372
|
+
formatStreamingLine(spinner) {
|
|
12351
13373
|
const elapsed = ((Date.now() - this.callStartTime) / 1e3).toFixed(1);
|
|
12352
13374
|
const outTokens = this.callOutputTokensEstimated ? Math.round(this.callOutputChars / FALLBACK_CHARS_PER_TOKEN) : this.callOutputTokens;
|
|
12353
13375
|
const parts = [];
|
|
@@ -12381,7 +13403,7 @@ var StreamProgress = class {
|
|
|
12381
13403
|
if (callCost > 0) {
|
|
12382
13404
|
parts.push(import_chalk4.default.cyan(`$${formatCost(callCost)}`));
|
|
12383
13405
|
}
|
|
12384
|
-
|
|
13406
|
+
return `${parts.join(import_chalk4.default.dim(" | "))} ${import_chalk4.default.cyan(spinner)}`;
|
|
12385
13407
|
}
|
|
12386
13408
|
/**
|
|
12387
13409
|
* Calculates live cost estimate for the current streaming call.
|
|
@@ -12418,7 +13440,10 @@ var StreamProgress = class {
|
|
|
12418
13440
|
}
|
|
12419
13441
|
return this.callInputTokens / limits.contextWindow * 100;
|
|
12420
13442
|
}
|
|
12421
|
-
|
|
13443
|
+
/**
|
|
13444
|
+
* Format the cumulative mode progress line (returns string, doesn't write).
|
|
13445
|
+
*/
|
|
13446
|
+
formatCumulativeLine(spinner) {
|
|
12422
13447
|
const elapsed = ((Date.now() - this.totalStartTime) / 1e3).toFixed(1);
|
|
12423
13448
|
const parts = [];
|
|
12424
13449
|
if (this.model) {
|
|
@@ -12434,10 +13459,10 @@ var StreamProgress = class {
|
|
|
12434
13459
|
parts.push(import_chalk4.default.dim("cost:") + import_chalk4.default.cyan(` $${formatCost(this.totalCost)}`));
|
|
12435
13460
|
}
|
|
12436
13461
|
parts.push(import_chalk4.default.dim(`${elapsed}s`));
|
|
12437
|
-
|
|
13462
|
+
return `${parts.join(import_chalk4.default.dim(" | "))} ${import_chalk4.default.cyan(spinner)}`;
|
|
12438
13463
|
}
|
|
12439
13464
|
/**
|
|
12440
|
-
* Pauses the progress indicator and clears
|
|
13465
|
+
* Pauses the progress indicator and clears all rendered lines.
|
|
12441
13466
|
* Can be resumed with start().
|
|
12442
13467
|
*/
|
|
12443
13468
|
pause() {
|
|
@@ -12451,10 +13476,9 @@ var StreamProgress = class {
|
|
|
12451
13476
|
this.interval = null;
|
|
12452
13477
|
}
|
|
12453
13478
|
this.isRunning = false;
|
|
12454
|
-
|
|
12455
|
-
|
|
12456
|
-
|
|
12457
|
-
}
|
|
13479
|
+
this.clearRenderedLines();
|
|
13480
|
+
this.hasRendered = false;
|
|
13481
|
+
this.lastRenderLineCount = 0;
|
|
12458
13482
|
}
|
|
12459
13483
|
/**
|
|
12460
13484
|
* Completes the progress indicator and clears the line.
|
|
@@ -12642,6 +13666,7 @@ function configToAgentOptions(config) {
|
|
|
12642
13666
|
if (config.docker !== void 0) result.docker = config.docker;
|
|
12643
13667
|
if (config["docker-cwd-permission"] !== void 0)
|
|
12644
13668
|
result.dockerCwdPermission = config["docker-cwd-permission"];
|
|
13669
|
+
if (config.subagents !== void 0) result.subagents = config.subagents;
|
|
12645
13670
|
return result;
|
|
12646
13671
|
}
|
|
12647
13672
|
|
|
@@ -12829,7 +13854,12 @@ async function executeAgent(promptArg, options, env) {
|
|
|
12829
13854
|
return void 0;
|
|
12830
13855
|
}
|
|
12831
13856
|
};
|
|
12832
|
-
const
|
|
13857
|
+
const resolvedSubagentConfig = buildSubagentConfigMap(
|
|
13858
|
+
options.model,
|
|
13859
|
+
options.subagents,
|
|
13860
|
+
options.globalSubagents
|
|
13861
|
+
);
|
|
13862
|
+
const builder = new AgentBuilder(client).withModel(options.model).withSubagentConfig(resolvedSubagentConfig).withLogger(env.createLogger("llmist:cli:agent")).withHooks({
|
|
12833
13863
|
observers: {
|
|
12834
13864
|
// onLLMCallStart: Start progress indicator for each LLM call
|
|
12835
13865
|
// This showcases how to react to agent lifecycle events
|
|
@@ -13025,6 +14055,38 @@ Denied: ${result.reason ?? "by user"}`
|
|
|
13025
14055
|
"Maximize efficiency by batching independent operations in a single response."
|
|
13026
14056
|
].join(" ")
|
|
13027
14057
|
);
|
|
14058
|
+
if (!options.quiet) {
|
|
14059
|
+
builder.withNestedEventCallback((event) => {
|
|
14060
|
+
if (event.type === "llm_call_start") {
|
|
14061
|
+
const info = event.event;
|
|
14062
|
+
const nestedId = `${event.gadgetInvocationId}:${info.iteration}`;
|
|
14063
|
+
progress.addNestedAgent(
|
|
14064
|
+
nestedId,
|
|
14065
|
+
event.gadgetInvocationId,
|
|
14066
|
+
event.depth,
|
|
14067
|
+
info.model,
|
|
14068
|
+
info.iteration,
|
|
14069
|
+
info.inputTokens
|
|
14070
|
+
);
|
|
14071
|
+
} else if (event.type === "llm_call_end") {
|
|
14072
|
+
const info = event.event;
|
|
14073
|
+
const nestedId = `${event.gadgetInvocationId}:${info.iteration}`;
|
|
14074
|
+
progress.updateNestedAgent(nestedId, info.outputTokens);
|
|
14075
|
+
setTimeout(() => progress.removeNestedAgent(nestedId), 100);
|
|
14076
|
+
} else if (event.type === "gadget_call") {
|
|
14077
|
+
const gadgetEvent = event.event;
|
|
14078
|
+
progress.addNestedGadget(
|
|
14079
|
+
gadgetEvent.call.invocationId,
|
|
14080
|
+
event.depth,
|
|
14081
|
+
event.gadgetInvocationId,
|
|
14082
|
+
gadgetEvent.call.gadgetName
|
|
14083
|
+
);
|
|
14084
|
+
} else if (event.type === "gadget_result") {
|
|
14085
|
+
const resultEvent = event.event;
|
|
14086
|
+
progress.removeNestedGadget(resultEvent.result.invocationId);
|
|
14087
|
+
}
|
|
14088
|
+
});
|
|
14089
|
+
}
|
|
13028
14090
|
let agent;
|
|
13029
14091
|
if (options.image || options.audio) {
|
|
13030
14092
|
const parts = [text(prompt)];
|
|
@@ -13049,10 +14111,22 @@ Denied: ${result.reason ?? "by user"}`
|
|
|
13049
14111
|
try {
|
|
13050
14112
|
for await (const event of agent.run()) {
|
|
13051
14113
|
if (event.type === "text") {
|
|
13052
|
-
progress.pause();
|
|
13053
14114
|
textBuffer += event.content;
|
|
14115
|
+
} else if (event.type === "gadget_call") {
|
|
14116
|
+
flushTextBuffer();
|
|
14117
|
+
if (!options.quiet) {
|
|
14118
|
+
progress.addGadget(
|
|
14119
|
+
event.call.invocationId,
|
|
14120
|
+
event.call.gadgetName,
|
|
14121
|
+
event.call.parameters
|
|
14122
|
+
);
|
|
14123
|
+
progress.start();
|
|
14124
|
+
}
|
|
13054
14125
|
} else if (event.type === "gadget_result") {
|
|
13055
14126
|
flushTextBuffer();
|
|
14127
|
+
if (!options.quiet) {
|
|
14128
|
+
progress.removeGadget(event.result.invocationId);
|
|
14129
|
+
}
|
|
13056
14130
|
progress.pause();
|
|
13057
14131
|
if (options.quiet) {
|
|
13058
14132
|
if (event.result.gadgetName === "TellUser" && event.result.parameters?.message) {
|
|
@@ -13067,6 +14141,9 @@ Denied: ${result.reason ?? "by user"}`
|
|
|
13067
14141
|
`
|
|
13068
14142
|
);
|
|
13069
14143
|
}
|
|
14144
|
+
if (progress.hasInFlightGadgets()) {
|
|
14145
|
+
progress.start();
|
|
14146
|
+
}
|
|
13070
14147
|
}
|
|
13071
14148
|
}
|
|
13072
14149
|
} catch (error) {
|
|
@@ -13099,14 +14176,16 @@ Denied: ${result.reason ?? "by user"}`
|
|
|
13099
14176
|
}
|
|
13100
14177
|
}
|
|
13101
14178
|
}
|
|
13102
|
-
function registerAgentCommand(program, env, config) {
|
|
14179
|
+
function registerAgentCommand(program, env, config, globalSubagents) {
|
|
13103
14180
|
const cmd = program.command(COMMANDS.agent).description("Run the llmist agent loop with optional gadgets.").argument("[prompt]", "Prompt for the agent loop. Falls back to stdin when available.");
|
|
13104
14181
|
addAgentOptions(cmd, config);
|
|
13105
14182
|
cmd.action(
|
|
13106
14183
|
(prompt, options) => executeAction(() => {
|
|
13107
14184
|
const mergedOptions = {
|
|
13108
14185
|
...options,
|
|
13109
|
-
gadgetApproval: config?.["gadget-approval"]
|
|
14186
|
+
gadgetApproval: config?.["gadget-approval"],
|
|
14187
|
+
subagents: config?.subagents,
|
|
14188
|
+
globalSubagents
|
|
13110
14189
|
};
|
|
13111
14190
|
return executeAgent(prompt, mergedOptions, env);
|
|
13112
14191
|
}, env)
|
|
@@ -13206,6 +14285,104 @@ function registerCompleteCommand(program, env, config) {
|
|
|
13206
14285
|
);
|
|
13207
14286
|
}
|
|
13208
14287
|
|
|
14288
|
+
// src/cli/init-command.ts
|
|
14289
|
+
var import_node_fs12 = require("fs");
|
|
14290
|
+
var import_node_path14 = require("path");
|
|
14291
|
+
var STARTER_CONFIG = `# ~/.llmist/cli.toml
|
|
14292
|
+
# llmist CLI configuration file
|
|
14293
|
+
#
|
|
14294
|
+
# This is a minimal starter config. For a comprehensive example with all options:
|
|
14295
|
+
# https://github.com/zbigniewsobiecki/llmist/blob/main/examples/cli.example.toml
|
|
14296
|
+
#
|
|
14297
|
+
# Key concepts:
|
|
14298
|
+
# - Any section can inherit from others using: inherits = "section-name"
|
|
14299
|
+
# - Prompts can use templates with Eta syntax: <%~ include("@prompt-name") %>
|
|
14300
|
+
# - Custom sections become CLI commands: [my-command] -> llmist my-command
|
|
14301
|
+
|
|
14302
|
+
#\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
14303
|
+
# GLOBAL OPTIONS
|
|
14304
|
+
# These apply to all commands. CLI flags override these settings.
|
|
14305
|
+
#\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
14306
|
+
[global]
|
|
14307
|
+
# log-level = "info" # silly, trace, debug, info, warn, error, fatal
|
|
14308
|
+
# log-file = "/tmp/llmist.log" # Enable file logging (JSON format)
|
|
14309
|
+
|
|
14310
|
+
#\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
14311
|
+
# COMPLETE COMMAND DEFAULTS
|
|
14312
|
+
# For single LLM responses: llmist complete "prompt"
|
|
14313
|
+
# Model format: provider:model (e.g., openai:gpt-4o, anthropic:claude-sonnet-4-5)
|
|
14314
|
+
#\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
14315
|
+
[complete]
|
|
14316
|
+
# model = "openai:gpt-4o"
|
|
14317
|
+
# temperature = 0.7 # 0-2, higher = more creative
|
|
14318
|
+
# max-tokens = 4096 # Maximum response length
|
|
14319
|
+
|
|
14320
|
+
#\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
14321
|
+
# AGENT COMMAND DEFAULTS
|
|
14322
|
+
# For tool-using agents: llmist agent "prompt"
|
|
14323
|
+
#\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
14324
|
+
[agent]
|
|
14325
|
+
# model = "anthropic:claude-sonnet-4-5"
|
|
14326
|
+
# max-iterations = 15 # Max tool-use loops before stopping
|
|
14327
|
+
# gadgets = [ # Tools the agent can use
|
|
14328
|
+
# "ListDirectory",
|
|
14329
|
+
# "ReadFile",
|
|
14330
|
+
# "WriteFile",
|
|
14331
|
+
# ]
|
|
14332
|
+
|
|
14333
|
+
#\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
14334
|
+
# CUSTOM COMMANDS
|
|
14335
|
+
# Any other section becomes a new CLI command!
|
|
14336
|
+
# Uncomment below to create: llmist summarize "your text"
|
|
14337
|
+
#\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
14338
|
+
# [summarize]
|
|
14339
|
+
# type = "complete" # "complete" or "agent"
|
|
14340
|
+
# description = "Summarize text concisely."
|
|
14341
|
+
# system = "Summarize the following text in 2-3 bullet points."
|
|
14342
|
+
# temperature = 0.3
|
|
14343
|
+
`;
|
|
14344
|
+
async function executeInit(_options, env) {
|
|
14345
|
+
const configPath = getConfigPath();
|
|
14346
|
+
const configDir = (0, import_node_path14.dirname)(configPath);
|
|
14347
|
+
if ((0, import_node_fs12.existsSync)(configPath)) {
|
|
14348
|
+
env.stderr.write(`Configuration already exists at ${configPath}
|
|
14349
|
+
`);
|
|
14350
|
+
env.stderr.write("\n");
|
|
14351
|
+
env.stderr.write(`To view it: cat ${configPath}
|
|
14352
|
+
`);
|
|
14353
|
+
env.stderr.write(`To reset: rm ${configPath} && llmist init
|
|
14354
|
+
`);
|
|
14355
|
+
return;
|
|
14356
|
+
}
|
|
14357
|
+
if (!(0, import_node_fs12.existsSync)(configDir)) {
|
|
14358
|
+
(0, import_node_fs12.mkdirSync)(configDir, { recursive: true });
|
|
14359
|
+
}
|
|
14360
|
+
(0, import_node_fs12.writeFileSync)(configPath, STARTER_CONFIG, "utf-8");
|
|
14361
|
+
env.stderr.write(`Created ${configPath}
|
|
14362
|
+
`);
|
|
14363
|
+
env.stderr.write("\n");
|
|
14364
|
+
env.stderr.write("Next steps:\n");
|
|
14365
|
+
env.stderr.write(" 1. Set your API key:\n");
|
|
14366
|
+
env.stderr.write(" export OPENAI_API_KEY=sk-...\n");
|
|
14367
|
+
env.stderr.write(" export ANTHROPIC_API_KEY=sk-...\n");
|
|
14368
|
+
env.stderr.write(" export GEMINI_API_KEY=...\n");
|
|
14369
|
+
env.stderr.write("\n");
|
|
14370
|
+
env.stderr.write(` 2. Customize your config:
|
|
14371
|
+
`);
|
|
14372
|
+
env.stderr.write(` $EDITOR ${configPath}
|
|
14373
|
+
`);
|
|
14374
|
+
env.stderr.write("\n");
|
|
14375
|
+
env.stderr.write(" 3. See all options:\n");
|
|
14376
|
+
env.stderr.write(
|
|
14377
|
+
" https://github.com/zbigniewsobiecki/llmist/blob/main/examples/cli.example.toml\n"
|
|
14378
|
+
);
|
|
14379
|
+
env.stderr.write("\n");
|
|
14380
|
+
env.stderr.write('Try it: llmist complete "Hello, world!"\n');
|
|
14381
|
+
}
|
|
14382
|
+
function registerInitCommand(program, env) {
|
|
14383
|
+
program.command(COMMANDS.init).description("Initialize llmist configuration at ~/.llmist/cli.toml").action((options) => executeAction(() => executeInit(options, env), env));
|
|
14384
|
+
}
|
|
14385
|
+
|
|
13209
14386
|
// src/cli/environment.ts
|
|
13210
14387
|
var import_node_readline = __toESM(require("readline"), 1);
|
|
13211
14388
|
var import_chalk6 = __toESM(require("chalk"), 1);
|
|
@@ -13308,7 +14485,7 @@ function createCommandEnvironment(baseEnv, config) {
|
|
|
13308
14485
|
createLogger: createLoggerFactory(loggerConfig)
|
|
13309
14486
|
};
|
|
13310
14487
|
}
|
|
13311
|
-
function registerCustomCommand(program, name, config, env) {
|
|
14488
|
+
function registerCustomCommand(program, name, config, env, globalSubagents) {
|
|
13312
14489
|
const type = config.type ?? "agent";
|
|
13313
14490
|
const description = config.description ?? `Custom ${type} command`;
|
|
13314
14491
|
const cmd = program.command(name).description(description).argument("[prompt]", "Prompt for the command. Falls back to stdin when available.");
|
|
@@ -13333,7 +14510,8 @@ function registerCustomCommand(program, name, config, env) {
|
|
|
13333
14510
|
const configDefaults = configToAgentOptions(config);
|
|
13334
14511
|
const options = {
|
|
13335
14512
|
...configDefaults,
|
|
13336
|
-
...cliOptions
|
|
14513
|
+
...cliOptions,
|
|
14514
|
+
globalSubagents
|
|
13337
14515
|
};
|
|
13338
14516
|
await executeAgent(prompt, options, cmdEnv);
|
|
13339
14517
|
}, cmdEnv);
|
|
@@ -13776,7 +14954,7 @@ function registerGadgetCommand(program, env) {
|
|
|
13776
14954
|
}
|
|
13777
14955
|
|
|
13778
14956
|
// src/cli/image-command.ts
|
|
13779
|
-
var
|
|
14957
|
+
var import_node_fs13 = require("fs");
|
|
13780
14958
|
var DEFAULT_IMAGE_MODEL = "dall-e-3";
|
|
13781
14959
|
async function executeImage(promptArg, options, env) {
|
|
13782
14960
|
const prompt = await resolvePrompt(promptArg, env);
|
|
@@ -13800,7 +14978,7 @@ async function executeImage(promptArg, options, env) {
|
|
|
13800
14978
|
const imageData = result.images[0];
|
|
13801
14979
|
if (imageData.b64Json) {
|
|
13802
14980
|
const buffer = Buffer.from(imageData.b64Json, "base64");
|
|
13803
|
-
(0,
|
|
14981
|
+
(0, import_node_fs13.writeFileSync)(options.output, buffer);
|
|
13804
14982
|
if (!options.quiet) {
|
|
13805
14983
|
env.stderr.write(`${SUMMARY_PREFIX} Image saved to ${options.output}
|
|
13806
14984
|
`);
|
|
@@ -14241,7 +15419,7 @@ function registerModelsCommand(program, env) {
|
|
|
14241
15419
|
}
|
|
14242
15420
|
|
|
14243
15421
|
// src/cli/speech-command.ts
|
|
14244
|
-
var
|
|
15422
|
+
var import_node_fs14 = require("fs");
|
|
14245
15423
|
var DEFAULT_SPEECH_MODEL = "tts-1";
|
|
14246
15424
|
var DEFAULT_VOICE = "nova";
|
|
14247
15425
|
async function executeSpeech(textArg, options, env) {
|
|
@@ -14264,7 +15442,7 @@ async function executeSpeech(textArg, options, env) {
|
|
|
14264
15442
|
});
|
|
14265
15443
|
const audioBuffer = Buffer.from(result.audio);
|
|
14266
15444
|
if (options.output) {
|
|
14267
|
-
(0,
|
|
15445
|
+
(0, import_node_fs14.writeFileSync)(options.output, audioBuffer);
|
|
14268
15446
|
if (!options.quiet) {
|
|
14269
15447
|
env.stderr.write(`${SUMMARY_PREFIX} Audio saved to ${options.output}
|
|
14270
15448
|
`);
|
|
@@ -14338,17 +15516,18 @@ function createProgram(env, config) {
|
|
|
14338
15516
|
writeErr: (str) => env.stderr.write(str)
|
|
14339
15517
|
});
|
|
14340
15518
|
registerCompleteCommand(program, env, config?.complete);
|
|
14341
|
-
registerAgentCommand(program, env, config?.agent);
|
|
15519
|
+
registerAgentCommand(program, env, config?.agent, config?.subagents);
|
|
14342
15520
|
registerImageCommand(program, env, config?.image);
|
|
14343
15521
|
registerSpeechCommand(program, env, config?.speech);
|
|
14344
15522
|
registerVisionCommand(program, env);
|
|
14345
15523
|
registerModelsCommand(program, env);
|
|
14346
15524
|
registerGadgetCommand(program, env);
|
|
15525
|
+
registerInitCommand(program, env);
|
|
14347
15526
|
if (config) {
|
|
14348
15527
|
const customNames = getCustomCommandNames(config);
|
|
14349
15528
|
for (const name of customNames) {
|
|
14350
15529
|
const cmdConfig = config[name];
|
|
14351
|
-
registerCustomCommand(program, name, cmdConfig, env);
|
|
15530
|
+
registerCustomCommand(program, name, cmdConfig, env, config.subagents);
|
|
14352
15531
|
}
|
|
14353
15532
|
}
|
|
14354
15533
|
return program;
|