@juspay/neurolink 12.27.2 → 12.28.1

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.
Files changed (42) hide show
  1. package/CHANGELOG.md +2 -2
  2. package/dist/agent/directTools.d.ts +118 -94
  3. package/dist/agent/directTools.js +578 -446
  4. package/dist/agent/index.d.ts +1 -0
  5. package/dist/agent/index.js +1 -0
  6. package/dist/agent/prompts/terminalAgentPrompt.d.ts +13 -0
  7. package/dist/agent/prompts/terminalAgentPrompt.js +59 -0
  8. package/dist/browser/neurolink.min.js +384 -378
  9. package/dist/cli/factories/commandFactory.d.ts +25 -0
  10. package/dist/cli/factories/commandFactory.js +78 -8
  11. package/dist/cli/loop/optionsSchema.d.ts +1 -1
  12. package/dist/core/baseProvider.d.ts +17 -1
  13. package/dist/core/baseProvider.js +48 -1
  14. package/dist/core/modules/ToolsManager.d.ts +7 -1
  15. package/dist/core/modules/ToolsManager.js +8 -0
  16. package/dist/index.d.ts +1 -0
  17. package/dist/index.js +1 -0
  18. package/dist/mcp/toolRegistry.d.ts +12 -1
  19. package/dist/mcp/toolRegistry.js +26 -2
  20. package/dist/neurolink.d.ts +13 -0
  21. package/dist/neurolink.js +97 -10
  22. package/dist/session/globalSessionState.d.ts +8 -0
  23. package/dist/session/globalSessionState.js +17 -0
  24. package/dist/types/agentMode.d.ts +9 -0
  25. package/dist/types/agentMode.js +1 -0
  26. package/dist/types/cli.d.ts +6 -0
  27. package/dist/types/config.d.ts +9 -0
  28. package/dist/types/fileToolRoots.d.ts +17 -0
  29. package/dist/types/fileToolRoots.js +7 -0
  30. package/dist/types/generate.d.ts +17 -0
  31. package/dist/types/index.d.ts +2 -0
  32. package/dist/types/index.js +2 -0
  33. package/dist/types/providers.d.ts +6 -0
  34. package/dist/types/stream.d.ts +7 -0
  35. package/dist/types/tools.d.ts +7 -0
  36. package/dist/utils/fileToolRoots.d.ts +34 -0
  37. package/dist/utils/fileToolRoots.js +111 -0
  38. package/dist/utils/messageBuilder.js +32 -1
  39. package/dist/utils/pathSandbox.d.ts +9 -6
  40. package/dist/utils/pathSandbox.js +35 -9
  41. package/docs-site/static/search-index.json +6 -5
  42. 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";
@@ -48,6 +49,21 @@ const DERIVED_PROVIDER_CHOICES = [
48
49
  ];
49
50
  /** Space-separated form for the bash-completion script, kept in sync with DERIVED_PROVIDER_CHOICES by construction. */
50
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
+ };
51
67
  /**
52
68
  * CLI Command Factory for generate commands
53
69
  */
@@ -221,6 +237,11 @@ export class CLICommandFactory {
221
237
  description: "System prompt to guide AI behavior",
222
238
  alias: "s",
223
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
+ },
224
245
  // Output control options
225
246
  format: {
226
247
  choices: ["text", "json", "table"],
@@ -736,6 +757,26 @@ export class CLICommandFactory {
736
757
  alias: "skills-dir",
737
758
  },
738
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
+ };
739
780
  // Helper method to build options for commands. `excludeOptions` drops
740
781
  // common flags from registration entirely (not just from validation) —
741
782
  // #1191 round-5: batch uses this to omit `commonOptions.file` so `--file`
@@ -882,6 +923,8 @@ export class CLICommandFactory {
882
923
  stopSequences: argv.stopSequences,
883
924
  enabledToolNames: argv.enabledToolNames,
884
925
  systemPrompt: argv.system,
926
+ agentMode: argv.agentMode,
927
+ maxSteps: argv.maxSteps,
885
928
  timeout: argv.timeout,
886
929
  disableTools: argv.disableTools,
887
930
  enableAnalytics: argv.enableAnalytics,
@@ -1531,7 +1574,8 @@ export class CLICommandFactory {
1531
1574
  .example('$0 generate "Summarize this report" --pdf ./report.pdf', "Analyze a PDF document")
1532
1575
  .example('$0 generate "Key trends?" --csv large-data.csv --csvMaxRows 100', "Analyze a CSV with a row limit")
1533
1576
  .example('$0 generate "Compare the chart and the report" --image ./chart.png --pdf ./report.pdf', "Combine multiple file types in one prompt")
1534
- .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);
1535
1579
  },
1536
1580
  handler: async (argv) => await CLICommandFactory.executeGenerate(argv),
1537
1581
  };
@@ -1556,7 +1600,7 @@ export class CLICommandFactory {
1556
1600
  .example('$0 stream "Narrate this video" --video path/to/video.mp4', "Stream video analysis")
1557
1601
  .example('$0 stream "Describe this image" --image ./photo.jpg', "Stream image analysis (multimodal input)")
1558
1602
  .example('$0 stream "Summarize this document" --pdf ./report.pdf', "Stream PDF analysis")
1559
- .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);
1560
1604
  },
1561
1605
  handler: async (argv) => await CLICommandFactory.executeStream(argv),
1562
1606
  };
@@ -2261,7 +2305,7 @@ export class CLICommandFactory {
2261
2305
  logger.error("Error during SDK shutdown:", shutdownError);
2262
2306
  }
2263
2307
  if (!globalSession.getCurrentSessionId()) {
2264
- process.exit();
2308
+ await CLICommandFactory.exitAfterStdoutDrain();
2265
2309
  }
2266
2310
  }
2267
2311
  }
@@ -2562,7 +2606,7 @@ export class CLICommandFactory {
2562
2606
  }
2563
2607
  if (!globalSession.getCurrentSessionId()) {
2564
2608
  await CLICommandFactory.flushLangfuseTraces();
2565
- process.exit(0);
2609
+ await CLICommandFactory.exitAfterStdoutDrain(0);
2566
2610
  }
2567
2611
  }
2568
2612
  /**
@@ -2627,7 +2671,7 @@ export class CLICommandFactory {
2627
2671
  }
2628
2672
  if (!globalSession.getCurrentSessionId()) {
2629
2673
  await CLICommandFactory.flushLangfuseTraces();
2630
- process.exit(0);
2674
+ await CLICommandFactory.exitAfterStdoutDrain(0);
2631
2675
  }
2632
2676
  return;
2633
2677
  }
@@ -2646,6 +2690,7 @@ export class CLICommandFactory {
2646
2690
  if (skillsConfig) {
2647
2691
  globalSession.setSkillsConfig(skillsConfig);
2648
2692
  }
2693
+ globalSession.setFileToolRoots(argv.toolRoot);
2649
2694
  // Initialize SDK and session
2650
2695
  const sdk = globalSession.getOrCreateNeuroLink();
2651
2696
  const sessionVariables = CLICommandFactory.normalizeLoopSessionVariables(globalSession.getSessionVariables());
@@ -2700,6 +2745,8 @@ export class CLICommandFactory {
2700
2745
  topK: enhancedOptions.topK,
2701
2746
  stopSequences: enhancedOptions.stopSequences,
2702
2747
  systemPrompt: enhancedOptions.systemPrompt,
2748
+ agentMode: enhancedOptions.agentMode,
2749
+ maxSteps: enhancedOptions.maxSteps,
2703
2750
  timeout: enhancedOptions.timeout
2704
2751
  ? enhancedOptions.timeout * 1000
2705
2752
  : undefined,
@@ -2892,7 +2939,7 @@ export class CLICommandFactory {
2892
2939
  }
2893
2940
  if (!globalSession.getCurrentSessionId()) {
2894
2941
  await CLICommandFactory.flushLangfuseTraces();
2895
- process.exit(0);
2942
+ await CLICommandFactory.exitAfterStdoutDrain(0);
2896
2943
  }
2897
2944
  }
2898
2945
  /**
@@ -2914,6 +2961,7 @@ export class CLICommandFactory {
2914
2961
  if (skillsConfig) {
2915
2962
  globalSession.setSkillsConfig(skillsConfig);
2916
2963
  }
2964
+ globalSession.setFileToolRoots(argv.toolRoot);
2917
2965
  const sdk = globalSession.getOrCreateNeuroLink();
2918
2966
  const sessionVariables = CLICommandFactory.normalizeLoopSessionVariables(globalSession.getSessionVariables());
2919
2967
  const enhancedOptions = { ...options, ...sessionVariables };
@@ -2949,6 +2997,8 @@ export class CLICommandFactory {
2949
2997
  topK: enhancedOptions.topK,
2950
2998
  stopSequences: enhancedOptions.stopSequences,
2951
2999
  systemPrompt: enhancedOptions.systemPrompt,
3000
+ agentMode: enhancedOptions.agentMode,
3001
+ maxSteps: enhancedOptions.maxSteps,
2952
3002
  timeout: enhancedOptions.timeout
2953
3003
  ? enhancedOptions.timeout * 1000
2954
3004
  : undefined,
@@ -3391,7 +3441,7 @@ export class CLICommandFactory {
3391
3441
  await CLICommandFactory.handleStreamOutput(options, fullContent);
3392
3442
  if (!globalSession.getCurrentSessionId()) {
3393
3443
  await CLICommandFactory.flushLangfuseTraces();
3394
- process.exit(0);
3444
+ await CLICommandFactory.exitAfterStdoutDrain(0);
3395
3445
  }
3396
3446
  }
3397
3447
  catch (error) {
@@ -3462,6 +3512,7 @@ export class CLICommandFactory {
3462
3512
  if (skillsConfig) {
3463
3513
  globalSession.setSkillsConfig(skillsConfig);
3464
3514
  }
3515
+ globalSession.setFileToolRoots(argv.toolRoot);
3465
3516
  const sdk = globalSession.getOrCreateNeuroLink();
3466
3517
  const sessionVariables = CLICommandFactory.normalizeLoopSessionVariables(globalSession.getSessionVariables());
3467
3518
  const enhancedOptions = { ...options, ...sessionVariables };
@@ -3595,7 +3646,7 @@ export class CLICommandFactory {
3595
3646
  CLICommandFactory.handleOutput(results, options);
3596
3647
  if (!globalSession.getCurrentSessionId()) {
3597
3648
  await CLICommandFactory.flushLangfuseTraces();
3598
- process.exit(0);
3649
+ await CLICommandFactory.exitAfterStdoutDrain(0);
3599
3650
  }
3600
3651
  }
3601
3652
  catch (error) {
@@ -4561,6 +4612,25 @@ export class CLICommandFactory {
4561
4612
  logger.error("[CLI] Error flushing Langfuse traces", { error });
4562
4613
  }
4563
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
+ }
4564
4634
  }
4565
4635
  /** Re-export of CLICommandFactory's static option definitions for direct import by tests/tooling. */
4566
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>;
@@ -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 readonly directTools;
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
@@ -2,7 +2,7 @@
2
2
  * MCP Tool Registry - Extended Registry with Tool Management
3
3
  * Updated to match industry standard camelCase interfaces
4
4
  */
5
- import type { MCPServerInfo, ToolImplementation, ToolInfo, ExecutionContext, HITLManager } from "../types/index.js";
5
+ import type { MCPServerInfo, ToolImplementation, ToolInfo, ExecutionContext, FileToolRootPolicy, HITLManager } from "../types/index.js";
6
6
  import { MCPRegistry } from "./registry.js";
7
7
  export declare class MCPToolRegistry extends MCPRegistry {
8
8
  private tools;
@@ -10,6 +10,7 @@ export declare class MCPToolRegistry extends MCPRegistry {
10
10
  private toolExecutionStats;
11
11
  private builtInServerInfos;
12
12
  private hitlManager?;
13
+ private fileToolRootResolver;
13
14
  constructor();
14
15
  /**
15
16
  * Set HITL manager for human-in-the-loop safety mechanisms
@@ -20,6 +21,16 @@ export declare class MCPToolRegistry extends MCPRegistry {
20
21
  * Get current HITL manager
21
22
  */
22
23
  getHITLManager(): HITLManager | undefined;
24
+ /**
25
+ * Resolve file-tool roots for built-in tools executed on this registry
26
+ * without a per-call policy. Set by the NeuroLink instance that created it.
27
+ */
28
+ setFileToolRootResolver(resolver: () => FileToolRootPolicy): void;
29
+ /**
30
+ * The built-in tool bound to the policy that applies to this execution: one
31
+ * NeuroLink bound on the context, else this registry's owner's roots.
32
+ */
33
+ private directToolFor;
23
34
  /**
24
35
  * Register all direct tools from directAgentTools
25
36
  */
@@ -6,7 +6,8 @@ import { MCPRegistry } from "./registry.js";
6
6
  import { registryLogger } from "../utils/logger.js";
7
7
  import { randomUUID } from "crypto";
8
8
  import { shouldDisableBuiltinTools } from "../utils/toolUtils.js";
9
- import { directAgentTools } from "../agent/directTools.js";
9
+ import { createDirectAgentTools, directAgentTools, } from "../agent/directTools.js";
10
+ import { boundFileToolRoots, resolveFileToolRootPolicy, } from "../utils/fileToolRoots.js";
10
11
  import { convertZodToJsonSchema } from "../utils/schemaConversion.js";
11
12
  import { detectCategory, createMCPServerInfo } from "../utils/mcpDefaults.js";
12
13
  import { FlexibleToolValidator } from "./flexibleToolValidator.js";
@@ -21,6 +22,9 @@ export class MCPToolRegistry extends MCPRegistry {
21
22
  toolExecutionStats = new Map();
22
23
  builtInServerInfos = []; // DIRECT storage for MCPServerInfo
23
24
  hitlManager; // Optional HITL manager for safety mechanisms
25
+ // The owning NeuroLink's file-tool roots, for callers (HTTP tool routes, CLI)
26
+ // that execute built-in tools on this registry without a bound policy.
27
+ fileToolRootResolver = () => resolveFileToolRootPolicy({});
24
28
  constructor() {
25
29
  super();
26
30
  if (!shouldDisableBuiltinTools()) {
@@ -46,6 +50,25 @@ export class MCPToolRegistry extends MCPRegistry {
46
50
  getHITLManager() {
47
51
  return this.hitlManager;
48
52
  }
53
+ /**
54
+ * Resolve file-tool roots for built-in tools executed on this registry
55
+ * without a per-call policy. Set by the NeuroLink instance that created it.
56
+ */
57
+ setFileToolRootResolver(resolver) {
58
+ this.fileToolRootResolver = resolver;
59
+ }
60
+ /**
61
+ * The built-in tool bound to the policy that applies to this execution: one
62
+ * NeuroLink bound on the context, else this registry's owner's roots.
63
+ */
64
+ directToolFor(toolName, fallback, context) {
65
+ const policy = boundFileToolRoots(context) ?? this.fileToolRootResolver();
66
+ if (policy.roots === null) {
67
+ return fallback;
68
+ }
69
+ const bound = createDirectAgentTools(policy);
70
+ return bound[toolName] ?? fallback;
71
+ }
49
72
  /**
50
73
  * Register all direct tools from directAgentTools
51
74
  */
@@ -75,7 +98,8 @@ export class MCPToolRegistry extends MCPRegistry {
75
98
  try {
76
99
  // Direct tools from AI SDK expect their specific parameter structure
77
100
  // Each tool validates its own parameters, so we safely pass them through
78
- const result = await toolDef.execute(params, {
101
+ const boundTool = this.directToolFor(toolName, toolDef, context);
102
+ const result = await boundTool.execute(params, {
79
103
  toolCallId: context?.sessionId || "unknown",
80
104
  messages: [],
81
105
  });
@@ -1388,6 +1388,19 @@ export declare class NeuroLink {
1388
1388
  * Called by `BaseProvider.applyToolFiltering` so the tool gate composes the
1389
1389
  * instance policy with per-call options on every generate/stream call.
1390
1390
  */
1391
+ /**
1392
+ * The policy an entry point bound to `options`, or — for an internal path
1393
+ * that built its own options — the instance policy narrowed by any
1394
+ * per-call `toolRoots`. Never trusts a public field.
1395
+ */
1396
+ private fileToolRootsFor;
1397
+ /**
1398
+ * A worker's tool config: it inherits this instance's file-tool roots, and a
1399
+ * worker-level `fileRoots` may only narrow them (throws otherwise).
1400
+ */
1401
+ private workerToolsConfig;
1402
+ /** File-tool roots for one call: per-call roots narrow the instance's `tools.fileRoots`. */
1403
+ private resolveFileToolRoots;
1391
1404
  getToolsConfig(): ToolConfig | undefined;
1392
1405
  /**
1393
1406
  * Tools discovered via `search_tools` for a session (`tools.discovery`