@autorag/librarian 2.13.9 → 2.13.10

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,26 @@ 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
+ function convertResult(details) {
43119
+ const { result, links } = details;
43120
+ const files = result.files.slice(0, MAX_CONVERT_FILES).map((file) => ({
43121
+ source: file.source,
43122
+ state: file.state,
43123
+ outputs: file.outputs,
43124
+ ...file.error !== undefined ? { error: file.error } : {},
43125
+ ...file.notice !== undefined ? { notice: file.notice } : {}
43126
+ }));
43127
+ const body = {
43128
+ target: result.target,
43129
+ converted: result.converted,
43130
+ skipped: result.skipped,
43131
+ failed: result.failed,
43132
+ files,
43133
+ ...result.files.length > files.length ? { omittedFiles: result.files.length - files.length } : {},
43134
+ links
43135
+ };
43136
+ return result.converted === 0 && result.failed > 0 ? toolError("convert-failed", "No file was converted.", body) : jsonResult({ ok: true, ...body });
43137
+ }
43043
43138
  function isToolEnabled(name, options) {
43044
43139
  return options.tools === undefined || options.tools.includes(name);
43045
43140
  }
@@ -43334,6 +43429,46 @@ function createAutoRAGMcpServer(lite, options = {}) {
43334
43429
  return datasource === undefined ? toolError("datasource-not-found", `No configured datasource named ${datasourceId}.`, { datasourceId }) : jsonResult({ ok: true, datasource });
43335
43430
  });
43336
43431
  }
43432
+ const convertEnabled = lite.config.filee?.enabled !== false;
43433
+ if (convertEnabled && !readOnly && isToolEnabled("autorag.convert", options)) {
43434
+ server.registerTool("autorag.convert", {
43435
+ title: "Convert Files",
43436
+ 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.",
43437
+ inputSchema: convertInput,
43438
+ outputSchema: objectOutput,
43439
+ annotations: { readOnlyHint: false, idempotentHint: false, destructiveHint: false, openWorldHint: false }
43440
+ }, async ({ inputs, to, outputDir, recursive, quality, keepDates }, ctx) => {
43441
+ try {
43442
+ const details = await lite.convertFiles({
43443
+ inputs,
43444
+ to,
43445
+ ...outputDir !== undefined ? { outputDir } : {},
43446
+ ...recursive !== undefined ? { recursive } : {},
43447
+ ...quality !== undefined ? { quality } : {},
43448
+ ...keepDates !== undefined ? { keepDates } : {}
43449
+ }, ctx.mcpReq.signal);
43450
+ return convertResult(details);
43451
+ } catch (error) {
43452
+ return toolError("convert-failed", error instanceof Error ? error.message : String(error));
43453
+ }
43454
+ });
43455
+ }
43456
+ if (convertEnabled && isToolEnabled("autorag.convert.formats", options)) {
43457
+ server.registerTool("autorag.convert.formats", {
43458
+ title: "List Convertible Formats",
43459
+ 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.",
43460
+ inputSchema: convertFormatsInput,
43461
+ outputSchema: objectOutput,
43462
+ annotations: { readOnlyHint: true, openWorldHint: false }
43463
+ }, async ({ from }, ctx) => {
43464
+ try {
43465
+ const formats = await lite.listFileConversions(from, ctx.mcpReq.signal);
43466
+ return jsonResult({ ok: true, ...from !== undefined ? { from } : {}, formats });
43467
+ } catch (error) {
43468
+ return toolError("convert-failed", error instanceof Error ? error.message : String(error));
43469
+ }
43470
+ });
43471
+ }
43337
43472
  if (!readOnly && isToolEnabled("autorag.report", options)) {
43338
43473
  server.registerTool("autorag.report", {
43339
43474
  title: "Persist AutoRAG Curated Report",
@@ -43421,9 +43556,11 @@ function isInvokedDirectly() {
43421
43556
  async function main() {
43422
43557
  const readOnly = parseBoolean(process.env.AUTORAG_MCP_READ_ONLY);
43423
43558
  const tools = parseTools(process.env.AUTORAG_MCP_TOOLS);
43424
- const lite = createAutoRAGLite({
43425
- env: process.env,
43426
- readOnly
43559
+ const lite = createReloadingLite({
43560
+ create: () => createAutoRAGLite({ env: process.env, readOnly }),
43561
+ signature: () => configSignature(process.env),
43562
+ onError: (error) => process.stderr.write(`autorag-mcp: keeping the previous config, the new one failed: ${error.message}
43563
+ `)
43427
43564
  });
43428
43565
  const server = createAutoRAGMcpServer(lite, { readOnly, tools });
43429
43566
  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;
@@ -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;
package/dist/profiles.js CHANGED
@@ -14789,6 +14789,35 @@ var init_embedding = __esm(() => {
14789
14789
  created = new Set;
14790
14790
  });
14791
14791
 
14792
+ // src/filesystem/path-exclusions.ts
14793
+ import { matchesGlob, posix as posix3, win32 as win322 } from "node:path";
14794
+ function isGlobExclusion(entry) {
14795
+ return /[*?[]/u.test(entry);
14796
+ }
14797
+ function matchesGlobExclusion(path, glob) {
14798
+ const slashed = path.replaceAll("\\", "/");
14799
+ if (matchesGlob(slashed, glob))
14800
+ return true;
14801
+ return glob.startsWith("**/") && matchesGlob(slashed, glob.slice(3));
14802
+ }
14803
+ function createPathExclusionMatcher(excludedPaths, platform = process.platform) {
14804
+ const paths = platform === "win32" ? win322 : posix3;
14805
+ const normalize = (path) => {
14806
+ const absolute = paths.resolve(path);
14807
+ return platform === "win32" ? absolute.toLowerCase() : absolute;
14808
+ };
14809
+ const globs = excludedPaths.filter(isGlobExclusion).map((glob) => platform === "win32" ? glob.toLowerCase() : glob);
14810
+ const excluded = excludedPaths.filter((path) => !isGlobExclusion(path)).map((path) => {
14811
+ const absolute = normalize(path);
14812
+ return { absolute, prefix: absolute.endsWith(paths.sep) ? absolute : `${absolute}${paths.sep}` };
14813
+ });
14814
+ return (path) => {
14815
+ const absolute = normalize(path);
14816
+ return excluded.some((entry) => absolute === entry.absolute || absolute.startsWith(entry.prefix)) || globs.some((glob) => matchesGlobExclusion(absolute, glob));
14817
+ };
14818
+ }
14819
+ var init_path_exclusions = () => {};
14820
+
14792
14821
  // src/language.ts
14793
14822
  function supportedList() {
14794
14823
  return AUTORAG_LANGUAGE_TAGS.join(", ");
@@ -15377,7 +15406,7 @@ function resolveExcludePaths(value, origin) {
15377
15406
  if (!Array.isArray(value) || value.some((path) => typeof path !== "string" || path.length === 0)) {
15378
15407
  throw new ConfigError("Config field 'excludePaths' must be a non-empty string array");
15379
15408
  }
15380
- return resolveSearchPaths(value, origin);
15409
+ return value.map((entry) => isGlobExclusion(entry) ? entry : resolvePersistedPath(entry, origin));
15381
15410
  }
15382
15411
  function resolvePersistedPath(path, origin) {
15383
15412
  return isAbsolute5(path) ? path : resolve6(origin, path);
@@ -16682,6 +16711,7 @@ var init_config4 = __esm(() => {
16682
16711
  init_factory();
16683
16712
  init_embedding();
16684
16713
  init_file_lock();
16714
+ init_path_exclusions();
16685
16715
  init_language();
16686
16716
  init_atomic_write();
16687
16717
  init_rerank();
@@ -16978,7 +17008,7 @@ var init_automation_profiles = __esm(() => {
16978
17008
 
16979
17009
  // src/automation/config.ts
16980
17010
  import { existsSync as existsSync14, readFileSync as readFileSync16, realpathSync as realpathSync2 } from "node:fs";
16981
- import { basename as basename6, dirname as dirname12, isAbsolute as isAbsolute6, join as join24, matchesGlob, relative as relative4, resolve as resolve7 } from "node:path";
17011
+ import { basename as basename6, dirname as dirname12, isAbsolute as isAbsolute6, join as join24, matchesGlob as matchesGlob2, relative as relative4, resolve as resolve7 } from "node:path";
16982
17012
  function automationsConfigPath(env = process.env) {
16983
17013
  return join24(resolveAutoRAGHome(env), "automations.json");
16984
17014
  }
@@ -17154,9 +17184,9 @@ function deleteAutomation(configPath, id) {
17154
17184
  function globMatches(relPosix, pattern) {
17155
17185
  if (pattern === "**")
17156
17186
  return true;
17157
- if (matchesGlob(relPosix, pattern))
17187
+ if (matchesGlob2(relPosix, pattern))
17158
17188
  return true;
17159
- if (pattern.startsWith("**/") && matchesGlob(relPosix, pattern.slice(3)))
17189
+ if (pattern.startsWith("**/") && matchesGlob2(relPosix, pattern.slice(3)))
17160
17190
  return true;
17161
17191
  return false;
17162
17192
  }
@@ -18612,25 +18642,6 @@ var init_bundle = __esm(() => {
18612
18642
  init_atomic_write();
18613
18643
  });
18614
18644
 
18615
- // src/filesystem/path-exclusions.ts
18616
- import { posix as posix3, win32 as win322 } from "node:path";
18617
- function createPathExclusionMatcher(excludedPaths, platform = process.platform) {
18618
- const paths = platform === "win32" ? win322 : posix3;
18619
- const normalize = (path) => {
18620
- const absolute = paths.resolve(path);
18621
- return platform === "win32" ? absolute.toLowerCase() : absolute;
18622
- };
18623
- const excluded = excludedPaths.map((path) => {
18624
- const absolute = normalize(path);
18625
- return { absolute, prefix: absolute.endsWith(paths.sep) ? absolute : `${absolute}${paths.sep}` };
18626
- });
18627
- return (path) => {
18628
- const absolute = normalize(path);
18629
- return excluded.some((entry) => absolute === entry.absolute || absolute.startsWith(entry.prefix));
18630
- };
18631
- }
18632
- var init_path_exclusions = () => {};
18633
-
18634
18645
  // src/filesystem/shared-exclusive-queue.ts
18635
18646
  class SharedExclusiveQueue {
18636
18647
  sharedSinceExclusive = [];
@@ -24211,7 +24222,7 @@ var init_cloud = __esm(() => {
24211
24222
  // src/mirror/sync.ts
24212
24223
  import { createHash as createHash17 } from "node:crypto";
24213
24224
  import { opendir as opendir2, readFile as readFile2, stat as stat4 } from "node:fs/promises";
24214
- import { basename as basename13, dirname as dirname21, extname as extname9, join as join42, resolve as resolve15, sep as sep9 } from "node:path";
24225
+ import { basename as basename13, dirname as dirname21, extname as extname9, join as join42, resolve as resolve15 } from "node:path";
24215
24226
  import { setTimeout as sleep4 } from "node:timers/promises";
24216
24227
  function errorCode(error) {
24217
24228
  if (!(error instanceof Error))
@@ -24758,13 +24769,14 @@ function normalizeExtension2(extension) {
24758
24769
  return lower.startsWith(".") ? lower : `.${lower}`;
24759
24770
  }
24760
24771
  function isPathExcluded(sourcePath, excluded) {
24761
- if (excluded === undefined)
24772
+ if (excluded === undefined || excluded.size === 0)
24762
24773
  return false;
24763
- for (const excludedPath of excluded) {
24764
- if (sourcePath === excludedPath || sourcePath.startsWith(`${excludedPath}${sep9}`))
24765
- return true;
24774
+ let matcher = matcherBySet.get(excluded);
24775
+ if (matcher === undefined) {
24776
+ matcher = createPathExclusionMatcher([...excluded]);
24777
+ matcherBySet.set(excluded, matcher);
24766
24778
  }
24767
- return false;
24779
+ return matcher(sourcePath);
24768
24780
  }
24769
24781
  function walkLimits(options) {
24770
24782
  return {
@@ -24931,10 +24943,11 @@ async function collectFiles(state, directory, depth) {
24931
24943
  warnDirectoryUnreadable(state, directory, code);
24932
24944
  }
24933
24945
  }
24934
- 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;
24946
+ 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;
24935
24947
  var init_sync = __esm(() => {
24936
24948
  init_cli2();
24937
24949
  init_cloud_placeholder();
24950
+ init_path_exclusions();
24938
24951
  init_source_paths();
24939
24952
  init_binary();
24940
24953
  init_stage();
@@ -24993,6 +25006,7 @@ var init_sync = __esm(() => {
24993
25006
  }
24994
25007
  };
24995
25008
  STANDALONE_OCR_IMAGE_EXTENSIONS = new Set(KORDOC_IMAGE_EXTENSIONS);
25009
+ matcherBySet = new WeakMap;
24996
25010
  SEARCH_ROOT_UNAVAILABLE_MESSAGES = {
24997
25011
  gone: "A search root no longer exists; its previously indexed documents were left untouched.",
24998
25012
  "not-a-directory": "A search root is no longer a directory; its previously indexed documents were left untouched.",
@@ -38548,7 +38562,7 @@ var init_web_search_tool = __esm(() => {
38548
38562
  // src/agent/agent.ts
38549
38563
  import { randomUUID as randomUUID7 } from "node:crypto";
38550
38564
  import { existsSync as existsSync34, watch as fsWatch, mkdirSync as mkdirSync24, realpathSync as realpathSync8, statSync as statSync11, writeFileSync as writeFileSync17 } from "node:fs";
38551
- import { basename as basename15, dirname as dirname27, join as join54, relative as relative8, resolve as resolve18, sep as sep10 } from "node:path";
38565
+ import { basename as basename15, dirname as dirname27, join as join54, relative as relative8, resolve as resolve18, sep as sep9 } from "node:path";
38552
38566
  import { clampThinkingLevel } from "@earendil-works/pi-ai/compat";
38553
38567
  import { getAgentDir as getAgentDir6, keyText } from "@earendil-works/pi-coding-agent";
38554
38568
  function describeDecisionModel(status) {
@@ -41221,7 +41235,7 @@ ${this.discoveryHint((tools) => `When exploring local files and folders, activel
41221
41235
  return { ok: false, reason: "cloud-root-skipped", stdout: "", stderr: "", code: null };
41222
41236
  }
41223
41237
  const canonicalSourcePath = canonicalPath(sourcePath);
41224
- const exclude = scope.nestedCloudRoots.map((root) => relative8(canonicalSourcePath, root.path).split(sep10).join("/")).filter((pattern) => pattern !== "" && !pattern.startsWith(".."));
41238
+ const exclude = scope.nestedCloudRoots.map((root) => relative8(canonicalSourcePath, root.path).split(sep9).join("/")).filter((pattern) => pattern !== "" && !pattern.startsWith(".."));
41225
41239
  return client.prepare(sourcePath, { ...options, ...exclude.length === 0 ? {} : { exclude } });
41226
41240
  }));
41227
41241
  }
@@ -41735,6 +41749,8 @@ function toMinSyncReasonDiagnostic(reason) {
41735
41749
  };
41736
41750
  }
41737
41751
  function pinExcludedPath(path) {
41752
+ if (isGlobExclusion(path))
41753
+ return path;
41738
41754
  const resolvedPath = resolve18(path);
41739
41755
  try {
41740
41756
  return join54(realpathSync8(dirname27(resolvedPath)), basename15(resolvedPath));
@@ -41783,6 +41799,7 @@ var init_agent = __esm(() => {
41783
41799
  init_embedding();
41784
41800
  init_everything();
41785
41801
  init_cloud_placeholder();
41802
+ init_path_exclusions();
41786
41803
  init_fsearch();
41787
41804
  init_diagnostics();
41788
41805
  init_jikji();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@autorag/librarian",
3
- "version": "2.13.9",
3
+ "version": "2.13.10",
4
4
  "description": "Personal librarian agent for office work on the Pi framework, with pluggable retrieval methods",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {