@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.cjs
CHANGED
|
@@ -25,6 +25,7 @@ let node_fs_promises = require("node:fs/promises");
|
|
|
25
25
|
let node_path = require("node:path");
|
|
26
26
|
let _robota_sdk_agent_core = require("@robota-sdk/agent-core");
|
|
27
27
|
let node_child_process = require("node:child_process");
|
|
28
|
+
let _robota_sdk_agent_process = require("@robota-sdk/agent-process");
|
|
28
29
|
let zod = require("zod");
|
|
29
30
|
let node_crypto = require("node:crypto");
|
|
30
31
|
let fast_glob = require("fast-glob");
|
|
@@ -445,111 +446,6 @@ function validateToolParameters(parameters, schemaRequired, schemaProperties, ad
|
|
|
445
446
|
};
|
|
446
447
|
}
|
|
447
448
|
//#endregion
|
|
448
|
-
//#region src/implementations/function-tool/schema-converter.ts
|
|
449
|
-
/**
|
|
450
|
-
* Convert Zod schema to JSON Schema format with safe undefined handling
|
|
451
|
-
*/
|
|
452
|
-
function zodToJsonSchema(schema, options = {}) {
|
|
453
|
-
const properties = {};
|
|
454
|
-
const required = [];
|
|
455
|
-
const schemaDef = schema._def;
|
|
456
|
-
if (!schemaDef) throw new Error("Zod schema is missing _def; cannot convert to JSON schema.");
|
|
457
|
-
if (schemaDef.typeName === "ZodObject" && schemaDef.shape) {
|
|
458
|
-
const shape = typeof schemaDef.shape === "function" ? schemaDef.shape() : schemaDef.shape;
|
|
459
|
-
for (const [key, typeObj] of Object.entries(shape)) {
|
|
460
|
-
properties[key] = convertZodTypeToProperty(typeObj);
|
|
461
|
-
if (isRequiredField(typeObj)) required.push(key);
|
|
462
|
-
}
|
|
463
|
-
}
|
|
464
|
-
return {
|
|
465
|
-
type: "object",
|
|
466
|
-
properties,
|
|
467
|
-
required,
|
|
468
|
-
...(options.allowAdditionalProperties || schemaDef.unknownKeys === "passthrough") && { additionalProperties: true }
|
|
469
|
-
};
|
|
470
|
-
}
|
|
471
|
-
/**
|
|
472
|
-
* Convert individual Zod type to parameter schema with safe undefined handling
|
|
473
|
-
*/
|
|
474
|
-
function convertZodTypeToProperty(typeObj) {
|
|
475
|
-
const typeDef = typeObj._def;
|
|
476
|
-
if (!typeDef) throw new Error("Zod type is missing _def; cannot convert to JSON schema.");
|
|
477
|
-
const base = {};
|
|
478
|
-
if (typeDef.description) base.description = typeDef.description;
|
|
479
|
-
switch (typeDef.typeName) {
|
|
480
|
-
case "ZodString": return {
|
|
481
|
-
type: "string",
|
|
482
|
-
...base
|
|
483
|
-
};
|
|
484
|
-
case "ZodNumber": return {
|
|
485
|
-
type: "number",
|
|
486
|
-
...base
|
|
487
|
-
};
|
|
488
|
-
case "ZodBoolean": return {
|
|
489
|
-
type: "boolean",
|
|
490
|
-
...base
|
|
491
|
-
};
|
|
492
|
-
case "ZodArray":
|
|
493
|
-
if (!typeDef.type) throw new Error("ZodArray is missing item type; cannot convert to JSON schema.");
|
|
494
|
-
return {
|
|
495
|
-
type: "array",
|
|
496
|
-
items: convertZodTypeToProperty(typeDef.type),
|
|
497
|
-
...base
|
|
498
|
-
};
|
|
499
|
-
case "ZodObject": return {
|
|
500
|
-
type: "object",
|
|
501
|
-
...base
|
|
502
|
-
};
|
|
503
|
-
case "ZodEnum": {
|
|
504
|
-
const enumValues = typeDef.values;
|
|
505
|
-
if (!enumValues || !Array.isArray(enumValues)) throw new Error("ZodEnum is missing enum values; cannot convert to JSON schema.");
|
|
506
|
-
return {
|
|
507
|
-
type: "string",
|
|
508
|
-
enum: enumValues,
|
|
509
|
-
...base
|
|
510
|
-
};
|
|
511
|
-
}
|
|
512
|
-
case "ZodOptional":
|
|
513
|
-
if (typeDef.innerType) return {
|
|
514
|
-
...convertZodTypeToProperty(typeDef.innerType),
|
|
515
|
-
...base
|
|
516
|
-
};
|
|
517
|
-
throw new Error("ZodOptional is missing innerType; cannot convert to JSON schema.");
|
|
518
|
-
case "ZodNullable":
|
|
519
|
-
if (typeDef.innerType) return {
|
|
520
|
-
...convertZodTypeToProperty(typeDef.innerType),
|
|
521
|
-
...base
|
|
522
|
-
};
|
|
523
|
-
throw new Error("ZodNullable is missing innerType; cannot convert to JSON schema.");
|
|
524
|
-
case "ZodDefault":
|
|
525
|
-
if (typeDef.innerType) return {
|
|
526
|
-
...convertZodTypeToProperty(typeDef.innerType),
|
|
527
|
-
...base
|
|
528
|
-
};
|
|
529
|
-
throw new Error("ZodDefault is missing innerType; cannot convert to JSON schema.");
|
|
530
|
-
case "ZodRecord":
|
|
531
|
-
if (typeDef.valueType) return {
|
|
532
|
-
type: "object",
|
|
533
|
-
additionalProperties: convertZodTypeToProperty(typeDef.valueType),
|
|
534
|
-
...base
|
|
535
|
-
};
|
|
536
|
-
return {
|
|
537
|
-
type: "object",
|
|
538
|
-
additionalProperties: { type: "string" },
|
|
539
|
-
...base
|
|
540
|
-
};
|
|
541
|
-
default: throw new Error(`Unsupported Zod type: ${String(typeDef.typeName)}`);
|
|
542
|
-
}
|
|
543
|
-
}
|
|
544
|
-
/**
|
|
545
|
-
* Check if a Zod field is required (not optional or nullable)
|
|
546
|
-
*/
|
|
547
|
-
function isRequiredField(typeObj) {
|
|
548
|
-
const typeDef = typeObj._def;
|
|
549
|
-
if (!typeDef) throw new Error("Zod schema is missing _def; cannot determine required fields.");
|
|
550
|
-
return typeDef.typeName !== "ZodOptional" && typeDef.typeName !== "ZodNullable" && typeDef.typeName !== "ZodDefault";
|
|
551
|
-
}
|
|
552
|
-
//#endregion
|
|
553
449
|
//#region src/implementations/function-tool.ts
|
|
554
450
|
/**
|
|
555
451
|
* Function tool implementation
|
|
@@ -653,39 +549,59 @@ function createZodFunctionTool(name, description, zodSchema, fn) {
|
|
|
653
549
|
const schema = {
|
|
654
550
|
name,
|
|
655
551
|
description,
|
|
656
|
-
parameters: zodToJsonSchema(zodSchema)
|
|
552
|
+
parameters: (0, _robota_sdk_agent_core.zodToJsonSchema)(zodSchema)
|
|
657
553
|
};
|
|
658
554
|
const wrappedFn = async (parameters, context) => {
|
|
659
555
|
const parseResult = zodSchema.safeParse(parameters);
|
|
660
556
|
if (!parseResult.success) throw new _robota_sdk_agent_core.ValidationError(`Zod validation failed: ${parseResult.error}`);
|
|
661
|
-
const result = await fn(parseResult.data
|
|
557
|
+
const result = await fn(parseResult.data, context);
|
|
662
558
|
return typeof result === "string" ? result : JSON.stringify(result);
|
|
663
559
|
};
|
|
664
560
|
return new FunctionTool(schema, wrappedFn);
|
|
665
561
|
}
|
|
666
562
|
//#endregion
|
|
667
|
-
//#region src/builtins/
|
|
563
|
+
//#region src/builtins/shell-tool.ts
|
|
668
564
|
/**
|
|
669
|
-
*
|
|
565
|
+
* ShellTool — execute a host shell command via child_process.spawn (TERM-008).
|
|
566
|
+
*
|
|
567
|
+
* Cross-platform: the shell is resolved per OS through `resolvePlatformShell()` (POSIX `sh`/`bash`,
|
|
568
|
+
* Windows PowerShell). The tool name is `Shell` and its description is built dynamically from the
|
|
569
|
+
* resolved shell so the model is told the active shell/OS and writes the right syntax.
|
|
670
570
|
*
|
|
671
|
-
* Returns
|
|
672
|
-
*
|
|
673
|
-
* exited non-zero — the LLM can decide what to do with that information).
|
|
571
|
+
* Returns an IToolInvocationResult JSON string. A non-zero exit is returned as success:true with
|
|
572
|
+
* exitCode set (the command ran, it just exited non-zero — the LLM decides what to do with that).
|
|
674
573
|
*/
|
|
574
|
+
/** POSIX children are spawned detached so a process-group kill reaps grandchildren (CORE-023). */
|
|
575
|
+
const SPAWN_DETACHED = process.platform !== "win32";
|
|
675
576
|
const DEFAULT_TIMEOUT_MS$2 = 12e4;
|
|
676
|
-
const
|
|
677
|
-
command: zod.z.string().describe("The
|
|
577
|
+
const ShellSchema = zod.z.object({
|
|
578
|
+
command: zod.z.string().describe("The shell command to execute"),
|
|
678
579
|
timeout: zod.z.number().optional().describe("Optional timeout in milliseconds (max 600000). Default is 120000 (2 minutes)"),
|
|
679
580
|
workingDirectory: zod.z.string().optional().describe("Working directory for the command. Defaults to the current working directory")
|
|
680
581
|
});
|
|
681
|
-
/**
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
582
|
+
/** Build the OS-aware tool description so the model writes syntax the host shell can run. */
|
|
583
|
+
function buildShellToolDescription(shell) {
|
|
584
|
+
return [
|
|
585
|
+
`Executes a command in the host shell and returns its output.`,
|
|
586
|
+
``,
|
|
587
|
+
`Active shell: ${shell.label}. ${shell.syntaxHint}`,
|
|
588
|
+
``,
|
|
589
|
+
`The working directory persists between commands, but shell state does not.`,
|
|
590
|
+
``,
|
|
591
|
+
`IMPORTANT: Avoid using this tool to run \`find\`, \`grep\`, \`cat\`, \`head\`, \`tail\`, \`sed\`, \`awk\`, or \`echo\` commands. Instead, use the appropriate dedicated tool:`,
|
|
592
|
+
` - File search: Use Glob (NOT find or ls)`,
|
|
593
|
+
` - Content search: Use Grep (NOT grep or rg)`,
|
|
594
|
+
` - Read files: Use Read (NOT cat/head/tail)`,
|
|
595
|
+
` - Edit files: Use Edit (NOT sed/awk)`,
|
|
596
|
+
``,
|
|
597
|
+
`For simple commands, keep the description brief (5-10 words). For complex commands, include enough context to clarify what the command does.`,
|
|
598
|
+
``,
|
|
599
|
+
`Output is limited to 30,000 characters. Longer output will be middle-truncated.`
|
|
600
|
+
].join("\n");
|
|
601
|
+
}
|
|
602
|
+
/** Run a shell command through the sandbox client, surfacing failures as a structured result. */
|
|
603
|
+
async function runInSandbox(command, timeout, workingDirectory, options) {
|
|
604
|
+
try {
|
|
689
605
|
const sandboxResult = await options.sandboxClient.run(command, {
|
|
690
606
|
timeoutMs: timeout,
|
|
691
607
|
workingDirectory
|
|
@@ -704,20 +620,37 @@ async function runBash(args, options = {}) {
|
|
|
704
620
|
};
|
|
705
621
|
return JSON.stringify(result);
|
|
706
622
|
}
|
|
623
|
+
}
|
|
624
|
+
/**
|
|
625
|
+
* Run a shell command and return stdout + stderr.
|
|
626
|
+
* Resolves with the IToolInvocationResult JSON string.
|
|
627
|
+
*/
|
|
628
|
+
async function runShell(args, options = {}, signal) {
|
|
629
|
+
const { command, timeout: rawTimeout = DEFAULT_TIMEOUT_MS$2, workingDirectory } = args;
|
|
630
|
+
const timeout = Math.min(rawTimeout, 6e5);
|
|
631
|
+
if (options.sandboxClient) return runInSandbox(command, timeout, workingDirectory, options);
|
|
632
|
+
const shell = (0, _robota_sdk_agent_core.resolvePlatformShell)();
|
|
633
|
+
if (signal?.aborted) return JSON.stringify({
|
|
634
|
+
success: false,
|
|
635
|
+
output: "",
|
|
636
|
+
error: "Aborted before start"
|
|
637
|
+
});
|
|
707
638
|
return new Promise((resolve) => {
|
|
708
639
|
const stdoutChunks = [];
|
|
709
640
|
const stderrChunks = [];
|
|
710
641
|
let timedOut = false;
|
|
711
642
|
let settled = false;
|
|
712
|
-
const child = (0, node_child_process.spawn)(
|
|
643
|
+
const child = (0, node_child_process.spawn)(shell.command, shell.commandArgs(command), {
|
|
713
644
|
cwd: workingDirectory ?? process.cwd(),
|
|
714
645
|
env: process.env,
|
|
715
646
|
stdio: [
|
|
716
647
|
"pipe",
|
|
717
648
|
"pipe",
|
|
718
649
|
"pipe"
|
|
719
|
-
]
|
|
650
|
+
],
|
|
651
|
+
detached: SPAWN_DETACHED
|
|
720
652
|
});
|
|
653
|
+
child.stdin?.end();
|
|
721
654
|
child.stdout.on("data", (chunk) => {
|
|
722
655
|
stdoutChunks.push(chunk);
|
|
723
656
|
});
|
|
@@ -726,7 +659,7 @@ async function runBash(args, options = {}) {
|
|
|
726
659
|
});
|
|
727
660
|
const timer = setTimeout(() => {
|
|
728
661
|
timedOut = true;
|
|
729
|
-
|
|
662
|
+
(0, _robota_sdk_agent_process.killProcessTree)(child, { processGroup: SPAWN_DETACHED });
|
|
730
663
|
settle({
|
|
731
664
|
success: false,
|
|
732
665
|
output: Buffer.concat(stdoutChunks).toString("utf8"),
|
|
@@ -737,8 +670,18 @@ async function runBash(args, options = {}) {
|
|
|
737
670
|
if (settled) return;
|
|
738
671
|
settled = true;
|
|
739
672
|
clearTimeout(timer);
|
|
673
|
+
signal?.removeEventListener("abort", onAbort);
|
|
740
674
|
resolve(JSON.stringify(result));
|
|
741
675
|
}
|
|
676
|
+
function onAbort() {
|
|
677
|
+
(0, _robota_sdk_agent_process.killProcessTree)(child, { processGroup: SPAWN_DETACHED });
|
|
678
|
+
settle({
|
|
679
|
+
success: false,
|
|
680
|
+
output: Buffer.concat(stdoutChunks).toString("utf8"),
|
|
681
|
+
error: "Aborted"
|
|
682
|
+
});
|
|
683
|
+
}
|
|
684
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
742
685
|
child.on("error", (err) => {
|
|
743
686
|
settle({
|
|
744
687
|
success: false,
|
|
@@ -768,21 +711,37 @@ async function runBash(args, options = {}) {
|
|
|
768
711
|
});
|
|
769
712
|
}
|
|
770
713
|
/**
|
|
771
|
-
*
|
|
714
|
+
* Build a host-shell command tool under a given registered name. Both `Shell` and the
|
|
715
|
+
* model-familiar `Bash` are registered as aliases of this one OS-aware implementation
|
|
716
|
+
* (TERM-008): the shell is resolved per OS and the description names the active shell so the
|
|
717
|
+
* model writes the right syntax regardless of which alias it calls.
|
|
772
718
|
*/
|
|
773
|
-
function
|
|
774
|
-
return createZodFunctionTool(
|
|
775
|
-
return
|
|
719
|
+
function createHostShellTool(name, options) {
|
|
720
|
+
return createZodFunctionTool(name, buildShellToolDescription((0, _robota_sdk_agent_core.resolvePlatformShell)()), ShellSchema, async (params, context) => {
|
|
721
|
+
return runShell(params, options, context?.signal);
|
|
776
722
|
});
|
|
777
723
|
}
|
|
778
724
|
/**
|
|
779
|
-
*
|
|
725
|
+
* Create a `Shell` tool instance — register with the Robota agent tools registry.
|
|
726
|
+
* The description is resolved at creation time for the host's active shell.
|
|
780
727
|
*/
|
|
728
|
+
function createShellTool(options = {}) {
|
|
729
|
+
return createHostShellTool("Shell", options);
|
|
730
|
+
}
|
|
731
|
+
/**
|
|
732
|
+
* Create a `Bash` tool instance — the model-familiar alias of the same OS-aware shell tool.
|
|
733
|
+
*/
|
|
734
|
+
function createBashTool(options = {}) {
|
|
735
|
+
return createHostShellTool("Bash", options);
|
|
736
|
+
}
|
|
737
|
+
/** `Shell` tool instance — register with the Robota agent tools registry. */
|
|
738
|
+
const shellTool = createShellTool();
|
|
739
|
+
/** `Bash` tool instance — model-familiar alias of {@link shellTool}. */
|
|
781
740
|
const bashTool = createBashTool();
|
|
782
741
|
//#endregion
|
|
783
742
|
//#region src/builtins/path-guard.ts
|
|
784
743
|
/**
|
|
785
|
-
* Returns a JSON-serialized
|
|
744
|
+
* Returns a JSON-serialized IToolInvocationResult error when filePath is outside cwd.
|
|
786
745
|
* Returns undefined when the path is within cwd or cwd is not set.
|
|
787
746
|
*/
|
|
788
747
|
function checkPathWithinCwd(filePath, cwd) {
|
|
@@ -1317,7 +1276,7 @@ function classifyFetchError(err) {
|
|
|
1317
1276
|
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.`;
|
|
1318
1277
|
return `Network error: ${err.message} Check that the URL is correct and the server is reachable.`;
|
|
1319
1278
|
}
|
|
1320
|
-
async function runWebFetch(args) {
|
|
1279
|
+
async function runWebFetch(args, signal) {
|
|
1321
1280
|
const { url, headers } = args;
|
|
1322
1281
|
try {
|
|
1323
1282
|
new URL(url);
|
|
@@ -1332,12 +1291,13 @@ async function runWebFetch(args) {
|
|
|
1332
1291
|
try {
|
|
1333
1292
|
const controller = new AbortController();
|
|
1334
1293
|
const timeout = setTimeout(() => controller.abort(), DEFAULT_TIMEOUT_MS$1);
|
|
1294
|
+
const fetchSignal = signal ? AbortSignal.any([controller.signal, signal]) : controller.signal;
|
|
1335
1295
|
const response = await fetch(url, {
|
|
1336
1296
|
headers: {
|
|
1337
1297
|
"User-Agent": "Robota-CLI/3.0",
|
|
1338
1298
|
...headers ?? {}
|
|
1339
1299
|
},
|
|
1340
|
-
signal:
|
|
1300
|
+
signal: fetchSignal,
|
|
1341
1301
|
redirect: "follow"
|
|
1342
1302
|
});
|
|
1343
1303
|
clearTimeout(timeout);
|
|
@@ -1375,7 +1335,7 @@ async function runWebFetch(args) {
|
|
|
1375
1335
|
return JSON.stringify(result);
|
|
1376
1336
|
}
|
|
1377
1337
|
}
|
|
1378
|
-
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));
|
|
1338
|
+
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));
|
|
1379
1339
|
//#endregion
|
|
1380
1340
|
//#region src/builtins/web-search-tool.ts
|
|
1381
1341
|
/**
|
|
@@ -1390,7 +1350,7 @@ const WebSearchSchema = zod.z.object({
|
|
|
1390
1350
|
query: zod.z.string().describe("The search query"),
|
|
1391
1351
|
limit: zod.z.number().optional().describe(`Maximum number of results to return (default: ${DEFAULT_LIMIT})`)
|
|
1392
1352
|
});
|
|
1393
|
-
async function runWebSearch(args) {
|
|
1353
|
+
async function runWebSearch(args, signal) {
|
|
1394
1354
|
const { query, limit = DEFAULT_LIMIT } = args;
|
|
1395
1355
|
const apiKey = process.env["BRAVE_API_KEY"];
|
|
1396
1356
|
if (!apiKey) return JSON.stringify({
|
|
@@ -1401,6 +1361,7 @@ async function runWebSearch(args) {
|
|
|
1401
1361
|
try {
|
|
1402
1362
|
const controller = new AbortController();
|
|
1403
1363
|
const timeout = setTimeout(() => controller.abort(), DEFAULT_TIMEOUT_MS);
|
|
1364
|
+
const fetchSignal = signal ? AbortSignal.any([controller.signal, signal]) : controller.signal;
|
|
1404
1365
|
const params = new URLSearchParams({
|
|
1405
1366
|
q: query,
|
|
1406
1367
|
count: String(Math.min(limit, 20))
|
|
@@ -1411,7 +1372,7 @@ async function runWebSearch(args) {
|
|
|
1411
1372
|
"Accept-Encoding": "gzip",
|
|
1412
1373
|
"X-Subscription-Token": apiKey
|
|
1413
1374
|
},
|
|
1414
|
-
signal:
|
|
1375
|
+
signal: fetchSignal
|
|
1415
1376
|
});
|
|
1416
1377
|
clearTimeout(timeout);
|
|
1417
1378
|
if (!response.ok) {
|
|
@@ -1441,26 +1402,139 @@ async function runWebSearch(args) {
|
|
|
1441
1402
|
return JSON.stringify(result);
|
|
1442
1403
|
}
|
|
1443
1404
|
}
|
|
1444
|
-
const webSearchTool = createZodFunctionTool("WebSearch", "Search the web and return results with title, URL, and snippet.", WebSearchSchema, async (params) => runWebSearch(params));
|
|
1405
|
+
const webSearchTool = createZodFunctionTool("WebSearch", "Search the web and return results with title, URL, and snippet.", WebSearchSchema, async (params, context) => runWebSearch(params, context?.signal));
|
|
1406
|
+
//#endregion
|
|
1407
|
+
//#region src/builtins/ask-user-question-tool.ts
|
|
1408
|
+
/**
|
|
1409
|
+
* AskUserQuestionTool — let the model ask the user structured questions mid-turn (CMD-005).
|
|
1410
|
+
*
|
|
1411
|
+
* Built on the CMD-004 ask seam: each question maps onto the `IActionRequest` SSOT and is issued
|
|
1412
|
+
* through the injected `IToolExecutionContext.ask` port; the attached environment renders it (Ink
|
|
1413
|
+
* dialog, web modal, programmatic pre-answer) and the answers return as the tool result.
|
|
1414
|
+
*
|
|
1415
|
+
* Contract points (spec CMD-005):
|
|
1416
|
+
* - 1–4 questions per call, asked sequentially (the channel's ask queue renders one at a time).
|
|
1417
|
+
* - Cancellation is data, not an exception: a dismissed question yields `{ cancelled: true }` and the
|
|
1418
|
+
* remaining unasked questions of the same call are marked cancelled too (no per-item re-prompt).
|
|
1419
|
+
* - No `context.ask` (headless/automation): returns `{ unavailable: true, reason }` — never a silent
|
|
1420
|
+
* guess, never a thrown error, so the model can continue autonomously.
|
|
1421
|
+
*/
|
|
1422
|
+
const MAX_QUESTIONS = 4;
|
|
1423
|
+
const QuestionSchema = zod.z.object({
|
|
1424
|
+
question: zod.z.string().min(1).describe("The complete question to ask the user."),
|
|
1425
|
+
header: zod.z.string().optional().describe("Very short topic label for the question (e.g. \"Auth method\")."),
|
|
1426
|
+
options: zod.z.array(zod.z.union([zod.z.string().min(1).describe("Display text of this choice."), zod.z.object({
|
|
1427
|
+
label: zod.z.string().min(1).describe("Display text of this choice."),
|
|
1428
|
+
description: zod.z.string().optional().describe("What choosing this option means.")
|
|
1429
|
+
})])).optional().describe("Predefined choices (strings or {label, description}). Omit for pure free text."),
|
|
1430
|
+
multiSelect: zod.z.boolean().optional().describe("Allow selecting multiple options (default: single select)."),
|
|
1431
|
+
allowFreeText: zod.z.boolean().optional().describe("Allow a typed custom answer besides the options (default: true).")
|
|
1432
|
+
});
|
|
1433
|
+
const AskUserQuestionSchema = zod.z.object({ questions: zod.z.array(QuestionSchema).min(1).max(MAX_QUESTIONS).describe(`Questions to ask the user (1-${MAX_QUESTIONS}), rendered one at a time.`) });
|
|
1434
|
+
const ASK_USER_QUESTION_DESCRIPTION = [
|
|
1435
|
+
"Ask the user one or more structured questions and wait for their answers.",
|
|
1436
|
+
"",
|
|
1437
|
+
"Use this when you are blocked on a decision only the user can make — ambiguous requirements,",
|
|
1438
|
+
"mutually exclusive approaches, or choices with real trade-offs. Do not use it for decisions with",
|
|
1439
|
+
"a conventional default or facts you can verify yourself.",
|
|
1440
|
+
"",
|
|
1441
|
+
`Provide 1-${MAX_QUESTIONS} questions. Each question offers predefined options and/or free text:`,
|
|
1442
|
+
" - options + default: user picks one option (or types a custom answer unless allowFreeText: false)",
|
|
1443
|
+
" - multiSelect: true: user may pick several options",
|
|
1444
|
+
" - no options: pure free-text entry",
|
|
1445
|
+
"",
|
|
1446
|
+
"The result is a JSON array with one entry per question: the selected option labels in `values`",
|
|
1447
|
+
"and/or the typed answer in `text`, or `cancelled: true` if the user dismissed the question.",
|
|
1448
|
+
"If no interactive user is attached (headless run), the result is `{ unavailable: true }` —",
|
|
1449
|
+
"continue autonomously with your best judgment and say what you assumed."
|
|
1450
|
+
].join("\n");
|
|
1451
|
+
function toActionRequest(question) {
|
|
1452
|
+
const options = (question.options ?? []).map((option) => typeof option === "string" ? { label: option } : option);
|
|
1453
|
+
const multi = question.multiSelect === true && options.length > 1;
|
|
1454
|
+
return {
|
|
1455
|
+
id: `ask_${(0, node_crypto.randomUUID)()}`,
|
|
1456
|
+
title: question.question,
|
|
1457
|
+
...question.header !== void 0 ? { description: question.header } : {},
|
|
1458
|
+
...options.length > 0 ? { options: options.map((o) => ({
|
|
1459
|
+
value: o.label,
|
|
1460
|
+
label: o.label,
|
|
1461
|
+
...o.description !== void 0 ? { description: o.description } : {}
|
|
1462
|
+
})) } : {},
|
|
1463
|
+
minSelect: options.length > 0 ? 1 : 0,
|
|
1464
|
+
maxSelect: multi ? options.length : 1,
|
|
1465
|
+
allowFreeText: question.allowFreeText !== false || options.length === 0
|
|
1466
|
+
};
|
|
1467
|
+
}
|
|
1468
|
+
async function askQuestions(args, ask) {
|
|
1469
|
+
const answers = [];
|
|
1470
|
+
let dismissed = false;
|
|
1471
|
+
for (const question of args.questions) {
|
|
1472
|
+
if (dismissed) {
|
|
1473
|
+
answers.push({
|
|
1474
|
+
question: question.question,
|
|
1475
|
+
cancelled: true
|
|
1476
|
+
});
|
|
1477
|
+
continue;
|
|
1478
|
+
}
|
|
1479
|
+
const response = await ask(toActionRequest(question));
|
|
1480
|
+
if (response.type === "cancelled") {
|
|
1481
|
+
dismissed = true;
|
|
1482
|
+
answers.push({
|
|
1483
|
+
question: question.question,
|
|
1484
|
+
cancelled: true
|
|
1485
|
+
});
|
|
1486
|
+
continue;
|
|
1487
|
+
}
|
|
1488
|
+
answers.push({
|
|
1489
|
+
question: question.question,
|
|
1490
|
+
values: [...response.values],
|
|
1491
|
+
...response.text !== void 0 ? { text: response.text } : {}
|
|
1492
|
+
});
|
|
1493
|
+
}
|
|
1494
|
+
return { answers };
|
|
1495
|
+
}
|
|
1496
|
+
/**
|
|
1497
|
+
* Create an `AskUserQuestion` tool instance — register with the Robota agent tools registry.
|
|
1498
|
+
*/
|
|
1499
|
+
function createAskUserQuestionTool() {
|
|
1500
|
+
return createZodFunctionTool("AskUserQuestion", ASK_USER_QUESTION_DESCRIPTION, AskUserQuestionSchema, async (params, context) => {
|
|
1501
|
+
const args = params;
|
|
1502
|
+
const ask = context?.ask;
|
|
1503
|
+
const output = ask ? await askQuestions(args, ask) : {
|
|
1504
|
+
unavailable: true,
|
|
1505
|
+
reason: "no interactive user attached"
|
|
1506
|
+
};
|
|
1507
|
+
const result = {
|
|
1508
|
+
success: true,
|
|
1509
|
+
output: JSON.stringify(output)
|
|
1510
|
+
};
|
|
1511
|
+
return JSON.stringify(result);
|
|
1512
|
+
});
|
|
1513
|
+
}
|
|
1514
|
+
/** `AskUserQuestion` tool instance — register with the Robota agent tools registry. */
|
|
1515
|
+
const askUserQuestionTool = createAskUserQuestionTool();
|
|
1445
1516
|
//#endregion
|
|
1446
1517
|
exports.E2BSandboxClient = E2BSandboxClient;
|
|
1447
1518
|
exports.FunctionTool = FunctionTool;
|
|
1448
1519
|
exports.InMemorySandboxClient = InMemorySandboxClient;
|
|
1449
1520
|
exports.ToolRegistry = ToolRegistry;
|
|
1450
1521
|
exports.applyWorkspaceManifest = applyWorkspaceManifest;
|
|
1522
|
+
exports.askUserQuestionTool = askUserQuestionTool;
|
|
1451
1523
|
exports.bashTool = bashTool;
|
|
1524
|
+
exports.createAskUserQuestionTool = createAskUserQuestionTool;
|
|
1452
1525
|
exports.createBashTool = createBashTool;
|
|
1453
1526
|
exports.createEditTool = createEditTool;
|
|
1454
1527
|
exports.createFunctionTool = createFunctionTool;
|
|
1455
1528
|
exports.createReadTool = createReadTool;
|
|
1529
|
+
exports.createShellTool = createShellTool;
|
|
1456
1530
|
exports.createWriteTool = createWriteTool;
|
|
1457
1531
|
exports.createZodFunctionTool = createZodFunctionTool;
|
|
1458
1532
|
exports.editTool = editTool;
|
|
1459
1533
|
exports.globTool = globTool;
|
|
1460
1534
|
exports.grepTool = grepTool;
|
|
1461
1535
|
exports.readTool = readTool;
|
|
1536
|
+
exports.shellTool = shellTool;
|
|
1462
1537
|
exports.validateWorkspaceManifestPath = validateWorkspaceManifestPath;
|
|
1463
1538
|
exports.webFetchTool = webFetchTool;
|
|
1464
1539
|
exports.webSearchTool = webSearchTool;
|
|
1465
1540
|
exports.writeTool = writeTool;
|
|
1466
|
-
exports.zodToJsonSchema = zodToJsonSchema;
|
package/dist/node/index.d.ts
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { IEventService, IFunctionTool, IParameterValidationResult, ITool, IToolExecutionContext, IToolRegistry, IToolResult, IToolSchema, TToolExecutor, TToolParameters, TUniversalValue } from "@robota-sdk/agent-core";
|
|
2
|
+
import { TypeOf, ZodType } from "zod";
|
|
2
3
|
|
|
3
4
|
//#region src/types/tool-result.d.ts
|
|
4
5
|
/**
|
|
5
6
|
* Result returned by a CLI tool invocation
|
|
6
7
|
*/
|
|
7
|
-
interface
|
|
8
|
+
interface IToolInvocationResult {
|
|
8
9
|
success: boolean;
|
|
9
10
|
output: string;
|
|
10
11
|
error?: string;
|
|
@@ -233,70 +234,6 @@ declare class ToolRegistry implements IToolRegistry {
|
|
|
233
234
|
private validateToolSchema;
|
|
234
235
|
}
|
|
235
236
|
//#endregion
|
|
236
|
-
//#region src/implementations/function-tool/types.d.ts
|
|
237
|
-
/**
|
|
238
|
-
* Zod schema compatibility types
|
|
239
|
-
*
|
|
240
|
-
* Widened to `unknown` so that actual Zod schemas (ZodObject<...>) are structurally
|
|
241
|
-
* assignable without `as unknown as IZodSchema` casts at call sites.
|
|
242
|
-
*/
|
|
243
|
-
interface IZodParseResult {
|
|
244
|
-
success: boolean;
|
|
245
|
-
data?: unknown;
|
|
246
|
-
error?: unknown;
|
|
247
|
-
}
|
|
248
|
-
interface IZodSchemaDef {
|
|
249
|
-
typeName?: string;
|
|
250
|
-
innerType?: IZodSchema;
|
|
251
|
-
valueType?: IZodSchema;
|
|
252
|
-
checks?: Array<{
|
|
253
|
-
kind: string;
|
|
254
|
-
value?: TUniversalValue;
|
|
255
|
-
}>;
|
|
256
|
-
shape?: () => Record<string, IZodSchema>;
|
|
257
|
-
type?: IZodSchema;
|
|
258
|
-
values?: TUniversalValue[];
|
|
259
|
-
description?: string;
|
|
260
|
-
unknownKeys?: 'passthrough' | 'strip' | 'strict';
|
|
261
|
-
}
|
|
262
|
-
interface IZodSchema {
|
|
263
|
-
parse(value: unknown): unknown;
|
|
264
|
-
safeParse(value: unknown): IZodParseResult;
|
|
265
|
-
_def?: IZodSchemaDef;
|
|
266
|
-
}
|
|
267
|
-
/**
|
|
268
|
-
* Parameter type validation options
|
|
269
|
-
*/
|
|
270
|
-
interface IFunctionToolValidationOptions {
|
|
271
|
-
strict?: boolean;
|
|
272
|
-
allowUnknown?: boolean;
|
|
273
|
-
validateTypes?: boolean;
|
|
274
|
-
}
|
|
275
|
-
/**
|
|
276
|
-
* Schema conversion options
|
|
277
|
-
*/
|
|
278
|
-
interface ISchemaConversionOptions {
|
|
279
|
-
includeDescription?: boolean;
|
|
280
|
-
strictTypes?: boolean;
|
|
281
|
-
allowAdditionalProperties?: boolean;
|
|
282
|
-
}
|
|
283
|
-
/**
|
|
284
|
-
* Tool execution metadata
|
|
285
|
-
*/
|
|
286
|
-
interface IFunctionToolExecutionMetadata {
|
|
287
|
-
executionTime: number;
|
|
288
|
-
toolName: string;
|
|
289
|
-
parameters: TToolParameters;
|
|
290
|
-
}
|
|
291
|
-
/**
|
|
292
|
-
* Tool result with metadata
|
|
293
|
-
*/
|
|
294
|
-
interface IFunctionToolResult {
|
|
295
|
-
success: boolean;
|
|
296
|
-
data: TUniversalValue;
|
|
297
|
-
metadata?: IFunctionToolExecutionMetadata;
|
|
298
|
-
}
|
|
299
|
-
//#endregion
|
|
300
237
|
//#region src/implementations/function-tool.d.ts
|
|
301
238
|
/**
|
|
302
239
|
* Function tool implementation
|
|
@@ -348,22 +285,47 @@ declare function createFunctionTool(name: string, description: string, parameter
|
|
|
348
285
|
/**
|
|
349
286
|
* Helper function to create a function tool from Zod schema
|
|
350
287
|
*/
|
|
351
|
-
declare function createZodFunctionTool(name: string, description: string, zodSchema:
|
|
288
|
+
declare function createZodFunctionTool<S extends ZodType>(name: string, description: string, zodSchema: S, fn: TToolExecutor<TypeOf<S>>): FunctionTool;
|
|
352
289
|
//#endregion
|
|
353
|
-
//#region src/implementations/function-tool/
|
|
290
|
+
//#region src/implementations/function-tool/types.d.ts
|
|
291
|
+
/**
|
|
292
|
+
* Parameter type validation options
|
|
293
|
+
*/
|
|
294
|
+
interface IFunctionToolValidationOptions {
|
|
295
|
+
strict?: boolean;
|
|
296
|
+
allowUnknown?: boolean;
|
|
297
|
+
validateTypes?: boolean;
|
|
298
|
+
}
|
|
354
299
|
/**
|
|
355
|
-
*
|
|
300
|
+
* Tool execution metadata
|
|
356
301
|
*/
|
|
357
|
-
|
|
302
|
+
interface IFunctionToolExecutionMetadata {
|
|
303
|
+
executionTime: number;
|
|
304
|
+
toolName: string;
|
|
305
|
+
parameters: TToolParameters;
|
|
306
|
+
}
|
|
307
|
+
/**
|
|
308
|
+
* Tool result with metadata
|
|
309
|
+
*/
|
|
310
|
+
interface IFunctionToolResult {
|
|
311
|
+
success: boolean;
|
|
312
|
+
data: TUniversalValue;
|
|
313
|
+
metadata?: IFunctionToolExecutionMetadata;
|
|
314
|
+
}
|
|
358
315
|
//#endregion
|
|
359
|
-
//#region src/builtins/
|
|
316
|
+
//#region src/builtins/shell-tool.d.ts
|
|
360
317
|
/**
|
|
361
|
-
* Create a
|
|
318
|
+
* Create a `Shell` tool instance — register with the Robota agent tools registry.
|
|
319
|
+
* The description is resolved at creation time for the host's active shell.
|
|
362
320
|
*/
|
|
363
|
-
declare function
|
|
321
|
+
declare function createShellTool(options?: ISandboxToolOptions): FunctionTool;
|
|
364
322
|
/**
|
|
365
|
-
*
|
|
323
|
+
* Create a `Bash` tool instance — the model-familiar alias of the same OS-aware shell tool.
|
|
366
324
|
*/
|
|
325
|
+
declare function createBashTool(options?: ISandboxToolOptions): FunctionTool;
|
|
326
|
+
/** `Shell` tool instance — register with the Robota agent tools registry. */
|
|
327
|
+
declare const shellTool: FunctionTool;
|
|
328
|
+
/** `Bash` tool instance — model-familiar alias of {@link shellTool}. */
|
|
367
329
|
declare const bashTool: FunctionTool;
|
|
368
330
|
//#endregion
|
|
369
331
|
//#region src/builtins/read-tool.d.ts
|
|
@@ -436,5 +398,13 @@ declare const webFetchTool: FunctionTool;
|
|
|
436
398
|
*/
|
|
437
399
|
declare const webSearchTool: FunctionTool;
|
|
438
400
|
//#endregion
|
|
439
|
-
|
|
401
|
+
//#region src/builtins/ask-user-question-tool.d.ts
|
|
402
|
+
/**
|
|
403
|
+
* Create an `AskUserQuestion` tool instance — register with the Robota agent tools registry.
|
|
404
|
+
*/
|
|
405
|
+
declare function createAskUserQuestionTool(): FunctionTool;
|
|
406
|
+
/** `AskUserQuestion` tool instance — register with the Robota agent tools registry. */
|
|
407
|
+
declare const askUserQuestionTool: FunctionTool;
|
|
408
|
+
//#endregion
|
|
409
|
+
export { E2BSandboxClient, FunctionTool, type IE2BSandboxAdapter, type IE2BSandboxClientOptions, type IFunctionToolExecutionMetadata, type IFunctionToolResult, type IFunctionToolValidationOptions, type IInMemorySandboxClientOptions, type ISandboxClient, type ISandboxRunOptions, type ISandboxRunResult, type ISandboxToolOptions, type IToolInvocationResult, type IWorkspaceManifest, type IWorkspaceManifestAppliedEntry, type IWorkspaceManifestApplyOptions, type IWorkspaceManifestApplyResult, type IWorkspaceManifestAzureBlobMountEntry, type IWorkspaceManifestDirectoryEntry, type IWorkspaceManifestFileEntry, type IWorkspaceManifestGcsMountEntry, type IWorkspaceManifestGitRepositoryEntry, type IWorkspaceManifestLocalDirectoryEntry, type IWorkspaceManifestLocalFileEntry, type IWorkspaceManifestPermissions, type IWorkspaceManifestR2MountEntry, type IWorkspaceManifestS3MountEntry, InMemorySandboxClient, type TInMemorySandboxRunHandler, type TWorkspaceManifestApplyStatus, type TWorkspaceManifestEntry, ToolRegistry, applyWorkspaceManifest, askUserQuestionTool, bashTool, createAskUserQuestionTool, createBashTool, createEditTool, createFunctionTool, createReadTool, createShellTool, createWriteTool, createZodFunctionTool, editTool, globTool, grepTool, readTool, shellTool, validateWorkspaceManifestPath, webFetchTool, webSearchTool, writeTool };
|
|
440
410
|
//# sourceMappingURL=index.d.ts.map
|