@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.
Files changed (60) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/chat-compaction-evidence.d.ts +12 -0
  3. package/dist/chat-compaction-evidence.d.ts.map +1 -0
  4. package/dist/chat-compaction-evidence.js +46 -0
  5. package/dist/chat-handlers.d.ts +16 -0
  6. package/dist/chat-handlers.d.ts.map +1 -1
  7. package/dist/chat-handlers.js +78 -28
  8. package/dist/chat-stream-handlers.d.ts.map +1 -1
  9. package/dist/chat-stream-handlers.js +13 -1
  10. package/dist/conversation-compaction.d.ts +12 -0
  11. package/dist/conversation-compaction.d.ts.map +1 -0
  12. package/dist/conversation-compaction.js +102 -0
  13. package/dist/deps.d.ts +3 -0
  14. package/dist/deps.d.ts.map +1 -1
  15. package/dist/deps.js +3 -2
  16. package/dist/files.d.ts +18 -0
  17. package/dist/files.d.ts.map +1 -1
  18. package/dist/files.js +174 -0
  19. package/dist/gateway-readiness.d.ts +6 -0
  20. package/dist/gateway-readiness.d.ts.map +1 -0
  21. package/dist/gateway-readiness.js +624 -0
  22. package/dist/grounded-context-diagnostics.d.ts +5 -0
  23. package/dist/grounded-context-diagnostics.d.ts.map +1 -0
  24. package/dist/grounded-context-diagnostics.js +77 -0
  25. package/dist/grounded-orchestrator.d.ts +2 -0
  26. package/dist/grounded-orchestrator.d.ts.map +1 -1
  27. package/dist/grounded-orchestrator.js +122 -53
  28. package/dist/grounded-qa-hybrid.d.ts.map +1 -1
  29. package/dist/grounded-qa-hybrid.js +7 -4
  30. package/dist/grounded-qa-multi-source.d.ts.map +1 -1
  31. package/dist/grounded-qa-multi-source.js +49 -2
  32. package/dist/grounded-qa.d.ts +4 -0
  33. package/dist/grounded-qa.d.ts.map +1 -1
  34. package/dist/grounded-qa.js +36 -2
  35. package/dist/index.d.ts +3 -1
  36. package/dist/index.d.ts.map +1 -1
  37. package/dist/index.js +6 -1
  38. package/dist/local-knowledge-grounded-qa.d.ts.map +1 -1
  39. package/dist/local-knowledge-grounded-qa.js +11 -2
  40. package/dist/routes.d.ts.map +1 -1
  41. package/dist/routes.js +5 -10
  42. package/dist/run-handlers.d.ts +0 -1
  43. package/dist/run-handlers.d.ts.map +1 -1
  44. package/dist/run-handlers.js +0 -217
  45. package/dist/store/db.d.ts.map +1 -1
  46. package/dist/store/db.js +2 -1
  47. package/dist/store/index.d.ts +1 -1
  48. package/dist/store/index.d.ts.map +1 -1
  49. package/dist/store/messages.d.ts +2 -1
  50. package/dist/store/messages.d.ts.map +1 -1
  51. package/dist/store/messages.js +46 -4
  52. package/dist/store/schema.d.ts +1 -1
  53. package/dist/store/schema.d.ts.map +1 -1
  54. package/dist/store/schema.js +7 -1
  55. package/dist/store/types.d.ts +3 -2
  56. package/dist/store/types.d.ts.map +1 -1
  57. package/package.json +19 -19
  58. package/dist/grounded-handoff.d.ts +0 -4
  59. package/dist/grounded-handoff.d.ts.map +0 -1
  60. 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"}