@oscharko-dev/keiko-server 0.2.6 → 0.2.8
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/dist/.tsbuildinfo +1 -1
- package/dist/chat-compaction-evidence.d.ts +12 -0
- package/dist/chat-compaction-evidence.d.ts.map +1 -0
- package/dist/chat-compaction-evidence.js +46 -0
- package/dist/chat-handlers.d.ts +16 -0
- package/dist/chat-handlers.d.ts.map +1 -1
- package/dist/chat-handlers.js +78 -28
- package/dist/chat-stream-handlers.d.ts.map +1 -1
- package/dist/chat-stream-handlers.js +13 -1
- package/dist/conversation-compaction.d.ts +12 -0
- package/dist/conversation-compaction.d.ts.map +1 -0
- package/dist/conversation-compaction.js +102 -0
- package/dist/deps.d.ts +3 -0
- package/dist/deps.d.ts.map +1 -1
- package/dist/deps.js +3 -2
- package/dist/files.d.ts +18 -0
- package/dist/files.d.ts.map +1 -1
- package/dist/files.js +174 -0
- package/dist/gateway-readiness.d.ts +6 -0
- package/dist/gateway-readiness.d.ts.map +1 -0
- package/dist/gateway-readiness.js +624 -0
- package/dist/grounded-context-diagnostics.d.ts +5 -0
- package/dist/grounded-context-diagnostics.d.ts.map +1 -0
- package/dist/grounded-context-diagnostics.js +77 -0
- package/dist/grounded-orchestrator.d.ts +2 -0
- package/dist/grounded-orchestrator.d.ts.map +1 -1
- package/dist/grounded-orchestrator.js +122 -53
- package/dist/grounded-qa-hybrid.d.ts.map +1 -1
- package/dist/grounded-qa-hybrid.js +7 -4
- package/dist/grounded-qa-multi-source.d.ts.map +1 -1
- package/dist/grounded-qa-multi-source.js +49 -2
- package/dist/grounded-qa.d.ts +4 -0
- package/dist/grounded-qa.d.ts.map +1 -1
- package/dist/grounded-qa.js +36 -2
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -1
- package/dist/local-knowledge-grounded-qa.d.ts.map +1 -1
- package/dist/local-knowledge-grounded-qa.js +11 -2
- package/dist/routes.d.ts.map +1 -1
- package/dist/routes.js +5 -10
- package/dist/run-handlers.d.ts +0 -1
- package/dist/run-handlers.d.ts.map +1 -1
- package/dist/run-handlers.js +0 -217
- package/dist/store/db.d.ts.map +1 -1
- package/dist/store/db.js +2 -1
- package/dist/store/index.d.ts +1 -1
- package/dist/store/index.d.ts.map +1 -1
- package/dist/store/messages.d.ts +2 -1
- package/dist/store/messages.d.ts.map +1 -1
- package/dist/store/messages.js +46 -4
- package/dist/store/schema.d.ts +1 -1
- package/dist/store/schema.d.ts.map +1 -1
- package/dist/store/schema.js +7 -1
- package/dist/store/types.d.ts +3 -2
- package/dist/store/types.d.ts.map +1 -1
- package/package.json +19 -19
- package/dist/grounded-handoff.d.ts +0 -4
- package/dist/grounded-handoff.d.ts.map +0 -1
- package/dist/grounded-handoff.js +0 -445
package/dist/files.js
CHANGED
|
@@ -9,6 +9,10 @@ import { EDITOR_SESSION_SCHEMA_VERSION, parseEditorDocumentVersion, } from "@osc
|
|
|
9
9
|
import { DENIED_MESSAGE, pathIsDenied } from "./files-deny.js";
|
|
10
10
|
import { errorBody } from "./routes.js";
|
|
11
11
|
const MAX_DIRECTORY_ENTRIES = 1_000;
|
|
12
|
+
const DEFAULT_FILE_SEARCH_LIMIT = 24;
|
|
13
|
+
const MAX_FILE_SEARCH_LIMIT = 50;
|
|
14
|
+
const MAX_FILE_SEARCH_QUERY_CHARS = 120;
|
|
15
|
+
const MAX_FILE_SEARCH_SCAN = 20_000;
|
|
12
16
|
const MAX_TEXT_PREVIEW_BYTES = 1_000_000;
|
|
13
17
|
const MAX_IMAGE_PREVIEW_BYTES = 3_000_000;
|
|
14
18
|
const STABLE_CONTENT_READ_ATTEMPTS = 3;
|
|
@@ -352,6 +356,170 @@ export async function readFilesTree(store, rootInput, pathInput, redactor = stat
|
|
|
352
356
|
truncated: listed.truncated,
|
|
353
357
|
};
|
|
354
358
|
}
|
|
359
|
+
function parseSearchLimit(rawLimit) {
|
|
360
|
+
if (rawLimit === null || rawLimit.trim().length === 0)
|
|
361
|
+
return DEFAULT_FILE_SEARCH_LIMIT;
|
|
362
|
+
const parsed = Number(rawLimit);
|
|
363
|
+
if (!Number.isInteger(parsed) || parsed < 1) {
|
|
364
|
+
throw new FilesError(400, "BAD_LIMIT", "The search limit must be a positive integer.");
|
|
365
|
+
}
|
|
366
|
+
return Math.min(parsed, MAX_FILE_SEARCH_LIMIT);
|
|
367
|
+
}
|
|
368
|
+
function normalizeSearchQuery(queryInput) {
|
|
369
|
+
const query = (queryInput ?? "").trim().replace(/\s+/gu, " ");
|
|
370
|
+
if (query.includes("\0")) {
|
|
371
|
+
throw new FilesError(400, "BAD_QUERY", "The search query contains an invalid character.");
|
|
372
|
+
}
|
|
373
|
+
if (query.length > MAX_FILE_SEARCH_QUERY_CHARS) {
|
|
374
|
+
throw new FilesError(400, "BAD_QUERY", `The search query must be at most ${String(MAX_FILE_SEARCH_QUERY_CHARS)} characters.`);
|
|
375
|
+
}
|
|
376
|
+
return query;
|
|
377
|
+
}
|
|
378
|
+
function searchTokens(query) {
|
|
379
|
+
return query
|
|
380
|
+
.toLocaleLowerCase()
|
|
381
|
+
.split(/\s+/u)
|
|
382
|
+
.map((token) => token.trim())
|
|
383
|
+
.filter((token) => token.length > 0);
|
|
384
|
+
}
|
|
385
|
+
function matchesSearch(relativePath, tokens) {
|
|
386
|
+
const lowerPath = relativePath.toLocaleLowerCase();
|
|
387
|
+
return tokens.every((token) => lowerPath.includes(token));
|
|
388
|
+
}
|
|
389
|
+
function fileSearchScore(relativePath, query) {
|
|
390
|
+
const lowerPath = relativePath.toLocaleLowerCase();
|
|
391
|
+
const lowerName = basename(relativePath).toLocaleLowerCase();
|
|
392
|
+
const lowerQuery = query.toLocaleLowerCase();
|
|
393
|
+
if (lowerName === lowerQuery)
|
|
394
|
+
return 0;
|
|
395
|
+
if (lowerName.startsWith(lowerQuery))
|
|
396
|
+
return 100 + relativePath.length;
|
|
397
|
+
if (lowerPath === lowerQuery)
|
|
398
|
+
return 200 + relativePath.length;
|
|
399
|
+
if (lowerPath.startsWith(lowerQuery))
|
|
400
|
+
return 300 + relativePath.length;
|
|
401
|
+
const nameIndex = lowerName.indexOf(lowerQuery);
|
|
402
|
+
if (nameIndex >= 0)
|
|
403
|
+
return 400 + nameIndex + relativePath.length;
|
|
404
|
+
const pathIndex = lowerPath.indexOf(lowerQuery);
|
|
405
|
+
if (pathIndex >= 0)
|
|
406
|
+
return 600 + pathIndex + relativePath.length;
|
|
407
|
+
return 1_000 + relativePath.length;
|
|
408
|
+
}
|
|
409
|
+
function directoryOf(relativePath) {
|
|
410
|
+
const dir = pathPosix.dirname(relativePath);
|
|
411
|
+
return dir === "." ? "" : dir;
|
|
412
|
+
}
|
|
413
|
+
function entryVisibleToFileSearch(relativePath, entry, redactor) {
|
|
414
|
+
return (metadataIsSafe(relativePath, redactor) &&
|
|
415
|
+
!pathIsDenied(relativePath) &&
|
|
416
|
+
!entry.isSymbolicLink());
|
|
417
|
+
}
|
|
418
|
+
async function addFileSearchCandidate(args) {
|
|
419
|
+
if (!matchesSearch(args.relativePath, args.tokens))
|
|
420
|
+
return;
|
|
421
|
+
let info;
|
|
422
|
+
try {
|
|
423
|
+
info = await lstat(args.nativePath);
|
|
424
|
+
}
|
|
425
|
+
catch {
|
|
426
|
+
return;
|
|
427
|
+
}
|
|
428
|
+
args.state.candidates.push({
|
|
429
|
+
score: fileSearchScore(args.relativePath, args.query),
|
|
430
|
+
result: {
|
|
431
|
+
root: args.root.root,
|
|
432
|
+
path: args.relativePath,
|
|
433
|
+
name: args.entryName,
|
|
434
|
+
directory: directoryOf(args.relativePath),
|
|
435
|
+
extension: extensionOf(args.entryName),
|
|
436
|
+
sizeBytes: info.size,
|
|
437
|
+
modifiedAt: info.mtimeMs,
|
|
438
|
+
},
|
|
439
|
+
});
|
|
440
|
+
}
|
|
441
|
+
async function collectFileSearchEntry(args) {
|
|
442
|
+
const relativePath = childRelative(args.current.relativePath, args.entry.name);
|
|
443
|
+
if (!entryVisibleToFileSearch(relativePath, args.entry, args.redactor))
|
|
444
|
+
return;
|
|
445
|
+
const nativePath = join(args.current.path, args.entry.name);
|
|
446
|
+
if (args.entry.isDirectory()) {
|
|
447
|
+
args.state.stack.push({ path: nativePath, relativePath });
|
|
448
|
+
return;
|
|
449
|
+
}
|
|
450
|
+
if (!args.entry.isFile())
|
|
451
|
+
return;
|
|
452
|
+
args.state.scannedFileCount += 1;
|
|
453
|
+
if (args.state.scannedFileCount > MAX_FILE_SEARCH_SCAN) {
|
|
454
|
+
args.state.scanTruncated = true;
|
|
455
|
+
return;
|
|
456
|
+
}
|
|
457
|
+
await addFileSearchCandidate({
|
|
458
|
+
root: args.root,
|
|
459
|
+
query: args.query,
|
|
460
|
+
relativePath,
|
|
461
|
+
nativePath,
|
|
462
|
+
entryName: args.entry.name,
|
|
463
|
+
tokens: args.tokens,
|
|
464
|
+
state: args.state,
|
|
465
|
+
});
|
|
466
|
+
}
|
|
467
|
+
async function collectFileSearchDirectory(args) {
|
|
468
|
+
let dir;
|
|
469
|
+
try {
|
|
470
|
+
dir = await opendir(args.current.path);
|
|
471
|
+
}
|
|
472
|
+
catch {
|
|
473
|
+
return;
|
|
474
|
+
}
|
|
475
|
+
try {
|
|
476
|
+
for await (const entry of dir) {
|
|
477
|
+
await collectFileSearchEntry({ ...args, entry });
|
|
478
|
+
if (args.state.scanTruncated)
|
|
479
|
+
break;
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
finally {
|
|
483
|
+
await dir.close().catch(() => undefined);
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
async function collectFileSearchResults(args) {
|
|
487
|
+
const tokens = searchTokens(args.query);
|
|
488
|
+
if (tokens.length === 0) {
|
|
489
|
+
return { results: [], truncated: false, scannedFileCount: 0 };
|
|
490
|
+
}
|
|
491
|
+
const state = {
|
|
492
|
+
candidates: [],
|
|
493
|
+
stack: [{ path: args.root.realRoot, relativePath: "" }],
|
|
494
|
+
scannedFileCount: 0,
|
|
495
|
+
scanTruncated: false,
|
|
496
|
+
};
|
|
497
|
+
while (state.stack.length > 0) {
|
|
498
|
+
const current = state.stack.pop();
|
|
499
|
+
if (current === undefined)
|
|
500
|
+
break;
|
|
501
|
+
await collectFileSearchDirectory({ ...args, current, tokens, state });
|
|
502
|
+
if (state.scanTruncated)
|
|
503
|
+
break;
|
|
504
|
+
}
|
|
505
|
+
state.candidates.sort((a, b) => a.score - b.score || a.result.path.localeCompare(b.result.path));
|
|
506
|
+
return {
|
|
507
|
+
results: state.candidates.slice(0, args.limit).map((candidate) => candidate.result),
|
|
508
|
+
truncated: state.scanTruncated || state.candidates.length > args.limit,
|
|
509
|
+
scannedFileCount: Math.min(state.scannedFileCount, MAX_FILE_SEARCH_SCAN),
|
|
510
|
+
};
|
|
511
|
+
}
|
|
512
|
+
export async function searchFiles(store, rootInput, queryInput, limitInput, redactor = staticFilesMetadataRedactor) {
|
|
513
|
+
const root = await resolveRoot(store, rootInput, redactor);
|
|
514
|
+
const query = normalizeSearchQuery(queryInput);
|
|
515
|
+
const limit = Math.min(Math.max(limitInput ?? DEFAULT_FILE_SEARCH_LIMIT, 1), MAX_FILE_SEARCH_LIMIT);
|
|
516
|
+
const collected = await collectFileSearchResults({ root, query, limit, redactor });
|
|
517
|
+
return {
|
|
518
|
+
root: root.root,
|
|
519
|
+
query,
|
|
520
|
+
...collected,
|
|
521
|
+
};
|
|
522
|
+
}
|
|
355
523
|
const IMAGE_MIME = {
|
|
356
524
|
png: "image/png",
|
|
357
525
|
jpg: "image/jpeg",
|
|
@@ -677,6 +845,12 @@ export async function handleFilesTree(ctx, deps) {
|
|
|
677
845
|
body: await readFilesTree(deps.store, ctx.url.searchParams.get("root"), ctx.url.searchParams.get("path"), deps.redactor),
|
|
678
846
|
}));
|
|
679
847
|
}
|
|
848
|
+
export async function handleFilesSearch(ctx, deps) {
|
|
849
|
+
return runFilesHandler(async () => ({
|
|
850
|
+
status: 200,
|
|
851
|
+
body: await searchFiles(deps.store, ctx.url.searchParams.get("root"), ctx.url.searchParams.get("q") ?? ctx.url.searchParams.get("query"), parseSearchLimit(ctx.url.searchParams.get("limit")), deps.redactor),
|
|
852
|
+
}));
|
|
853
|
+
}
|
|
680
854
|
export async function handleFilesPreview(ctx, deps) {
|
|
681
855
|
return runFilesHandler(async () => ({
|
|
682
856
|
status: 200,
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { GatewayReadinessReport, GatewayReadinessRequest } from "@oscharko-dev/keiko-contracts/bff-wire";
|
|
2
|
+
import type { UiHandlerDeps } from "./deps.js";
|
|
3
|
+
import type { RouteContext, RouteResult } from "./routes.js";
|
|
4
|
+
export declare function runGatewayReadiness(request: GatewayReadinessRequest, deps: UiHandlerDeps): Promise<GatewayReadinessReport | RouteResult>;
|
|
5
|
+
export declare function handleGatewayReadiness(ctx: RouteContext, deps: UiHandlerDeps): Promise<RouteResult>;
|
|
6
|
+
//# sourceMappingURL=gateway-readiness.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gateway-readiness.d.ts","sourceRoot":"","sources":["../src/gateway-readiness.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAIV,sBAAsB,EACtB,uBAAuB,EACxB,MAAM,wCAAwC,CAAC;AAChD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAE/C,OAAO,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AA40B7D,wBAAsB,mBAAmB,CACvC,OAAO,EAAE,uBAAuB,EAChC,IAAI,EAAE,aAAa,GAClB,OAAO,CAAC,sBAAsB,GAAG,WAAW,CAAC,CAuB/C;AAED,wBAAsB,sBAAsB,CAC1C,GAAG,EAAE,YAAY,EACjB,IAAI,EAAE,aAAa,GAClB,OAAO,CAAC,WAAW,CAAC,CAMtB"}
|