@autorag/librarian 2.13.9 → 2.13.11

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/mcp/index.js CHANGED
@@ -3669,20 +3669,30 @@ var init_bundle = __esm(() => {
3669
3669
  });
3670
3670
 
3671
3671
  // src/filesystem/path-exclusions.ts
3672
- import { posix as posix2, win32 as win322 } from "node:path";
3672
+ import { matchesGlob, posix as posix2, win32 as win322 } from "node:path";
3673
+ function isGlobExclusion(entry) {
3674
+ return /[*?[]/u.test(entry);
3675
+ }
3676
+ function matchesGlobExclusion(path, glob) {
3677
+ const slashed = path.replaceAll("\\", "/");
3678
+ if (matchesGlob(slashed, glob))
3679
+ return true;
3680
+ return glob.startsWith("**/") && matchesGlob(slashed, glob.slice(3));
3681
+ }
3673
3682
  function createPathExclusionMatcher(excludedPaths, platform = process.platform) {
3674
3683
  const paths = platform === "win32" ? win322 : posix2;
3675
3684
  const normalize = (path) => {
3676
3685
  const absolute = paths.resolve(path);
3677
3686
  return platform === "win32" ? absolute.toLowerCase() : absolute;
3678
3687
  };
3679
- const excluded = excludedPaths.map((path) => {
3688
+ const globs = excludedPaths.filter(isGlobExclusion).map((glob) => platform === "win32" ? glob.toLowerCase() : glob);
3689
+ const excluded = excludedPaths.filter((path) => !isGlobExclusion(path)).map((path) => {
3680
3690
  const absolute = normalize(path);
3681
3691
  return { absolute, prefix: absolute.endsWith(paths.sep) ? absolute : `${absolute}${paths.sep}` };
3682
3692
  });
3683
3693
  return (path) => {
3684
3694
  const absolute = normalize(path);
3685
- return excluded.some((entry) => absolute === entry.absolute || absolute.startsWith(entry.prefix));
3695
+ return excluded.some((entry) => absolute === entry.absolute || absolute.startsWith(entry.prefix)) || globs.some((glob) => matchesGlobExclusion(absolute, glob));
3686
3696
  };
3687
3697
  }
3688
3698
  var init_path_exclusions = () => {};
@@ -10334,7 +10344,7 @@ var init_cloud = __esm(() => {
10334
10344
  // src/mirror/sync.ts
10335
10345
  import { createHash as createHash11 } from "node:crypto";
10336
10346
  import { opendir, readFile, stat as stat3 } from "node:fs/promises";
10337
- import { basename as basename9, dirname as dirname12, extname as extname6, join as join27, resolve as resolve11, sep as sep6 } from "node:path";
10347
+ import { basename as basename9, dirname as dirname12, extname as extname6, join as join27, resolve as resolve11 } from "node:path";
10338
10348
  import { setTimeout as sleep3 } from "node:timers/promises";
10339
10349
  function errorCode(error) {
10340
10350
  if (!(error instanceof Error))
@@ -10881,13 +10891,14 @@ function normalizeExtension2(extension) {
10881
10891
  return lower.startsWith(".") ? lower : `.${lower}`;
10882
10892
  }
10883
10893
  function isPathExcluded(sourcePath, excluded) {
10884
- if (excluded === undefined)
10894
+ if (excluded === undefined || excluded.size === 0)
10885
10895
  return false;
10886
- for (const excludedPath of excluded) {
10887
- if (sourcePath === excludedPath || sourcePath.startsWith(`${excludedPath}${sep6}`))
10888
- return true;
10896
+ let matcher = matcherBySet.get(excluded);
10897
+ if (matcher === undefined) {
10898
+ matcher = createPathExclusionMatcher([...excluded]);
10899
+ matcherBySet.set(excluded, matcher);
10889
10900
  }
10890
- return false;
10901
+ return matcher(sourcePath);
10891
10902
  }
10892
10903
  function walkLimits(options) {
10893
10904
  return {
@@ -11054,10 +11065,11 @@ async function collectFiles(state, directory, depth) {
11054
11065
  warnDirectoryUnreadable(state, directory, code);
11055
11066
  }
11056
11067
  }
11057
- var DEFAULT_MAX_SOURCE_BYTES, DEFAULT_MAX_DELETE_RATIO = 0.3, MASS_DELETE_FLOOR = 100, DEFAULT_MAX_SCANNED_ENTRIES = 2000000, DEFAULT_MAX_WALK_DEPTH = 64, DEFAULT_MIRROR_BATCH_SIZE = 25, PRODUCT_ARTIFACT_FILE_NAMES, SKIP_DIR_NAMES, PARSER_FAILURE_MESSAGE = "The registered parser failed on this file; it was skipped during indexing.", PARSER_CRASH_MESSAGE = "The registered parser crashed unexpectedly on this file; it was skipped during indexing.", DEFAULT_TRANSIENT_RETRIES = 3, DEFAULT_TRANSIENT_BACKOFF_MS = 50, TRANSIENT_ERROR_CODES, WORKSPACE_ERROR_CODES, MirrorWorkspaceError, STANDALONE_OCR_IMAGE_EXTENSIONS, SEARCH_ROOT_UNAVAILABLE_MESSAGES;
11068
+ var DEFAULT_MAX_SOURCE_BYTES, DEFAULT_MAX_DELETE_RATIO = 0.3, MASS_DELETE_FLOOR = 100, DEFAULT_MAX_SCANNED_ENTRIES = 2000000, DEFAULT_MAX_WALK_DEPTH = 64, DEFAULT_MIRROR_BATCH_SIZE = 25, PRODUCT_ARTIFACT_FILE_NAMES, SKIP_DIR_NAMES, PARSER_FAILURE_MESSAGE = "The registered parser failed on this file; it was skipped during indexing.", PARSER_CRASH_MESSAGE = "The registered parser crashed unexpectedly on this file; it was skipped during indexing.", DEFAULT_TRANSIENT_RETRIES = 3, DEFAULT_TRANSIENT_BACKOFF_MS = 50, TRANSIENT_ERROR_CODES, WORKSPACE_ERROR_CODES, MirrorWorkspaceError, STANDALONE_OCR_IMAGE_EXTENSIONS, matcherBySet, SEARCH_ROOT_UNAVAILABLE_MESSAGES;
11058
11069
  var init_sync = __esm(() => {
11059
11070
  init_cli2();
11060
11071
  init_cloud_placeholder();
11072
+ init_path_exclusions();
11061
11073
  init_source_paths();
11062
11074
  init_binary();
11063
11075
  init_stage();
@@ -11116,6 +11128,7 @@ var init_sync = __esm(() => {
11116
11128
  }
11117
11129
  };
11118
11130
  STANDALONE_OCR_IMAGE_EXTENSIONS = new Set(KORDOC_IMAGE_EXTENSIONS);
11131
+ matcherBySet = new WeakMap;
11119
11132
  SEARCH_ROOT_UNAVAILABLE_MESSAGES = {
11120
11133
  gone: "A search root no longer exists; its previously indexed documents were left untouched.",
11121
11134
  "not-a-directory": "A search root is no longer a directory; its previously indexed documents were left untouched.",
@@ -16190,7 +16203,7 @@ var init_automation_profiles = __esm(() => {
16190
16203
 
16191
16204
  // src/automation/config.ts
16192
16205
  import { existsSync as existsSync19, readFileSync as readFileSync14, realpathSync as realpathSync7 } from "node:fs";
16193
- import { basename as basename10, dirname as dirname15, isAbsolute as isAbsolute8, join as join30, matchesGlob, relative as relative5, resolve as resolve12 } from "node:path";
16206
+ import { basename as basename10, dirname as dirname15, isAbsolute as isAbsolute8, join as join30, matchesGlob as matchesGlob2, relative as relative5, resolve as resolve12 } from "node:path";
16194
16207
  function automationsConfigPath(env = process.env) {
16195
16208
  return join30(resolveAutoRAGHome(env), "automations.json");
16196
16209
  }
@@ -16366,9 +16379,9 @@ function deleteAutomation(configPath, id) {
16366
16379
  function globMatches(relPosix, pattern) {
16367
16380
  if (pattern === "**")
16368
16381
  return true;
16369
- if (matchesGlob(relPosix, pattern))
16382
+ if (matchesGlob2(relPosix, pattern))
16370
16383
  return true;
16371
- if (pattern.startsWith("**/") && matchesGlob(relPosix, pattern.slice(3)))
16384
+ if (pattern.startsWith("**/") && matchesGlob2(relPosix, pattern.slice(3)))
16372
16385
  return true;
16373
16386
  return false;
16374
16387
  }
@@ -17165,7 +17178,7 @@ var init_permission_judgment = __esm(() => {
17165
17178
 
17166
17179
  // src/agent/permission-gate.ts
17167
17180
  import { statSync as statSync7 } from "node:fs";
17168
- import { isAbsolute as isAbsolute9, relative as relative6, sep as sep7 } from "node:path";
17181
+ import { isAbsolute as isAbsolute9, relative as relative6, sep as sep6 } from "node:path";
17169
17182
  import { matchesKey } from "@earendil-works/pi-tui";
17170
17183
  function pathExists(path) {
17171
17184
  try {
@@ -17228,7 +17241,7 @@ class PermissionController {
17228
17241
  if (path === root)
17229
17242
  return true;
17230
17243
  const rel = relative6(root, path);
17231
- if (rel !== "" && rel !== ".." && !rel.startsWith(`..${sep7}`) && !isAbsolute9(rel))
17244
+ if (rel !== "" && rel !== ".." && !rel.startsWith(`..${sep6}`) && !isAbsolute9(rel))
17232
17245
  return true;
17233
17246
  }
17234
17247
  return false;
@@ -18680,7 +18693,7 @@ var init_connector = () => {};
18680
18693
  // src/datasource/sidecar/config.ts
18681
18694
  import { readFileSync as readFileSync17 } from "node:fs";
18682
18695
  import { homedir as homedir5 } from "node:os";
18683
- import { dirname as dirname17, isAbsolute as isAbsolute10, join as join34, resolve as resolve14, sep as sep8 } from "node:path";
18696
+ import { dirname as dirname17, isAbsolute as isAbsolute10, join as join34, resolve as resolve14, sep as sep7 } from "node:path";
18684
18697
  import { parse as parseToml } from "smol-toml";
18685
18698
  function resolveSidecarOptions(input) {
18686
18699
  const { connector, workspaceRoot } = input;
@@ -18720,7 +18733,7 @@ function archivePathFromCrawlerConfig(input) {
18720
18733
  function expandHome(path) {
18721
18734
  if (path === "~")
18722
18735
  return homedir5();
18723
- const prefixed = path.startsWith("~/") || sep8 === "\\" && path.startsWith("~\\");
18736
+ const prefixed = path.startsWith("~/") || sep7 === "\\" && path.startsWith("~\\");
18724
18737
  return prefixed ? join34(homedir5(), path.slice(2)) : path;
18725
18738
  }
18726
18739
  var init_config3 = __esm(() => {
@@ -25974,7 +25987,7 @@ import {
25974
25987
  writeFileSync as writeFileSync13
25975
25988
  } from "node:fs";
25976
25989
  import { opendir as opendir2, stat as stat4 } from "node:fs/promises";
25977
- import { basename as basename11, dirname as dirname23, extname as extname8, join as join40, relative as relative7, sep as sep9 } from "node:path";
25990
+ import { basename as basename11, dirname as dirname23, extname as extname8, join as join40, relative as relative7, sep as sep8 } from "node:path";
25978
25991
  function errnoCode2(error) {
25979
25992
  const code = error instanceof Error ? error.code : undefined;
25980
25993
  return typeof code === "string" ? code : "UNKNOWN";
@@ -26044,7 +26057,7 @@ async function listVaultNotes(vaultPath) {
26044
26057
  return { notes, unreadableFolders, collisions };
26045
26058
  }
26046
26059
  function toRelative(vaultPath, path) {
26047
- return relative7(vaultPath, path).split(sep9).join("/").normalize("NFC");
26060
+ return relative7(vaultPath, path).split(sep8).join("/").normalize("NFC");
26048
26061
  }
26049
26062
  function loadStagingState2(statePath) {
26050
26063
  try {
@@ -26128,7 +26141,7 @@ function removeUnexpected(filesRoot, directory, wanted) {
26128
26141
  return removed;
26129
26142
  }
26130
26143
  function toStagedRelative(filesRoot, path) {
26131
- return relative7(filesRoot, path).split(sep9).join("/").normalize("NFC");
26144
+ return relative7(filesRoot, path).split(sep8).join("/").normalize("NFC");
26132
26145
  }
26133
26146
  var NOTE_EXTENSIONS, SKIPPED_DIRECTORIES, MAX_WALK_DEPTH = 64, VaultUnavailableError;
26134
26147
  var init_staging = __esm(() => {
@@ -28049,7 +28062,7 @@ function resolveExcludePaths(value, origin) {
28049
28062
  if (!Array.isArray(value) || value.some((path) => typeof path !== "string" || path.length === 0)) {
28050
28063
  throw new ConfigError("Config field 'excludePaths' must be a non-empty string array");
28051
28064
  }
28052
- return resolveSearchPaths(value, origin);
28065
+ return value.map((entry) => isGlobExclusion(entry) ? entry : resolvePersistedPath(entry, origin));
28053
28066
  }
28054
28067
  function resolvePersistedPath(path, origin) {
28055
28068
  return isAbsolute11(path) ? path : resolve15(origin, path);
@@ -29354,6 +29367,7 @@ var init_config5 = __esm(() => {
29354
29367
  init_factory();
29355
29368
  init_embedding();
29356
29369
  init_file_lock();
29370
+ init_path_exclusions();
29357
29371
  init_language();
29358
29372
  init_atomic_write();
29359
29373
  init_rerank();
@@ -38607,7 +38621,7 @@ var init_web_search_tool = __esm(() => {
38607
38621
  // src/agent/agent.ts
38608
38622
  import { randomUUID as randomUUID7 } from "node:crypto";
38609
38623
  import { existsSync as existsSync34, watch as fsWatch, mkdirSync as mkdirSync24, realpathSync as realpathSync8, statSync as statSync11, writeFileSync as writeFileSync17 } from "node:fs";
38610
- import { basename as basename15, dirname as dirname27, join as join55, relative as relative8, resolve as resolve18, sep as sep10 } from "node:path";
38624
+ import { basename as basename15, dirname as dirname27, join as join55, relative as relative8, resolve as resolve18, sep as sep9 } from "node:path";
38611
38625
  import { clampThinkingLevel } from "@earendil-works/pi-ai/compat";
38612
38626
  import { getAgentDir as getAgentDir6, keyText } from "@earendil-works/pi-coding-agent";
38613
38627
  function describeDecisionModel(status) {
@@ -41280,7 +41294,7 @@ ${this.discoveryHint((tools) => `When exploring local files and folders, activel
41280
41294
  return { ok: false, reason: "cloud-root-skipped", stdout: "", stderr: "", code: null };
41281
41295
  }
41282
41296
  const canonicalSourcePath = canonicalPath(sourcePath);
41283
- const exclude = scope.nestedCloudRoots.map((root) => relative8(canonicalSourcePath, root.path).split(sep10).join("/")).filter((pattern) => pattern !== "" && !pattern.startsWith(".."));
41297
+ const exclude = scope.nestedCloudRoots.map((root) => relative8(canonicalSourcePath, root.path).split(sep9).join("/")).filter((pattern) => pattern !== "" && !pattern.startsWith(".."));
41284
41298
  return client.prepare(sourcePath, { ...options, ...exclude.length === 0 ? {} : { exclude } });
41285
41299
  }));
41286
41300
  }
@@ -41794,6 +41808,8 @@ function toMinSyncReasonDiagnostic(reason) {
41794
41808
  };
41795
41809
  }
41796
41810
  function pinExcludedPath(path) {
41811
+ if (isGlobExclusion(path))
41812
+ return path;
41797
41813
  const resolvedPath = resolve18(path);
41798
41814
  try {
41799
41815
  return join55(realpathSync8(dirname27(resolvedPath)), basename15(resolvedPath));
@@ -41842,6 +41858,7 @@ var init_agent = __esm(() => {
41842
41858
  init_embedding();
41843
41859
  init_everything();
41844
41860
  init_cloud_placeholder();
41861
+ init_path_exclusions();
41845
41862
  init_fsearch();
41846
41863
  init_diagnostics();
41847
41864
  init_jikji();
@@ -42002,6 +42019,12 @@ class AutoRAGLite {
42002
42019
  getMemorySchema() {
42003
42020
  return structuredClone(this.memory.getSchema());
42004
42021
  }
42022
+ convertFiles(request, signal) {
42023
+ return this.agent.convertFiles(request, signal);
42024
+ }
42025
+ listFileConversions(from, signal) {
42026
+ return this.agent.listFileConversions(from, signal);
42027
+ }
42005
42028
  }
42006
42029
  function createAutoRAGLite(options = {}) {
42007
42030
  const input = { flags: options.flags ?? {} };
@@ -42693,6 +42716,48 @@ import { realpathSync as realpathSync10 } from "node:fs";
42693
42716
  import { fileURLToPath as fileURLToPath6 } from "node:url";
42694
42717
  import { StdioServerTransport } from "@modelcontextprotocol/server/stdio";
42695
42718
 
42719
+ // src/mcp/reloading-lite.ts
42720
+ init_config5();
42721
+ import { statSync as statSync12 } from "node:fs";
42722
+ function createReloadingLite(options) {
42723
+ let attempted = options.signature();
42724
+ let current = options.create();
42725
+ const sync = () => {
42726
+ const next = options.signature();
42727
+ if (next === attempted)
42728
+ return current;
42729
+ attempted = next;
42730
+ try {
42731
+ current = options.create();
42732
+ } catch (error) {
42733
+ options.onError?.(error instanceof Error ? error : new Error(String(error)));
42734
+ }
42735
+ return current;
42736
+ };
42737
+ return new Proxy({}, {
42738
+ get(_target, property) {
42739
+ const runtime = sync();
42740
+ const value = Reflect.get(runtime, property, runtime);
42741
+ return typeof value === "function" ? value.bind(runtime) : value;
42742
+ },
42743
+ has(_target, property) {
42744
+ return property in sync();
42745
+ }
42746
+ });
42747
+ }
42748
+ function configSignature(env = process.env) {
42749
+ const { configPath, legacyPath } = resolveConfigPath({ flags: {}, env });
42750
+ return [configPath, legacyPath].map((path) => path === undefined ? "-" : fingerprint(path)).join("|");
42751
+ }
42752
+ function fingerprint(path) {
42753
+ try {
42754
+ const stats = statSync12(path);
42755
+ return `${stats.ino}:${stats.size}:${stats.mtimeMs}`;
42756
+ } catch {
42757
+ return "absent";
42758
+ }
42759
+ }
42760
+
42696
42761
  // src/mcp/server.ts
42697
42762
  init_evidence();
42698
42763
  init_report();
@@ -42706,7 +42771,7 @@ import { z as z4 } from "zod";
42706
42771
  // src/filesystem/name-search.ts
42707
42772
  import { realpathSync as realpathSync9 } from "node:fs";
42708
42773
  import { lstat as lstat2, readdir as readdir5 } from "node:fs/promises";
42709
- import { basename as basename16, dirname as dirname28, isAbsolute as isAbsolute14, join as join56, relative as relative9, resolve as resolve19, sep as sep11 } from "node:path";
42774
+ import { basename as basename16, dirname as dirname28, isAbsolute as isAbsolute14, join as join56, relative as relative9, resolve as resolve19, sep as sep10 } from "node:path";
42710
42775
  var SKIP_DIR_NAMES2 = {
42711
42776
  ".git": true,
42712
42777
  ".autorag": true,
@@ -42734,7 +42799,7 @@ function fsErrorCode(error) {
42734
42799
  return typeof code === "string" ? code : undefined;
42735
42800
  }
42736
42801
  function isContained(childReal, parentReal) {
42737
- const prefix = parentReal.endsWith(sep11) ? parentReal : `${parentReal}${sep11}`;
42802
+ const prefix = parentReal.endsWith(sep10) ? parentReal : `${parentReal}${sep10}`;
42738
42803
  return childReal === parentReal || childReal.startsWith(prefix);
42739
42804
  }
42740
42805
  function pinExcludedPath2(path) {
@@ -42747,14 +42812,14 @@ function pinExcludedPath2(path) {
42747
42812
  }
42748
42813
  function isExcluded2(absPath, excluded) {
42749
42814
  for (const excludedPath of excluded) {
42750
- const prefix = excludedPath.endsWith(sep11) ? excludedPath : `${excludedPath}${sep11}`;
42815
+ const prefix = excludedPath.endsWith(sep10) ? excludedPath : `${excludedPath}${sep10}`;
42751
42816
  if (absPath === excludedPath || absPath.startsWith(prefix))
42752
42817
  return true;
42753
42818
  }
42754
42819
  return false;
42755
42820
  }
42756
42821
  function matchesEntry(context, absPath, name) {
42757
- const haystackRaw = context.matchPath ? relative9(context.rootReal, absPath).split(sep11).join("/") : name;
42822
+ const haystackRaw = context.matchPath ? relative9(context.rootReal, absPath).split(sep10).join("/") : name;
42758
42823
  const haystack = context.matchCase ? haystackRaw : haystackRaw.toLowerCase();
42759
42824
  const needle = context.matchCase ? context.query : context.query.toLowerCase();
42760
42825
  return haystack.includes(needle);
@@ -42895,7 +42960,7 @@ async function filterFileNameSearchMatches(roots, candidates, excludePaths = [])
42895
42960
  continue;
42896
42961
  if (isExcluded2(real, excluded))
42897
42962
  continue;
42898
- const segments = real.split(sep11);
42963
+ const segments = real.split(sep10);
42899
42964
  if (segments.some((segment) => SKIP_DIR_NAMES2[segment] === true))
42900
42965
  continue;
42901
42966
  matches.push({ path: real, type: stat.isDirectory() ? "folder" : "file" });
@@ -43019,6 +43084,16 @@ var evidenceInput = z4.strictObject({
43019
43084
  sessionId: z4.string().trim().min(1),
43020
43085
  resultNumber: z4.number().int().positive().optional()
43021
43086
  });
43087
+ var convertInput = z4.strictObject({
43088
+ inputs: z4.array(z4.string().trim().min(1)).min(1).max(500),
43089
+ to: z4.string().trim().min(1),
43090
+ outputDir: z4.string().trim().min(1).optional(),
43091
+ recursive: z4.boolean().optional(),
43092
+ quality: z4.number().int().min(1).max(100).optional(),
43093
+ keepDates: z4.boolean().optional()
43094
+ });
43095
+ var convertFormatsInput = z4.strictObject({ from: z4.string().trim().min(1).optional() });
43096
+ var MAX_CONVERT_FILES = 200;
43022
43097
  function exactGroupsFromScan(scan) {
43023
43098
  const groups = new Map;
43024
43099
  for (const file of scan.files) {
@@ -43040,6 +43115,57 @@ function jsonResult(value, isError = false) {
43040
43115
  function toolError(code, message, extra = {}) {
43041
43116
  return jsonResult({ ok: false, errorCode: code, message, ...extra }, true);
43042
43117
  }
43118
+ var DIAGNOSTIC_EXAMPLES_PER_CODE = 3;
43119
+ function capDiagnostics(diagnostics) {
43120
+ const byCode = new Map;
43121
+ for (const entry of diagnostics) {
43122
+ const list = byCode.get(entry.code);
43123
+ if (list === undefined)
43124
+ byCode.set(entry.code, [entry]);
43125
+ else
43126
+ list.push(entry);
43127
+ }
43128
+ const out = [];
43129
+ for (const [code, entries] of byCode) {
43130
+ out.push(...entries.slice(0, DIAGNOSTIC_EXAMPLES_PER_CODE));
43131
+ const rest = entries.slice(DIAGNOSTIC_EXAMPLES_PER_CODE);
43132
+ if (rest.length === 0)
43133
+ continue;
43134
+ const reasons = new Map;
43135
+ for (const entry of rest) {
43136
+ const key = entry.reason ?? "unspecified";
43137
+ reasons.set(key, (reasons.get(key) ?? 0) + 1);
43138
+ }
43139
+ const split = [...reasons].map(([reason, count]) => `${reason}: ${count}`).join(", ");
43140
+ const severity = entries.some((entry) => entry.severity === "error") ? "error" : entries.some((entry) => entry.severity === "warning") ? "warning" : "info";
43141
+ out.push({
43142
+ code,
43143
+ severity,
43144
+ message: `${rest.length} more ${code} entries omitted (${split}); the first ${DIAGNOSTIC_EXAMPLES_PER_CODE} are shown above.`
43145
+ });
43146
+ }
43147
+ return out;
43148
+ }
43149
+ function convertResult(details) {
43150
+ const { result, links } = details;
43151
+ const files = result.files.slice(0, MAX_CONVERT_FILES).map((file) => ({
43152
+ source: file.source,
43153
+ state: file.state,
43154
+ outputs: file.outputs,
43155
+ ...file.error !== undefined ? { error: file.error } : {},
43156
+ ...file.notice !== undefined ? { notice: file.notice } : {}
43157
+ }));
43158
+ const body = {
43159
+ target: result.target,
43160
+ converted: result.converted,
43161
+ skipped: result.skipped,
43162
+ failed: result.failed,
43163
+ files,
43164
+ ...result.files.length > files.length ? { omittedFiles: result.files.length - files.length } : {},
43165
+ links
43166
+ };
43167
+ return result.converted === 0 && result.failed > 0 ? toolError("convert-failed", "No file was converted.", body) : jsonResult({ ok: true, ...body });
43168
+ }
43043
43169
  function isToolEnabled(name, options) {
43044
43170
  return options.tools === undefined || options.tools.includes(name);
43045
43171
  }
@@ -43165,7 +43291,10 @@ function createAutoRAGMcpServer(lite, options = {}) {
43165
43291
  inputSchema: emptyInput,
43166
43292
  outputSchema: objectOutput,
43167
43293
  annotations: { readOnlyHint: true, openWorldHint: false }
43168
- }, async () => jsonResult(await lite.getRefreshStatus()));
43294
+ }, async () => {
43295
+ const status = await lite.getRefreshStatus();
43296
+ return jsonResult({ ...status, diagnostics: capDiagnostics(status.diagnostics) });
43297
+ });
43169
43298
  }
43170
43299
  if (isToolEnabled("autorag.search", options)) {
43171
43300
  server.registerTool("autorag.search", {
@@ -43204,7 +43333,7 @@ function createAutoRAGMcpServer(lite, options = {}) {
43204
43333
  content: result.content
43205
43334
  })),
43206
43335
  unsearched: retrieved.unsearched,
43207
- diagnostics: [...status.diagnostics, ...retrieved.diagnostics]
43336
+ diagnostics: capDiagnostics([...status.diagnostics, ...retrieved.diagnostics])
43208
43337
  });
43209
43338
  } catch (error) {
43210
43339
  return toolError(error instanceof RetrievalSelectionError ? "invalid-selection" : "search-failed", error instanceof Error ? error.message : String(error), { retryable: !(error instanceof RetrievalSelectionError) });
@@ -43259,7 +43388,7 @@ function createAutoRAGMcpServer(lite, options = {}) {
43259
43388
  content: result.content
43260
43389
  })),
43261
43390
  unsearched: retrieved.unsearched,
43262
- diagnostics: [...status.diagnostics, ...retrieved.diagnostics]
43391
+ diagnostics: capDiagnostics([...status.diagnostics, ...retrieved.diagnostics])
43263
43392
  });
43264
43393
  } catch (error) {
43265
43394
  return toolError(error instanceof RetrievalSelectionError ? "invalid-selection" : "search-failed", error instanceof Error ? error.message : String(error), { datasourceId: entry.datasourceId, retryable: !(error instanceof RetrievalSelectionError) });
@@ -43334,6 +43463,46 @@ function createAutoRAGMcpServer(lite, options = {}) {
43334
43463
  return datasource === undefined ? toolError("datasource-not-found", `No configured datasource named ${datasourceId}.`, { datasourceId }) : jsonResult({ ok: true, datasource });
43335
43464
  });
43336
43465
  }
43466
+ const convertEnabled = lite.config.filee?.enabled !== false;
43467
+ if (convertEnabled && !readOnly && isToolEnabled("autorag.convert", options)) {
43468
+ server.registerTool("autorag.convert", {
43469
+ title: "Convert Files",
43470
+ description: "Convert local files to another format offline with Filee: documents, spreadsheets, slides, HWP/HWPX, images, e-books, archives, audio, video, data. Writes new files next to each source (or into outputDir) and never replaces an existing file: a colliding name gets a number added. Each output is compared with its source by Dupey, so the result says whether the copy would count as a duplicate. Use autorag.convert.formats when unsure what a source can become.",
43471
+ inputSchema: convertInput,
43472
+ outputSchema: objectOutput,
43473
+ annotations: { readOnlyHint: false, idempotentHint: false, destructiveHint: false, openWorldHint: false }
43474
+ }, async ({ inputs, to, outputDir, recursive, quality, keepDates }, ctx) => {
43475
+ try {
43476
+ const details = await lite.convertFiles({
43477
+ inputs,
43478
+ to,
43479
+ ...outputDir !== undefined ? { outputDir } : {},
43480
+ ...recursive !== undefined ? { recursive } : {},
43481
+ ...quality !== undefined ? { quality } : {},
43482
+ ...keepDates !== undefined ? { keepDates } : {}
43483
+ }, ctx.mcpReq.signal);
43484
+ return convertResult(details);
43485
+ } catch (error) {
43486
+ return toolError("convert-failed", error instanceof Error ? error.message : String(error));
43487
+ }
43488
+ });
43489
+ }
43490
+ if (convertEnabled && isToolEnabled("autorag.convert.formats", options)) {
43491
+ server.registerTool("autorag.convert.formats", {
43492
+ title: "List Convertible Formats",
43493
+ description: "List the file formats Filee knows, or what one source format (heic, docx, hwp, mkv) can be converted to, including targets that need an optional engine. Read-only.",
43494
+ inputSchema: convertFormatsInput,
43495
+ outputSchema: objectOutput,
43496
+ annotations: { readOnlyHint: true, openWorldHint: false }
43497
+ }, async ({ from }, ctx) => {
43498
+ try {
43499
+ const formats = await lite.listFileConversions(from, ctx.mcpReq.signal);
43500
+ return jsonResult({ ok: true, ...from !== undefined ? { from } : {}, formats });
43501
+ } catch (error) {
43502
+ return toolError("convert-failed", error instanceof Error ? error.message : String(error));
43503
+ }
43504
+ });
43505
+ }
43337
43506
  if (!readOnly && isToolEnabled("autorag.report", options)) {
43338
43507
  server.registerTool("autorag.report", {
43339
43508
  title: "Persist AutoRAG Curated Report",
@@ -43421,9 +43590,11 @@ function isInvokedDirectly() {
43421
43590
  async function main() {
43422
43591
  const readOnly = parseBoolean(process.env.AUTORAG_MCP_READ_ONLY);
43423
43592
  const tools = parseTools(process.env.AUTORAG_MCP_TOOLS);
43424
- const lite = createAutoRAGLite({
43425
- env: process.env,
43426
- readOnly
43593
+ const lite = createReloadingLite({
43594
+ create: () => createAutoRAGLite({ env: process.env, readOnly }),
43595
+ signature: () => configSignature(process.env),
43596
+ onError: (error) => process.stderr.write(`autorag-mcp: keeping the previous config, the new one failed: ${error.message}
43597
+ `)
43427
43598
  });
43428
43599
  const server = createAutoRAGMcpServer(lite, { readOnly, tools });
43429
43600
  await server.connect(new StdioServerTransport);
@@ -0,0 +1,25 @@
1
+ import type { AutoRAGLite } from "../core.js";
2
+ export interface ReloadingLiteOptions {
3
+ /** Build a runtime from the config as it is now. May throw (missing or unparsable config). */
4
+ readonly create: () => AutoRAGLite;
5
+ /** A cheap fingerprint of whatever `create` reads; a different value means the runtime is out of date. */
6
+ readonly signature: () => string;
7
+ /** A config that could not be loaded. Called once per distinct broken config, never on every call. */
8
+ readonly onError?: (error: Error) => void;
9
+ }
10
+ /**
11
+ * A runtime that follows its config file. An MCP server is started once and then lives for as long as its host
12
+ * keeps it (Hermes keeps one per gateway, across chat sessions), so a config written after it started, which is
13
+ * exactly what first-time setup does, would otherwise never be seen: every search kept answering `index-not-ready`.
14
+ *
15
+ * Each access compares the config's signature (two `stat` calls) and rebuilds the runtime when it changed. The first
16
+ * build happens eagerly, so a bad config still fails at start. A later config that cannot be loaded (half-written,
17
+ * mistyped) keeps the last working runtime and is reported once, then retried when the file changes again.
18
+ */
19
+ export declare function createReloadingLite(options: ReloadingLiteOptions): AutoRAGLite;
20
+ /**
21
+ * Fingerprint of the config file `createAutoRAGLite` would read: its inode, size and modification time, or `absent`.
22
+ * An explicit `AUTORAG_CONFIG`, else `<AUTORAG_HOME>/config.json` (and the legacy `autorag.config.json` beside the
23
+ * working directory) are covered, in the same order the resolver uses.
24
+ */
25
+ export declare function configSignature(env?: NodeJS.ProcessEnv): string;
@@ -6,4 +6,20 @@ export interface AutoRAGMcpServerOptions {
6
6
  /** Test/embedded override; defaults to the current Node platform. */
7
7
  readonly platform?: NodeJS.Platform;
8
8
  }
9
+ interface DiagnosticLike {
10
+ readonly code: string;
11
+ readonly severity: string;
12
+ readonly message: string;
13
+ readonly source?: string;
14
+ readonly reason?: string;
15
+ }
16
+ /**
17
+ * Keep an MCP response small when one code repeats once per file. A corpus whose parser changed reports one
18
+ * `stale-index` entry per source (1,100 of them, about 750 KB, on a real 160k-file index), which the model then has to
19
+ * read through on every search and status call. Each code keeps its first few entries verbatim and one summary entry
20
+ * says how many more there were and how they split by `reason`, so "why is it stale" survives and a different code is
21
+ * never hidden behind a flood of this one. Short lists pass through unchanged.
22
+ */
23
+ export declare function capDiagnostics<T extends DiagnosticLike>(diagnostics: readonly T[]): (T | DiagnosticLike)[];
9
24
  export declare function createAutoRAGMcpServer(lite: AutoRAGLite, options?: AutoRAGMcpServerOptions): McpServer;
25
+ export {};
@@ -187,4 +187,5 @@ export declare class MirrorWorkspaceError extends Error {
187
187
  export declare function syncParsedMirrors(options: ParsedMirrorSyncOptions): Promise<ParsedMirrorSyncResult>;
188
188
  /** Ask MinSync's read-only plan which current source revisions still need work. */
189
189
  export declare function detectMirrorStaleness(options: ParsedMirrorSyncOptions): Promise<ParsedMirrorDiagnostic[]>;
190
+ /** A path is excluded when it equals an excluded path, sits under one, or matches an excluded glob. */
190
191
  export declare function isPathExcluded(sourcePath: string, excluded: ReadonlySet<string> | undefined): boolean;