@fractary/codex-mcp 0.8.0 → 0.9.0

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/cli.cjs CHANGED
@@ -5586,6 +5586,7 @@ var {
5586
5586
  // ../../sdk/js/dist/index.js
5587
5587
  init_cjs_shims();
5588
5588
  var import_micromatch = __toESM(require_micromatch(), 1);
5589
+ var path3 = __toESM(require("path"), 1);
5589
5590
  var import_path = __toESM(require("path"), 1);
5590
5591
  var import_child_process = require("child_process");
5591
5592
 
@@ -12237,6 +12238,7 @@ var safeLoadAll = renamed("safeLoadAll", "loadAll");
12237
12238
  var safeDump = renamed("safeDump", "dump");
12238
12239
 
12239
12240
  // ../../sdk/js/dist/index.js
12241
+ var fs3 = __toESM(require("fs/promises"), 1);
12240
12242
  var import_promises = __toESM(require("fs/promises"), 1);
12241
12243
  var import_util4 = require("util");
12242
12244
  var __defProp2 = Object.defineProperty;
@@ -12488,9 +12490,45 @@ function resolveReference(uri, options = {}) {
12488
12490
  };
12489
12491
  if (isCurrentProject && parsed.path) {
12490
12492
  resolved.localPath = parsed.path;
12493
+ if (options.config) {
12494
+ const fileSource = detectFilePluginSource(parsed.path, options.config);
12495
+ if (fileSource) {
12496
+ resolved.sourceType = "file-plugin";
12497
+ resolved.filePluginSource = fileSource.name;
12498
+ resolved.localPath = fileSource.fullPath;
12499
+ }
12500
+ }
12491
12501
  }
12492
12502
  return resolved;
12493
12503
  }
12504
+ function detectFilePluginSource(filePath, config) {
12505
+ if (!config?.file?.sources) {
12506
+ return null;
12507
+ }
12508
+ for (const [sourceName, source] of Object.entries(config.file.sources)) {
12509
+ const basePath = source.local?.base_path;
12510
+ if (!basePath) {
12511
+ continue;
12512
+ }
12513
+ const normalizedPath = filePath.replace(/^\.\//, "").replace(/^\//, "");
12514
+ const normalizedBasePath = basePath.replace(/^\.\//, "").replace(/^\//, "").replace(/\/$/, "");
12515
+ if (normalizedPath.startsWith(normalizedBasePath)) {
12516
+ return {
12517
+ name: sourceName,
12518
+ fullPath: import_path.default.join(basePath, normalizedPath.substring(normalizedBasePath.length).replace(/^\//, ""))
12519
+ };
12520
+ }
12521
+ const sourceNameInPath = normalizedBasePath.split("/").pop();
12522
+ if (sourceNameInPath && normalizedPath.startsWith(sourceNameInPath + "/")) {
12523
+ const pathWithoutSource = normalizedPath.substring(sourceNameInPath.length + 1);
12524
+ return {
12525
+ name: sourceName,
12526
+ fullPath: import_path.default.join(basePath, pathWithoutSource)
12527
+ };
12528
+ }
12529
+ }
12530
+ return null;
12531
+ }
12494
12532
  var TTL = {
12495
12533
  ONE_HOUR: 3600,
12496
12534
  ONE_DAY: 86400,
@@ -12662,6 +12700,41 @@ var CodexConfigSchema = external_exports.object({
12662
12700
  // Archive configuration
12663
12701
  archive: ArchiveConfigSchema.optional()
12664
12702
  }).strict();
12703
+ var FileSourceSchema = external_exports.object({
12704
+ type: external_exports.enum(["s3", "r2", "gcs", "local"]),
12705
+ bucket: external_exports.string().optional(),
12706
+ prefix: external_exports.string().optional(),
12707
+ region: external_exports.string().optional(),
12708
+ local: external_exports.object({
12709
+ base_path: external_exports.string()
12710
+ }),
12711
+ push: external_exports.object({
12712
+ compress: external_exports.boolean().optional(),
12713
+ keep_local: external_exports.boolean().optional()
12714
+ }).optional(),
12715
+ auth: external_exports.object({
12716
+ profile: external_exports.string().optional()
12717
+ }).optional()
12718
+ }).refine(
12719
+ (data) => {
12720
+ if (data.type !== "local" && !data.bucket) {
12721
+ return false;
12722
+ }
12723
+ return true;
12724
+ },
12725
+ {
12726
+ message: "Bucket is required for s3, r2, and gcs storage types",
12727
+ path: ["bucket"]
12728
+ }
12729
+ );
12730
+ var FileConfigSchema = external_exports.object({
12731
+ schema_version: external_exports.string(),
12732
+ sources: external_exports.record(FileSourceSchema)
12733
+ });
12734
+ external_exports.object({
12735
+ file: FileConfigSchema.optional(),
12736
+ codex: CodexConfigSchema.optional()
12737
+ });
12665
12738
  init_matcher();
12666
12739
  init_matcher();
12667
12740
  var DEFAULT_FETCH_OPTIONS = {
@@ -12680,8 +12753,8 @@ function mergeFetchOptions(options) {
12680
12753
  ...options
12681
12754
  };
12682
12755
  }
12683
- function detectContentType(path6) {
12684
- const ext = path6.split(".").pop()?.toLowerCase();
12756
+ function detectContentType(path7) {
12757
+ const ext = path7.split(".").pop()?.toLowerCase();
12685
12758
  const mimeTypes = {
12686
12759
  md: "text/markdown",
12687
12760
  markdown: "text/markdown",
@@ -13146,6 +13219,294 @@ var HttpStorage = class {
13146
13219
  }
13147
13220
  }
13148
13221
  };
13222
+ var FileSourceResolver = class {
13223
+ constructor(config) {
13224
+ this.config = config;
13225
+ this.initializeSources();
13226
+ }
13227
+ sources = /* @__PURE__ */ new Map();
13228
+ /**
13229
+ * Initialize sources from config
13230
+ */
13231
+ initializeSources() {
13232
+ if (!this.config.file?.sources) {
13233
+ return;
13234
+ }
13235
+ for (const [name, sourceConfig] of Object.entries(this.config.file.sources)) {
13236
+ const resolved = {
13237
+ name,
13238
+ type: "file-plugin",
13239
+ localPath: sourceConfig.local.base_path,
13240
+ isCurrentProject: true,
13241
+ config: sourceConfig
13242
+ };
13243
+ if (sourceConfig.bucket && sourceConfig.type !== "local") {
13244
+ resolved.bucket = sourceConfig.bucket;
13245
+ resolved.prefix = sourceConfig.prefix;
13246
+ resolved.remotePath = this.buildRemotePath(sourceConfig);
13247
+ }
13248
+ this.sources.set(name, resolved);
13249
+ }
13250
+ }
13251
+ /**
13252
+ * Build remote path from source config
13253
+ */
13254
+ buildRemotePath(source) {
13255
+ const protocol = source.type === "s3" ? "s3://" : source.type === "r2" ? "r2://" : "gcs://";
13256
+ const bucket = source.bucket;
13257
+ const prefix = source.prefix ? `/${source.prefix}` : "";
13258
+ return `${protocol}${bucket}${prefix}`;
13259
+ }
13260
+ /**
13261
+ * Get all available file plugin sources
13262
+ *
13263
+ * @returns Array of resolved file sources
13264
+ */
13265
+ getAvailableSources() {
13266
+ return Array.from(this.sources.values());
13267
+ }
13268
+ /**
13269
+ * Resolve a source by name
13270
+ *
13271
+ * @param name - Source name (e.g., "specs", "logs")
13272
+ * @returns Resolved source or null if not found
13273
+ */
13274
+ resolveSource(name) {
13275
+ return this.sources.get(name) || null;
13276
+ }
13277
+ /**
13278
+ * Check if a path belongs to any file plugin source
13279
+ *
13280
+ * @param path - File path to check
13281
+ * @returns True if path matches any source's base_path
13282
+ */
13283
+ isFilePluginPath(path7) {
13284
+ return this.getSourceForPath(path7) !== null;
13285
+ }
13286
+ /**
13287
+ * Get the source for a given path
13288
+ *
13289
+ * Matches path against all source base_paths and returns the matching source.
13290
+ * Uses longest-match strategy if multiple sources match.
13291
+ *
13292
+ * @param path - File path to match
13293
+ * @returns Matching source or null
13294
+ */
13295
+ getSourceForPath(path7) {
13296
+ let bestMatch = null;
13297
+ let bestMatchLength = 0;
13298
+ for (const source of this.sources.values()) {
13299
+ const normalizedPath = this.normalizePath(path7);
13300
+ const normalizedBasePath = this.normalizePath(source.localPath);
13301
+ if (normalizedPath.startsWith(normalizedBasePath)) {
13302
+ const matchLength = normalizedBasePath.length;
13303
+ if (matchLength > bestMatchLength) {
13304
+ bestMatch = source;
13305
+ bestMatchLength = matchLength;
13306
+ }
13307
+ }
13308
+ }
13309
+ return bestMatch;
13310
+ }
13311
+ /**
13312
+ * Normalize path for comparison
13313
+ * - Remove leading "./" or "/"
13314
+ * - Remove trailing "/"
13315
+ * - Convert to lowercase for case-insensitive comparison
13316
+ */
13317
+ normalizePath(path7) {
13318
+ return path7.replace(/^\.\//, "").replace(/^\//, "").replace(/\/$/, "").toLowerCase();
13319
+ }
13320
+ /**
13321
+ * Get source names
13322
+ *
13323
+ * @returns Array of source names
13324
+ */
13325
+ getSourceNames() {
13326
+ return Array.from(this.sources.keys());
13327
+ }
13328
+ /**
13329
+ * Check if sources are configured
13330
+ *
13331
+ * @returns True if any file plugin sources are configured
13332
+ */
13333
+ hasSources() {
13334
+ return this.sources.size > 0;
13335
+ }
13336
+ };
13337
+ var FilePluginFileNotFoundError = class _FilePluginFileNotFoundError extends Error {
13338
+ constructor(filePath, sourceName, options) {
13339
+ const includeCloudSuggestions = options?.includeCloudSuggestions !== false;
13340
+ const storageType = options?.storageType;
13341
+ let message = `File not found: ${filePath}
13342
+
13343
+ `;
13344
+ if (includeCloudSuggestions) {
13345
+ if (storageType) {
13346
+ message += `This file may be in cloud storage (${storageType}).
13347
+
13348
+ `;
13349
+ } else {
13350
+ message += `This file may not have been synced from remote storage yet.
13351
+ `;
13352
+ }
13353
+ message += `To fetch from cloud storage, run:
13354
+ `;
13355
+ message += ` file pull ${sourceName}
13356
+
13357
+ `;
13358
+ message += `Or sync all sources:
13359
+ `;
13360
+ message += ` file sync`;
13361
+ } else {
13362
+ message += `Please ensure the file exists locally or pull it from cloud storage.`;
13363
+ }
13364
+ super(message);
13365
+ this.filePath = filePath;
13366
+ this.sourceName = sourceName;
13367
+ this.name = "FilePluginFileNotFoundError";
13368
+ if (Error.captureStackTrace) {
13369
+ Error.captureStackTrace(this, _FilePluginFileNotFoundError);
13370
+ }
13371
+ }
13372
+ };
13373
+ var FilePluginStorage = class {
13374
+ constructor(options) {
13375
+ this.options = options;
13376
+ this.sourceResolver = new FileSourceResolver(options.config);
13377
+ this.baseDir = options.baseDir || process.cwd();
13378
+ }
13379
+ name = "file-plugin";
13380
+ type = "local";
13381
+ // Reuse local type for compatibility
13382
+ sourceResolver;
13383
+ baseDir;
13384
+ /**
13385
+ * Check if this provider can handle the reference
13386
+ *
13387
+ * Only handles:
13388
+ * - Current project references
13389
+ * - With sourceType === 'file-plugin'
13390
+ *
13391
+ * @param reference - Resolved reference
13392
+ * @returns True if this provider can handle the reference
13393
+ */
13394
+ canHandle(reference) {
13395
+ return reference.isCurrentProject && reference.sourceType === "file-plugin";
13396
+ }
13397
+ /**
13398
+ * Fetch content for a reference
13399
+ *
13400
+ * Reads from local filesystem based on the resolved local path.
13401
+ * If file not found and S3 fallback is enabled, throws helpful error.
13402
+ *
13403
+ * @param reference - Resolved reference
13404
+ * @param options - Fetch options (unused for local reads)
13405
+ * @returns Fetch result with content
13406
+ */
13407
+ async fetch(reference, _options) {
13408
+ if (!reference.localPath) {
13409
+ throw new Error(`File plugin reference missing localPath: ${reference.uri}`);
13410
+ }
13411
+ if (!reference.filePluginSource) {
13412
+ throw new Error(`File plugin reference missing source name: ${reference.uri}`);
13413
+ }
13414
+ const absolutePath = path3.isAbsolute(reference.localPath) ? path3.resolve(reference.localPath) : path3.resolve(this.baseDir, reference.localPath);
13415
+ const source = this.sourceResolver.resolveSource(reference.filePluginSource);
13416
+ if (source) {
13417
+ const allowedDir = path3.resolve(this.baseDir, source.localPath);
13418
+ if (!absolutePath.startsWith(allowedDir + path3.sep) && absolutePath !== allowedDir) {
13419
+ throw new Error(
13420
+ `Path traversal detected: ${reference.localPath} resolves outside allowed directory ${source.localPath}`
13421
+ );
13422
+ }
13423
+ }
13424
+ try {
13425
+ const content = await fs3.readFile(absolutePath);
13426
+ const contentType = this.detectContentType(absolutePath);
13427
+ return {
13428
+ content,
13429
+ contentType,
13430
+ size: content.length,
13431
+ source: "file-plugin",
13432
+ metadata: {
13433
+ filePluginSource: reference.filePluginSource,
13434
+ localPath: absolutePath
13435
+ }
13436
+ };
13437
+ } catch (error) {
13438
+ if (error.code === "ENOENT") {
13439
+ const source2 = this.sourceResolver.resolveSource(reference.filePluginSource);
13440
+ throw this.createFileNotFoundError(reference, source2);
13441
+ }
13442
+ throw error;
13443
+ }
13444
+ }
13445
+ /**
13446
+ * Check if a reference exists
13447
+ *
13448
+ * @param reference - Resolved reference
13449
+ * @param options - Fetch options (unused)
13450
+ * @returns True if file exists
13451
+ */
13452
+ async exists(reference, _options) {
13453
+ if (!reference.localPath) {
13454
+ return false;
13455
+ }
13456
+ const absolutePath = path3.isAbsolute(reference.localPath) ? path3.resolve(reference.localPath) : path3.resolve(this.baseDir, reference.localPath);
13457
+ if (reference.filePluginSource) {
13458
+ const source = this.sourceResolver.resolveSource(reference.filePluginSource);
13459
+ if (source) {
13460
+ const allowedDir = path3.resolve(this.baseDir, source.localPath);
13461
+ if (!absolutePath.startsWith(allowedDir + path3.sep) && absolutePath !== allowedDir) {
13462
+ return false;
13463
+ }
13464
+ }
13465
+ }
13466
+ try {
13467
+ await fs3.access(absolutePath);
13468
+ return true;
13469
+ } catch {
13470
+ return false;
13471
+ }
13472
+ }
13473
+ /**
13474
+ * Detect content type from file extension
13475
+ */
13476
+ detectContentType(filePath) {
13477
+ const ext = path3.extname(filePath).toLowerCase();
13478
+ const mimeTypes = {
13479
+ ".md": "text/markdown",
13480
+ ".txt": "text/plain",
13481
+ ".json": "application/json",
13482
+ ".yaml": "text/yaml",
13483
+ ".yml": "text/yaml",
13484
+ ".html": "text/html",
13485
+ ".xml": "application/xml",
13486
+ ".log": "text/plain",
13487
+ ".js": "application/javascript",
13488
+ ".ts": "application/typescript",
13489
+ ".py": "text/x-python",
13490
+ ".sh": "application/x-sh"
13491
+ };
13492
+ return mimeTypes[ext] || "application/octet-stream";
13493
+ }
13494
+ /**
13495
+ * Create a helpful error message when file is not found
13496
+ */
13497
+ createFileNotFoundError(reference, source) {
13498
+ const includeCloudSuggestions = this.options.enableS3Fallback !== false;
13499
+ const storageType = source?.config.type;
13500
+ return new FilePluginFileNotFoundError(
13501
+ reference.localPath || reference.path || "",
13502
+ reference.filePluginSource || "",
13503
+ {
13504
+ includeCloudSuggestions,
13505
+ storageType
13506
+ }
13507
+ );
13508
+ }
13509
+ };
13149
13510
  var execFileAsync = (0, import_util4.promisify)(import_child_process.execFile);
13150
13511
  async function execFileNoThrow(command, args = [], options) {
13151
13512
  try {
@@ -13293,10 +13654,10 @@ var S3ArchiveStorage = class {
13293
13654
  *
13294
13655
  * Used to organize archives by type
13295
13656
  */
13296
- detectType(path6) {
13297
- if (path6.startsWith("specs/")) return "specs";
13298
- if (path6.startsWith("docs/")) return "docs";
13299
- if (path6.includes("/logs/")) return "logs";
13657
+ detectType(path7) {
13658
+ if (path7.startsWith("specs/")) return "specs";
13659
+ if (path7.startsWith("docs/")) return "docs";
13660
+ if (path7.includes("/logs/")) return "logs";
13300
13661
  return "misc";
13301
13662
  }
13302
13663
  /**
@@ -13307,9 +13668,9 @@ var S3ArchiveStorage = class {
13307
13668
  * - *.md (all markdown files)
13308
13669
  * - docs/*.md (markdown files in docs/)
13309
13670
  */
13310
- matchesPatterns(path6, patterns) {
13671
+ matchesPatterns(path7, patterns) {
13311
13672
  for (const pattern of patterns) {
13312
- if (this.matchesPattern(path6, pattern)) {
13673
+ if (this.matchesPattern(path7, pattern)) {
13313
13674
  return true;
13314
13675
  }
13315
13676
  }
@@ -13318,14 +13679,14 @@ var S3ArchiveStorage = class {
13318
13679
  /**
13319
13680
  * Check if path matches a single pattern
13320
13681
  */
13321
- matchesPattern(path6, pattern) {
13682
+ matchesPattern(path7, pattern) {
13322
13683
  const DOUBLE_STAR = "\0DOUBLE_STAR\0";
13323
13684
  let regexPattern = pattern.replace(/\*\*/g, DOUBLE_STAR);
13324
13685
  regexPattern = regexPattern.replace(/[.[\](){}+^$|\\]/g, "\\$&");
13325
13686
  regexPattern = regexPattern.replace(/\*/g, "[^/]*").replace(/\?/g, "[^/]");
13326
13687
  regexPattern = regexPattern.replace(new RegExp(DOUBLE_STAR, "g"), ".*");
13327
13688
  const regex = new RegExp(`^${regexPattern}$`);
13328
- return regex.test(path6);
13689
+ return regex.test(path7);
13329
13690
  }
13330
13691
  };
13331
13692
  var StorageManager = class {
@@ -13338,7 +13699,10 @@ var StorageManager = class {
13338
13699
  if (config.s3Archive) {
13339
13700
  this.providers.set("s3-archive", new S3ArchiveStorage(config.s3Archive));
13340
13701
  }
13341
- this.priority = config.priority || (config.s3Archive ? ["local", "s3-archive", "github", "http"] : ["local", "github", "http"]);
13702
+ if (config.filePlugin) {
13703
+ this.providers.set("file-plugin", new FilePluginStorage(config.filePlugin));
13704
+ }
13705
+ this.priority = config.priority || (config.filePlugin && config.s3Archive ? ["file-plugin", "local", "s3-archive", "github", "http"] : config.filePlugin ? ["file-plugin", "local", "github", "http"] : config.s3Archive ? ["local", "s3-archive", "github", "http"] : ["local", "github", "http"]);
13342
13706
  }
13343
13707
  /**
13344
13708
  * Register a custom storage provider
@@ -13806,8 +14170,17 @@ var CacheManager = class {
13806
14170
  * Get content for a reference
13807
14171
  *
13808
14172
  * Implements cache-first strategy with stale-while-revalidate.
14173
+ *
14174
+ * EXCEPTION: File plugin sources (current project files) bypass cache entirely.
14175
+ * They are always read fresh from disk for optimal development experience.
13809
14176
  */
13810
14177
  async get(reference, options) {
14178
+ if (reference.isCurrentProject && reference.sourceType === "file-plugin") {
14179
+ if (!this.storage) {
14180
+ throw new Error("Storage manager not set");
14181
+ }
14182
+ return await this.storage.fetch(reference, options);
14183
+ }
13811
14184
  const ttl = options?.ttl ?? this.config.defaultTtl;
13812
14185
  let entry = this.memoryCache.get(reference.uri);
13813
14186
  if (!entry && this.persistence) {
@@ -14013,13 +14386,18 @@ var CacheManager = class {
14013
14386
  }
14014
14387
  /**
14015
14388
  * Fetch content and store in cache
14389
+ *
14390
+ * EXCEPTION: File plugin sources are not cached (should not reach here,
14391
+ * but added as safety check).
14016
14392
  */
14017
14393
  async fetchAndCache(reference, ttl, options) {
14018
14394
  if (!this.storage) {
14019
14395
  throw new Error("Storage manager not set");
14020
14396
  }
14021
14397
  const result = await this.storage.fetch(reference, options);
14022
- await this.set(reference.uri, result, ttl);
14398
+ if (!(reference.isCurrentProject && reference.sourceType === "file-plugin")) {
14399
+ await this.set(reference.uri, result, ttl);
14400
+ }
14023
14401
  return result;
14024
14402
  }
14025
14403
  /**
@@ -14203,6 +14581,14 @@ var CODEX_TOOLS = [
14203
14581
  },
14204
14582
  required: ["pattern"]
14205
14583
  }
14584
+ },
14585
+ {
14586
+ name: "codex_file_sources_list",
14587
+ description: "List file plugin sources available in the current project.",
14588
+ inputSchema: {
14589
+ type: "object",
14590
+ properties: {}
14591
+ }
14206
14592
  }
14207
14593
  ];
14208
14594
  function textResult(text, isError = false) {
@@ -14239,9 +14625,12 @@ async function handleFetch(args, ctx) {
14239
14625
  }
14240
14626
  try {
14241
14627
  let result;
14628
+ const isFilePlugin = ref.isCurrentProject && ref.sourceType === "file-plugin";
14242
14629
  if (noCache) {
14243
14630
  result = await ctx.storage.fetch(ref, { branch });
14244
- await ctx.cache.set(uri, result);
14631
+ if (!isFilePlugin) {
14632
+ await ctx.cache.set(uri, result);
14633
+ }
14245
14634
  } else {
14246
14635
  result = await ctx.cache.get(ref, { branch });
14247
14636
  }
@@ -14249,6 +14638,10 @@ async function handleFetch(args, ctx) {
14249
14638
  return resourceResult(uri, content, result.contentType);
14250
14639
  } catch (error) {
14251
14640
  const message = error instanceof Error ? error.message : String(error);
14641
+ const errorName = error instanceof Error ? error.name : "Error";
14642
+ if (errorName === "FilePluginFileNotFoundError") {
14643
+ return textResult(message, true);
14644
+ }
14252
14645
  return textResult(`Failed to fetch ${uri}: ${message}`, true);
14253
14646
  }
14254
14647
  }
@@ -14347,6 +14740,33 @@ async function handleCacheClear(args, ctx) {
14347
14740
  return textResult(`Failed to clear cache: ${message}`, true);
14348
14741
  }
14349
14742
  }
14743
+ async function handleFileSourcesList(ctx) {
14744
+ try {
14745
+ const filePluginProvider = ctx.storage.getProvider("file-plugin");
14746
+ if (!filePluginProvider) {
14747
+ return textResult(`No file plugin sources configured.
14748
+
14749
+ To enable file plugin integration, the storage manager must be initialized with filePlugin configuration.
14750
+
14751
+ File plugin sources allow codex to access current project artifacts (specs, logs, etc.) directly from the local filesystem without caching.`);
14752
+ }
14753
+ return textResult(`File plugin provider is configured.
14754
+
14755
+ File plugin sources are available for current project artifact access.
14756
+ Sources are read directly from local filesystem without caching.
14757
+
14758
+ To see available sources, check the unified configuration at:
14759
+ .fractary/config.yaml
14760
+
14761
+ Look for the 'file.sources' section which defines:
14762
+ - specs: .fractary/specs
14763
+ - logs: .fractary/logs
14764
+ - etc.`);
14765
+ } catch (error) {
14766
+ const message = error instanceof Error ? error.message : String(error);
14767
+ return textResult(`Failed to list file sources: ${message}`, true);
14768
+ }
14769
+ }
14350
14770
  async function handleToolCall(name, args, ctx) {
14351
14771
  switch (name) {
14352
14772
  case "codex_document_fetch":
@@ -14357,6 +14777,8 @@ async function handleToolCall(name, args, ctx) {
14357
14777
  return handleList(args, ctx);
14358
14778
  case "codex_cache_clear":
14359
14779
  return handleCacheClear(args, ctx);
14780
+ case "codex_file_sources_list":
14781
+ return handleFileSourcesList(ctx);
14360
14782
  default:
14361
14783
  return textResult(`Unknown tool: ${name}`, true);
14362
14784
  }