LibreChat/config
Danny Avila 181d705579
🧹 fix: Clean Up Orphaned Agent File Stubs After Deletion (#12781)
* 🧹 fix: Prune Orphaned File References on File Deletion

Deleting a file via the Manage Files tab left its file_id in every agent's
tool_resources.*.file_ids. Stubs accumulate until the frontend dedupe keys
them as duplicates and blocks all new uploads (issue #12776).

- Add removeAgentResourceFilesFromAllAgents in packages/data-schemas: a
  single updateMany/$pullAll across every EToolResources category.
- Invoke it from processDeleteRequest after db.deleteFiles so every
  referencing agent is cleaned up, not just the one passed in req.body.
- Wrap the cleanup in try/catch so a stale agent update cannot mask a
  successful file deletion.

* 🧼 fix: Prune Orphaned File References on Agent Update

Already-affected agents would stay broken even after the delete-time fix:
the stubs sit on the agent document until something strips them. Heal them
on the next save (issue #12776).

- Add collectToolResourceFileIds + stripFileIdsFromToolResources helpers
  in @librechat/api — centralizing the tool_resources traversal used by
  the controller and the follow-up migration script.
- In updateAgentHandler, check the effective tool_resources against the
  files collection. When orphans are found, either strip them from the
  incoming tool_resources (if the update sets them) or run the bulk
  cleanup (if the update leaves tool_resources untouched).

* 🧰 chore: Add Migration to Clean Up Orphaned Agent File References

Complements the delete-time and save-time fixes by healing agents that
already accumulated orphan stubs before the upgrade (issue #12776). The
script is idempotent — re-running it on a clean database is a no-op.

- Add config/migrate-orphaned-agent-files.js following the existing
  migrate-*.js convention: --dry-run by default omitted (writes by
  default) and --batch-size= tuning knob. Streams agents via cursor.
- Register migrate:orphaned-agent-files and :dry-run npm scripts.
- Reuse collectToolResourceFileIds from @librechat/api so migration and
  runtime share the same traversal logic.

* 🩹 fix: Address Codex/Copilot Review on Orphaned Agent File Cleanup

Refines the #12776 fix series based on automated review feedback.

- Scope save-time pruning to the current agent only. When a PATCH
  carries tool_resources, strip orphans from the incoming payload and
  pay the DB round-trip only then. Removes the collection-wide
  updateMany previously triggered when tool_resources was absent
  (Codex P2 / Copilot).
- Wrap the orphan check in try/catch so a transient db.getFiles
  failure can't turn a good save into a 500 (comprehensive review #1).
- Replace Object.values(EToolResources) casts with an explicit list of
  agent-side categories in both orphans.ts and agent.ts. code_interpreter
  belongs to the Assistants API and isn't a key of AgentToolResources —
  including it was a type lie and generated dead MongoDB clauses
  (comprehensive review #3, #8).
- Export TOOL_RESOURCE_KEYS from @librechat/api and consume it in the
  migration script, dropping one duplicated definition (#4).
- Cap migration results.details at 50 sample entries so the memory
  footprint stays bounded on deployments with thousands of corrupted
  agents (Codex P3).
- Add migrate:orphaned-agent-files:batch npm script to match the
  convention set by migrate-agent-permissions / migrate-prompt-permissions
  (#7).
- Add controller-level tests covering the three orphan-pruning paths:
  strip from incoming tool_resources, leave alone when tool_resources
  is absent, swallow db.getFiles errors and still save (#6).
- Back pre-existing "should validate tool_resources in updates" test's
  file_ids with real File docs — the new pruning would otherwise strip
  them, and that test is about OCR conversion / schema filtering, not
  file existence. Register the File model in beforeAll so the fixture
  works.

* 🩹 fix: Tighten TOOL_RESOURCE_KEYS Type and Align Migration Sample Output

Two follow-ups from the second review pass.

- Type data-schemas' TOOL_RESOURCE_KEYS as ReadonlyArray<keyof
  AgentToolResources> instead of readonly string[]. Data-schemas depends
  on data-provider, so the import is clean. Catches typos and aligns
  with the matching export in @librechat/api — doesn't guarantee
  exhaustiveness, but that's a TypeScript limitation, not a workspace
  one.
- Align the migration's console output with DETAIL_SAMPLE_LIMIT: print
  every collected detail (up to 50) and, when more agents were affected
  than the sample size allowed, show a truncation notice. The old hard
  cap of 25 meant affected agents in the 26-50 range were collected
  but never shown.

*  test: Add Integration Coverage for Orphan Cleanup Paths (#12776)

Exercise the delete-time and migration paths end-to-end against a real
in-memory Mongo. Catches integration bugs the isolated unit tests on
each layer couldn't.

- api/server/services/Files/process.integration.spec.js — the primary
  repro: seed an Agent + File, call processDeleteRequest, assert the
  file_id disappears from every referencing agent's tool_resources
  while unrelated agents stay untouched. Also covers the no-op case
  and confirms a failure in the new cleanup step cannot roll back the
  file deletion itself.
- api/test/migrate-orphaned-agent-files.spec.js — drives the migration
  module: --dry-run reports without writing, apply mode prunes across
  every tool_resource category, re-running is idempotent, and
  DETAIL_SAMPLE_LIMIT caps the in-memory sample on wide corruption.
  Mocks only the connect helper (the spec owns the mongoose instance)
  so the real migration code path — cursor, $pullAll, reduce — runs.

* 🔒 fix: Run Orphan Cleanup Migration in System Tenant Context

Codex P2 catch: under TENANT_ISOLATION_STRICT=true, the migration
throws on the very first Agent.countDocuments() because the tenant
isolation plugin fail-closes on queries without tenant context — which
makes migrate:orphaned-agent-files unusable on the exact deployments
most likely to have accumulated corruption.

- Wrap the scan/prune body in runAsSystem so queries bypass the tenant
  filter (SYSTEM_TENANT_ID sentinel). The migration legitimately needs
  cross-tenant visibility — this is the same pattern seedDatabase and
  the S3 refresh job already use.
- Add a regression test that spies on Agent.countDocuments() and
  asserts the active tenantStorage context is SYSTEM_TENANT_ID during
  the call. Pins the wrap against future regressions without the
  brittleness of toggling the strict-mode env var (which caches on
  first read).

Note: the delete-time and save-time paths already run inside an
authenticated HTTP request where tenantStorage.run is set by auth
middleware, so the cleanup naturally scopes to the current tenant —
which is the correct behavior there since file ownership is
tenant-scoped.

* 🧹 chore: Drop Unused path Import From Process Integration Spec

Leftover from an earlier iteration that resolved the migration path
via path.resolve before I switched to a relative require. The import
does nothing now — removing it.
2026-04-22 11:35:48 -07:00
..
__tests__ 📦 refactor: Consolidate DB models, encapsulating Mongoose usage in data-schemas (#11830) 2026-03-21 14:28:53 -04:00
translations 🌵 chore: Remove deprecated 'prompt-caching' Anthropic header (#11313) 2026-01-12 19:12:36 -05:00
add-balance.js 📦 refactor: Consolidate DB models, encapsulating Mongoose usage in data-schemas (#11830) 2026-03-21 14:28:53 -04:00
ban-user.js 🐍 fix: Use Standard Mongoose Module Resolution in Config Scripts (#9143) 2025-08-19 11:15:09 -04:00
connect.js 🏗️ refactor: Extract DB layers to data-schemas for shared use (#7650) 2025-05-30 22:18:13 -04:00
create-user.js 📬 fix: Email Verification Handling in Create-User Command (#11408) 2026-01-21 14:03:49 -05:00
delete-banner.js 🐍 fix: Use Standard Mongoose Module Resolution in Config Scripts (#9143) 2025-08-19 11:15:09 -04:00
delete-user.js 🐘 feat: FerretDB Compatibility (#11769) 2026-03-21 14:28:49 -04:00
deployed-update.js 🏷️ chore: Remove Docker Images by Named Tag in deployed-update.js (#12138) 2026-03-08 21:48:22 -04:00
flush-cache.js 🪂 refactor: MCP Server Init Fallback (#10608) 2025-11-20 16:47:00 -05:00
helpers.js 🚀 feat: Banner (#3952) 2024-09-11 09:34:25 -04:00
invite-user.js 🛜 refactor: Streamline App Config Usage (#9234) 2025-08-26 12:10:18 -04:00
list-balances.js 🐍 fix: Use Standard Mongoose Module Resolution in Config Scripts (#9143) 2025-08-19 11:15:09 -04:00
list-users.js 🐍 fix: Use Standard Mongoose Module Resolution in Config Scripts (#9143) 2025-08-19 11:15:09 -04:00
migrate-agent-permissions.js 🗑️ chore: Remove Deprecated Project Model and Associated Fields (#11773) 2026-03-21 14:28:53 -04:00
migrate-orphaned-agent-files.js 🧹 fix: Clean Up Orphaned Agent File Stubs After Deletion (#12781) 2026-04-22 11:35:48 -07:00
migrate-prompt-permissions.js 🗑️ chore: Remove Deprecated Project Model and Associated Fields (#11773) 2026-03-21 14:28:53 -04:00
packages.js 🧠 feat: User Memories for Conversational Context (#7760) 2025-06-07 18:52:22 -04:00
prepare.js feat(prepare.js): add script to install husky only if NODE_ENV is not 'CI' (#512) 2023-06-13 11:36:54 -04:00
reset-meili-sync.js 🔧 refactor: batching documents on meili index reset (#11165) 2026-01-02 10:50:06 -05:00
reset-password.js 🐍 fix: Use Standard Mongoose Module Resolution in Config Scripts (#9143) 2025-08-19 11:15:09 -04:00
reset-terms.js 🐍 fix: Use Standard Mongoose Module Resolution in Config Scripts (#9143) 2025-08-19 11:15:09 -04:00
set-balance.js 🔧 chore: Update ESLint Config & Run Linter (#10986) 2025-12-15 17:55:25 -05:00
smart-reinstall.js 📝 docs: Add AGENTS.md for Project Structure and Coding Standards (#11866) 2026-02-19 16:33:43 -05:00
stop-backend.js 🔄 refactor: Consolidate Ask/Edit Controllers (#1365) 2023-12-15 15:47:40 -05:00
test-subdirectory-setup.sh 🧭 fix: Subdirectory Deployment Auth Redirect Path Doubling (#12077) 2026-03-05 01:38:44 -05:00
update-banner.js 📌 feat: Add Support for Persistable (Non-Dismissible) Banners (#10730) 2025-12-11 16:37:22 -05:00
update.js 📦 feat: Move Shared Components to @librechat/client (#8685) 2025-07-27 12:19:01 -04:00
user-stats.js 🐍 fix: Use Standard Mongoose Module Resolution in Config Scripts (#9143) 2025-08-19 11:15:09 -04:00