@juspay/neurolink 9.83.0 → 9.84.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.
Files changed (117) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/agent/agent.d.ts +104 -0
  3. package/dist/agent/agent.js +401 -0
  4. package/dist/agent/agentNetwork.d.ts +87 -0
  5. package/dist/agent/agentNetwork.js +458 -0
  6. package/dist/agent/communication/index.d.ts +9 -0
  7. package/dist/agent/communication/index.js +9 -0
  8. package/dist/agent/communication/message-bus.d.ts +113 -0
  9. package/dist/agent/communication/message-bus.js +406 -0
  10. package/dist/agent/coordination/coordinator.d.ts +83 -0
  11. package/dist/agent/coordination/coordinator.js +598 -0
  12. package/dist/agent/coordination/index.d.ts +10 -0
  13. package/dist/agent/coordination/index.js +10 -0
  14. package/dist/agent/coordination/task-distributor.d.ts +113 -0
  15. package/dist/agent/coordination/task-distributor.js +585 -0
  16. package/dist/agent/index.d.ts +38 -0
  17. package/dist/agent/index.js +44 -0
  18. package/dist/agent/orchestration/index.d.ts +10 -0
  19. package/dist/agent/orchestration/index.js +10 -0
  20. package/dist/agent/orchestration/orchestrator.d.ts +114 -0
  21. package/dist/agent/orchestration/orchestrator.js +476 -0
  22. package/dist/agent/orchestration/topology.d.ts +164 -0
  23. package/dist/agent/orchestration/topology.js +520 -0
  24. package/dist/agent/prompts/routingPrompts.d.ts +63 -0
  25. package/dist/agent/prompts/routingPrompts.js +201 -0
  26. package/dist/browser/neurolink.min.js +377 -358
  27. package/dist/cli/commands/agent.d.ts +37 -0
  28. package/dist/cli/commands/agent.js +718 -0
  29. package/dist/cli/factories/commandFactory.d.ts +8 -0
  30. package/dist/cli/factories/commandFactory.js +116 -0
  31. package/dist/cli/loop/optionsSchema.d.ts +1 -1
  32. package/dist/cli/parser.js +4 -0
  33. package/dist/index.d.ts +6 -0
  34. package/dist/index.js +19 -0
  35. package/dist/lib/agent/agent.d.ts +104 -0
  36. package/dist/lib/agent/agent.js +402 -0
  37. package/dist/lib/agent/agentNetwork.d.ts +87 -0
  38. package/dist/lib/agent/agentNetwork.js +459 -0
  39. package/dist/lib/agent/communication/index.d.ts +9 -0
  40. package/dist/lib/agent/communication/index.js +10 -0
  41. package/dist/lib/agent/communication/message-bus.d.ts +113 -0
  42. package/dist/lib/agent/communication/message-bus.js +407 -0
  43. package/dist/lib/agent/coordination/coordinator.d.ts +83 -0
  44. package/dist/lib/agent/coordination/coordinator.js +599 -0
  45. package/dist/lib/agent/coordination/index.d.ts +10 -0
  46. package/dist/lib/agent/coordination/index.js +11 -0
  47. package/dist/lib/agent/coordination/task-distributor.d.ts +113 -0
  48. package/dist/lib/agent/coordination/task-distributor.js +586 -0
  49. package/dist/lib/agent/index.d.ts +38 -0
  50. package/dist/lib/agent/index.js +45 -0
  51. package/dist/lib/agent/orchestration/index.d.ts +10 -0
  52. package/dist/lib/agent/orchestration/index.js +11 -0
  53. package/dist/lib/agent/orchestration/orchestrator.d.ts +114 -0
  54. package/dist/lib/agent/orchestration/orchestrator.js +477 -0
  55. package/dist/lib/agent/orchestration/topology.d.ts +164 -0
  56. package/dist/lib/agent/orchestration/topology.js +521 -0
  57. package/dist/lib/agent/prompts/routingPrompts.d.ts +63 -0
  58. package/dist/lib/agent/prompts/routingPrompts.js +202 -0
  59. package/dist/lib/index.d.ts +6 -0
  60. package/dist/lib/index.js +19 -0
  61. package/dist/lib/neurolink.d.ts +129 -0
  62. package/dist/lib/neurolink.js +276 -0
  63. package/dist/lib/processors/config/{fileTypes.js → fileExtensions.js} +1 -1
  64. package/dist/lib/processors/config/index.d.ts +2 -2
  65. package/dist/lib/processors/config/index.js +2 -2
  66. package/dist/lib/processors/config/{mimeTypes.js → mimeConstants.js} +1 -1
  67. package/dist/lib/processors/index.js +8 -0
  68. package/dist/lib/providers/googleAiStudio.js +3 -3
  69. package/dist/lib/providers/googleNativeGemini3.d.ts +18 -0
  70. package/dist/lib/providers/googleNativeGemini3.js +47 -1
  71. package/dist/lib/providers/googleVertex.js +5 -5
  72. package/dist/lib/types/agentNetwork.d.ts +1184 -0
  73. package/dist/lib/types/agentNetwork.js +8 -0
  74. package/dist/lib/types/cli.d.ts +66 -0
  75. package/dist/lib/types/generate.d.ts +53 -0
  76. package/dist/lib/types/index.d.ts +2 -0
  77. package/dist/lib/types/index.js +3 -0
  78. package/dist/lib/types/ioProcessor.d.ts +119 -0
  79. package/dist/lib/types/ioProcessor.js +10 -0
  80. package/dist/lib/types/stream.d.ts +36 -0
  81. package/dist/lib/utils/piiDetector.d.ts +24 -0
  82. package/dist/lib/utils/piiDetector.js +221 -0
  83. package/dist/lib/utils/responseValidator.d.ts +21 -0
  84. package/dist/lib/utils/responseValidator.js +354 -0
  85. package/dist/lib/utils/tripwireEvaluator.d.ts +73 -0
  86. package/dist/lib/utils/tripwireEvaluator.js +285 -0
  87. package/dist/neurolink.d.ts +129 -0
  88. package/dist/neurolink.js +276 -0
  89. package/dist/processors/config/index.d.ts +2 -2
  90. package/dist/processors/config/index.js +2 -2
  91. package/dist/processors/index.js +8 -0
  92. package/dist/providers/googleAiStudio.js +3 -3
  93. package/dist/providers/googleNativeGemini3.d.ts +18 -0
  94. package/dist/providers/googleNativeGemini3.js +47 -1
  95. package/dist/providers/googleVertex.js +5 -5
  96. package/dist/types/agentNetwork.d.ts +1184 -0
  97. package/dist/types/agentNetwork.js +7 -0
  98. package/dist/types/cli.d.ts +66 -0
  99. package/dist/types/generate.d.ts +53 -0
  100. package/dist/types/index.d.ts +2 -0
  101. package/dist/types/index.js +3 -0
  102. package/dist/types/ioProcessor.d.ts +119 -0
  103. package/dist/types/ioProcessor.js +9 -0
  104. package/dist/types/stream.d.ts +36 -0
  105. package/dist/utils/piiDetector.d.ts +24 -0
  106. package/dist/utils/piiDetector.js +220 -0
  107. package/dist/utils/responseValidator.d.ts +21 -0
  108. package/dist/utils/responseValidator.js +353 -0
  109. package/dist/utils/tripwireEvaluator.d.ts +73 -0
  110. package/dist/utils/tripwireEvaluator.js +284 -0
  111. package/package.json +1 -1
  112. /package/dist/lib/processors/config/{fileTypes.d.ts → fileExtensions.d.ts} +0 -0
  113. /package/dist/lib/processors/config/{mimeTypes.d.ts → mimeConstants.d.ts} +0 -0
  114. /package/dist/processors/config/{fileTypes.d.ts → fileExtensions.d.ts} +0 -0
  115. /package/dist/processors/config/{fileTypes.js → fileExtensions.js} +0 -0
  116. /package/dist/processors/config/{mimeTypes.d.ts → mimeConstants.d.ts} +0 -0
  117. /package/dist/processors/config/{mimeTypes.js → mimeConstants.js} +0 -0
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Agent Network Types for Multi-Agent Orchestration
3
+ *
4
+ * These types define the core abstractions for NeuroLink's multi-agent system,
5
+ * enabling intelligent routing, agent collaboration, and hierarchical networks.
6
+ */
7
+ export {};
8
+ //# sourceMappingURL=agentNetwork.js.map
@@ -1550,3 +1550,69 @@ export type CliClassifierRouterFlags = {
1550
1550
  /** LLM-classifier hard timeout in ms (--classifier-timeout). */
1551
1551
  classifierTimeout?: number;
1552
1552
  };
1553
+ /**
1554
+ * Agent command arguments for multi-agent orchestration
1555
+ */
1556
+ export type CliAgentCommandArgs = BaseCommandArgs & {
1557
+ /** Agent ID */
1558
+ id?: string;
1559
+ /** Agent name */
1560
+ name?: string;
1561
+ /** Agent description */
1562
+ description?: string;
1563
+ /** Agent instructions/system prompt */
1564
+ instructions?: string;
1565
+ /** AI provider to use */
1566
+ provider?: string;
1567
+ /** Model name */
1568
+ model?: string;
1569
+ /** Tools available to the agent */
1570
+ tools?: string[];
1571
+ /** Maximum execution steps */
1572
+ maxSteps?: number;
1573
+ /** Temperature setting */
1574
+ temperature?: number;
1575
+ /** Input prompt for execution */
1576
+ input?: string;
1577
+ /** Context data as JSON */
1578
+ context?: string;
1579
+ /** Agent definition file path */
1580
+ file?: string;
1581
+ /** Output file path */
1582
+ output?: string;
1583
+ /** Enable streaming output */
1584
+ stream?: boolean;
1585
+ /** Show detailed information */
1586
+ detailed?: boolean;
1587
+ };
1588
+ /**
1589
+ * Network command arguments for agent network orchestration
1590
+ */
1591
+ export type CliNetworkCommandArgs = BaseCommandArgs & {
1592
+ /** Network ID */
1593
+ id?: string;
1594
+ /** Network name */
1595
+ name?: string;
1596
+ /** Network description */
1597
+ description?: string;
1598
+ /** Network configuration file path */
1599
+ file?: string;
1600
+ /** Input message for execution */
1601
+ input?: string;
1602
+ /** Maximum execution steps */
1603
+ maxSteps?: number;
1604
+ /** Execution timeout in ms */
1605
+ timeout?: number;
1606
+ /** Context data as JSON */
1607
+ context?: string;
1608
+ /** Output file path */
1609
+ output?: string;
1610
+ /** Enable streaming output */
1611
+ stream?: boolean;
1612
+ /** Router provider */
1613
+ routerProvider?: string;
1614
+ /** Router model */
1615
+ routerModel?: string;
1616
+ /** Show detailed information */
1617
+ detailed?: boolean;
1618
+ };
@@ -18,6 +18,7 @@ import type { FileWithMetadata } from "./file.js";
18
18
  import type { WorkflowConfig } from "./workflow.js";
19
19
  import type { Schema, Tool, ToolChoice } from "./tools.js";
20
20
  import type { StepResult, LanguageModel } from "./providers.js";
21
+ import type { ProcessorPipelineConfig } from "./ioProcessor.js";
21
22
  /**
22
23
  * Generate function options type - Primary method for content generation
23
24
  * Supports multimodal content while maintaining backward compatibility
@@ -570,6 +571,50 @@ export type GenerateOptions = {
570
571
  */
571
572
  additionalUsers?: AdditionalMemoryUser[];
572
573
  };
574
+ /**
575
+ * PII detection — scans and optionally redacts PII from input before the LLM call.
576
+ * @example { enabled: true, action: "redact", detectTypes: ["ssn", "email"] }
577
+ */
578
+ piiDetection?: {
579
+ enabled?: boolean;
580
+ action?: "redact" | "abort" | "warn";
581
+ detectTypes?: Array<"email" | "phone" | "ssn" | "creditCard" | "ipAddress" | "address" | "name" | "dateOfBirth" | "passport" | "driversLicense">;
582
+ customPatterns?: RegExp[];
583
+ allowList?: string[];
584
+ redactionText?: string;
585
+ };
586
+ /**
587
+ * Response validation — validates and optionally transforms the LLM response.
588
+ * Supports retry-with-feedback when `retryOnFailure: true`.
589
+ * @example { maxLength: 5000, truncationAction: "truncate", retryOnFailure: true }
590
+ */
591
+ responseValidation?: {
592
+ minLength?: number;
593
+ maxLength?: number;
594
+ requiredPhrases?: string[];
595
+ forbiddenPhrases?: string[];
596
+ jsonSchema?: Record<string, unknown>;
597
+ customValidator?: (text: string) => {
598
+ category: string;
599
+ severity: "error" | "warning" | "info";
600
+ message: string;
601
+ } | null;
602
+ truncationAction?: "abort" | "retry" | "truncate" | "warn";
603
+ truncationSuffix?: string;
604
+ retryOnFailure?: boolean;
605
+ maxRetries?: number;
606
+ };
607
+ /** Input validation — validates input text before any processing. */
608
+ inputValidation?: {
609
+ trimWhitespace?: boolean;
610
+ minLength?: number;
611
+ maxLength?: number;
612
+ requireContent?: boolean;
613
+ };
614
+ /**
615
+ * @deprecated Use `piiDetection`, `responseValidation`, and `inputValidation` instead.
616
+ */
617
+ processors?: ProcessorPipelineConfig;
573
618
  };
574
619
  /**
575
620
  * Represents an additional user whose memory should be included in a generate/stream call.
@@ -1192,6 +1237,14 @@ export type TextGenerationOptions = {
1192
1237
  * callers can correlate generation traces back to their own request lifecycle.
1193
1238
  */
1194
1239
  requestId?: string;
1240
+ /** PII detection config — forwarded from GenerateOptions/StreamOptions. */
1241
+ piiDetection?: GenerateOptions["piiDetection"];
1242
+ /** Response validation config — forwarded from GenerateOptions/StreamOptions. */
1243
+ responseValidation?: GenerateOptions["responseValidation"];
1244
+ /** Input validation config — forwarded from GenerateOptions/StreamOptions. */
1245
+ inputValidation?: GenerateOptions["inputValidation"];
1246
+ /** @deprecated Use `piiDetection`, `responseValidation`, `inputValidation` instead. */
1247
+ processors?: ProcessorPipelineConfig;
1195
1248
  };
1196
1249
  /**
1197
1250
  * Text generation result (consolidated from core types)
@@ -57,6 +57,7 @@ export * from "./voice.js";
57
57
  export * from "./universalProviderOptions.js";
58
58
  export * from "./utilities.js";
59
59
  export * from "./workflow.js";
60
+ export * from "./ioProcessor.js";
60
61
  export * from "./exporter.js";
61
62
  export * from "./span.js";
62
63
  export * from "./imageGen.js";
@@ -72,3 +73,4 @@ export * from "./safeFetch.js";
72
73
  export * from "./modelPool.js";
73
74
  export * from "./requestRouter.js";
74
75
  export * from "./classifierRouter.js";
76
+ export * from "./agentNetwork.js";
@@ -59,6 +59,7 @@ export * from "./universalProviderOptions.js";
59
59
  export * from "./utilities.js";
60
60
  export * from "./workflow.js";
61
61
  // Processor base types are re-exported via ./processor.js
62
+ export * from "./ioProcessor.js";
62
63
  export * from "./exporter.js";
63
64
  export * from "./span.js";
64
65
  export * from "./imageGen.js";
@@ -82,4 +83,6 @@ export * from "./modelPool.js";
82
83
  export * from "./requestRouter.js";
83
84
  // ClassifierRouter — classify → pick model + tools from a base pool (M9.x+)
84
85
  export * from "./classifierRouter.js";
86
+ // Multi-Agent orchestration types
87
+ export * from "./agentNetwork.js";
85
88
  //# sourceMappingURL=index.js.map
@@ -0,0 +1,119 @@
1
+ /**
2
+ * Safety Utility Types for NeuroLink
3
+ *
4
+ * Types used by the PII detection, response validation, and tripwire
5
+ * evaluation utilities wired into generate() and stream().
6
+ *
7
+ * @module ioProcessor
8
+ */
9
+ export type PiiType = "email" | "phone" | "ssn" | "creditCard" | "ipAddress" | "address" | "name" | "dateOfBirth" | "passport" | "driversLicense";
10
+ export type PiiDetectionConfig = {
11
+ enabled?: boolean;
12
+ action: "redact" | "abort" | "warn";
13
+ detectTypes?: PiiType[];
14
+ customPatterns?: RegExp[];
15
+ allowList?: string[];
16
+ redactionText?: string;
17
+ };
18
+ export type DetectedPII = {
19
+ type: PiiType | "custom";
20
+ value: string;
21
+ position: {
22
+ start: number;
23
+ end: number;
24
+ };
25
+ /** Which field the PII was found in (e.g. "text", "messages[2]") */
26
+ field: string;
27
+ };
28
+ export type PiiDetectionResult = {
29
+ /** Redacted text when action=redact, otherwise the original text */
30
+ text: string;
31
+ detectedPII: DetectedPII[];
32
+ action: "continue" | "abort";
33
+ /** Human-readable message about what was found */
34
+ feedback?: string;
35
+ };
36
+ export type ResponseValidationConfig = {
37
+ /** Minimum required character length for the response */
38
+ minLength?: number;
39
+ /** Maximum allowed character length for the response */
40
+ maxLength?: number;
41
+ /** Phrases that must appear in the response (case-insensitive) */
42
+ requiredPhrases?: string[];
43
+ /** Phrases that must NOT appear in the response (case-insensitive) */
44
+ forbiddenPhrases?: string[];
45
+ /** JSON Schema to validate the response against (response must be valid JSON) */
46
+ jsonSchema?: Record<string, unknown>;
47
+ /** Custom validation function; return a ValidationIssue to signal failure, null to pass */
48
+ customValidator?: (text: string) => ValidationIssue | null;
49
+ /**
50
+ * Action to take when maxLength is exceeded:
51
+ * - "truncate" — slice text to maxLength + suffix (default)
52
+ * - "abort" — return action:"abort"
53
+ * - "retry" — return action:"retry" with feedback
54
+ * - "warn" — log a warning but return the unmodified text
55
+ */
56
+ truncationAction?: "abort" | "retry" | "truncate" | "warn";
57
+ /** Suffix appended when truncating (default: "..."). Never causes the final string to exceed maxLength. */
58
+ truncationSuffix?: string;
59
+ /** Return action:"retry" when any error-severity issue is found */
60
+ retryOnFailure?: boolean;
61
+ /** Maximum allowed retry count (informational — caller enforces the loop) */
62
+ maxRetries?: number;
63
+ };
64
+ export type ValidationIssue = {
65
+ /** Short machine-readable category (e.g., "length", "json_schema", "phrase") */
66
+ category: string;
67
+ severity: "error" | "warning" | "info";
68
+ message: string;
69
+ /** Optional field path (useful for JSON schema errors) */
70
+ field?: string;
71
+ };
72
+ export type ResponseValidationResult = {
73
+ /** Possibly-mutated response text (truncation may shorten it) */
74
+ text: string;
75
+ /** What the caller should do next */
76
+ action: "continue" | "abort" | "retry";
77
+ /** All issues found during validation */
78
+ issues: ValidationIssue[];
79
+ /** Human-readable summary suitable for inclusion in a retry prompt */
80
+ feedback?: string;
81
+ /** The retryCount that was passed in (echoed back for convenience) */
82
+ retryCount?: number;
83
+ };
84
+ export type TripwireAction = "abort" | "warn" | "log";
85
+ export type TripwireData = {
86
+ responseText?: string;
87
+ inputText?: string;
88
+ latencyMs?: number;
89
+ tokenUsage?: {
90
+ input?: number;
91
+ output?: number;
92
+ total?: number;
93
+ };
94
+ messageCount?: number;
95
+ finishReason?: string;
96
+ };
97
+ export type TripwireConfig = {
98
+ id: string;
99
+ name: string;
100
+ description: string;
101
+ action: TripwireAction;
102
+ condition: (data: TripwireData) => boolean;
103
+ message?: string | ((data: TripwireData) => string);
104
+ };
105
+ export type TripwireResult = {
106
+ triggered: boolean;
107
+ tripwire?: TripwireConfig;
108
+ message?: string;
109
+ action?: TripwireAction;
110
+ };
111
+ /** @deprecated Use piiDetection/responseValidation/inputValidation on GenerateOptions instead */
112
+ export type ProcessorPipelineConfig = {
113
+ name?: string;
114
+ stopOnAbort?: boolean;
115
+ pipelineTimeoutMs?: number;
116
+ inputProcessors?: unknown[];
117
+ outputProcessors?: unknown[];
118
+ settings?: Record<string, unknown>;
119
+ };
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Safety Utility Types for NeuroLink
3
+ *
4
+ * Types used by the PII detection, response validation, and tripwire
5
+ * evaluation utilities wired into generate() and stream().
6
+ *
7
+ * @module ioProcessor
8
+ */
9
+ export {};
10
+ //# sourceMappingURL=ioProcessor.js.map
@@ -17,6 +17,7 @@ import type { FileWithMetadata } from "./file.js";
17
17
  import type { WorkflowConfig } from "./workflow.js";
18
18
  import type { LanguageModel, StepResult } from "./providers.js";
19
19
  import type { Tool, ToolChoice } from "./tools.js";
20
+ import type { ProcessorPipelineConfig } from "./ioProcessor.js";
20
21
  /**
21
22
  * Progress tracking and metadata for streaming operations
22
23
  */
@@ -498,6 +499,41 @@ export type StreamOptions = {
498
499
  */
499
500
  additionalUsers?: AdditionalMemoryUser[];
500
501
  };
502
+ /** PII detection — scans and optionally redacts PII from input before the LLM call. */
503
+ piiDetection?: {
504
+ enabled?: boolean;
505
+ action?: "redact" | "abort" | "warn";
506
+ detectTypes?: Array<"email" | "phone" | "ssn" | "creditCard" | "ipAddress" | "address" | "name" | "dateOfBirth" | "passport" | "driversLicense">;
507
+ customPatterns?: RegExp[];
508
+ allowList?: string[];
509
+ redactionText?: string;
510
+ };
511
+ /** Response validation — validates accumulated stream content after completion. */
512
+ responseValidation?: {
513
+ minLength?: number;
514
+ maxLength?: number;
515
+ requiredPhrases?: string[];
516
+ forbiddenPhrases?: string[];
517
+ jsonSchema?: Record<string, unknown>;
518
+ customValidator?: (text: string) => {
519
+ category: string;
520
+ severity: "error" | "warning" | "info";
521
+ message: string;
522
+ } | null;
523
+ truncationAction?: "abort" | "retry" | "truncate" | "warn";
524
+ truncationSuffix?: string;
525
+ retryOnFailure?: boolean;
526
+ maxRetries?: number;
527
+ };
528
+ /** Input validation — validates input text before any processing. */
529
+ inputValidation?: {
530
+ trimWhitespace?: boolean;
531
+ minLength?: number;
532
+ maxLength?: number;
533
+ requireContent?: boolean;
534
+ };
535
+ /** @deprecated Use `piiDetection`, `responseValidation`, and `inputValidation` instead. */
536
+ processors?: ProcessorPipelineConfig;
501
537
  };
502
538
  /**
503
539
  * Stream function result type - Primary output format for streaming
@@ -0,0 +1,24 @@
1
+ /**
2
+ * PII Detection Utility
3
+ *
4
+ * Standalone utility for detecting and redacting personally identifiable
5
+ * information (PII) from text. Used by generate() and stream() directly.
6
+ *
7
+ * @module piiDetector
8
+ */
9
+ import type { PiiDetectionConfig, PiiDetectionResult } from "../types/index.js";
10
+ /**
11
+ * Detect and optionally redact PII from a text string.
12
+ *
13
+ * Detection runs per-field (each named segment scanned independently) so that
14
+ * offset positions always refer to the correct field's text — not a concatenated
15
+ * blob (Bug C3 fix).
16
+ *
17
+ * Custom patterns are validated before execution to prevent catastrophic
18
+ * backtracking (Bug C10 fix).
19
+ *
20
+ * @param text - The input text to scan
21
+ * @param config - PII detection configuration
22
+ * @returns Detection result with (optionally) redacted text and found PII
23
+ */
24
+ export declare function detectAndRedactPII(text: string, config: PiiDetectionConfig): Promise<PiiDetectionResult>;
@@ -0,0 +1,221 @@
1
+ /**
2
+ * PII Detection Utility
3
+ *
4
+ * Standalone utility for detecting and redacting personally identifiable
5
+ * information (PII) from text. Used by generate() and stream() directly.
6
+ *
7
+ * @module piiDetector
8
+ */
9
+ import { logger } from "./logger.js";
10
+ // ============================================================================
11
+ // Built-in patterns
12
+ // ============================================================================
13
+ const PII_PATTERNS = {
14
+ email: /\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b/gi,
15
+ phone: /\b(\+?1[-.\s]?)?(\(?\d{3}\)?[-.\s]?)?\d{3}[-.\s]?\d{4}\b/g,
16
+ ssn: /\b\d{3}[-\s]?\d{2}[-\s]?\d{4}\b/g,
17
+ creditCard: /\b(?:\d{4}[-\s]?){3}\d{4}\b/g,
18
+ ipAddress: /\b(?:\d{1,3}\.){3}\d{1,3}\b/g,
19
+ address: /\b\d+\s+[\w\s]+(?:street|st|avenue|ave|road|rd|boulevard|blvd|drive|dr|lane|ln|court|ct)\b/gi,
20
+ name: /\b[A-Z][a-z]+\s+[A-Z][a-z]+\b/g,
21
+ dateOfBirth: /\b(?:0?[1-9]|1[0-2])[-/](?:0?[1-9]|[12]\d|3[01])[-/](?:19|20)\d{2}\b/g,
22
+ passport: /\b[A-Z]{1,2}\d{6,9}\b/g,
23
+ driversLicense: /\b[A-Z]{1,2}\d{5,8}\b/g,
24
+ };
25
+ const DEFAULT_PII_TYPES = ["email", "phone", "ssn", "creditCard"];
26
+ // ============================================================================
27
+ // Catastrophic backtracking detection (Bug C10)
28
+ // ============================================================================
29
+ /**
30
+ * Heuristic check for patterns that can cause catastrophic backtracking.
31
+ * Looks for nested quantifiers such as (a+)+ or (a*)* and similar structures.
32
+ */
33
+ function hasCatastrophicBacktracking(pattern) {
34
+ const src = pattern.source;
35
+ // Simple string-based check for nested quantifiers: (X+)+ (X*)+ etc.
36
+ // Avoids using a regex here (CodeQL flags polynomial regex on untrusted input).
37
+ for (let i = 0; i < src.length - 1; i++) {
38
+ if (src[i] === ")" && (src[i + 1] === "+" || src[i + 1] === "*")) {
39
+ // Found a closing group followed by a quantifier — check if the group
40
+ // itself contains a quantifier.
41
+ const groupEnd = i;
42
+ let depth = 1;
43
+ let j = groupEnd - 1;
44
+ while (j >= 0 && depth > 0) {
45
+ if (src[j] === ")") {
46
+ depth++;
47
+ }
48
+ if (src[j] === "(") {
49
+ depth--;
50
+ }
51
+ j--;
52
+ }
53
+ const groupStart = j + 1;
54
+ const groupBody = src.slice(groupStart + 1, groupEnd);
55
+ if (groupBody.includes("+") || groupBody.includes("*")) {
56
+ return true;
57
+ }
58
+ }
59
+ }
60
+ return false;
61
+ }
62
+ /**
63
+ * Validate a custom RegExp before use (Bug C10).
64
+ * Returns an error string if invalid, or null if safe.
65
+ */
66
+ function validateCustomPattern(pattern) {
67
+ if (!(pattern instanceof RegExp)) {
68
+ return "Custom pattern is not a RegExp instance";
69
+ }
70
+ if (hasCatastrophicBacktracking(pattern)) {
71
+ return `Custom pattern /${pattern.source}/ contains nested quantifiers that may cause catastrophic backtracking`;
72
+ }
73
+ return null;
74
+ }
75
+ // ============================================================================
76
+ // Per-field detection helpers (Bug C3 fix)
77
+ // ============================================================================
78
+ /**
79
+ * Scan a single field's text with one regex pattern.
80
+ * Returns DetectedPII entries with field-local offsets.
81
+ */
82
+ function scanField(fieldText, fieldName, type, pattern) {
83
+ // Reset lastIndex to avoid cross-call state leaking between scans.
84
+ // We mutate the pattern's lastIndex directly rather than constructing a new
85
+ // RegExp from .source/.flags (which CodeQL flags as regex injection).
86
+ pattern.lastIndex = 0;
87
+ const regex = pattern;
88
+ const results = [];
89
+ let match = regex.exec(fieldText);
90
+ while (match !== null) {
91
+ results.push({
92
+ type,
93
+ value: match[0],
94
+ position: { start: match.index, end: match.index + match[0].length },
95
+ field: fieldName,
96
+ });
97
+ // Guard against zero-width matches causing infinite loops
98
+ if (match[0].length === 0) {
99
+ regex.lastIndex++;
100
+ }
101
+ match = regex.exec(fieldText);
102
+ }
103
+ return results;
104
+ }
105
+ /**
106
+ * Apply redactions to a single field's text given its DetectedPII entries.
107
+ * Redacts in reverse position order so earlier offsets stay valid.
108
+ */
109
+ function redactField(fieldText, hits, redactionText) {
110
+ // Sort descending by start position
111
+ const sorted = [...hits].sort((a, b) => b.position.start - a.position.start);
112
+ let result = fieldText;
113
+ for (const hit of sorted) {
114
+ result =
115
+ result.slice(0, hit.position.start) +
116
+ redactionText +
117
+ result.slice(hit.position.end);
118
+ }
119
+ return result;
120
+ }
121
+ // ============================================================================
122
+ // Public API
123
+ // ============================================================================
124
+ /**
125
+ * Detect and optionally redact PII from a text string.
126
+ *
127
+ * Detection runs per-field (each named segment scanned independently) so that
128
+ * offset positions always refer to the correct field's text — not a concatenated
129
+ * blob (Bug C3 fix).
130
+ *
131
+ * Custom patterns are validated before execution to prevent catastrophic
132
+ * backtracking (Bug C10 fix).
133
+ *
134
+ * @param text - The input text to scan
135
+ * @param config - PII detection configuration
136
+ * @returns Detection result with (optionally) redacted text and found PII
137
+ */
138
+ export async function detectAndRedactPII(text, config) {
139
+ const { action, detectTypes = DEFAULT_PII_TYPES, customPatterns = [], allowList = [], redactionText = "[REDACTED]", } = config;
140
+ if (!text) {
141
+ return { text, detectedPII: [], action: "continue" };
142
+ }
143
+ // Validate custom patterns up-front (Bug C10)
144
+ const validatedCustomPatterns = [];
145
+ for (const pattern of customPatterns) {
146
+ const err = validateCustomPattern(pattern);
147
+ if (err) {
148
+ logger.warn(`piiDetector: skipping invalid custom pattern — ${err}`);
149
+ continue;
150
+ }
151
+ validatedCustomPatterns.push(pattern);
152
+ }
153
+ const allDetected = [];
154
+ // Scan built-in PII types (Bug C3: scan the field in isolation)
155
+ for (const piiType of detectTypes) {
156
+ if (allowList.includes(piiType)) {
157
+ continue;
158
+ }
159
+ const pattern = PII_PATTERNS[piiType];
160
+ if (!pattern) {
161
+ continue;
162
+ }
163
+ const hits = scanField(text, "text", piiType, pattern);
164
+ allDetected.push(...hits);
165
+ }
166
+ // Scan custom patterns
167
+ for (const pattern of validatedCustomPatterns) {
168
+ let hits = [];
169
+ try {
170
+ hits = scanField(text, "text", "custom", pattern);
171
+ }
172
+ catch (err) {
173
+ logger.warn(`piiDetector: custom pattern /${pattern.source}/ threw during execution — skipping`, err);
174
+ }
175
+ allDetected.push(...hits);
176
+ }
177
+ if (allDetected.length === 0) {
178
+ if (logger.shouldLog("debug")) {
179
+ logger.debug("piiDetector: no PII detected in input");
180
+ }
181
+ return { text, detectedPII: [], action: "continue" };
182
+ }
183
+ if (logger.shouldLog("debug")) {
184
+ logger.debug(`piiDetector: detected ${allDetected.length} PII occurrence(s) — action=${action}`);
185
+ }
186
+ // Summarise findings for feedback
187
+ const typeCounts = allDetected.reduce((acc, d) => {
188
+ acc[d.type] = (acc[d.type] ?? 0) + 1;
189
+ return acc;
190
+ }, {});
191
+ const summary = Object.entries(typeCounts)
192
+ .map(([t, n]) => `${t} (${n})`)
193
+ .join(", ");
194
+ if (action === "abort") {
195
+ return {
196
+ text,
197
+ detectedPII: allDetected,
198
+ action: "abort",
199
+ feedback: `PII detected: ${summary}. Request blocked for security.`,
200
+ };
201
+ }
202
+ if (action === "redact") {
203
+ // Redact per-field (Bug C3: field hits already carry field-local offsets)
204
+ const fieldHits = allDetected.filter((d) => d.field === "text");
205
+ const redactedText = redactField(text, fieldHits, redactionText);
206
+ return {
207
+ text: redactedText,
208
+ detectedPII: allDetected,
209
+ action: "continue",
210
+ feedback: `PII redacted: ${summary}.`,
211
+ };
212
+ }
213
+ // action === "warn"
214
+ return {
215
+ text,
216
+ detectedPII: allDetected,
217
+ action: "continue",
218
+ feedback: `PII detected (not redacted): ${summary}.`,
219
+ };
220
+ }
221
+ //# sourceMappingURL=piiDetector.js.map
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Response Validator
3
+ *
4
+ * Standalone utility for validating and optionally mutating LLM response text.
5
+ * Handles length checks (including truncation), phrase checks, JSON schema
6
+ * validation, and custom validators. Returns a structured result telling the
7
+ * caller whether to continue, abort, or retry — the caller owns the retry loop.
8
+ *
9
+ * No async I/O, no telemetry spans, no processor classes.
10
+ */
11
+ import type { ResponseValidationConfig, ResponseValidationResult } from "../types/index.js";
12
+ /**
13
+ * Validate (and optionally mutate) an LLM response.
14
+ *
15
+ * @param responseText - The raw text returned by the model.
16
+ * @param config - Validation rules and action preferences.
17
+ * @param retryCount - Current retry iteration (passed through to result).
18
+ * @returns A {@link ResponseValidationResult} with the (possibly truncated)
19
+ * text, a disposition action, all issues found, and optional feedback.
20
+ */
21
+ export declare function validateResponse(responseText: string, config: ResponseValidationConfig, retryCount?: number): ResponseValidationResult;