@juspay/neurolink 12.27.1 → 12.28.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/CHANGELOG.md +3 -3
- package/dist/agent/directTools.d.ts +118 -94
- package/dist/agent/directTools.js +578 -446
- package/dist/agent/index.d.ts +1 -0
- package/dist/agent/index.js +1 -0
- package/dist/agent/prompts/terminalAgentPrompt.d.ts +13 -0
- package/dist/agent/prompts/terminalAgentPrompt.js +59 -0
- package/dist/browser/neurolink.min.js +382 -378
- package/dist/cli/factories/commandFactory.d.ts +25 -0
- package/dist/cli/factories/commandFactory.js +94 -8
- package/dist/cli/loop/optionsSchema.d.ts +1 -1
- package/dist/cli/loop/session.js +6 -0
- package/dist/cli/utils/stdinRef.d.ts +16 -0
- package/dist/cli/utils/stdinRef.js +21 -0
- package/dist/core/baseProvider.d.ts +17 -1
- package/dist/core/baseProvider.js +48 -1
- package/dist/core/modules/ToolsManager.d.ts +7 -1
- package/dist/core/modules/ToolsManager.js +8 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/mcp/externalServerManager.js +173 -8
- package/dist/mcp/mcpCircuitBreaker.d.ts +8 -3
- package/dist/mcp/mcpCircuitBreaker.js +25 -9
- package/dist/mcp/mcpClientFactory.d.ts +9 -4
- package/dist/mcp/mcpClientFactory.js +26 -15
- package/dist/mcp/toolDiscoveryService.d.ts +8 -3
- package/dist/mcp/toolDiscoveryService.js +17 -7
- package/dist/mcp/toolRegistry.d.ts +12 -1
- package/dist/mcp/toolRegistry.js +26 -2
- package/dist/neurolink.d.ts +13 -0
- package/dist/neurolink.js +97 -10
- package/dist/session/globalSessionState.d.ts +8 -0
- package/dist/session/globalSessionState.js +17 -0
- package/dist/types/agentMode.d.ts +9 -0
- package/dist/types/agentMode.js +1 -0
- package/dist/types/cli.d.ts +6 -0
- package/dist/types/config.d.ts +9 -0
- package/dist/types/fileToolRoots.d.ts +17 -0
- package/dist/types/fileToolRoots.js +7 -0
- package/dist/types/generate.d.ts +17 -0
- package/dist/types/index.d.ts +2 -0
- package/dist/types/index.js +2 -0
- package/dist/types/providers.d.ts +6 -0
- package/dist/types/stream.d.ts +7 -0
- package/dist/types/tools.d.ts +7 -0
- package/dist/utils/fileToolRoots.d.ts +34 -0
- package/dist/utils/fileToolRoots.js +111 -0
- package/dist/utils/pathSandbox.d.ts +9 -6
- package/dist/utils/pathSandbox.js +35 -9
- package/docs-site/static/search-index.json +6 -5
- package/package.json +6 -2
|
@@ -145,6 +145,11 @@ export declare class CLICommandFactory {
|
|
|
145
145
|
description: string;
|
|
146
146
|
alias: string;
|
|
147
147
|
};
|
|
148
|
+
toolRoot: {
|
|
149
|
+
type: "string";
|
|
150
|
+
array: boolean;
|
|
151
|
+
description: string;
|
|
152
|
+
};
|
|
148
153
|
format: {
|
|
149
154
|
choices: string[];
|
|
150
155
|
default: string;
|
|
@@ -609,6 +614,17 @@ export declare class CLICommandFactory {
|
|
|
609
614
|
alias: string;
|
|
610
615
|
};
|
|
611
616
|
};
|
|
617
|
+
static readonly agentOptions: {
|
|
618
|
+
agentMode: {
|
|
619
|
+
type: "boolean";
|
|
620
|
+
description: string;
|
|
621
|
+
};
|
|
622
|
+
maxSteps: {
|
|
623
|
+
type: "number";
|
|
624
|
+
description: string;
|
|
625
|
+
coerce: (value: unknown) => number;
|
|
626
|
+
};
|
|
627
|
+
};
|
|
612
628
|
private static buildOptions;
|
|
613
629
|
private static processCliImages;
|
|
614
630
|
private static processCliCSVFiles;
|
|
@@ -920,6 +936,10 @@ export declare class CLICommandFactory {
|
|
|
920
936
|
* Flush Langfuse traces before exit
|
|
921
937
|
*/
|
|
922
938
|
private static flushLangfuseTraces;
|
|
939
|
+
/** process.exit discards piped stdout queued past the 64 KiB buffer; resolves false if the reader stalls past the bound. */
|
|
940
|
+
private static flushStdout;
|
|
941
|
+
/** Forced exit that fails loudly when queued output could not be delivered. */
|
|
942
|
+
private static exitAfterStdoutDrain;
|
|
923
943
|
}
|
|
924
944
|
/** Re-export of CLICommandFactory's static option definitions for direct import by tests/tooling. */
|
|
925
945
|
export declare const commonOptions: {
|
|
@@ -1048,6 +1068,11 @@ export declare const commonOptions: {
|
|
|
1048
1068
|
description: string;
|
|
1049
1069
|
alias: string;
|
|
1050
1070
|
};
|
|
1071
|
+
toolRoot: {
|
|
1072
|
+
type: "string";
|
|
1073
|
+
array: boolean;
|
|
1074
|
+
description: string;
|
|
1075
|
+
};
|
|
1051
1076
|
format: {
|
|
1052
1077
|
choices: string[];
|
|
1053
1078
|
default: string;
|
|
@@ -2,6 +2,7 @@ import fs from "node:fs";
|
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import chalk from "chalk";
|
|
4
4
|
import ora from "ora";
|
|
5
|
+
import { STEP_LIMITS } from "../../core/constants.js";
|
|
5
6
|
import { ModelResolver } from "../../models/modelResolver.js";
|
|
6
7
|
import { providerChoicesFor } from "../../factories/mediaHandlerCatalog.js";
|
|
7
8
|
import { globalSession } from "../../session/globalSessionState.js";
|
|
@@ -15,6 +16,7 @@ import { createThinkingConfigFromRecord } from "../../utils/thinkingConfig.js";
|
|
|
15
16
|
import { buildToolRoutingConfigFromCli } from "../utils/toolRoutingFlags.js";
|
|
16
17
|
import { buildClassifierRouterConfigFromCli } from "../utils/classifierRouterFlags.js";
|
|
17
18
|
import { buildSkillsConfigFromCli } from "../utils/skillsFlags.js";
|
|
19
|
+
import { ensureStdinRef } from "../utils/stdinRef.js";
|
|
18
20
|
import { SkillsManager } from "../../skills/skillsManager.js";
|
|
19
21
|
import { configManager } from "../commands/config.js";
|
|
20
22
|
import { MCPCommandFactory } from "../commands/mcp.js";
|
|
@@ -47,6 +49,21 @@ const DERIVED_PROVIDER_CHOICES = [
|
|
|
47
49
|
];
|
|
48
50
|
/** Space-separated form for the bash-completion script, kept in sync with DERIVED_PROVIDER_CHOICES by construction. */
|
|
49
51
|
export const BASH_COMPLETION_PROVIDERS = DERIVED_PROVIDER_CHOICES.join(" ");
|
|
52
|
+
/** EX_IOERR (sysexits.h): the result could not be fully written to stdout. */
|
|
53
|
+
const STDOUT_DRAIN_FAILED_EXIT_CODE = 74;
|
|
54
|
+
const DEFAULT_STDOUT_DRAIN_TIMEOUT_MS = 30_000;
|
|
55
|
+
const STDOUT_DRAIN_WARNING = "neurolink: stdout drain timed out; output may be incomplete\n";
|
|
56
|
+
let stdoutDrainTimeoutMs;
|
|
57
|
+
const resolveStdoutDrainTimeoutMs = () => {
|
|
58
|
+
if (stdoutDrainTimeoutMs === undefined) {
|
|
59
|
+
const override = Number(process.env.NEUROLINK_STDOUT_DRAIN_TIMEOUT_MS);
|
|
60
|
+
stdoutDrainTimeoutMs =
|
|
61
|
+
Number.isFinite(override) && override > 0
|
|
62
|
+
? override
|
|
63
|
+
: DEFAULT_STDOUT_DRAIN_TIMEOUT_MS;
|
|
64
|
+
}
|
|
65
|
+
return stdoutDrainTimeoutMs;
|
|
66
|
+
};
|
|
50
67
|
/**
|
|
51
68
|
* CLI Command Factory for generate commands
|
|
52
69
|
*/
|
|
@@ -220,6 +237,11 @@ export class CLICommandFactory {
|
|
|
220
237
|
description: "System prompt to guide AI behavior",
|
|
221
238
|
alias: "s",
|
|
222
239
|
},
|
|
240
|
+
toolRoot: {
|
|
241
|
+
type: "string",
|
|
242
|
+
array: true,
|
|
243
|
+
description: "Directory the built-in file tools may read and write (repeatable). Defaults to the working directory. Constrains bash's working directory, not its commands.",
|
|
244
|
+
},
|
|
223
245
|
// Output control options
|
|
224
246
|
format: {
|
|
225
247
|
choices: ["text", "json", "table"],
|
|
@@ -735,6 +757,26 @@ export class CLICommandFactory {
|
|
|
735
757
|
alias: "skills-dir",
|
|
736
758
|
},
|
|
737
759
|
};
|
|
760
|
+
// Registered on generate and stream only; the flags mean nothing elsewhere.
|
|
761
|
+
static agentOptions = {
|
|
762
|
+
agentMode: {
|
|
763
|
+
type: "boolean",
|
|
764
|
+
description: "Run as an autonomous terminal agent: prepend Neurolink's versioned agent instructions to the system prompt",
|
|
765
|
+
},
|
|
766
|
+
maxSteps: {
|
|
767
|
+
type: "number",
|
|
768
|
+
description: `Maximum tool-calling steps before the turn stops (integer ${STEP_LIMITS.min}-${STEP_LIMITS.max}, default ${STEP_LIMITS.default}; some providers cap lower)`,
|
|
769
|
+
coerce: (value) => {
|
|
770
|
+
if (typeof value !== "number" ||
|
|
771
|
+
!Number.isInteger(value) ||
|
|
772
|
+
value < STEP_LIMITS.min ||
|
|
773
|
+
value > STEP_LIMITS.max) {
|
|
774
|
+
throw new Error(`--max-steps must be an integer between ${STEP_LIMITS.min} and ${STEP_LIMITS.max}`);
|
|
775
|
+
}
|
|
776
|
+
return value;
|
|
777
|
+
},
|
|
778
|
+
},
|
|
779
|
+
};
|
|
738
780
|
// Helper method to build options for commands. `excludeOptions` drops
|
|
739
781
|
// common flags from registration entirely (not just from validation) —
|
|
740
782
|
// #1191 round-5: batch uses this to omit `commonOptions.file` so `--file`
|
|
@@ -881,6 +923,8 @@ export class CLICommandFactory {
|
|
|
881
923
|
stopSequences: argv.stopSequences,
|
|
882
924
|
enabledToolNames: argv.enabledToolNames,
|
|
883
925
|
systemPrompt: argv.system,
|
|
926
|
+
agentMode: argv.agentMode,
|
|
927
|
+
maxSteps: argv.maxSteps,
|
|
884
928
|
timeout: argv.timeout,
|
|
885
929
|
disableTools: argv.disableTools,
|
|
886
930
|
enableAnalytics: argv.enableAnalytics,
|
|
@@ -1530,7 +1574,8 @@ export class CLICommandFactory {
|
|
|
1530
1574
|
.example('$0 generate "Summarize this report" --pdf ./report.pdf', "Analyze a PDF document")
|
|
1531
1575
|
.example('$0 generate "Key trends?" --csv large-data.csv --csvMaxRows 100', "Analyze a CSV with a row limit")
|
|
1532
1576
|
.example('$0 generate "Compare the chart and the report" --image ./chart.png --pdf ./report.pdf', "Combine multiple file types in one prompt")
|
|
1533
|
-
.example('$0 generate "What is in this file?" --file ./data.json', "Auto-detect a file type with --file")
|
|
1577
|
+
.example('$0 generate "What is in this file?" --file ./data.json', "Auto-detect a file type with --file")
|
|
1578
|
+
.example('$0 generate "Fix the failing config" --agent-mode --max-steps 60', "Run as an autonomous terminal agent with a step budget"), CLICommandFactory.agentOptions);
|
|
1534
1579
|
},
|
|
1535
1580
|
handler: async (argv) => await CLICommandFactory.executeGenerate(argv),
|
|
1536
1581
|
};
|
|
@@ -1555,7 +1600,7 @@ export class CLICommandFactory {
|
|
|
1555
1600
|
.example('$0 stream "Narrate this video" --video path/to/video.mp4', "Stream video analysis")
|
|
1556
1601
|
.example('$0 stream "Describe this image" --image ./photo.jpg', "Stream image analysis (multimodal input)")
|
|
1557
1602
|
.example('$0 stream "Summarize this document" --pdf ./report.pdf', "Stream PDF analysis")
|
|
1558
|
-
.example('$0 stream "Explain this dataset" --csv ./data.csv --csv-format markdown', "Stream CSV analysis"));
|
|
1603
|
+
.example('$0 stream "Explain this dataset" --csv ./data.csv --csv-format markdown', "Stream CSV analysis"), CLICommandFactory.agentOptions);
|
|
1559
1604
|
},
|
|
1560
1605
|
handler: async (argv) => await CLICommandFactory.executeStream(argv),
|
|
1561
1606
|
};
|
|
@@ -2260,7 +2305,7 @@ export class CLICommandFactory {
|
|
|
2260
2305
|
logger.error("Error during SDK shutdown:", shutdownError);
|
|
2261
2306
|
}
|
|
2262
2307
|
if (!globalSession.getCurrentSessionId()) {
|
|
2263
|
-
|
|
2308
|
+
await CLICommandFactory.exitAfterStdoutDrain();
|
|
2264
2309
|
}
|
|
2265
2310
|
}
|
|
2266
2311
|
}
|
|
@@ -2275,6 +2320,11 @@ export class CLICommandFactory {
|
|
|
2275
2320
|
const isSttOnly = !!(argv.stt &&
|
|
2276
2321
|
argv.inputAudio);
|
|
2277
2322
|
if (!argv.input && !process.stdin.isTTY) {
|
|
2323
|
+
// The SDK's MCP stdio transport unrefs stdin on import so an idle
|
|
2324
|
+
// process can exit on its own (see externalServerManager.ts). This
|
|
2325
|
+
// path actually reads stdin and must block on it, so ref it back
|
|
2326
|
+
// before consuming.
|
|
2327
|
+
ensureStdinRef();
|
|
2278
2328
|
let stdinData = "";
|
|
2279
2329
|
process.stdin.setEncoding("utf8");
|
|
2280
2330
|
for await (const chunk of process.stdin) {
|
|
@@ -2556,7 +2606,7 @@ export class CLICommandFactory {
|
|
|
2556
2606
|
}
|
|
2557
2607
|
if (!globalSession.getCurrentSessionId()) {
|
|
2558
2608
|
await CLICommandFactory.flushLangfuseTraces();
|
|
2559
|
-
|
|
2609
|
+
await CLICommandFactory.exitAfterStdoutDrain(0);
|
|
2560
2610
|
}
|
|
2561
2611
|
}
|
|
2562
2612
|
/**
|
|
@@ -2621,7 +2671,7 @@ export class CLICommandFactory {
|
|
|
2621
2671
|
}
|
|
2622
2672
|
if (!globalSession.getCurrentSessionId()) {
|
|
2623
2673
|
await CLICommandFactory.flushLangfuseTraces();
|
|
2624
|
-
|
|
2674
|
+
await CLICommandFactory.exitAfterStdoutDrain(0);
|
|
2625
2675
|
}
|
|
2626
2676
|
return;
|
|
2627
2677
|
}
|
|
@@ -2640,6 +2690,7 @@ export class CLICommandFactory {
|
|
|
2640
2690
|
if (skillsConfig) {
|
|
2641
2691
|
globalSession.setSkillsConfig(skillsConfig);
|
|
2642
2692
|
}
|
|
2693
|
+
globalSession.setFileToolRoots(argv.toolRoot);
|
|
2643
2694
|
// Initialize SDK and session
|
|
2644
2695
|
const sdk = globalSession.getOrCreateNeuroLink();
|
|
2645
2696
|
const sessionVariables = CLICommandFactory.normalizeLoopSessionVariables(globalSession.getSessionVariables());
|
|
@@ -2694,6 +2745,8 @@ export class CLICommandFactory {
|
|
|
2694
2745
|
topK: enhancedOptions.topK,
|
|
2695
2746
|
stopSequences: enhancedOptions.stopSequences,
|
|
2696
2747
|
systemPrompt: enhancedOptions.systemPrompt,
|
|
2748
|
+
agentMode: enhancedOptions.agentMode,
|
|
2749
|
+
maxSteps: enhancedOptions.maxSteps,
|
|
2697
2750
|
timeout: enhancedOptions.timeout
|
|
2698
2751
|
? enhancedOptions.timeout * 1000
|
|
2699
2752
|
: undefined,
|
|
@@ -2886,7 +2939,7 @@ export class CLICommandFactory {
|
|
|
2886
2939
|
}
|
|
2887
2940
|
if (!globalSession.getCurrentSessionId()) {
|
|
2888
2941
|
await CLICommandFactory.flushLangfuseTraces();
|
|
2889
|
-
|
|
2942
|
+
await CLICommandFactory.exitAfterStdoutDrain(0);
|
|
2890
2943
|
}
|
|
2891
2944
|
}
|
|
2892
2945
|
/**
|
|
@@ -2908,6 +2961,7 @@ export class CLICommandFactory {
|
|
|
2908
2961
|
if (skillsConfig) {
|
|
2909
2962
|
globalSession.setSkillsConfig(skillsConfig);
|
|
2910
2963
|
}
|
|
2964
|
+
globalSession.setFileToolRoots(argv.toolRoot);
|
|
2911
2965
|
const sdk = globalSession.getOrCreateNeuroLink();
|
|
2912
2966
|
const sessionVariables = CLICommandFactory.normalizeLoopSessionVariables(globalSession.getSessionVariables());
|
|
2913
2967
|
const enhancedOptions = { ...options, ...sessionVariables };
|
|
@@ -2943,6 +2997,8 @@ export class CLICommandFactory {
|
|
|
2943
2997
|
topK: enhancedOptions.topK,
|
|
2944
2998
|
stopSequences: enhancedOptions.stopSequences,
|
|
2945
2999
|
systemPrompt: enhancedOptions.systemPrompt,
|
|
3000
|
+
agentMode: enhancedOptions.agentMode,
|
|
3001
|
+
maxSteps: enhancedOptions.maxSteps,
|
|
2946
3002
|
timeout: enhancedOptions.timeout
|
|
2947
3003
|
? enhancedOptions.timeout * 1000
|
|
2948
3004
|
: undefined,
|
|
@@ -3322,6 +3378,11 @@ export class CLICommandFactory {
|
|
|
3322
3378
|
// as the prompt). Skip the stdin/empty-input rejection in that case.
|
|
3323
3379
|
const isSttOnly = !!argv.stt && !!argv.inputAudio;
|
|
3324
3380
|
if (!argv.input && !process.stdin.isTTY) {
|
|
3381
|
+
// The SDK's MCP stdio transport unrefs stdin on import so an idle
|
|
3382
|
+
// process can exit on its own (see externalServerManager.ts). This
|
|
3383
|
+
// path actually reads stdin and must block on it, so ref it back
|
|
3384
|
+
// before consuming.
|
|
3385
|
+
ensureStdinRef();
|
|
3325
3386
|
let stdinData = "";
|
|
3326
3387
|
process.stdin.setEncoding("utf8");
|
|
3327
3388
|
for await (const chunk of process.stdin) {
|
|
@@ -3380,7 +3441,7 @@ export class CLICommandFactory {
|
|
|
3380
3441
|
await CLICommandFactory.handleStreamOutput(options, fullContent);
|
|
3381
3442
|
if (!globalSession.getCurrentSessionId()) {
|
|
3382
3443
|
await CLICommandFactory.flushLangfuseTraces();
|
|
3383
|
-
|
|
3444
|
+
await CLICommandFactory.exitAfterStdoutDrain(0);
|
|
3384
3445
|
}
|
|
3385
3446
|
}
|
|
3386
3447
|
catch (error) {
|
|
@@ -3451,6 +3512,7 @@ export class CLICommandFactory {
|
|
|
3451
3512
|
if (skillsConfig) {
|
|
3452
3513
|
globalSession.setSkillsConfig(skillsConfig);
|
|
3453
3514
|
}
|
|
3515
|
+
globalSession.setFileToolRoots(argv.toolRoot);
|
|
3454
3516
|
const sdk = globalSession.getOrCreateNeuroLink();
|
|
3455
3517
|
const sessionVariables = CLICommandFactory.normalizeLoopSessionVariables(globalSession.getSessionVariables());
|
|
3456
3518
|
const enhancedOptions = { ...options, ...sessionVariables };
|
|
@@ -3584,7 +3646,7 @@ export class CLICommandFactory {
|
|
|
3584
3646
|
CLICommandFactory.handleOutput(results, options);
|
|
3585
3647
|
if (!globalSession.getCurrentSessionId()) {
|
|
3586
3648
|
await CLICommandFactory.flushLangfuseTraces();
|
|
3587
|
-
|
|
3649
|
+
await CLICommandFactory.exitAfterStdoutDrain(0);
|
|
3588
3650
|
}
|
|
3589
3651
|
}
|
|
3590
3652
|
catch (error) {
|
|
@@ -4320,6 +4382,11 @@ export class CLICommandFactory {
|
|
|
4320
4382
|
logger.always(chalk.yellow(`Usage: neurolink memory delete ${argv.sessionId} --force`));
|
|
4321
4383
|
return;
|
|
4322
4384
|
}
|
|
4385
|
+
// The SDK's MCP stdio transport unrefs stdin on import so an idle
|
|
4386
|
+
// process can exit on its own (see externalServerManager.ts). This
|
|
4387
|
+
// prompt actually reads stdin and must block on it, so ref it back
|
|
4388
|
+
// before consuming.
|
|
4389
|
+
ensureStdinRef();
|
|
4323
4390
|
const readline = await import("readline");
|
|
4324
4391
|
const rl = readline.createInterface({
|
|
4325
4392
|
input: process.stdin,
|
|
@@ -4545,6 +4612,25 @@ export class CLICommandFactory {
|
|
|
4545
4612
|
logger.error("[CLI] Error flushing Langfuse traces", { error });
|
|
4546
4613
|
}
|
|
4547
4614
|
}
|
|
4615
|
+
/** process.exit discards piped stdout queued past the 64 KiB buffer; resolves false if the reader stalls past the bound. */
|
|
4616
|
+
static flushStdout() {
|
|
4617
|
+
return new Promise((resolve) => {
|
|
4618
|
+
const timer = setTimeout(() => resolve(false), resolveStdoutDrainTimeoutMs());
|
|
4619
|
+
timer.unref();
|
|
4620
|
+
process.stdout.write("", () => {
|
|
4621
|
+
clearTimeout(timer);
|
|
4622
|
+
resolve(true);
|
|
4623
|
+
});
|
|
4624
|
+
});
|
|
4625
|
+
}
|
|
4626
|
+
/** Forced exit that fails loudly when queued output could not be delivered. */
|
|
4627
|
+
static async exitAfterStdoutDrain(exitCode) {
|
|
4628
|
+
if (await CLICommandFactory.flushStdout()) {
|
|
4629
|
+
process.exit(exitCode);
|
|
4630
|
+
}
|
|
4631
|
+
process.stderr.write(STDOUT_DRAIN_WARNING);
|
|
4632
|
+
process.exit(STDOUT_DRAIN_FAILED_EXIT_CODE);
|
|
4633
|
+
}
|
|
4548
4634
|
}
|
|
4549
4635
|
/** Re-export of CLICommandFactory's static option definitions for direct import by tests/tooling. */
|
|
4550
4636
|
export const commonOptions = CLICommandFactory.commonOptions;
|
|
@@ -4,4 +4,4 @@ import type { OptionSchema, TextGenerationOptions } from "../../types/index.js";
|
|
|
4
4
|
* This object provides metadata for validation and help text in the CLI loop.
|
|
5
5
|
* It is derived from the main TextGenerationOptions interface to ensure consistency.
|
|
6
6
|
*/
|
|
7
|
-
export declare const textGenerationOptionsSchema: Record<keyof Omit<TextGenerationOptions, "prompt" | "input" | "schema" | "tools" | "context" | "conversationHistory" | "conversationMessages" | "conversationMemoryConfig" | "originalPrompt" | "middleware" | "expectedOutcome" | "evaluationCriteria" | "region" | "csvOptions" | "pdfOptions" | "imageOptions" | "tts" | "stt" | "thinkingConfig" | "requestId" | "fileRegistry" | "abortSignal" | "toolFilter" | "excludeTools" | "toolChoice" | "prepareStep" | "credentials" | "onFinish" | "onError" | "processors" | "piiDetection" | "responseValidation" | "inputValidation" | "toolExecutionCapture" | "toolExecutionRecorder">, OptionSchema>;
|
|
7
|
+
export declare const textGenerationOptionsSchema: Record<keyof Omit<TextGenerationOptions, "prompt" | "input" | "schema" | "tools" | "context" | "conversationHistory" | "conversationMessages" | "conversationMemoryConfig" | "originalPrompt" | "middleware" | "expectedOutcome" | "evaluationCriteria" | "region" | "csvOptions" | "pdfOptions" | "imageOptions" | "tts" | "stt" | "thinkingConfig" | "requestId" | "fileRegistry" | "abortSignal" | "toolFilter" | "excludeTools" | "toolChoice" | "prepareStep" | "credentials" | "onFinish" | "onError" | "processors" | "piiDetection" | "responseValidation" | "inputValidation" | "toolExecutionCapture" | "toolExecutionRecorder" | "toolRoots">, OptionSchema>;
|
package/dist/cli/loop/session.js
CHANGED
|
@@ -10,6 +10,7 @@ import { tracers } from "../../telemetry/tracers.js";
|
|
|
10
10
|
import { handleError } from "../errorHandler.js";
|
|
11
11
|
import { ConversationSelector } from "./conversationSelector.js";
|
|
12
12
|
import { textGenerationOptionsSchema } from "./optionsSchema.js";
|
|
13
|
+
import { ensureStdinRef } from "../utils/stdinRef.js";
|
|
13
14
|
// Banner Art
|
|
14
15
|
const NEUROLINK_BANNER = `
|
|
15
16
|
▗▖ ▗▖▗▄▄▄▖▗▖ ▗▖▗▄▄▖ ▗▄▖ ▗▖ ▗▄▄▄▖▗▖ ▗▖▗▖ ▗▖
|
|
@@ -455,6 +456,11 @@ export class LoopSession {
|
|
|
455
456
|
* Get command input with history support using readline
|
|
456
457
|
*/
|
|
457
458
|
async getCommandWithHistory() {
|
|
459
|
+
// The SDK's MCP stdio transport unrefs stdin on import so an idle
|
|
460
|
+
// process can exit on its own (see externalServerManager.ts). The
|
|
461
|
+
// interactive loop reads a line from stdin on every prompt and must
|
|
462
|
+
// block on it, so ref it back before consuming.
|
|
463
|
+
ensureStdinRef();
|
|
458
464
|
return new Promise((resolve) => {
|
|
459
465
|
const rl = readline.createInterface({
|
|
460
466
|
input: process.stdin,
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The SDK unrefs `process.stdin` on import (see externalServerManager.ts) so
|
|
3
|
+
* that a process which never reads stdin can still exit on its own instead
|
|
4
|
+
* of being kept alive forever by a handle it never uses. Any CLI code path
|
|
5
|
+
* that actually reads from stdin — piped generate/stream input, a readline
|
|
6
|
+
* confirmation prompt, the interactive loop's line reader — must ref it
|
|
7
|
+
* back first, or the process can exit (or simply never treat stdin as a
|
|
8
|
+
* reason to stay alive) before the read completes.
|
|
9
|
+
*
|
|
10
|
+
* Centralizing the call here means a new stdin consumer only has to
|
|
11
|
+
* remember to call this, not to rediscover and re-derive the reason why.
|
|
12
|
+
* Three call sites each doing this inline already produced one that forgot
|
|
13
|
+
* to (the memory-delete confirmation prompt) — a fourth is just as likely
|
|
14
|
+
* to repeat that.
|
|
15
|
+
*/
|
|
16
|
+
export declare function ensureStdinRef(): void;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The SDK unrefs `process.stdin` on import (see externalServerManager.ts) so
|
|
3
|
+
* that a process which never reads stdin can still exit on its own instead
|
|
4
|
+
* of being kept alive forever by a handle it never uses. Any CLI code path
|
|
5
|
+
* that actually reads from stdin — piped generate/stream input, a readline
|
|
6
|
+
* confirmation prompt, the interactive loop's line reader — must ref it
|
|
7
|
+
* back first, or the process can exit (or simply never treat stdin as a
|
|
8
|
+
* reason to stay alive) before the read completes.
|
|
9
|
+
*
|
|
10
|
+
* Centralizing the call here means a new stdin consumer only has to
|
|
11
|
+
* remember to call this, not to rediscover and re-derive the reason why.
|
|
12
|
+
* Three call sites each doing this inline already produced one that forgot
|
|
13
|
+
* to (the memory-delete confirmation prompt) — a fourth is just as likely
|
|
14
|
+
* to repeat that.
|
|
15
|
+
*/
|
|
16
|
+
export function ensureStdinRef() {
|
|
17
|
+
if (process.stdin && typeof process.stdin.ref === "function") {
|
|
18
|
+
process.stdin.ref();
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
//# sourceMappingURL=stdinRef.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { AIProviderName } from "../constants/enums.js";
|
|
2
2
|
import type { EvaluationData } from "../index.js";
|
|
3
3
|
import type { NeuroLink } from "../neurolink.js";
|
|
4
|
-
import type { UnknownRecord, MiddlewareFactoryOptions, StreamOptions, StreamResult, AIProvider, AnalyticsData, EnhancedGenerateResult, TextGenerationOptions, TextGenerationResult, ValidationSchema, EmbedInput, DecisionRequest, DecisionResult } from "../types/index.js";
|
|
4
|
+
import type { FileToolRootPolicy, UnknownRecord, MiddlewareFactoryOptions, StreamOptions, StreamResult, AIProvider, AnalyticsData, EnhancedGenerateResult, TextGenerationOptions, TextGenerationResult, ValidationSchema, EmbedInput, DecisionRequest, DecisionResult } from "../types/index.js";
|
|
5
5
|
import { TelemetryHandler } from "./modules/TelemetryHandler.js";
|
|
6
6
|
import type { LanguageModel, ModelMessage, RawUsageObject, Tool, ToolCallRepairFunction, ToolSet } from "../types/index.js";
|
|
7
7
|
export declare abstract class BaseProvider implements AIProvider {
|
|
@@ -11,6 +11,7 @@ export declare abstract class BaseProvider implements AIProvider {
|
|
|
11
11
|
protected middlewareOptions?: MiddlewareFactoryOptions;
|
|
12
12
|
protected readonly directTools: {};
|
|
13
13
|
protected mcpTools?: Record<string, Tool>;
|
|
14
|
+
private fileToolRootsBound;
|
|
14
15
|
protected customTools?: Map<string, unknown>;
|
|
15
16
|
protected toolExecutor?: (toolName: string, params: unknown) => Promise<unknown>;
|
|
16
17
|
protected sessionId?: string;
|
|
@@ -532,6 +533,21 @@ export declare abstract class BaseProvider implements AIProvider {
|
|
|
532
533
|
/**
|
|
533
534
|
* Get all available tools - delegated to ToolsManager
|
|
534
535
|
*/
|
|
536
|
+
/**
|
|
537
|
+
* Bind the built-in file tools to one request's root policy. NeuroLink
|
|
538
|
+
* calls this before every provider call that carries the caller's options,
|
|
539
|
+
* because several providers override generate()/stream() and assemble
|
|
540
|
+
* their tools without passing through BaseProvider's entry points.
|
|
541
|
+
* Providers are created per request, so a bound policy never reaches
|
|
542
|
+
* another request.
|
|
543
|
+
*/
|
|
544
|
+
setFileToolRootPolicy(policy: FileToolRootPolicy | undefined): void;
|
|
545
|
+
/**
|
|
546
|
+
* Resolve roots for a provider called directly, without NeuroLink. A
|
|
547
|
+
* policy NeuroLink already bound is left alone unless these options carry
|
|
548
|
+
* their own. An invalid root throws, before any model request.
|
|
549
|
+
*/
|
|
550
|
+
private applyFileToolRoots;
|
|
535
551
|
protected getAllTools(): Promise<Record<string, Tool>>;
|
|
536
552
|
/**
|
|
537
553
|
* Calculate actual cost - delegated to TelemetryHandler
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { context, SpanKind, SpanStatusCode, trace } from "@opentelemetry/api";
|
|
2
|
-
import { directAgentTools } from "../agent/directTools.js";
|
|
2
|
+
import { createDirectAgentTools, directAgentTools, } from "../agent/directTools.js";
|
|
3
|
+
import { boundFileToolRoots, resolveFileToolRootPolicy, } from "../utils/fileToolRoots.js";
|
|
3
4
|
import { defaultProviderFor } from "../factories/mediaHandlerCatalog.js";
|
|
4
5
|
import { DECISION_PROVIDERS, PROVIDER_DESCRIPTORS_BY_NAME, } from "../factories/providerDescriptors.js";
|
|
5
6
|
import { MiddlewareFactory } from "../middleware/factory.js";
|
|
@@ -120,6 +121,7 @@ export class BaseProvider {
|
|
|
120
121
|
? {}
|
|
121
122
|
: directAgentTools;
|
|
122
123
|
mcpTools; // MCP tools loaded dynamically when available
|
|
124
|
+
fileToolRootsBound = false;
|
|
123
125
|
customTools; // Custom tools from registerTool()
|
|
124
126
|
toolExecutor; // Tool executor from setupToolExecutor
|
|
125
127
|
sessionId;
|
|
@@ -225,6 +227,7 @@ export class BaseProvider {
|
|
|
225
227
|
// maxTokens (getSafeMaxTokens consults the discovered output ceiling).
|
|
226
228
|
await this.ensureModelLimits();
|
|
227
229
|
let options = this.normalizeStreamOptions(optionsOrPrompt);
|
|
230
|
+
this.applyFileToolRoots(options);
|
|
228
231
|
// Before anything else, and before a single byte leaves the process: an
|
|
229
232
|
// execution policy this provider cannot honour is an error, and a policy
|
|
230
233
|
// whose shape could be read two ways is an error. Both are silent bugs at
|
|
@@ -1292,6 +1295,7 @@ export class BaseProvider {
|
|
|
1292
1295
|
await this.ensureModelLimits();
|
|
1293
1296
|
const options = this.normalizeTextOptions(optionsOrPrompt);
|
|
1294
1297
|
this.validateOptions(options);
|
|
1298
|
+
this.applyFileToolRoots(options);
|
|
1295
1299
|
const startTime = Date.now();
|
|
1296
1300
|
// One span per attempt. runGenerateInActiveContext ends the span on the
|
|
1297
1301
|
// way out (success or failure), so a model-fallback retry must not reuse
|
|
@@ -2156,7 +2160,50 @@ export class BaseProvider {
|
|
|
2156
2160
|
/**
|
|
2157
2161
|
* Get all available tools - delegated to ToolsManager
|
|
2158
2162
|
*/
|
|
2163
|
+
/**
|
|
2164
|
+
* Bind the built-in file tools to one request's root policy. NeuroLink
|
|
2165
|
+
* calls this before every provider call that carries the caller's options,
|
|
2166
|
+
* because several providers override generate()/stream() and assemble
|
|
2167
|
+
* their tools without passing through BaseProvider's entry points.
|
|
2168
|
+
* Providers are created per request, so a bound policy never reaches
|
|
2169
|
+
* another request.
|
|
2170
|
+
*/
|
|
2171
|
+
setFileToolRootPolicy(policy) {
|
|
2172
|
+
if (policy === undefined || Object.keys(this.directTools).length === 0) {
|
|
2173
|
+
return;
|
|
2174
|
+
}
|
|
2175
|
+
this.fileToolRootsBound = true;
|
|
2176
|
+
this.toolsManager.setDirectTools(policy.roots === null ? this.directTools : createDirectAgentTools(policy));
|
|
2177
|
+
}
|
|
2178
|
+
/**
|
|
2179
|
+
* Resolve roots for a provider called directly, without NeuroLink. A
|
|
2180
|
+
* policy NeuroLink already bound is left alone unless these options carry
|
|
2181
|
+
* their own. An invalid root throws, before any model request.
|
|
2182
|
+
*/
|
|
2183
|
+
applyFileToolRoots(options) {
|
|
2184
|
+
if (Object.keys(this.directTools).length === 0) {
|
|
2185
|
+
return;
|
|
2186
|
+
}
|
|
2187
|
+
const bound = boundFileToolRoots(options);
|
|
2188
|
+
if (bound !== undefined) {
|
|
2189
|
+
this.setFileToolRootPolicy(bound);
|
|
2190
|
+
return;
|
|
2191
|
+
}
|
|
2192
|
+
if (this.fileToolRootsBound && options.toolRoots === undefined) {
|
|
2193
|
+
return;
|
|
2194
|
+
}
|
|
2195
|
+
this.setFileToolRootPolicy(resolveFileToolRootPolicy({
|
|
2196
|
+
perCall: options.toolRoots,
|
|
2197
|
+
instance: this.neurolink?.getToolsConfig()?.fileRoots,
|
|
2198
|
+
}));
|
|
2199
|
+
}
|
|
2159
2200
|
async getAllTools() {
|
|
2201
|
+
// Any path that reaches tool assembly without a bound policy — an internal
|
|
2202
|
+
// caller, or a provider whose generate()/stream() skips the base entry
|
|
2203
|
+
// points — gets the instance's roots rather than the working directory.
|
|
2204
|
+
if (!this.fileToolRootsBound) {
|
|
2205
|
+
this.applyFileToolRoots({});
|
|
2206
|
+
}
|
|
2160
2207
|
return this.toolsManager.getAllTools();
|
|
2161
2208
|
}
|
|
2162
2209
|
/**
|
|
@@ -20,7 +20,7 @@ export declare function buildMCPSchemaValidator(toolName: string, schema: Record
|
|
|
20
20
|
}) | undefined>;
|
|
21
21
|
export declare class ToolsManager {
|
|
22
22
|
private readonly providerName;
|
|
23
|
-
private
|
|
23
|
+
private directTools;
|
|
24
24
|
private readonly neurolink?;
|
|
25
25
|
private readonly utilities?;
|
|
26
26
|
protected mcpTools?: Record<string, Tool>;
|
|
@@ -112,6 +112,12 @@ export declare class ToolsManager {
|
|
|
112
112
|
* Get direct tools (built-in agent tools)
|
|
113
113
|
*/
|
|
114
114
|
getDirectTools(): Record<string, unknown>;
|
|
115
|
+
/**
|
|
116
|
+
* Replace the direct tools for this provider instance — used to bind the
|
|
117
|
+
* built-in file tools to one request's root policy. Providers are created
|
|
118
|
+
* per request, so this never leaks a boundary into another request.
|
|
119
|
+
*/
|
|
120
|
+
setDirectTools(tools: Record<string, unknown>): void;
|
|
115
121
|
/**
|
|
116
122
|
* Get MCP tools
|
|
117
123
|
*/
|
|
@@ -550,6 +550,14 @@ export class ToolsManager {
|
|
|
550
550
|
getDirectTools() {
|
|
551
551
|
return this.directTools;
|
|
552
552
|
}
|
|
553
|
+
/**
|
|
554
|
+
* Replace the direct tools for this provider instance — used to bind the
|
|
555
|
+
* built-in file tools to one request's root policy. Providers are created
|
|
556
|
+
* per request, so this never leaks a boundary into another request.
|
|
557
|
+
*/
|
|
558
|
+
setDirectTools(tools) {
|
|
559
|
+
this.directTools = tools;
|
|
560
|
+
}
|
|
553
561
|
/**
|
|
554
562
|
* Get MCP tools
|
|
555
563
|
*/
|
package/dist/index.d.ts
CHANGED
|
@@ -504,5 +504,6 @@ export { validateResponse } from "./utils/responseValidator.js";
|
|
|
504
504
|
export { TripwireEvaluator, createDefaultTripwireEvaluator, commonTripwires, } from "./utils/tripwireEvaluator.js";
|
|
505
505
|
export { Agent } from "./agent/agent.js";
|
|
506
506
|
export { AgentNetwork } from "./agent/agentNetwork.js";
|
|
507
|
+
export { TERMINAL_AGENT_PROMPT_V1, TERMINAL_AGENT_PROMPT_V2, TERMINAL_AGENT_PROMPT_VERSION, } from "./agent/prompts/terminalAgentPrompt.js";
|
|
507
508
|
export { AgentCoordinator, TaskDistributor, MessageBus, NetworkOrchestrator, NetworkTopology, TopologyBuilder, } from "./agent/index.js";
|
|
508
509
|
export { createLocalUsageReader, getLocalUsageDescriptors, getRegisteredLocalUsageCliIds, readAllLocalUsage, registerLocalUsageReader, } from "./localUsage/index.js";
|
package/dist/index.js
CHANGED
|
@@ -823,6 +823,7 @@ export { TripwireEvaluator, createDefaultTripwireEvaluator, commonTripwires, } f
|
|
|
823
823
|
// ============================================================================
|
|
824
824
|
export { Agent } from "./agent/agent.js";
|
|
825
825
|
export { AgentNetwork } from "./agent/agentNetwork.js";
|
|
826
|
+
export { TERMINAL_AGENT_PROMPT_V1, TERMINAL_AGENT_PROMPT_V2, TERMINAL_AGENT_PROMPT_VERSION, } from "./agent/prompts/terminalAgentPrompt.js";
|
|
826
827
|
// Advanced agent orchestration exports
|
|
827
828
|
export {
|
|
828
829
|
// Coordination
|