LibreChat/client
Marco Beretta 336703fe48
🔔 fix: Report Agent Saves That Reuse the Newest Version Entry (#14824)
* fix: report agent saves that reuse the newest version entry

An update whose result matches the newest version is written without
recording a version entry. The Agent Builder derived its success message
from the version count, so every such save reported "No changes were
made" while the edit had in fact been persisted. Base the message on
whether the submission carried an edit of its own instead, and keep the
version count for the version history panel.

Also stop suppressing the version entry when the update carries an
atomic operator. isDuplicateVersion compares direct updates only, so it
cannot speak for the operator half; suppressing there applied a change
that no version entry recorded, leaving the document diverged from every
entry in its own history.

Closes #14809

* fix: count an avatar reset as a persisted edit

An avatar upload uses its own endpoint, but a reset rides the update
payload as avatar: null, so classifying every avatar-only submission as
non-persisted was wrong for resets. Clearing an avatar the newest
version never recorded reads as a duplicate to isDuplicateVersion, since
it skips a field when both sides are falsy, so the reset landed with the
version count unchanged and reported "No changes were made".

* fix: skip the version entry when an atomic operator changes nothing

An update carrying $push, $pull or $addToSet bypassed duplicate suppression on
the operator's mere presence. Re-attaching a resource file an agent already holds
makes $addToSet a no-op, so an agent with actions recorded a version entry for a
write that never touched the document, and its version count climbed on retries.

Resolve the operators against the current document instead. $push always appends
and $pull matches arbitrary criteria, so both still count as mutating; $addToSet
counts only when some value it adds is missing. Whatever cannot be compared
cheaply counts as mutating, since over-reporting costs a redundant version while
under-reporting would apply a change no version records.

* fix: confirm the submitted edit survived before claiming a save changed anything

Treating a dirty form as proof of a persisted edit reports success for a save
that stored nothing. The server can normalize a submission straight back to the
stored value: an MCP tool the user added is dropped when authorization rejects
it, and a skill is pruned when it no longer exists. Neither moves the version
count, so the toast claimed the agent was updated when it was untouched.

Capture the agent as it stands before the write, since the mutation replaces
that cache entry on success, and compare it against the one the server returns
across the fields the submission carried. Keep the dirty check alongside it: an
agent loaded through the basic projection carries fewer fields than the update
endpoint returns, and pairing the two keeps an untouched save honest either way.

* fix: compare a save against the expanded agent, not a basic projection

The panel falls back to the basic agent query whenever the expanded one has not
resolved, and that projection drops instructions, tools, edges, skills and the
rest while reducing model_parameters to a single flag. Comparing a submission
against it made every one of those fields read as changed, so a rejected MCP
tool or a pruned skill still reported success.

Compare against the expanded agent, the only projection carrying every field a
submission sends. When it is unavailable the comparison reports true and leaves
the dirty check to decide, since claiming nothing changed for a save that did is
the worse of the two errors. Renamed to say what it now answers.

* fix: drop the operator a suppressed update judged a no-op

Suppression reads whether $addToSet would add anything from a document fetched
before the write, and that reading cannot bind a concurrent one. A $pull landing
in between leaves the operator re-adding the value while the version entry has
already been suppressed, which is the one outcome this path exists to prevent: a
change applied with nothing in the history recording it.

Drop what was judged a no-op instead of racing it. Only $addToSet reaches here,
and only once every value it adds was found stored, so removing it makes the
suppression true by construction rather than true if nothing else writes first.

* fix: leave a suppressed update carrying no operator at all

Dropping only $addToSet left the invariant resting on which operators callers
happen to send. A present but empty $push or $pull counts as no operator when
deciding suppression, yet survived into the write, so the suppressed update was
operator-free by convention rather than by construction.

Drop all three. Reaching suppression already means none of them can change the
document, so removing them states that outright and keeps the write consistent
with the history it declines to record.
2026-08-14 12:20:51 -04:00
..
public 🪢 feat: Langfuse Fanout Connection Setting (#14108) 2026-07-29 18:33:10 -04:00
scripts
src 🔔 fix: Report Agent Saves That Reuse the Newest Version Entry (#14824) 2026-08-14 12:20:51 -04:00
sw 🛟 fix: Auto-Recover from Stale Service Worker Assets After Deploys (#13686) 2026-06-11 11:57:06 -04:00
test 🔒 chore: Upgrade react-router-dom to v7.18.2 (security) (#14582) 2026-08-01 17:19:52 -04:00
babel.config.cjs
check_updates.sh
index.html 📡 feat: add rum browser page-load diagnostics (#14106) 2026-07-05 11:32:53 -04:00
jest.config.cjs 🚀 chore: Prepare v0.8.8-rc1 (#14394) 2026-08-14 03:24:59 -04:00
jest.resolver.cjs ⬆️ chore: Migrate off deprecated @ariakit/react-core to @ariakit/react-components (#13940) 2026-06-24 23:13:57 -04:00
nginx.conf 🐳 feat: Bundle Admin Panel in Docker Compose Stacks (#13876) 2026-06-22 16:59:08 -04:00
package.json 🚀 chore: Prepare v0.8.8-rc1 (#14394) 2026-08-14 03:24:59 -04:00
postcss.config.cjs
tailwind.config.cjs 🎨 feat: Add Versioned Theme Foundation (#14709) 2026-08-10 13:41:03 -04:00
tsconfig.json 👷 ci: Type-check the Client Workspace (#13560) 2026-06-06 18:40:31 -04:00
vite.config.ts 🎨 refactor: adopt the @librechat/client design system (semantic color tokens + component migration) (#13879) 2026-08-06 09:15:17 -04:00