@juspay/neurolink 10.3.1 → 10.4.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 (70) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/browser/neurolink.min.js +382 -372
  3. package/dist/core/modules/GenerationHandler.d.ts +24 -0
  4. package/dist/core/modules/GenerationHandler.js +18 -3
  5. package/dist/index.d.ts +1 -0
  6. package/dist/index.js +3 -0
  7. package/dist/knowledge/context.d.ts +18 -0
  8. package/dist/knowledge/context.js +91 -0
  9. package/dist/knowledge/defaults.d.ts +24 -0
  10. package/dist/knowledge/defaults.js +29 -0
  11. package/dist/knowledge/engine.d.ts +35 -0
  12. package/dist/knowledge/engine.js +179 -0
  13. package/dist/knowledge/index.d.ts +15 -0
  14. package/dist/knowledge/index.js +15 -0
  15. package/dist/knowledge/indexCache.d.ts +19 -0
  16. package/dist/knowledge/indexCache.js +109 -0
  17. package/dist/knowledge/knowledgeIndex.d.ts +41 -0
  18. package/dist/knowledge/knowledgeIndex.js +204 -0
  19. package/dist/knowledge/normalize.d.ts +32 -0
  20. package/dist/knowledge/normalize.js +74 -0
  21. package/dist/knowledge/resolve.d.ts +18 -0
  22. package/dist/knowledge/resolve.js +156 -0
  23. package/dist/knowledge/retrieval.d.ts +16 -0
  24. package/dist/knowledge/retrieval.js +221 -0
  25. package/dist/lib/core/modules/GenerationHandler.d.ts +24 -0
  26. package/dist/lib/core/modules/GenerationHandler.js +18 -3
  27. package/dist/lib/files/fileTools.d.ts +1 -1
  28. package/dist/lib/index.d.ts +1 -0
  29. package/dist/lib/index.js +3 -0
  30. package/dist/lib/knowledge/context.d.ts +18 -0
  31. package/dist/lib/knowledge/context.js +92 -0
  32. package/dist/lib/knowledge/defaults.d.ts +24 -0
  33. package/dist/lib/knowledge/defaults.js +30 -0
  34. package/dist/lib/knowledge/engine.d.ts +35 -0
  35. package/dist/lib/knowledge/engine.js +180 -0
  36. package/dist/lib/knowledge/index.d.ts +15 -0
  37. package/dist/lib/knowledge/index.js +16 -0
  38. package/dist/lib/knowledge/indexCache.d.ts +19 -0
  39. package/dist/lib/knowledge/indexCache.js +110 -0
  40. package/dist/lib/knowledge/knowledgeIndex.d.ts +41 -0
  41. package/dist/lib/knowledge/knowledgeIndex.js +205 -0
  42. package/dist/lib/knowledge/normalize.d.ts +32 -0
  43. package/dist/lib/knowledge/normalize.js +75 -0
  44. package/dist/lib/knowledge/resolve.d.ts +18 -0
  45. package/dist/lib/knowledge/resolve.js +157 -0
  46. package/dist/lib/knowledge/retrieval.d.ts +16 -0
  47. package/dist/lib/knowledge/retrieval.js +222 -0
  48. package/dist/lib/neurolink.d.ts +14 -1
  49. package/dist/lib/neurolink.js +112 -3
  50. package/dist/lib/types/config.d.ts +12 -0
  51. package/dist/lib/types/conversation.d.ts +1 -1
  52. package/dist/lib/types/dynamic.d.ts +12 -0
  53. package/dist/lib/types/generate.d.ts +14 -0
  54. package/dist/lib/types/index.d.ts +1 -0
  55. package/dist/lib/types/index.js +1 -0
  56. package/dist/lib/types/knowledge.d.ts +342 -0
  57. package/dist/lib/types/knowledge.js +30 -0
  58. package/dist/lib/types/stream.d.ts +14 -0
  59. package/dist/neurolink.d.ts +14 -1
  60. package/dist/neurolink.js +112 -3
  61. package/dist/types/config.d.ts +12 -0
  62. package/dist/types/conversation.d.ts +1 -1
  63. package/dist/types/dynamic.d.ts +12 -0
  64. package/dist/types/generate.d.ts +14 -0
  65. package/dist/types/index.d.ts +1 -0
  66. package/dist/types/index.js +1 -0
  67. package/dist/types/knowledge.d.ts +342 -0
  68. package/dist/types/knowledge.js +29 -0
  69. package/dist/types/stream.d.ts +14 -0
  70. package/package.json +2 -1
@@ -0,0 +1,75 @@
1
+ /**
2
+ * Deterministic text normalization for knowledge grounding.
3
+ *
4
+ * The SAME normalizer runs at index-build time and query time so that a
5
+ * configuration key, a technical identifier, and a user's phrasing all
6
+ * collapse to the same comparable token stream:
7
+ *
8
+ * "Multi-Step Flow" -> "multi step flow"
9
+ * "enableMultiStepFlow" -> "enable multi step flow"
10
+ * "ACCOUNT_STATUS" -> "account status"
11
+ * " Access Policy " -> "access policy"
12
+ *
13
+ * The pipeline is intentionally conservative: it splits identifiers and
14
+ * separators and folds case/diacritics, but it NEVER invents synonyms.
15
+ * Reviewed acronym and spelling expansions belong in an entry's `aliases`,
16
+ * not in this function.
17
+ */
18
+ /**
19
+ * Insert spaces at camelCase / PascalCase / acronym boundaries. Must run
20
+ * BEFORE lowercasing because it relies on case information.
21
+ *
22
+ * "enableURLReading" -> "enable URL Reading"
23
+ * "customAccessOptionsV2" -> "custom Access Options V2"
24
+ */
25
+ const splitIdentifierBoundaries = (input) => input
26
+ // lower/number followed by an uppercase: "enableTwo" -> "enable Two"
27
+ .replace(/([\p{Ll}\p{N}])(\p{Lu})/gu, "$1 $2")
28
+ // acronym run followed by a Word: "URLReading" -> "URL Reading"
29
+ .replace(/(\p{Lu}+)(\p{Lu}\p{Ll})/gu, "$1 $2");
30
+ /**
31
+ * Normalize an arbitrary string into a single space-delimited lowercase
32
+ * phrase. Idempotent: `normalizeText(normalizeText(x)) === normalizeText(x)`.
33
+ */
34
+ export const normalizeText = (input) => {
35
+ if (!input) {
36
+ return "";
37
+ }
38
+ const boundaries = splitIdentifierBoundaries(
39
+ // NFKC folds compatibility forms; NFD + mark-strip removes diacritics.
40
+ input
41
+ .normalize("NFKC")
42
+ .normalize("NFD")
43
+ .replace(/\p{M}+/gu, ""));
44
+ return (boundaries
45
+ // any run of non-letter/non-number (snake_case, kebab-case, dots, slashes,
46
+ // punctuation, and existing whitespace) collapses to a single space.
47
+ .replace(/[^\p{L}\p{N}]+/gu, " ")
48
+ .trim()
49
+ .toLowerCase());
50
+ };
51
+ /**
52
+ * Normalize then split into tokens. Empty input yields an empty array.
53
+ * Numbers and alphanumeric identifiers are preserved as their own tokens.
54
+ */
55
+ export const tokenize = (input) => {
56
+ const normalized = normalizeText(input);
57
+ return normalized ? normalized.split(" ") : [];
58
+ };
59
+ /**
60
+ * Normalize each candidate phrase and drop blanks/duplicates while preserving
61
+ * first-seen order. Used to build the exact-key and alias key sets.
62
+ */
63
+ export const normalizePhrases = (phrases) => {
64
+ const seen = new Set();
65
+ const out = [];
66
+ for (const phrase of phrases) {
67
+ const normalized = normalizeText(phrase);
68
+ if (normalized && !seen.has(normalized)) {
69
+ seen.add(normalized);
70
+ out.push(normalized);
71
+ }
72
+ }
73
+ return out;
74
+ };
75
+ //# sourceMappingURL=normalize.js.map
@@ -0,0 +1,18 @@
1
+ import type { KnowledgeEntryInput, KnowledgeManifest, KnowledgeNormalizeOptions, KnowledgeNormalizeResult, KnowledgeSource, NormalizedKnowledgeEntry } from "../types/index.js";
2
+ /**
3
+ * Resolve one authored entry into a complete `NormalizedKnowledgeEntry`,
4
+ * materializing every omitted optional (optional arrays to `[]`, `body` to "",
5
+ * `kind` to "text", `status` to "active") so downstream code never re-checks
6
+ * it. Authored arrays are cloned so the normalized snapshot is isolated from
7
+ * caller-owned references.
8
+ */
9
+ export declare const resolveEntry: (input: KnowledgeEntryInput, version: string) => NormalizedKnowledgeEntry;
10
+ /** Expand a build manifest into sources — one per catalog. */
11
+ export declare const manifestToSources: (manifest: KnowledgeManifest) => KnowledgeSource[];
12
+ /**
13
+ * Resolve and validate every source into normalized entries. `validation.ok`
14
+ * is false when any error-level issue was found; the caller (index builder)
15
+ * must not swap in an index built from an invalid set. Async so the public
16
+ * contract is stable if a future source kind needs asynchronous loading.
17
+ */
18
+ export declare const normalizeAndValidate: (sources: KnowledgeSource[], options: KnowledgeNormalizeOptions) => Promise<KnowledgeNormalizeResult>;
@@ -0,0 +1,157 @@
1
+ /**
2
+ * Source loading, default resolution, and generic validation.
3
+ *
4
+ * Required fields (id/title/summary/domain/integrations) come straight
5
+ * from the authored entry; omitted optionals fall back to SDK defaults. This
6
+ * covers only provider-neutral invariants (ids, required fields, enum values,
7
+ * relationship integrity). Host-specific rules — configuration-key coverage,
8
+ * taxonomy conflicts — are enforced by the host build BEFORE the manifest
9
+ * reaches NeuroLink. Errors block indexing; warnings are surfaced but do not.
10
+ */
11
+ import { decodeArray, decodeString } from "type-decoder/dist/index.js";
12
+ import { normalizeText } from "./normalize.js";
13
+ const SDK_DEFAULT_KIND = "text";
14
+ const SDK_DEFAULT_STATUS = "active";
15
+ const STATUSES = new Set(["draft", "active", "deprecated"]);
16
+ const KINDS = new Set([
17
+ "concept",
18
+ "text",
19
+ "configuration",
20
+ "tool",
21
+ "procedure",
22
+ "policy",
23
+ "troubleshooting",
24
+ ]);
25
+ /**
26
+ * Resolve one authored entry into a complete `NormalizedKnowledgeEntry`,
27
+ * materializing every omitted optional (optional arrays to `[]`, `body` to "",
28
+ * `kind` to "text", `status` to "active") so downstream code never re-checks
29
+ * it. Authored arrays are cloned so the normalized snapshot is isolated from
30
+ * caller-owned references.
31
+ */
32
+ export const resolveEntry = (input, version) => ({
33
+ id: input.id,
34
+ title: input.title,
35
+ summary: input.summary,
36
+ domain: input.domain,
37
+ integrations: decodeArray(input.integrations, decodeString) ?? [],
38
+ kind: input.kind ?? SDK_DEFAULT_KIND,
39
+ status: input.status ?? SDK_DEFAULT_STATUS,
40
+ body: input.body ?? "",
41
+ aliases: [...(input.aliases ?? [])],
42
+ keywords: [...(input.keywords ?? [])],
43
+ relatedEntryIds: [...(input.relatedEntryIds ?? [])],
44
+ parentEntryId: input.parentEntryId,
45
+ version,
46
+ });
47
+ /** Expand a build manifest into sources — one per catalog. */
48
+ export const manifestToSources = (manifest) => manifest.catalogs.map((catalog) => ({
49
+ id: catalog.id,
50
+ version: manifest.contentVersion,
51
+ entries: catalog.entries,
52
+ }));
53
+ /** Resolve a source's entries and its effective version. */
54
+ const loadSourceEntries = (source, manifestVersion) => ({
55
+ id: source.id,
56
+ version: source.version ?? manifestVersion,
57
+ entries: source.entries,
58
+ });
59
+ const error = (code, message, extra) => ({ level: "error", code, message, ...extra });
60
+ const warn = (code, message, extra) => ({ level: "warning", code, message, ...extra });
61
+ /**
62
+ * Resolve and validate every source into normalized entries. `validation.ok`
63
+ * is false when any error-level issue was found; the caller (index builder)
64
+ * must not swap in an index built from an invalid set. Async so the public
65
+ * contract is stable if a future source kind needs asynchronous loading.
66
+ */
67
+ export const normalizeAndValidate = async (sources, options) => {
68
+ const issues = [];
69
+ const entries = [];
70
+ const seenIds = new Set();
71
+ const aliasOwner = new Map();
72
+ for (const source of sources) {
73
+ const loaded = loadSourceEntries(source, options.manifestVersion);
74
+ if (!loaded.id) {
75
+ issues.push(error("missing-source-id", "Source has no id"));
76
+ }
77
+ for (const input of loaded.entries) {
78
+ if (!input.id) {
79
+ issues.push(error("missing-entry-id", "Entry has no id", { sourceId: loaded.id }));
80
+ continue;
81
+ }
82
+ if (seenIds.has(input.id)) {
83
+ issues.push(error("duplicate-entry-id", `Duplicate entry id "${input.id}"`, {
84
+ entryId: input.id,
85
+ }));
86
+ continue;
87
+ }
88
+ seenIds.add(input.id);
89
+ if (!input.title || !input.title.trim()) {
90
+ issues.push(error("missing-title", "Entry is missing a title", {
91
+ entryId: input.id,
92
+ }));
93
+ }
94
+ if (!input.summary || !input.summary.trim()) {
95
+ issues.push(error("missing-summary", "Entry is missing a summary", {
96
+ entryId: input.id,
97
+ }));
98
+ }
99
+ if (!input.domain || !input.domain.trim()) {
100
+ issues.push(error("missing-domain", "Entry is missing a domain", {
101
+ entryId: input.id,
102
+ }));
103
+ }
104
+ if (input.integrations === undefined) {
105
+ issues.push(error("missing-integrations", "Entry is missing integrations", {
106
+ entryId: input.id,
107
+ }));
108
+ }
109
+ else if (decodeArray(input.integrations, decodeString) === null) {
110
+ issues.push(error("bad-integrations", "Entry integrations must be an array of strings", { entryId: input.id }));
111
+ }
112
+ const normalized = resolveEntry(input, loaded.version);
113
+ if (!KINDS.has(normalized.kind)) {
114
+ issues.push(error("bad-kind", `Unsupported kind "${normalized.kind}"`, {
115
+ entryId: normalized.id,
116
+ }));
117
+ }
118
+ if (!STATUSES.has(normalized.status)) {
119
+ issues.push(error("bad-status", `Unsupported status "${normalized.status}"`, {
120
+ entryId: normalized.id,
121
+ }));
122
+ }
123
+ for (const alias of normalized.aliases) {
124
+ const key = normalizeText(alias);
125
+ if (!key) {
126
+ issues.push(warn("empty-alias", `Alias "${alias}" is empty after normalization`, { entryId: normalized.id }));
127
+ continue;
128
+ }
129
+ const owner = aliasOwner.get(key);
130
+ if (owner && owner !== normalized.id) {
131
+ issues.push(warn("duplicate-alias", `Alias "${alias}" also used by "${owner}"`, { entryId: normalized.id }));
132
+ }
133
+ else {
134
+ aliasOwner.set(key, normalized.id);
135
+ }
136
+ }
137
+ entries.push(normalized);
138
+ }
139
+ }
140
+ // Relationship integrity — resolved after all entries are collected.
141
+ const idSet = new Set(entries.map((entry) => entry.id));
142
+ for (const entry of entries) {
143
+ for (const related of entry.relatedEntryIds) {
144
+ if (!idSet.has(related)) {
145
+ issues.push(warn("broken-relation", `relatedEntryId "${related}" not found`, {
146
+ entryId: entry.id,
147
+ }));
148
+ }
149
+ }
150
+ if (entry.parentEntryId && !idSet.has(entry.parentEntryId)) {
151
+ issues.push(warn("broken-parent", `parentEntryId "${entry.parentEntryId}" not found`, { entryId: entry.id }));
152
+ }
153
+ }
154
+ const ok = !issues.some((issue) => issue.level === "error");
155
+ return { entries, validation: { ok, issues } };
156
+ };
157
+ //# sourceMappingURL=resolve.js.map
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Lexical-first retrieval pipeline.
3
+ *
4
+ * For one turn: authorize/filter BEFORE ranking, resolve exact identifiers and
5
+ * reviewed aliases via query n-gram lookup, add field-aware BM25 over a bounded
6
+ * query+history window, combine the deterministic signals, take the top
7
+ * candidates, expand a bounded set of directly-related entries, and classify
8
+ * confidence. No LLM, no embeddings, no vector store.
9
+ */
10
+ import type { KnowledgeIndexSnapshot, KnowledgeResolvedRetrieval, KnowledgeRetrievalRequest, KnowledgeSelection } from "../types/index.js";
11
+ /**
12
+ * Run retrieval against a ready snapshot. Returns the primary selection, a
13
+ * bounded relationship expansion, the scored candidate list (diagnostics), and
14
+ * a confidence class. Context assembly is a separate, later step.
15
+ */
16
+ export declare const retrieve: (snapshot: KnowledgeIndexSnapshot, request: KnowledgeRetrievalRequest, config: KnowledgeResolvedRetrieval, blockedDomains?: string[]) => KnowledgeSelection;
@@ -0,0 +1,222 @@
1
+ /**
2
+ * Lexical-first retrieval pipeline.
3
+ *
4
+ * For one turn: authorize/filter BEFORE ranking, resolve exact identifiers and
5
+ * reviewed aliases via query n-gram lookup, add field-aware BM25 over a bounded
6
+ * query+history window, combine the deterministic signals, take the top
7
+ * candidates, expand a bounded set of directly-related entries, and classify
8
+ * confidence. No LLM, no embeddings, no vector store.
9
+ */
10
+ import { tokenize } from "./normalize.js";
11
+ /** Per-turn history text cap so a long prior turn cannot dominate the query. */
12
+ const HISTORY_TURN_CHARS = 400;
13
+ /** All contiguous token n-grams (length 1..maxLen), de-duplicated. */
14
+ const generateNGrams = (tokens, maxLen) => {
15
+ const grams = [];
16
+ const seen = new Set();
17
+ const limit = Math.min(maxLen, tokens.length);
18
+ for (let size = 1; size <= limit; size += 1) {
19
+ for (let start = 0; start + size <= tokens.length; start += 1) {
20
+ const gram = tokens.slice(start, start + size).join(" ");
21
+ if (!seen.has(gram)) {
22
+ seen.add(gram);
23
+ grams.push(gram);
24
+ }
25
+ }
26
+ }
27
+ return grams;
28
+ };
29
+ /** Longest indexed exact/alias phrase length, derived from the active snapshot. */
30
+ const getMaxIndexedPhraseLen = (...indexes) => {
31
+ let maxLen = 0;
32
+ for (const index of indexes) {
33
+ for (const phrase of index.keys()) {
34
+ const length = phrase === "" ? 0 : phrase.split(" ").length;
35
+ maxLen = Math.max(maxLen, length);
36
+ }
37
+ }
38
+ return maxLen;
39
+ };
40
+ /** Look each n-gram up in a phrase index; returns entry id -> matched phrases. */
41
+ const lookupPhrases = (grams, index) => {
42
+ const hits = new Map();
43
+ for (const gram of grams) {
44
+ const ids = index.get(gram);
45
+ if (!ids) {
46
+ continue;
47
+ }
48
+ for (const id of ids) {
49
+ const matched = hits.get(id) ?? [];
50
+ matched.push(gram);
51
+ hits.set(id, matched);
52
+ }
53
+ }
54
+ return hits;
55
+ };
56
+ /** Tokens fed to BM25: the current query plus a bounded recent-turn window. */
57
+ const buildLexicalTokens = (request) => {
58
+ const parts = [request.query];
59
+ for (const turn of request.recentTurns) {
60
+ parts.push(turn.text.slice(0, HISTORY_TURN_CHARS));
61
+ }
62
+ return parts.flatMap((part) => tokenize(part));
63
+ };
64
+ /**
65
+ * Authorization + metadata filter. Applied BEFORE ranking so restricted or
66
+ * disabled-integration content never enters candidates, traces, or model context.
67
+ */
68
+ const isAuthorized = (entry, request, blockedDomains) => {
69
+ if (entry.status !== "active") {
70
+ return false;
71
+ }
72
+ if (blockedDomains && blockedDomains.includes(entry.domain)) {
73
+ return false;
74
+ }
75
+ if (entry.integrations.length > 0) {
76
+ const active = new Set();
77
+ for (const integration of request.enabledIntegrations) {
78
+ active.add(integration.toLowerCase());
79
+ }
80
+ if (!entry.integrations.some((integration) => active.has(integration.toLowerCase()))) {
81
+ return false;
82
+ }
83
+ }
84
+ return true;
85
+ };
86
+ const compareCandidates = (left, right) => {
87
+ if (right.score !== left.score) {
88
+ return right.score - left.score;
89
+ }
90
+ if (left.id < right.id) {
91
+ return -1;
92
+ }
93
+ if (left.id > right.id) {
94
+ return 1;
95
+ }
96
+ return 0;
97
+ };
98
+ /**
99
+ * Confidence class. `high` requires an exact/alias hit at the top; otherwise a
100
+ * multi-field or dominant lexical top is `medium`, a single weak signal is
101
+ * `low`, and no candidates is `none`. Thresholds are pre-tuning heuristics.
102
+ */
103
+ const classifyConfidence = (candidates) => {
104
+ if (candidates.length === 0) {
105
+ return "none";
106
+ }
107
+ const [top, second] = candidates;
108
+ if (top.exact || top.alias) {
109
+ return "high";
110
+ }
111
+ const fieldsMatched = Object.keys(top.fieldScores).length;
112
+ const dominant = second ? top.score >= 1.5 * second.score : false;
113
+ if (fieldsMatched >= 2 || dominant) {
114
+ return "medium";
115
+ }
116
+ return "low";
117
+ };
118
+ /**
119
+ * Run retrieval against a ready snapshot. Returns the primary selection, a
120
+ * bounded relationship expansion, the scored candidate list (diagnostics), and
121
+ * a confidence class. Context assembly is a separate, later step.
122
+ */
123
+ export const retrieve = (snapshot, request, config, blockedDomains) => {
124
+ const queryTokens = tokenize(request.query);
125
+ const maxPhraseLen = getMaxIndexedPhraseLen(snapshot.exactIndex, snapshot.aliasIndex);
126
+ const grams = generateNGrams(queryTokens, maxPhraseLen);
127
+ const exactHits = lookupPhrases(grams, snapshot.exactIndex);
128
+ const aliasHits = lookupPhrases(grams, snapshot.aliasIndex);
129
+ const eligibleEntryIds = new Set();
130
+ for (const [id, entry] of snapshot.entriesById) {
131
+ if (isAuthorized(entry, request, blockedDomains)) {
132
+ eligibleEntryIds.add(id);
133
+ }
134
+ }
135
+ const lexicalTokens = buildLexicalTokens(request);
136
+ const lexicalMatches = snapshot.lexical.search(lexicalTokens, config.candidateLimit * 2, eligibleEntryIds);
137
+ const lexicalById = new Map(lexicalMatches.map((match) => [match.id, match]));
138
+ const candidateIds = new Set();
139
+ for (const id of exactHits.keys()) {
140
+ if (eligibleEntryIds.has(id)) {
141
+ candidateIds.add(id);
142
+ }
143
+ }
144
+ for (const id of aliasHits.keys()) {
145
+ if (eligibleEntryIds.has(id)) {
146
+ candidateIds.add(id);
147
+ }
148
+ }
149
+ for (const match of lexicalMatches) {
150
+ candidateIds.add(match.id);
151
+ }
152
+ const candidates = [];
153
+ for (const id of candidateIds) {
154
+ const entry = snapshot.entriesById.get(id);
155
+ if (!entry) {
156
+ continue;
157
+ }
158
+ const exact = exactHits.has(id);
159
+ const alias = aliasHits.has(id);
160
+ const lexicalMatch = lexicalById.get(id);
161
+ const lexical = lexicalMatch?.score ?? 0;
162
+ let score = lexical;
163
+ if (exact) {
164
+ score += config.exactBoost;
165
+ }
166
+ if (alias) {
167
+ score += config.aliasBoost;
168
+ }
169
+ candidates.push({
170
+ id,
171
+ score,
172
+ exact,
173
+ alias,
174
+ lexical,
175
+ fieldScores: lexicalMatch?.fieldScores ?? {},
176
+ matchedPhrases: [
177
+ ...(exactHits.get(id) ?? []),
178
+ ...(aliasHits.get(id) ?? []),
179
+ ],
180
+ });
181
+ }
182
+ candidates.sort(compareCandidates);
183
+ const trimmed = candidates.slice(0, config.candidateLimit);
184
+ const primary = [];
185
+ for (const candidate of trimmed.slice(0, config.resultLimit)) {
186
+ const entry = snapshot.entriesById.get(candidate.id);
187
+ if (entry) {
188
+ primary.push(entry);
189
+ }
190
+ }
191
+ const selectedIds = new Set(primary.map((entry) => entry.id));
192
+ const expanded = [];
193
+ for (const entry of primary) {
194
+ if (expanded.length >= config.relationLimit) {
195
+ break;
196
+ }
197
+ const relations = snapshot.relationIndex.get(entry.id) ?? [];
198
+ for (const relatedId of relations) {
199
+ if (expanded.length >= config.relationLimit) {
200
+ break;
201
+ }
202
+ if (selectedIds.has(relatedId)) {
203
+ continue;
204
+ }
205
+ const relatedEntry = snapshot.entriesById.get(relatedId);
206
+ if (!relatedEntry ||
207
+ !isAuthorized(relatedEntry, request, blockedDomains)) {
208
+ continue;
209
+ }
210
+ selectedIds.add(relatedId);
211
+ expanded.push(relatedEntry);
212
+ }
213
+ }
214
+ return {
215
+ primary,
216
+ expanded,
217
+ candidates: trimmed,
218
+ confidence: classifyConfidence(trimmed),
219
+ candidateCount: candidates.length,
220
+ };
221
+ };
222
+ //# sourceMappingURL=retrieval.js.map
@@ -6,7 +6,7 @@
6
6
  * Uses real MCP infrastructure for tool discovery and execution.
7
7
  */
8
8
  import type { AgentDefinition, AgentNetworkConfig, NetworkExecutionInput, NetworkExecutionOptions, NetworkExecutionResult, NetworkStreamChunk } from "./types/index.js";
9
- import type { CompactionConfig, CompactionResult, SpanData, ObservabilityConfig, MetricsSummary, MCPToolAnnotations, TraceView, AuthenticatedContext, AuthProvider, JsonObject, NeuroLinkEvents, TypedEventEmitter, MCPEnhancementsConfig, NeuroLinkAuthConfig, NeurolinkConstructorConfig, ChatMessage, ExternalMCPOperationResult, ExternalMCPServerInstance, ExternalMCPToolInfo, GenerateOptions, GenerateResult, ProviderStatus, TextGenerationOptions, TextGenerationResult, MCPExecutableTool, MCPServerInfo, MCPStatus, StreamOptions, StreamResult, ToolExecutionContext, ToolExecutionSummary, ToolInfo, ToolRegistrationOptions, BatchOperationResult, StreamGenerationEndContext, ToolRoutingServerDescriptor, ToolDedupConfig, ToolConfig } from "./types/index.js";
9
+ import type { CompactionConfig, CompactionResult, SpanData, ObservabilityConfig, MetricsSummary, MCPToolAnnotations, TraceView, AuthenticatedContext, AuthProvider, JsonObject, NeuroLinkEvents, TypedEventEmitter, MCPEnhancementsConfig, NeuroLinkAuthConfig, NeurolinkConstructorConfig, ChatMessage, ExternalMCPOperationResult, ExternalMCPServerInstance, ExternalMCPToolInfo, GenerateOptions, GenerateResult, ProviderStatus, TextGenerationOptions, TextGenerationResult, MCPExecutableTool, MCPServerInfo, MCPStatus, StreamOptions, StreamResult, ToolExecutionContext, ToolExecutionSummary, ToolInfo, ToolRegistrationOptions, BatchOperationResult, StreamGenerationEndContext, ToolRoutingServerDescriptor, ToolDedupConfig, ToolConfig, KnowledgeEngineStatus } from "./types/index.js";
10
10
  import { ConversationMemoryManager } from "./core/conversationMemoryManager.js";
11
11
  import type { RedisConversationMemoryManager } from "./core/redisConversationMemoryManager.js";
12
12
  import { ExternalServerManager } from "./mcp/externalServerManager.js";
@@ -107,6 +107,7 @@ export declare class NeuroLink {
107
107
  private toolRoutingConfig?;
108
108
  private toolRoutingCacheInstance?;
109
109
  private toolRoutingVectorCache?;
110
+ private knowledgeGroundingEngine?;
110
111
  private toolDedupConfig?;
111
112
  private toolsConfig?;
112
113
  /** Session-scoped pins of tools discovered via search_tools (tools.discovery mode). */
@@ -629,6 +630,7 @@ export declare class NeuroLink {
629
630
  * @param optionsOrPrompt.maxTokens - Maximum tokens to generate
630
631
  * @param optionsOrPrompt.thinkingConfig - Extended thinking configuration (thinkingLevel: 'minimal'|'low'|'medium'|'high')
631
632
  * @param optionsOrPrompt.context - Context with conversationId and userId for memory
633
+ * @param optionsOrPrompt.useKnowledgeGrounding - Whether to use the instance's configured knowledge for this call
632
634
  * @returns Promise resolving to generation result with content and metadata
633
635
  *
634
636
  * @example Basic text generation
@@ -904,6 +906,7 @@ export declare class NeuroLink {
904
906
  * @param options.enableEvaluation - Whether to include response quality evaluation
905
907
  * @param options.context - Additional context for the request
906
908
  * @param options.evaluationDomain - Domain for specialized evaluation
909
+ * @param options.useKnowledgeGrounding - Whether to use the instance's configured knowledge for this call
907
910
  *
908
911
  * @returns Promise resolving to StreamResult with an async iterable stream
909
912
  *
@@ -989,6 +992,16 @@ export declare class NeuroLink {
989
992
  * alone does not activate it.
990
993
  */
991
994
  setToolRoutingServers(servers: ToolRoutingServerDescriptor[]): void;
995
+ /** Knowledge-grounding engine health (null when it was not configured). */
996
+ getKnowledgeStatus(): KnowledgeEngineStatus | null;
997
+ private appendKnowledgeGroundingBlockToSystemPrompt;
998
+ /**
999
+ * Retrieve knowledge grounding for a public generate/stream call without
1000
+ * mutating its options. The outer call boundary decides how to apply the
1001
+ * returned context. Returns undefined when the call does not opt in,
1002
+ * grounding is disabled/not applicable, or retrieval fails open.
1003
+ */
1004
+ private retrieveKnowledgeGrounding;
992
1005
  private validateStreamRequestOptions;
993
1006
  private maybeHandleWorkflowStreamRequest;
994
1007
  private runStandardStreamRequest;