@robota-sdk/agent-tools 3.0.0-beta.76 → 3.0.0-beta.78
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/LICENSE +661 -21
- package/README.md +36 -25
- package/dist/browser/browser.d.ts +27 -70
- package/dist/browser/browser.d.ts.map +1 -1
- package/dist/browser/browser.js +1 -1
- package/dist/browser/browser.js.map +1 -1
- package/dist/node/index.cjs +212 -138
- package/dist/node/index.d.ts +44 -74
- package/dist/node/index.d.ts.map +1 -1
- package/dist/node/index.js +210 -139
- package/dist/node/index.js.map +1 -1
- package/package.json +6 -5
package/dist/node/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","names":[],"sources":["../../src/types/tool-result.ts","../../src/sandbox/types.ts","../../src/sandbox/e2b-sandbox-client.ts","../../src/sandbox/in-memory-sandbox-client.ts","../../src/sandbox/workspace-manifest.ts","../../src/registry/tool-registry.ts","../../src/implementations/function-tool
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":[],"sources":["../../src/types/tool-result.ts","../../src/sandbox/types.ts","../../src/sandbox/e2b-sandbox-client.ts","../../src/sandbox/in-memory-sandbox-client.ts","../../src/sandbox/workspace-manifest.ts","../../src/registry/tool-registry.ts","../../src/implementations/function-tool.ts","../../src/implementations/function-tool/types.ts","../../src/builtins/shell-tool.ts","../../src/builtins/read-tool.ts","../../src/builtins/write-tool.ts","../../src/builtins/edit-tool.ts","../../src/builtins/glob-tool.ts","../../src/builtins/grep-tool.ts","../../src/builtins/web-fetch-tool.ts","../../src/builtins/web-search-tool.ts","../../src/builtins/ask-user-question-tool.ts"],"mappings":"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"}
|
package/dist/node/index.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { chmod, mkdir, readFile, readdir, rename, rm, stat, writeFile } from "node:fs/promises";
|
|
2
2
|
import { basename, dirname, isAbsolute, join, posix, resolve, sep } from "node:path";
|
|
3
|
-
import { ToolExecutionError, ValidationError, logger } from "@robota-sdk/agent-core";
|
|
3
|
+
import { ToolExecutionError, ValidationError, logger, resolvePlatformShell, zodToJsonSchema } from "@robota-sdk/agent-core";
|
|
4
4
|
import { spawn } from "node:child_process";
|
|
5
|
+
import { killProcessTree } from "@robota-sdk/agent-process";
|
|
5
6
|
import { z } from "zod";
|
|
6
|
-
import { randomBytes } from "node:crypto";
|
|
7
|
+
import { randomBytes, randomUUID } from "node:crypto";
|
|
7
8
|
import fg from "fast-glob";
|
|
8
9
|
import pLimit from "p-limit";
|
|
9
10
|
//#region src/sandbox/e2b-sandbox-client.ts
|
|
@@ -420,111 +421,6 @@ function validateToolParameters(parameters, schemaRequired, schemaProperties, ad
|
|
|
420
421
|
};
|
|
421
422
|
}
|
|
422
423
|
//#endregion
|
|
423
|
-
//#region src/implementations/function-tool/schema-converter.ts
|
|
424
|
-
/**
|
|
425
|
-
* Convert Zod schema to JSON Schema format with safe undefined handling
|
|
426
|
-
*/
|
|
427
|
-
function zodToJsonSchema(schema, options = {}) {
|
|
428
|
-
const properties = {};
|
|
429
|
-
const required = [];
|
|
430
|
-
const schemaDef = schema._def;
|
|
431
|
-
if (!schemaDef) throw new Error("Zod schema is missing _def; cannot convert to JSON schema.");
|
|
432
|
-
if (schemaDef.typeName === "ZodObject" && schemaDef.shape) {
|
|
433
|
-
const shape = typeof schemaDef.shape === "function" ? schemaDef.shape() : schemaDef.shape;
|
|
434
|
-
for (const [key, typeObj] of Object.entries(shape)) {
|
|
435
|
-
properties[key] = convertZodTypeToProperty(typeObj);
|
|
436
|
-
if (isRequiredField(typeObj)) required.push(key);
|
|
437
|
-
}
|
|
438
|
-
}
|
|
439
|
-
return {
|
|
440
|
-
type: "object",
|
|
441
|
-
properties,
|
|
442
|
-
required,
|
|
443
|
-
...(options.allowAdditionalProperties || schemaDef.unknownKeys === "passthrough") && { additionalProperties: true }
|
|
444
|
-
};
|
|
445
|
-
}
|
|
446
|
-
/**
|
|
447
|
-
* Convert individual Zod type to parameter schema with safe undefined handling
|
|
448
|
-
*/
|
|
449
|
-
function convertZodTypeToProperty(typeObj) {
|
|
450
|
-
const typeDef = typeObj._def;
|
|
451
|
-
if (!typeDef) throw new Error("Zod type is missing _def; cannot convert to JSON schema.");
|
|
452
|
-
const base = {};
|
|
453
|
-
if (typeDef.description) base.description = typeDef.description;
|
|
454
|
-
switch (typeDef.typeName) {
|
|
455
|
-
case "ZodString": return {
|
|
456
|
-
type: "string",
|
|
457
|
-
...base
|
|
458
|
-
};
|
|
459
|
-
case "ZodNumber": return {
|
|
460
|
-
type: "number",
|
|
461
|
-
...base
|
|
462
|
-
};
|
|
463
|
-
case "ZodBoolean": return {
|
|
464
|
-
type: "boolean",
|
|
465
|
-
...base
|
|
466
|
-
};
|
|
467
|
-
case "ZodArray":
|
|
468
|
-
if (!typeDef.type) throw new Error("ZodArray is missing item type; cannot convert to JSON schema.");
|
|
469
|
-
return {
|
|
470
|
-
type: "array",
|
|
471
|
-
items: convertZodTypeToProperty(typeDef.type),
|
|
472
|
-
...base
|
|
473
|
-
};
|
|
474
|
-
case "ZodObject": return {
|
|
475
|
-
type: "object",
|
|
476
|
-
...base
|
|
477
|
-
};
|
|
478
|
-
case "ZodEnum": {
|
|
479
|
-
const enumValues = typeDef.values;
|
|
480
|
-
if (!enumValues || !Array.isArray(enumValues)) throw new Error("ZodEnum is missing enum values; cannot convert to JSON schema.");
|
|
481
|
-
return {
|
|
482
|
-
type: "string",
|
|
483
|
-
enum: enumValues,
|
|
484
|
-
...base
|
|
485
|
-
};
|
|
486
|
-
}
|
|
487
|
-
case "ZodOptional":
|
|
488
|
-
if (typeDef.innerType) return {
|
|
489
|
-
...convertZodTypeToProperty(typeDef.innerType),
|
|
490
|
-
...base
|
|
491
|
-
};
|
|
492
|
-
throw new Error("ZodOptional is missing innerType; cannot convert to JSON schema.");
|
|
493
|
-
case "ZodNullable":
|
|
494
|
-
if (typeDef.innerType) return {
|
|
495
|
-
...convertZodTypeToProperty(typeDef.innerType),
|
|
496
|
-
...base
|
|
497
|
-
};
|
|
498
|
-
throw new Error("ZodNullable is missing innerType; cannot convert to JSON schema.");
|
|
499
|
-
case "ZodDefault":
|
|
500
|
-
if (typeDef.innerType) return {
|
|
501
|
-
...convertZodTypeToProperty(typeDef.innerType),
|
|
502
|
-
...base
|
|
503
|
-
};
|
|
504
|
-
throw new Error("ZodDefault is missing innerType; cannot convert to JSON schema.");
|
|
505
|
-
case "ZodRecord":
|
|
506
|
-
if (typeDef.valueType) return {
|
|
507
|
-
type: "object",
|
|
508
|
-
additionalProperties: convertZodTypeToProperty(typeDef.valueType),
|
|
509
|
-
...base
|
|
510
|
-
};
|
|
511
|
-
return {
|
|
512
|
-
type: "object",
|
|
513
|
-
additionalProperties: { type: "string" },
|
|
514
|
-
...base
|
|
515
|
-
};
|
|
516
|
-
default: throw new Error(`Unsupported Zod type: ${String(typeDef.typeName)}`);
|
|
517
|
-
}
|
|
518
|
-
}
|
|
519
|
-
/**
|
|
520
|
-
* Check if a Zod field is required (not optional or nullable)
|
|
521
|
-
*/
|
|
522
|
-
function isRequiredField(typeObj) {
|
|
523
|
-
const typeDef = typeObj._def;
|
|
524
|
-
if (!typeDef) throw new Error("Zod schema is missing _def; cannot determine required fields.");
|
|
525
|
-
return typeDef.typeName !== "ZodOptional" && typeDef.typeName !== "ZodNullable" && typeDef.typeName !== "ZodDefault";
|
|
526
|
-
}
|
|
527
|
-
//#endregion
|
|
528
424
|
//#region src/implementations/function-tool.ts
|
|
529
425
|
/**
|
|
530
426
|
* Function tool implementation
|
|
@@ -633,34 +529,54 @@ function createZodFunctionTool(name, description, zodSchema, fn) {
|
|
|
633
529
|
const wrappedFn = async (parameters, context) => {
|
|
634
530
|
const parseResult = zodSchema.safeParse(parameters);
|
|
635
531
|
if (!parseResult.success) throw new ValidationError(`Zod validation failed: ${parseResult.error}`);
|
|
636
|
-
const result = await fn(parseResult.data
|
|
532
|
+
const result = await fn(parseResult.data, context);
|
|
637
533
|
return typeof result === "string" ? result : JSON.stringify(result);
|
|
638
534
|
};
|
|
639
535
|
return new FunctionTool(schema, wrappedFn);
|
|
640
536
|
}
|
|
641
537
|
//#endregion
|
|
642
|
-
//#region src/builtins/
|
|
538
|
+
//#region src/builtins/shell-tool.ts
|
|
643
539
|
/**
|
|
644
|
-
*
|
|
540
|
+
* ShellTool — execute a host shell command via child_process.spawn (TERM-008).
|
|
541
|
+
*
|
|
542
|
+
* Cross-platform: the shell is resolved per OS through `resolvePlatformShell()` (POSIX `sh`/`bash`,
|
|
543
|
+
* Windows PowerShell). The tool name is `Shell` and its description is built dynamically from the
|
|
544
|
+
* resolved shell so the model is told the active shell/OS and writes the right syntax.
|
|
645
545
|
*
|
|
646
|
-
* Returns
|
|
647
|
-
*
|
|
648
|
-
* exited non-zero — the LLM can decide what to do with that information).
|
|
546
|
+
* Returns an IToolInvocationResult JSON string. A non-zero exit is returned as success:true with
|
|
547
|
+
* exitCode set (the command ran, it just exited non-zero — the LLM decides what to do with that).
|
|
649
548
|
*/
|
|
549
|
+
/** POSIX children are spawned detached so a process-group kill reaps grandchildren (CORE-023). */
|
|
550
|
+
const SPAWN_DETACHED = process.platform !== "win32";
|
|
650
551
|
const DEFAULT_TIMEOUT_MS$2 = 12e4;
|
|
651
|
-
const
|
|
652
|
-
command: z.string().describe("The
|
|
552
|
+
const ShellSchema = z.object({
|
|
553
|
+
command: z.string().describe("The shell command to execute"),
|
|
653
554
|
timeout: z.number().optional().describe("Optional timeout in milliseconds (max 600000). Default is 120000 (2 minutes)"),
|
|
654
555
|
workingDirectory: z.string().optional().describe("Working directory for the command. Defaults to the current working directory")
|
|
655
556
|
});
|
|
656
|
-
/**
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
557
|
+
/** Build the OS-aware tool description so the model writes syntax the host shell can run. */
|
|
558
|
+
function buildShellToolDescription(shell) {
|
|
559
|
+
return [
|
|
560
|
+
`Executes a command in the host shell and returns its output.`,
|
|
561
|
+
``,
|
|
562
|
+
`Active shell: ${shell.label}. ${shell.syntaxHint}`,
|
|
563
|
+
``,
|
|
564
|
+
`The working directory persists between commands, but shell state does not.`,
|
|
565
|
+
``,
|
|
566
|
+
`IMPORTANT: Avoid using this tool to run \`find\`, \`grep\`, \`cat\`, \`head\`, \`tail\`, \`sed\`, \`awk\`, or \`echo\` commands. Instead, use the appropriate dedicated tool:`,
|
|
567
|
+
` - File search: Use Glob (NOT find or ls)`,
|
|
568
|
+
` - Content search: Use Grep (NOT grep or rg)`,
|
|
569
|
+
` - Read files: Use Read (NOT cat/head/tail)`,
|
|
570
|
+
` - Edit files: Use Edit (NOT sed/awk)`,
|
|
571
|
+
``,
|
|
572
|
+
`For simple commands, keep the description brief (5-10 words). For complex commands, include enough context to clarify what the command does.`,
|
|
573
|
+
``,
|
|
574
|
+
`Output is limited to 30,000 characters. Longer output will be middle-truncated.`
|
|
575
|
+
].join("\n");
|
|
576
|
+
}
|
|
577
|
+
/** Run a shell command through the sandbox client, surfacing failures as a structured result. */
|
|
578
|
+
async function runInSandbox(command, timeout, workingDirectory, options) {
|
|
579
|
+
try {
|
|
664
580
|
const sandboxResult = await options.sandboxClient.run(command, {
|
|
665
581
|
timeoutMs: timeout,
|
|
666
582
|
workingDirectory
|
|
@@ -679,20 +595,37 @@ async function runBash(args, options = {}) {
|
|
|
679
595
|
};
|
|
680
596
|
return JSON.stringify(result);
|
|
681
597
|
}
|
|
598
|
+
}
|
|
599
|
+
/**
|
|
600
|
+
* Run a shell command and return stdout + stderr.
|
|
601
|
+
* Resolves with the IToolInvocationResult JSON string.
|
|
602
|
+
*/
|
|
603
|
+
async function runShell(args, options = {}, signal) {
|
|
604
|
+
const { command, timeout: rawTimeout = DEFAULT_TIMEOUT_MS$2, workingDirectory } = args;
|
|
605
|
+
const timeout = Math.min(rawTimeout, 6e5);
|
|
606
|
+
if (options.sandboxClient) return runInSandbox(command, timeout, workingDirectory, options);
|
|
607
|
+
const shell = resolvePlatformShell();
|
|
608
|
+
if (signal?.aborted) return JSON.stringify({
|
|
609
|
+
success: false,
|
|
610
|
+
output: "",
|
|
611
|
+
error: "Aborted before start"
|
|
612
|
+
});
|
|
682
613
|
return new Promise((resolve) => {
|
|
683
614
|
const stdoutChunks = [];
|
|
684
615
|
const stderrChunks = [];
|
|
685
616
|
let timedOut = false;
|
|
686
617
|
let settled = false;
|
|
687
|
-
const child = spawn(
|
|
618
|
+
const child = spawn(shell.command, shell.commandArgs(command), {
|
|
688
619
|
cwd: workingDirectory ?? process.cwd(),
|
|
689
620
|
env: process.env,
|
|
690
621
|
stdio: [
|
|
691
622
|
"pipe",
|
|
692
623
|
"pipe",
|
|
693
624
|
"pipe"
|
|
694
|
-
]
|
|
625
|
+
],
|
|
626
|
+
detached: SPAWN_DETACHED
|
|
695
627
|
});
|
|
628
|
+
child.stdin?.end();
|
|
696
629
|
child.stdout.on("data", (chunk) => {
|
|
697
630
|
stdoutChunks.push(chunk);
|
|
698
631
|
});
|
|
@@ -701,7 +634,7 @@ async function runBash(args, options = {}) {
|
|
|
701
634
|
});
|
|
702
635
|
const timer = setTimeout(() => {
|
|
703
636
|
timedOut = true;
|
|
704
|
-
child
|
|
637
|
+
killProcessTree(child, { processGroup: SPAWN_DETACHED });
|
|
705
638
|
settle({
|
|
706
639
|
success: false,
|
|
707
640
|
output: Buffer.concat(stdoutChunks).toString("utf8"),
|
|
@@ -712,8 +645,18 @@ async function runBash(args, options = {}) {
|
|
|
712
645
|
if (settled) return;
|
|
713
646
|
settled = true;
|
|
714
647
|
clearTimeout(timer);
|
|
648
|
+
signal?.removeEventListener("abort", onAbort);
|
|
715
649
|
resolve(JSON.stringify(result));
|
|
716
650
|
}
|
|
651
|
+
function onAbort() {
|
|
652
|
+
killProcessTree(child, { processGroup: SPAWN_DETACHED });
|
|
653
|
+
settle({
|
|
654
|
+
success: false,
|
|
655
|
+
output: Buffer.concat(stdoutChunks).toString("utf8"),
|
|
656
|
+
error: "Aborted"
|
|
657
|
+
});
|
|
658
|
+
}
|
|
659
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
717
660
|
child.on("error", (err) => {
|
|
718
661
|
settle({
|
|
719
662
|
success: false,
|
|
@@ -743,21 +686,37 @@ async function runBash(args, options = {}) {
|
|
|
743
686
|
});
|
|
744
687
|
}
|
|
745
688
|
/**
|
|
746
|
-
*
|
|
689
|
+
* Build a host-shell command tool under a given registered name. Both `Shell` and the
|
|
690
|
+
* model-familiar `Bash` are registered as aliases of this one OS-aware implementation
|
|
691
|
+
* (TERM-008): the shell is resolved per OS and the description names the active shell so the
|
|
692
|
+
* model writes the right syntax regardless of which alias it calls.
|
|
747
693
|
*/
|
|
748
|
-
function
|
|
749
|
-
return createZodFunctionTool(
|
|
750
|
-
return
|
|
694
|
+
function createHostShellTool(name, options) {
|
|
695
|
+
return createZodFunctionTool(name, buildShellToolDescription(resolvePlatformShell()), ShellSchema, async (params, context) => {
|
|
696
|
+
return runShell(params, options, context?.signal);
|
|
751
697
|
});
|
|
752
698
|
}
|
|
753
699
|
/**
|
|
754
|
-
*
|
|
700
|
+
* Create a `Shell` tool instance — register with the Robota agent tools registry.
|
|
701
|
+
* The description is resolved at creation time for the host's active shell.
|
|
755
702
|
*/
|
|
703
|
+
function createShellTool(options = {}) {
|
|
704
|
+
return createHostShellTool("Shell", options);
|
|
705
|
+
}
|
|
706
|
+
/**
|
|
707
|
+
* Create a `Bash` tool instance — the model-familiar alias of the same OS-aware shell tool.
|
|
708
|
+
*/
|
|
709
|
+
function createBashTool(options = {}) {
|
|
710
|
+
return createHostShellTool("Bash", options);
|
|
711
|
+
}
|
|
712
|
+
/** `Shell` tool instance — register with the Robota agent tools registry. */
|
|
713
|
+
const shellTool = createShellTool();
|
|
714
|
+
/** `Bash` tool instance — model-familiar alias of {@link shellTool}. */
|
|
756
715
|
const bashTool = createBashTool();
|
|
757
716
|
//#endregion
|
|
758
717
|
//#region src/builtins/path-guard.ts
|
|
759
718
|
/**
|
|
760
|
-
* Returns a JSON-serialized
|
|
719
|
+
* Returns a JSON-serialized IToolInvocationResult error when filePath is outside cwd.
|
|
761
720
|
* Returns undefined when the path is within cwd or cwd is not set.
|
|
762
721
|
*/
|
|
763
722
|
function checkPathWithinCwd(filePath, cwd) {
|
|
@@ -1292,7 +1251,7 @@ function classifyFetchError(err) {
|
|
|
1292
1251
|
if (code === "CERT_HAS_EXPIRED" || code === "UNABLE_TO_VERIFY_LEAF_SIGNATURE") return `Network error: SSL certificate error (${code}). The server's certificate is invalid. Do not retry with the same URL.`;
|
|
1293
1252
|
return `Network error: ${err.message} Check that the URL is correct and the server is reachable.`;
|
|
1294
1253
|
}
|
|
1295
|
-
async function runWebFetch(args) {
|
|
1254
|
+
async function runWebFetch(args, signal) {
|
|
1296
1255
|
const { url, headers } = args;
|
|
1297
1256
|
try {
|
|
1298
1257
|
new URL(url);
|
|
@@ -1307,12 +1266,13 @@ async function runWebFetch(args) {
|
|
|
1307
1266
|
try {
|
|
1308
1267
|
const controller = new AbortController();
|
|
1309
1268
|
const timeout = setTimeout(() => controller.abort(), DEFAULT_TIMEOUT_MS$1);
|
|
1269
|
+
const fetchSignal = signal ? AbortSignal.any([controller.signal, signal]) : controller.signal;
|
|
1310
1270
|
const response = await fetch(url, {
|
|
1311
1271
|
headers: {
|
|
1312
1272
|
"User-Agent": "Robota-CLI/3.0",
|
|
1313
1273
|
...headers ?? {}
|
|
1314
1274
|
},
|
|
1315
|
-
signal:
|
|
1275
|
+
signal: fetchSignal,
|
|
1316
1276
|
redirect: "follow"
|
|
1317
1277
|
});
|
|
1318
1278
|
clearTimeout(timeout);
|
|
@@ -1350,7 +1310,7 @@ async function runWebFetch(args) {
|
|
|
1350
1310
|
return JSON.stringify(result);
|
|
1351
1311
|
}
|
|
1352
1312
|
}
|
|
1353
|
-
const webFetchTool = createZodFunctionTool("WebFetch", "Fetch a URL and return its content as text. HTML pages are converted to plain text.", WebFetchSchema, async (params) => runWebFetch(params));
|
|
1313
|
+
const webFetchTool = createZodFunctionTool("WebFetch", "Fetch a URL and return its content as text. HTML pages are converted to plain text.", WebFetchSchema, async (params, context) => runWebFetch(params, context?.signal));
|
|
1354
1314
|
//#endregion
|
|
1355
1315
|
//#region src/builtins/web-search-tool.ts
|
|
1356
1316
|
/**
|
|
@@ -1365,7 +1325,7 @@ const WebSearchSchema = z.object({
|
|
|
1365
1325
|
query: z.string().describe("The search query"),
|
|
1366
1326
|
limit: z.number().optional().describe(`Maximum number of results to return (default: ${DEFAULT_LIMIT})`)
|
|
1367
1327
|
});
|
|
1368
|
-
async function runWebSearch(args) {
|
|
1328
|
+
async function runWebSearch(args, signal) {
|
|
1369
1329
|
const { query, limit = DEFAULT_LIMIT } = args;
|
|
1370
1330
|
const apiKey = process.env["BRAVE_API_KEY"];
|
|
1371
1331
|
if (!apiKey) return JSON.stringify({
|
|
@@ -1376,6 +1336,7 @@ async function runWebSearch(args) {
|
|
|
1376
1336
|
try {
|
|
1377
1337
|
const controller = new AbortController();
|
|
1378
1338
|
const timeout = setTimeout(() => controller.abort(), DEFAULT_TIMEOUT_MS);
|
|
1339
|
+
const fetchSignal = signal ? AbortSignal.any([controller.signal, signal]) : controller.signal;
|
|
1379
1340
|
const params = new URLSearchParams({
|
|
1380
1341
|
q: query,
|
|
1381
1342
|
count: String(Math.min(limit, 20))
|
|
@@ -1386,7 +1347,7 @@ async function runWebSearch(args) {
|
|
|
1386
1347
|
"Accept-Encoding": "gzip",
|
|
1387
1348
|
"X-Subscription-Token": apiKey
|
|
1388
1349
|
},
|
|
1389
|
-
signal:
|
|
1350
|
+
signal: fetchSignal
|
|
1390
1351
|
});
|
|
1391
1352
|
clearTimeout(timeout);
|
|
1392
1353
|
if (!response.ok) {
|
|
@@ -1416,8 +1377,118 @@ async function runWebSearch(args) {
|
|
|
1416
1377
|
return JSON.stringify(result);
|
|
1417
1378
|
}
|
|
1418
1379
|
}
|
|
1419
|
-
const webSearchTool = createZodFunctionTool("WebSearch", "Search the web and return results with title, URL, and snippet.", WebSearchSchema, async (params) => runWebSearch(params));
|
|
1380
|
+
const webSearchTool = createZodFunctionTool("WebSearch", "Search the web and return results with title, URL, and snippet.", WebSearchSchema, async (params, context) => runWebSearch(params, context?.signal));
|
|
1381
|
+
//#endregion
|
|
1382
|
+
//#region src/builtins/ask-user-question-tool.ts
|
|
1383
|
+
/**
|
|
1384
|
+
* AskUserQuestionTool — let the model ask the user structured questions mid-turn (CMD-005).
|
|
1385
|
+
*
|
|
1386
|
+
* Built on the CMD-004 ask seam: each question maps onto the `IActionRequest` SSOT and is issued
|
|
1387
|
+
* through the injected `IToolExecutionContext.ask` port; the attached environment renders it (Ink
|
|
1388
|
+
* dialog, web modal, programmatic pre-answer) and the answers return as the tool result.
|
|
1389
|
+
*
|
|
1390
|
+
* Contract points (spec CMD-005):
|
|
1391
|
+
* - 1–4 questions per call, asked sequentially (the channel's ask queue renders one at a time).
|
|
1392
|
+
* - Cancellation is data, not an exception: a dismissed question yields `{ cancelled: true }` and the
|
|
1393
|
+
* remaining unasked questions of the same call are marked cancelled too (no per-item re-prompt).
|
|
1394
|
+
* - No `context.ask` (headless/automation): returns `{ unavailable: true, reason }` — never a silent
|
|
1395
|
+
* guess, never a thrown error, so the model can continue autonomously.
|
|
1396
|
+
*/
|
|
1397
|
+
const MAX_QUESTIONS = 4;
|
|
1398
|
+
const QuestionSchema = z.object({
|
|
1399
|
+
question: z.string().min(1).describe("The complete question to ask the user."),
|
|
1400
|
+
header: z.string().optional().describe("Very short topic label for the question (e.g. \"Auth method\")."),
|
|
1401
|
+
options: z.array(z.union([z.string().min(1).describe("Display text of this choice."), z.object({
|
|
1402
|
+
label: z.string().min(1).describe("Display text of this choice."),
|
|
1403
|
+
description: z.string().optional().describe("What choosing this option means.")
|
|
1404
|
+
})])).optional().describe("Predefined choices (strings or {label, description}). Omit for pure free text."),
|
|
1405
|
+
multiSelect: z.boolean().optional().describe("Allow selecting multiple options (default: single select)."),
|
|
1406
|
+
allowFreeText: z.boolean().optional().describe("Allow a typed custom answer besides the options (default: true).")
|
|
1407
|
+
});
|
|
1408
|
+
const AskUserQuestionSchema = z.object({ questions: z.array(QuestionSchema).min(1).max(MAX_QUESTIONS).describe(`Questions to ask the user (1-${MAX_QUESTIONS}), rendered one at a time.`) });
|
|
1409
|
+
const ASK_USER_QUESTION_DESCRIPTION = [
|
|
1410
|
+
"Ask the user one or more structured questions and wait for their answers.",
|
|
1411
|
+
"",
|
|
1412
|
+
"Use this when you are blocked on a decision only the user can make — ambiguous requirements,",
|
|
1413
|
+
"mutually exclusive approaches, or choices with real trade-offs. Do not use it for decisions with",
|
|
1414
|
+
"a conventional default or facts you can verify yourself.",
|
|
1415
|
+
"",
|
|
1416
|
+
`Provide 1-${MAX_QUESTIONS} questions. Each question offers predefined options and/or free text:`,
|
|
1417
|
+
" - options + default: user picks one option (or types a custom answer unless allowFreeText: false)",
|
|
1418
|
+
" - multiSelect: true: user may pick several options",
|
|
1419
|
+
" - no options: pure free-text entry",
|
|
1420
|
+
"",
|
|
1421
|
+
"The result is a JSON array with one entry per question: the selected option labels in `values`",
|
|
1422
|
+
"and/or the typed answer in `text`, or `cancelled: true` if the user dismissed the question.",
|
|
1423
|
+
"If no interactive user is attached (headless run), the result is `{ unavailable: true }` —",
|
|
1424
|
+
"continue autonomously with your best judgment and say what you assumed."
|
|
1425
|
+
].join("\n");
|
|
1426
|
+
function toActionRequest(question) {
|
|
1427
|
+
const options = (question.options ?? []).map((option) => typeof option === "string" ? { label: option } : option);
|
|
1428
|
+
const multi = question.multiSelect === true && options.length > 1;
|
|
1429
|
+
return {
|
|
1430
|
+
id: `ask_${randomUUID()}`,
|
|
1431
|
+
title: question.question,
|
|
1432
|
+
...question.header !== void 0 ? { description: question.header } : {},
|
|
1433
|
+
...options.length > 0 ? { options: options.map((o) => ({
|
|
1434
|
+
value: o.label,
|
|
1435
|
+
label: o.label,
|
|
1436
|
+
...o.description !== void 0 ? { description: o.description } : {}
|
|
1437
|
+
})) } : {},
|
|
1438
|
+
minSelect: options.length > 0 ? 1 : 0,
|
|
1439
|
+
maxSelect: multi ? options.length : 1,
|
|
1440
|
+
allowFreeText: question.allowFreeText !== false || options.length === 0
|
|
1441
|
+
};
|
|
1442
|
+
}
|
|
1443
|
+
async function askQuestions(args, ask) {
|
|
1444
|
+
const answers = [];
|
|
1445
|
+
let dismissed = false;
|
|
1446
|
+
for (const question of args.questions) {
|
|
1447
|
+
if (dismissed) {
|
|
1448
|
+
answers.push({
|
|
1449
|
+
question: question.question,
|
|
1450
|
+
cancelled: true
|
|
1451
|
+
});
|
|
1452
|
+
continue;
|
|
1453
|
+
}
|
|
1454
|
+
const response = await ask(toActionRequest(question));
|
|
1455
|
+
if (response.type === "cancelled") {
|
|
1456
|
+
dismissed = true;
|
|
1457
|
+
answers.push({
|
|
1458
|
+
question: question.question,
|
|
1459
|
+
cancelled: true
|
|
1460
|
+
});
|
|
1461
|
+
continue;
|
|
1462
|
+
}
|
|
1463
|
+
answers.push({
|
|
1464
|
+
question: question.question,
|
|
1465
|
+
values: [...response.values],
|
|
1466
|
+
...response.text !== void 0 ? { text: response.text } : {}
|
|
1467
|
+
});
|
|
1468
|
+
}
|
|
1469
|
+
return { answers };
|
|
1470
|
+
}
|
|
1471
|
+
/**
|
|
1472
|
+
* Create an `AskUserQuestion` tool instance — register with the Robota agent tools registry.
|
|
1473
|
+
*/
|
|
1474
|
+
function createAskUserQuestionTool() {
|
|
1475
|
+
return createZodFunctionTool("AskUserQuestion", ASK_USER_QUESTION_DESCRIPTION, AskUserQuestionSchema, async (params, context) => {
|
|
1476
|
+
const args = params;
|
|
1477
|
+
const ask = context?.ask;
|
|
1478
|
+
const output = ask ? await askQuestions(args, ask) : {
|
|
1479
|
+
unavailable: true,
|
|
1480
|
+
reason: "no interactive user attached"
|
|
1481
|
+
};
|
|
1482
|
+
const result = {
|
|
1483
|
+
success: true,
|
|
1484
|
+
output: JSON.stringify(output)
|
|
1485
|
+
};
|
|
1486
|
+
return JSON.stringify(result);
|
|
1487
|
+
});
|
|
1488
|
+
}
|
|
1489
|
+
/** `AskUserQuestion` tool instance — register with the Robota agent tools registry. */
|
|
1490
|
+
const askUserQuestionTool = createAskUserQuestionTool();
|
|
1420
1491
|
//#endregion
|
|
1421
|
-
export { E2BSandboxClient, FunctionTool, InMemorySandboxClient, ToolRegistry, applyWorkspaceManifest, bashTool, createBashTool, createEditTool, createFunctionTool, createReadTool, createWriteTool, createZodFunctionTool, editTool, globTool, grepTool, readTool, validateWorkspaceManifestPath, webFetchTool, webSearchTool, writeTool
|
|
1492
|
+
export { E2BSandboxClient, FunctionTool, InMemorySandboxClient, ToolRegistry, applyWorkspaceManifest, askUserQuestionTool, bashTool, createAskUserQuestionTool, createBashTool, createEditTool, createFunctionTool, createReadTool, createShellTool, createWriteTool, createZodFunctionTool, editTool, globTool, grepTool, readTool, shellTool, validateWorkspaceManifestPath, webFetchTool, webSearchTool, writeTool };
|
|
1422
1493
|
|
|
1423
1494
|
//# sourceMappingURL=index.js.map
|