veryfront 0.1.934 → 0.1.936
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/esm/deno.js +1 -1
- package/esm/src/knowledge/index.d.ts +57 -0
- package/esm/src/knowledge/index.d.ts.map +1 -1
- package/esm/src/knowledge/index.js +402 -1
- package/esm/src/platform/compat/index.d.ts +17 -0
- package/esm/src/platform/compat/index.d.ts.map +1 -0
- package/esm/src/platform/compat/index.js +22 -0
- package/esm/src/utils/version-constant.d.ts +1 -1
- package/esm/src/utils/version-constant.js +1 -1
- package/package.json +1 -1
package/esm/deno.js
CHANGED
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
*/
|
|
15
15
|
import "../../_dnt.polyfills.js";
|
|
16
16
|
import type { RagSearchOptions, RagSearchResult, RagStoreBackend } from "../embedding/index.js";
|
|
17
|
+
import type { Tool } from "../tool/types.js";
|
|
17
18
|
/** Configuration for project knowledge indexing and retrieval. */
|
|
18
19
|
export interface ProjectKnowledgeConfig {
|
|
19
20
|
/**
|
|
@@ -54,6 +55,49 @@ export interface ProjectKnowledgeResult {
|
|
|
54
55
|
matches: RagSearchResult[];
|
|
55
56
|
context: string;
|
|
56
57
|
}
|
|
58
|
+
export interface ProjectKnowledgeLookupInput {
|
|
59
|
+
project_reference?: string;
|
|
60
|
+
query?: string;
|
|
61
|
+
cursor?: string;
|
|
62
|
+
lookup_target?: unknown;
|
|
63
|
+
limit?: number;
|
|
64
|
+
shard_count?: number;
|
|
65
|
+
shard_index?: number;
|
|
66
|
+
}
|
|
67
|
+
export interface ProjectKnowledgeLookupFrontmatterField {
|
|
68
|
+
key: string;
|
|
69
|
+
value: string;
|
|
70
|
+
}
|
|
71
|
+
export interface ProjectKnowledgeLookupItem {
|
|
72
|
+
path: string;
|
|
73
|
+
matched_fields: string[];
|
|
74
|
+
frontmatter: ProjectKnowledgeLookupFrontmatterField[];
|
|
75
|
+
}
|
|
76
|
+
export interface ProjectKnowledgeLookupPageInfo {
|
|
77
|
+
self: string | null;
|
|
78
|
+
first: string | null;
|
|
79
|
+
next: string | null;
|
|
80
|
+
prev: string | null;
|
|
81
|
+
}
|
|
82
|
+
export interface ProjectKnowledgeLookupShard {
|
|
83
|
+
shard_index: number;
|
|
84
|
+
shard_count: number;
|
|
85
|
+
total_items: number;
|
|
86
|
+
}
|
|
87
|
+
export interface ProjectKnowledgeLookupOutput {
|
|
88
|
+
query: string;
|
|
89
|
+
mode: "search" | "browse";
|
|
90
|
+
data: ProjectKnowledgeLookupItem[];
|
|
91
|
+
page_info: ProjectKnowledgeLookupPageInfo;
|
|
92
|
+
returned: number;
|
|
93
|
+
total_matches: number;
|
|
94
|
+
shard: ProjectKnowledgeLookupShard;
|
|
95
|
+
}
|
|
96
|
+
export interface CreateSearchKnowledgeToolOptions extends ProjectKnowledgeConfig {
|
|
97
|
+
id?: string;
|
|
98
|
+
description?: string;
|
|
99
|
+
}
|
|
100
|
+
export type SearchKnowledgeTool = Tool<ProjectKnowledgeLookupInput, ProjectKnowledgeLookupOutput>;
|
|
57
101
|
/** Helper for indexing and retrieving project knowledge. */
|
|
58
102
|
export interface ProjectKnowledge {
|
|
59
103
|
/**
|
|
@@ -63,6 +107,12 @@ export interface ProjectKnowledge {
|
|
|
63
107
|
* ingestion, or another controlled lifecycle step.
|
|
64
108
|
*/
|
|
65
109
|
index(): Promise<void>;
|
|
110
|
+
/**
|
|
111
|
+
* Search the local OKF knowledge manifest using the same compact
|
|
112
|
+
* frontmatter-only response shape as Veryfront Cloud's `search_knowledge`
|
|
113
|
+
* tool. This does not build or query the embedding index.
|
|
114
|
+
*/
|
|
115
|
+
lookup(input: ProjectKnowledgeLookupInput): Promise<ProjectKnowledgeLookupOutput>;
|
|
66
116
|
retrieve(query: string, options?: ProjectKnowledgeRetrieveOptions): Promise<ProjectKnowledgeResult>;
|
|
67
117
|
search(query: string, options?: RagSearchOptions): Promise<RagSearchResult[]>;
|
|
68
118
|
}
|
|
@@ -70,6 +120,13 @@ export interface ProjectKnowledge {
|
|
|
70
120
|
export declare function normalizeKnowledgeQuery(query: string, maxChars?: number): string;
|
|
71
121
|
/** Format search results into a deterministic prompt context block. */
|
|
72
122
|
export declare function formatKnowledgeContext(results: RagSearchResult[]): string;
|
|
123
|
+
/**
|
|
124
|
+
* Search the local OKF knowledge manifest with the same input/output shape as
|
|
125
|
+
* Veryfront Cloud's `search_knowledge` MCP tool.
|
|
126
|
+
*/
|
|
127
|
+
export declare function searchProjectKnowledge(input: ProjectKnowledgeLookupInput, config?: ProjectKnowledgeConfig): Promise<ProjectKnowledgeLookupOutput>;
|
|
128
|
+
/** Create a local tool with the same id and response shape as hosted `search_knowledge`. */
|
|
129
|
+
export declare function createSearchKnowledgeTool(options?: CreateSearchKnowledgeToolOptions): SearchKnowledgeTool;
|
|
73
130
|
/** Create a project knowledge helper backed by the configured RAG store. */
|
|
74
131
|
export declare function projectKnowledge(config?: ProjectKnowledgeConfig): ProjectKnowledge;
|
|
75
132
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/src/knowledge/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,yBAAyB,CAAC;AAIjC,OAAO,KAAK,EACV,gBAAgB,EAChB,eAAe,EAEf,eAAe,EAChB,MAAM,uBAAuB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/src/knowledge/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -14,16 +14,395 @@
|
|
|
14
14
|
*/
|
|
15
15
|
import "../../_dnt.polyfills.js";
|
|
16
16
|
import { ragStore } from "../embedding/index.js";
|
|
17
|
-
import {
|
|
17
|
+
import { exists, readDir, readTextFile } from "../platform/compat/index.js";
|
|
18
|
+
import { extract } from "../platform/compat/std/front-matter-yaml.js";
|
|
19
|
+
import { isAbsolute, join, relative } from "../platform/compat/path/index.js";
|
|
20
|
+
import { tool } from "../tool/factory.js";
|
|
18
21
|
const DEFAULT_CONTENT_DIR = "knowledge";
|
|
19
22
|
const DEFAULT_STORAGE_PATH = "data/knowledge-index.json";
|
|
20
23
|
const DEFAULT_TOP_K = 3;
|
|
21
24
|
const DEFAULT_QUERY_MAX_CHARS = 500;
|
|
25
|
+
const DEFAULT_LOOKUP_LIMIT = 8;
|
|
26
|
+
const MAX_LOOKUP_LIMIT = 12;
|
|
27
|
+
const MAX_FRONTMATTER_FIELDS = 6;
|
|
28
|
+
const MAX_FRONTMATTER_VALUE_LENGTH = 240;
|
|
29
|
+
const KNOWLEDGE_LOOKUP_CURSOR_VERSION = 1;
|
|
30
|
+
const FRONTMATTER_FIELD_PRIORITY = [
|
|
31
|
+
"title",
|
|
32
|
+
"name",
|
|
33
|
+
"description",
|
|
34
|
+
"summary",
|
|
35
|
+
"source",
|
|
36
|
+
"source_type",
|
|
37
|
+
"added",
|
|
38
|
+
];
|
|
39
|
+
const SEARCH_KNOWLEDGE_INPUT_SCHEMA = {
|
|
40
|
+
type: "object",
|
|
41
|
+
properties: {
|
|
42
|
+
project_reference: {
|
|
43
|
+
type: "string",
|
|
44
|
+
description: "Project reference accepted for hosted/local parity.",
|
|
45
|
+
},
|
|
46
|
+
query: {
|
|
47
|
+
type: "string",
|
|
48
|
+
description: "Knowledge query to match against OKF frontmatter.",
|
|
49
|
+
},
|
|
50
|
+
cursor: {
|
|
51
|
+
type: "string",
|
|
52
|
+
description: "Cursor from a previous search_knowledge response.",
|
|
53
|
+
},
|
|
54
|
+
lookup_target: {
|
|
55
|
+
type: "object",
|
|
56
|
+
additionalProperties: true,
|
|
57
|
+
description: "Hosted lookup target accepted for API parity and ignored locally.",
|
|
58
|
+
},
|
|
59
|
+
limit: {
|
|
60
|
+
type: "integer",
|
|
61
|
+
description: "Maximum number of manifest entries to return.",
|
|
62
|
+
},
|
|
63
|
+
shard_count: {
|
|
64
|
+
type: "integer",
|
|
65
|
+
description: "Optional shard count for splitting large manifests.",
|
|
66
|
+
},
|
|
67
|
+
shard_index: {
|
|
68
|
+
type: "integer",
|
|
69
|
+
description: "Zero-based shard index.",
|
|
70
|
+
},
|
|
71
|
+
},
|
|
72
|
+
additionalProperties: false,
|
|
73
|
+
};
|
|
22
74
|
function resolveProjectPath(projectDir, path) {
|
|
23
75
|
if (!projectDir || isAbsolute(path))
|
|
24
76
|
return path;
|
|
25
77
|
return join(projectDir, path);
|
|
26
78
|
}
|
|
79
|
+
function toPosixPath(path) {
|
|
80
|
+
return path.replace(/\\/g, "/");
|
|
81
|
+
}
|
|
82
|
+
function stripTrailingSlash(path) {
|
|
83
|
+
return toPosixPath(path).replace(/\/+$/, "");
|
|
84
|
+
}
|
|
85
|
+
function buildManifestPath(config, absolutePath) {
|
|
86
|
+
const contentDir = config.contentDir ?? DEFAULT_CONTENT_DIR;
|
|
87
|
+
const contentDirPath = resolveProjectPath(config.projectDir, contentDir);
|
|
88
|
+
const relativeToContent = toPosixPath(relative(contentDirPath, absolutePath));
|
|
89
|
+
if (!isAbsolute(contentDir)) {
|
|
90
|
+
const normalizedContentDir = stripTrailingSlash(contentDir).replace(/^\.\//, "");
|
|
91
|
+
return `${normalizedContentDir}/${relativeToContent}`;
|
|
92
|
+
}
|
|
93
|
+
if (config.projectDir) {
|
|
94
|
+
return toPosixPath(relative(config.projectDir, absolutePath));
|
|
95
|
+
}
|
|
96
|
+
return relativeToContent;
|
|
97
|
+
}
|
|
98
|
+
async function collectMarkdownFiles(dir) {
|
|
99
|
+
if (!(await exists(dir)))
|
|
100
|
+
return [];
|
|
101
|
+
const files = [];
|
|
102
|
+
for await (const entry of readDir(dir)) {
|
|
103
|
+
const entryPath = join(dir, entry.name);
|
|
104
|
+
if (entry.isDirectory) {
|
|
105
|
+
files.push(...await collectMarkdownFiles(entryPath));
|
|
106
|
+
continue;
|
|
107
|
+
}
|
|
108
|
+
if (entry.isFile && entry.name.endsWith(".md")) {
|
|
109
|
+
files.push(entryPath);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
return files;
|
|
113
|
+
}
|
|
114
|
+
function normalizeText(value) {
|
|
115
|
+
return value
|
|
116
|
+
.normalize("NFKD")
|
|
117
|
+
.replace(/[\u0300-\u036f]/g, "")
|
|
118
|
+
.toLowerCase()
|
|
119
|
+
.replace(/[^a-z0-9]+/g, " ")
|
|
120
|
+
.trim();
|
|
121
|
+
}
|
|
122
|
+
function tokenize(value) {
|
|
123
|
+
return [
|
|
124
|
+
...new Set(normalizeText(value)
|
|
125
|
+
.split(/\s+/)
|
|
126
|
+
.filter((token) => token.length >= 2)),
|
|
127
|
+
];
|
|
128
|
+
}
|
|
129
|
+
function hashPath(value) {
|
|
130
|
+
let hash = 5381;
|
|
131
|
+
for (const char of value) {
|
|
132
|
+
hash = ((hash * 33) ^ char.charCodeAt(0)) >>> 0;
|
|
133
|
+
}
|
|
134
|
+
return hash;
|
|
135
|
+
}
|
|
136
|
+
function collapseFrontmatterValue(value) {
|
|
137
|
+
if (value == null)
|
|
138
|
+
return null;
|
|
139
|
+
let stringValue = "";
|
|
140
|
+
if (typeof value === "string") {
|
|
141
|
+
stringValue = value;
|
|
142
|
+
}
|
|
143
|
+
else if (typeof value === "number" || typeof value === "boolean") {
|
|
144
|
+
stringValue = String(value);
|
|
145
|
+
}
|
|
146
|
+
else if (value instanceof Date) {
|
|
147
|
+
if (Number.isNaN(value.getTime()))
|
|
148
|
+
return null;
|
|
149
|
+
const iso = value.toISOString();
|
|
150
|
+
stringValue = iso.endsWith("T00:00:00.000Z") ? iso.slice(0, 10) : iso;
|
|
151
|
+
}
|
|
152
|
+
else if (Array.isArray(value)) {
|
|
153
|
+
stringValue = value
|
|
154
|
+
.map((item) => collapseFrontmatterValue(item))
|
|
155
|
+
.filter((item) => typeof item === "string" && item.length > 0)
|
|
156
|
+
.join(", ");
|
|
157
|
+
}
|
|
158
|
+
else {
|
|
159
|
+
try {
|
|
160
|
+
stringValue = JSON.stringify(value);
|
|
161
|
+
}
|
|
162
|
+
catch {
|
|
163
|
+
stringValue = String(value);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
const collapsed = stringValue.replace(/\s+/g, " ").trim();
|
|
167
|
+
return collapsed.length > 0 ? collapsed : null;
|
|
168
|
+
}
|
|
169
|
+
function truncateFrontmatterValue(value) {
|
|
170
|
+
if (value.length <= MAX_FRONTMATTER_VALUE_LENGTH)
|
|
171
|
+
return value;
|
|
172
|
+
return `${value.slice(0, MAX_FRONTMATTER_VALUE_LENGTH - 3)}...`;
|
|
173
|
+
}
|
|
174
|
+
function compareFrontmatterFields(left, right) {
|
|
175
|
+
const leftPriority = FRONTMATTER_FIELD_PRIORITY.indexOf(left.key);
|
|
176
|
+
const rightPriority = FRONTMATTER_FIELD_PRIORITY.indexOf(right.key);
|
|
177
|
+
if (leftPriority !== rightPriority) {
|
|
178
|
+
if (leftPriority === -1)
|
|
179
|
+
return 1;
|
|
180
|
+
if (rightPriority === -1)
|
|
181
|
+
return -1;
|
|
182
|
+
return leftPriority - rightPriority;
|
|
183
|
+
}
|
|
184
|
+
return left.key.localeCompare(right.key);
|
|
185
|
+
}
|
|
186
|
+
function collectFrontmatter(frontmatter) {
|
|
187
|
+
return Object.entries(frontmatter)
|
|
188
|
+
.map(([key, rawValue]) => {
|
|
189
|
+
const value = collapseFrontmatterValue(rawValue);
|
|
190
|
+
return value ? { key, value } : null;
|
|
191
|
+
})
|
|
192
|
+
.filter((entry) => entry !== null)
|
|
193
|
+
.sort(compareFrontmatterFields);
|
|
194
|
+
}
|
|
195
|
+
function sanitizeFrontmatter(frontmatter) {
|
|
196
|
+
const searchableFrontmatter = collectFrontmatter(frontmatter);
|
|
197
|
+
return {
|
|
198
|
+
frontmatter: searchableFrontmatter.slice(0, MAX_FRONTMATTER_FIELDS).map((field) => ({
|
|
199
|
+
key: field.key,
|
|
200
|
+
value: truncateFrontmatterValue(field.value),
|
|
201
|
+
})),
|
|
202
|
+
searchableFrontmatter,
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
function toSearchableEntry(entry) {
|
|
206
|
+
return {
|
|
207
|
+
...entry,
|
|
208
|
+
normalizedPath: normalizeText(entry.path),
|
|
209
|
+
searchableFrontmatter: entry.searchableFrontmatter.map((field) => ({
|
|
210
|
+
...field,
|
|
211
|
+
normalizedKey: normalizeText(field.key),
|
|
212
|
+
normalizedValue: normalizeText(field.value),
|
|
213
|
+
})),
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
function encodeCursor(state) {
|
|
217
|
+
const bytes = new TextEncoder().encode(JSON.stringify(state));
|
|
218
|
+
let binary = "";
|
|
219
|
+
for (const byte of bytes)
|
|
220
|
+
binary += String.fromCharCode(byte);
|
|
221
|
+
return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
222
|
+
}
|
|
223
|
+
function decodeCursor(cursor) {
|
|
224
|
+
try {
|
|
225
|
+
const padded = cursor.replace(/-/g, "+").replace(/_/g, "/").padEnd(Math.ceil(cursor.length / 4) * 4, "=");
|
|
226
|
+
const binary = atob(padded);
|
|
227
|
+
const bytes = Uint8Array.from(binary, (char) => char.charCodeAt(0));
|
|
228
|
+
const parsed = JSON.parse(new TextDecoder().decode(bytes));
|
|
229
|
+
if (parsed.version !== KNOWLEDGE_LOOKUP_CURSOR_VERSION ||
|
|
230
|
+
typeof parsed.query !== "string" ||
|
|
231
|
+
typeof parsed.offset !== "number" ||
|
|
232
|
+
typeof parsed.limit !== "number" ||
|
|
233
|
+
typeof parsed.shardCount !== "number" ||
|
|
234
|
+
typeof parsed.shardIndex !== "number") {
|
|
235
|
+
throw new Error("Invalid cursor payload");
|
|
236
|
+
}
|
|
237
|
+
if (parsed.shardIndex < 0 || parsed.shardIndex >= parsed.shardCount) {
|
|
238
|
+
throw new Error("Cursor shard_index must be within shard_count");
|
|
239
|
+
}
|
|
240
|
+
return {
|
|
241
|
+
version: parsed.version,
|
|
242
|
+
query: parsed.query,
|
|
243
|
+
offset: parsed.offset,
|
|
244
|
+
limit: parsed.limit,
|
|
245
|
+
shardCount: parsed.shardCount,
|
|
246
|
+
shardIndex: parsed.shardIndex,
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
catch (error) {
|
|
250
|
+
const message = error instanceof Error ? error.message : "Could not decode cursor";
|
|
251
|
+
throw new Error(`Invalid knowledge lookup cursor: ${message}`);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
function scoreEntry(entry, query, queryTokens) {
|
|
255
|
+
const matchedFields = new Set();
|
|
256
|
+
const normalizedQuery = normalizeText(query);
|
|
257
|
+
let score = 0;
|
|
258
|
+
if (normalizedQuery && entry.normalizedPath.includes(normalizedQuery)) {
|
|
259
|
+
score += 40;
|
|
260
|
+
matchedFields.add("path");
|
|
261
|
+
}
|
|
262
|
+
for (const field of entry.searchableFrontmatter) {
|
|
263
|
+
if (normalizedQuery && field.normalizedValue.includes(normalizedQuery)) {
|
|
264
|
+
score += 28;
|
|
265
|
+
matchedFields.add(field.key);
|
|
266
|
+
}
|
|
267
|
+
if (normalizedQuery && field.normalizedKey.includes(normalizedQuery)) {
|
|
268
|
+
score += 12;
|
|
269
|
+
matchedFields.add(field.key);
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
if (queryTokens.length > 1) {
|
|
273
|
+
for (const token of queryTokens) {
|
|
274
|
+
if (entry.normalizedPath.includes(token)) {
|
|
275
|
+
score += 10;
|
|
276
|
+
matchedFields.add("path");
|
|
277
|
+
}
|
|
278
|
+
for (const field of entry.searchableFrontmatter) {
|
|
279
|
+
if (field.normalizedValue.includes(token)) {
|
|
280
|
+
score += 7;
|
|
281
|
+
matchedFields.add(field.key);
|
|
282
|
+
}
|
|
283
|
+
if (field.normalizedKey.includes(token)) {
|
|
284
|
+
score += 4;
|
|
285
|
+
matchedFields.add(field.key);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
return {
|
|
291
|
+
score,
|
|
292
|
+
matchedFields: [...matchedFields].sort((left, right) => left.localeCompare(right)),
|
|
293
|
+
};
|
|
294
|
+
}
|
|
295
|
+
async function getProjectKnowledgeManifest(config) {
|
|
296
|
+
const contentDir = resolveProjectPath(config.projectDir, config.contentDir ?? DEFAULT_CONTENT_DIR);
|
|
297
|
+
const files = (await collectMarkdownFiles(contentDir)).sort((left, right) => buildManifestPath(config, left).localeCompare(buildManifestPath(config, right)));
|
|
298
|
+
const manifest = [];
|
|
299
|
+
for (const file of files) {
|
|
300
|
+
let parsedFrontmatter = {};
|
|
301
|
+
try {
|
|
302
|
+
parsedFrontmatter = extract(await readTextFile(file)).attrs;
|
|
303
|
+
}
|
|
304
|
+
catch {
|
|
305
|
+
parsedFrontmatter = {};
|
|
306
|
+
}
|
|
307
|
+
manifest.push({
|
|
308
|
+
path: buildManifestPath(config, file),
|
|
309
|
+
...sanitizeFrontmatter(parsedFrontmatter),
|
|
310
|
+
});
|
|
311
|
+
}
|
|
312
|
+
return manifest;
|
|
313
|
+
}
|
|
314
|
+
function lookupKnowledgeManifest(manifest, input) {
|
|
315
|
+
const cursorState = input.cursor ? decodeCursor(input.cursor) : null;
|
|
316
|
+
const providedQuery = input.query?.trim() ?? "";
|
|
317
|
+
const resolvedQuery = (cursorState?.query ?? providedQuery).trim();
|
|
318
|
+
if (!resolvedQuery) {
|
|
319
|
+
throw new Error("search_knowledge requires a non-empty query");
|
|
320
|
+
}
|
|
321
|
+
if (cursorState && providedQuery && providedQuery !== cursorState.query) {
|
|
322
|
+
throw new Error("search_knowledge cursor query mismatch");
|
|
323
|
+
}
|
|
324
|
+
const resolvedShardCount = cursorState?.shardCount ?? input.shard_count ?? 1;
|
|
325
|
+
const resolvedShardIndex = cursorState?.shardIndex ?? input.shard_index ?? 0;
|
|
326
|
+
if (resolvedShardCount < 1) {
|
|
327
|
+
throw new Error("search_knowledge shard_count must be at least 1");
|
|
328
|
+
}
|
|
329
|
+
if (resolvedShardIndex < 0 || resolvedShardIndex >= resolvedShardCount) {
|
|
330
|
+
throw new Error("search_knowledge shard_index must be within shard_count");
|
|
331
|
+
}
|
|
332
|
+
const resolvedLimit = Math.min(Math.max(cursorState?.limit ?? input.limit ?? DEFAULT_LOOKUP_LIMIT, 1), MAX_LOOKUP_LIMIT);
|
|
333
|
+
const resolvedOffset = Math.max(cursorState?.offset ?? 0, 0);
|
|
334
|
+
const queryTokens = tokenize(resolvedQuery);
|
|
335
|
+
const shardEntries = manifest
|
|
336
|
+
.filter((entry) => hashPath(entry.path) % resolvedShardCount === resolvedShardIndex)
|
|
337
|
+
.map(toSearchableEntry);
|
|
338
|
+
const scoredEntries = shardEntries
|
|
339
|
+
.map((entry) => ({
|
|
340
|
+
entry,
|
|
341
|
+
...scoreEntry(entry, resolvedQuery, queryTokens),
|
|
342
|
+
}))
|
|
343
|
+
.sort((left, right) => {
|
|
344
|
+
if (right.score !== left.score)
|
|
345
|
+
return right.score - left.score;
|
|
346
|
+
return left.entry.path.localeCompare(right.entry.path);
|
|
347
|
+
});
|
|
348
|
+
const hasScoredMatches = scoredEntries.some((entry) => entry.score > 0);
|
|
349
|
+
const mode = hasScoredMatches ? "search" : "browse";
|
|
350
|
+
const orderedEntries = hasScoredMatches
|
|
351
|
+
? scoredEntries.filter((entry) => entry.score > 0)
|
|
352
|
+
: scoredEntries;
|
|
353
|
+
const pageEntries = orderedEntries.slice(resolvedOffset, resolvedOffset + resolvedLimit);
|
|
354
|
+
const nextOffset = resolvedOffset + pageEntries.length;
|
|
355
|
+
const hasMore = nextOffset < orderedEntries.length;
|
|
356
|
+
const nextCursor = hasMore
|
|
357
|
+
? encodeCursor({
|
|
358
|
+
version: KNOWLEDGE_LOOKUP_CURSOR_VERSION,
|
|
359
|
+
query: resolvedQuery,
|
|
360
|
+
offset: nextOffset,
|
|
361
|
+
limit: resolvedLimit,
|
|
362
|
+
shardCount: resolvedShardCount,
|
|
363
|
+
shardIndex: resolvedShardIndex,
|
|
364
|
+
})
|
|
365
|
+
: null;
|
|
366
|
+
return {
|
|
367
|
+
query: resolvedQuery,
|
|
368
|
+
mode,
|
|
369
|
+
data: pageEntries.map(({ entry, matchedFields }) => ({
|
|
370
|
+
path: entry.path,
|
|
371
|
+
matched_fields: matchedFields,
|
|
372
|
+
frontmatter: entry.frontmatter,
|
|
373
|
+
})),
|
|
374
|
+
page_info: {
|
|
375
|
+
self: input.cursor ?? null,
|
|
376
|
+
first: null,
|
|
377
|
+
next: nextCursor,
|
|
378
|
+
prev: null,
|
|
379
|
+
},
|
|
380
|
+
returned: pageEntries.length,
|
|
381
|
+
total_matches: orderedEntries.length,
|
|
382
|
+
shard: {
|
|
383
|
+
shard_index: resolvedShardIndex,
|
|
384
|
+
shard_count: resolvedShardCount,
|
|
385
|
+
total_items: shardEntries.length,
|
|
386
|
+
},
|
|
387
|
+
};
|
|
388
|
+
}
|
|
389
|
+
function coerceSearchKnowledgeInput(input) {
|
|
390
|
+
if (input === null || typeof input !== "object" || Array.isArray(input)) {
|
|
391
|
+
throw new Error("search_knowledge input must be an object");
|
|
392
|
+
}
|
|
393
|
+
const value = input;
|
|
394
|
+
return {
|
|
395
|
+
project_reference: typeof value.project_reference === "string"
|
|
396
|
+
? value.project_reference
|
|
397
|
+
: undefined,
|
|
398
|
+
query: typeof value.query === "string" ? value.query : undefined,
|
|
399
|
+
cursor: typeof value.cursor === "string" ? value.cursor : undefined,
|
|
400
|
+
lookup_target: value.lookup_target,
|
|
401
|
+
limit: typeof value.limit === "number" ? value.limit : undefined,
|
|
402
|
+
shard_count: typeof value.shard_count === "number" ? value.shard_count : undefined,
|
|
403
|
+
shard_index: typeof value.shard_index === "number" ? value.shard_index : undefined,
|
|
404
|
+
};
|
|
405
|
+
}
|
|
27
406
|
/** Normalize a knowledge query before retrieval. */
|
|
28
407
|
export function normalizeKnowledgeQuery(query, maxChars = DEFAULT_QUERY_MAX_CHARS) {
|
|
29
408
|
return query.replace(/\s+/g, " ").trim().slice(0, maxChars);
|
|
@@ -34,6 +413,25 @@ export function formatKnowledgeContext(results) {
|
|
|
34
413
|
.map((result) => `[${result.title}] (score: ${result.score.toFixed(2)})\n${result.text}`)
|
|
35
414
|
.join("\n\n---\n\n");
|
|
36
415
|
}
|
|
416
|
+
/**
|
|
417
|
+
* Search the local OKF knowledge manifest with the same input/output shape as
|
|
418
|
+
* Veryfront Cloud's `search_knowledge` MCP tool.
|
|
419
|
+
*/
|
|
420
|
+
export async function searchProjectKnowledge(input, config = {}) {
|
|
421
|
+
const manifest = await getProjectKnowledgeManifest(config);
|
|
422
|
+
return lookupKnowledgeManifest(manifest, input);
|
|
423
|
+
}
|
|
424
|
+
/** Create a local tool with the same id and response shape as hosted `search_knowledge`. */
|
|
425
|
+
export function createSearchKnowledgeTool(options = {}) {
|
|
426
|
+
const { id = "search_knowledge", description, ...config } = options;
|
|
427
|
+
return tool({
|
|
428
|
+
id,
|
|
429
|
+
description: description ??
|
|
430
|
+
"Retrieve a compact, cursor-based slice of the local project knowledge manifest.",
|
|
431
|
+
inputSchema: SEARCH_KNOWLEDGE_INPUT_SCHEMA,
|
|
432
|
+
execute: (input) => searchProjectKnowledge(coerceSearchKnowledgeInput(input), config),
|
|
433
|
+
});
|
|
434
|
+
}
|
|
37
435
|
/** Create a project knowledge helper backed by the configured RAG store. */
|
|
38
436
|
export function projectKnowledge(config = {}) {
|
|
39
437
|
const store = ragStore({
|
|
@@ -58,6 +456,9 @@ export function projectKnowledge(config = {}) {
|
|
|
58
456
|
}
|
|
59
457
|
return {
|
|
60
458
|
index,
|
|
459
|
+
lookup(input) {
|
|
460
|
+
return searchProjectKnowledge(input, config);
|
|
461
|
+
},
|
|
61
462
|
async retrieve(query, options) {
|
|
62
463
|
const normalizedQuery = normalizeKnowledgeQuery(query, options?.maxQueryChars ?? config.maxQueryChars);
|
|
63
464
|
if (!normalizedQuery)
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Platform Compat
|
|
3
|
+
*
|
|
4
|
+
* @module platform/compat
|
|
5
|
+
*/
|
|
6
|
+
export { createFileSystem, exists, type FileSystem, isNotFoundError, makeTempDir, mkdir, readDir, readTextFile, remove, stat, symlink, writeFile, writeTextFile, } from "./fs.js";
|
|
7
|
+
export { createKVStore, MemoryKv, openKv, polyfillDenoKv } from "./kv/index.js";
|
|
8
|
+
export { SqliteKv } from "./kv/index.js";
|
|
9
|
+
export type { Kv, KvEntry, KvListOptions, SqliteDatabase } from "./kv/index.js";
|
|
10
|
+
export { chdir, cwd, deleteEnv, env, execPath, exit, getArgs, getEnv, getEnvBoolean, getEnvNumber, getEnvOverlayStorage, getEnvString, getOsType, getRuntimeVersion, getStdout, getTerminalSize, isInteractive, isStdoutTTY, memoryUsage, onGlobalError, onSignal, pid, promptSync, runCommand, setEnv, unrefTimer, uptime, writeStdout, writeStdoutAsync, } from "./process.js";
|
|
11
|
+
export type { CommandOptions, CommandResult, EnvBooleanOptions } from "./process.js";
|
|
12
|
+
export { isBrowserEnvironment, isBun, isCloudflare, isDeno, isDenoCompiled, isNode, isNodeRuntime, isServerEnvironment, } from "./runtime.js";
|
|
13
|
+
export { createEscapeBuffer, type EscapeBuffer, getStdinReader, setRawMode, type StdinReader, waitForEnterOrExit, waitForKeypress, } from "./stdin.js";
|
|
14
|
+
export { dynamicImport } from "./dynamic-import.js";
|
|
15
|
+
export { importClaudeAgentSDK, importKreuzberg, importTransformers } from "./opaque-deps.js";
|
|
16
|
+
export { basename, dirname, extname, fromFileUrl, isAbsolute, join, relative, resolve, sep, } from "./path/index.js";
|
|
17
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/src/platform/compat/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,EACL,gBAAgB,EAChB,MAAM,EACN,KAAK,UAAU,EACf,eAAe,EACf,WAAW,EACX,KAAK,EACL,OAAO,EACP,YAAY,EACZ,MAAM,EACN,IAAI,EACJ,OAAO,EACP,SAAS,EACT,aAAa,GACd,MAAM,SAAS,CAAC;AAGjB,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAChF,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,YAAY,EAAE,EAAE,EAAE,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAGhF,OAAO,EACL,KAAK,EACL,GAAG,EACH,SAAS,EACT,GAAG,EACH,QAAQ,EACR,IAAI,EACJ,OAAO,EACP,MAAM,EACN,aAAa,EACb,YAAY,EACZ,oBAAoB,EACpB,YAAY,EACZ,SAAS,EACT,iBAAiB,EACjB,SAAS,EACT,eAAe,EACf,aAAa,EACb,WAAW,EACX,WAAW,EACX,aAAa,EACb,QAAQ,EACR,GAAG,EACH,UAAU,EACV,UAAU,EACV,MAAM,EACN,UAAU,EACV,MAAM,EACN,WAAW,EACX,gBAAgB,GACjB,MAAM,cAAc,CAAC;AACtB,YAAY,EAAE,cAAc,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAGrF,OAAO,EACL,oBAAoB,EACpB,KAAK,EACL,YAAY,EACZ,MAAM,EACN,cAAc,EACd,MAAM,EACN,aAAa,EACb,mBAAmB,GACpB,MAAM,cAAc,CAAC;AAGtB,OAAO,EACL,kBAAkB,EAClB,KAAK,YAAY,EACjB,cAAc,EACd,UAAU,EACV,KAAK,WAAW,EAChB,kBAAkB,EAClB,eAAe,GAChB,MAAM,YAAY,CAAC;AAGpB,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAGpD,OAAO,EAAE,oBAAoB,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAG7F,OAAO,EACL,QAAQ,EACR,OAAO,EACP,OAAO,EACP,WAAW,EACX,UAAU,EACV,IAAI,EACJ,QAAQ,EACR,OAAO,EACP,GAAG,GACJ,MAAM,iBAAiB,CAAC"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Platform Compat
|
|
3
|
+
*
|
|
4
|
+
* @module platform/compat
|
|
5
|
+
*/
|
|
6
|
+
// Compat: filesystem
|
|
7
|
+
export { createFileSystem, exists, isNotFoundError, makeTempDir, mkdir, readDir, readTextFile, remove, stat, symlink, writeFile, writeTextFile, } from "./fs.js";
|
|
8
|
+
// Compat: KV store
|
|
9
|
+
export { createKVStore, MemoryKv, openKv, polyfillDenoKv } from "./kv/index.js";
|
|
10
|
+
export { SqliteKv } from "./kv/index.js";
|
|
11
|
+
// Compat: process
|
|
12
|
+
export { chdir, cwd, deleteEnv, env, execPath, exit, getArgs, getEnv, getEnvBoolean, getEnvNumber, getEnvOverlayStorage, getEnvString, getOsType, getRuntimeVersion, getStdout, getTerminalSize, isInteractive, isStdoutTTY, memoryUsage, onGlobalError, onSignal, pid, promptSync, runCommand, setEnv, unrefTimer, uptime, writeStdout, writeStdoutAsync, } from "./process.js";
|
|
13
|
+
// Compat: runtime detection
|
|
14
|
+
export { isBrowserEnvironment, isBun, isCloudflare, isDeno, isDenoCompiled, isNode, isNodeRuntime, isServerEnvironment, } from "./runtime.js";
|
|
15
|
+
// Compat: stdin
|
|
16
|
+
export { createEscapeBuffer, getStdinReader, setRawMode, waitForEnterOrExit, waitForKeypress, } from "./stdin.js";
|
|
17
|
+
// Compat: dynamic import helper (hides specifiers from static analysis / deno compile)
|
|
18
|
+
export { dynamicImport } from "./dynamic-import.js";
|
|
19
|
+
// Compat: dynamic imports for optional deps (opaque) and platform-split deps (kreuzberg)
|
|
20
|
+
export { importClaudeAgentSDK, importKreuzberg, importTransformers } from "./opaque-deps.js";
|
|
21
|
+
// Compat: path
|
|
22
|
+
export { basename, dirname, extname, fromFileUrl, isAbsolute, join, relative, resolve, sep, } from "./path/index.js";
|