Skip to content

[pull] main from LibreChat-AI:main - #277

Merged
pull[bot] merged 17 commits into
innFactory:mainfrom
LibreChat-AI:main
Sep 27, 2026
Merged

pull[bot] merged 17 commits into
innFactory:mainfrom
LibreChat-AI:main

Conversation

@pull

@pull pull Bot commented Sep 27, 2026 •

Copy link
Copy Markdown

See Commits and Changes for more details.


Created by pull[bot] (v2.0.0-alpha.4)

Can you help keep this open source service alive? 💖 Please sponsor : )

danny-avila and others added 17 commits September 26, 2026 06:05
* 📬 fix: Show Background Results Still Waiting for the Agent

The chat header listed a background task as completed as soon as its tool
finished, while its result could still be queued behind a busy turn and
arrive minutes later as a new agent turn. The header also read only the
process-local registry, so tasks on another replica were invisible.

The task index now reads the durable delivery store the agent's
check_background_task already uses: each finished task carries
delivery pending, delivered or failed, and finished results known only to
the store are listed too. The header marks results still on their way,
keeps them ahead of delivered ones, and polls them every 10 s.

* fix: Retain And Index Background Result Delivery States

* fix: Pass Background Delivery CI Checks

---------

Co-authored-by: Lia <lia@librechat.ai>
* fix: Project Agents API Tools From Accepted Results

* test: Cover Non-Streaming Accepted Tool Projection

---------

Co-authored-by: Lia <lia@librechat.ai>
* 🧨 fix: Reject Invalid AES Credentials Before Startup

* 🎯 test: Format Credential Validation Cases

* 🧪 test: Use Valid AES Credentials in API Fixtures

---------

Co-authored-by: Lia <lia@librechat.ai>
…16365)

* 🛬 fix: Settle Background Tool Results Before the Server Shuts Down

* 🛬 fix: Preserve Background Shutdown Results Through Fallback

* 🧪 test: Match Formatted Cluster Shutdown Wiring

---------

Co-authored-by: Lia <lia@librechat.ai>
…ck (#16390)

* 🪜 feat: Title an Open Activity Fold and Reach Failed Calls in One Click

An open phase or tool group now reads as the title of the rows under it:
the header steps up to primary semibold, pins the stable label instead of
the live line, and sticks to the top while its rows scroll. Rows step in
24px under a hairline rail hung from the header glyph, one level per fold.

A failure is one click away from anywhere. A "N failed" pill beside the
header opens the fold and every failed row's output; a collapsed card peeks
its first failed call and error line under the header; the failed row
carries a red stripe, the error's first line as its subtitle, and names its
subject by the model's intent as the live header and peek already do.

The reveal travels down a FailedRevealContext counter that a group relays to
its own rows only once they have mounted, so a request made on a closed
card reaches a row three lazy disclosures below it.

* 🧪 test: Drive the Activity Fold Through the Real App and Fix What It Showed

A mock-lane spec runs a phase whose middle batch holds a call that failed
schema validation beside a slow one, watches the fold stream, then reaches
the error from the peek, from the pill, and again after a reload, in both
themes, with no console or page errors.

Three things the real app showed that the harness had not:

- The client counted only two of the server's three failed-output shapes,
  so a schema-validation failure that the server labelled `partial` rendered
  as a completed card with no pill. `isError` now mirrors
  `completedToolExecutionStatus`.
- A row's label lost its last letters to an ellipsis whenever its subtitle
  overflowed: the weighted shrink left the label a fraction of a pixel short.
  The subtitle now takes all of the shrink and the label caps at the row.
- The peek named an MCP call by its server while the row named the tool.
  Both now name it the way the row does, and the error line drops its bare
  `Error:` prefix the red glyph already speaks for.

* 🎯 fix: Answer a Failed-Call Reveal From Every Card's Disclosure Control

The reveal was consumed by the generic ToolCall row alone, so a failed
bash, code, memory, file or skill card, each rendered through its own
component, opened its group and stopped. The consumer now lives in
ProgressText, the disclosure control every card renders: a failed, openable,
closed row opens through the card's own toggle, and the one row the request
lets take focus lands the reader on its labeled button rather than on an
unnamed wrapper. The request carries a one-shot focus claim, relayed
through nested providers, so a phase with several failures opens all of
them and focuses only the first in document order, the one its peek named.

Also from the same review: an error line is derived only from output that
is an error, so a step failed by its run status or a status attachment no
longer shows ordinary output in red; a standalone group shows its failure
count once, on the pill, keeping the text for the accessible name; and the
label and subtitle share one shrinking box so the verdict, duration and
chevron after them never lose width.

* 🎯 fix: Make Failed Activity Cards Reachable From Fold Controls

* 🛡️ fix: Keep Legacy Search Errors Hidden Outside Failure Summaries

---------

Co-authored-by: Lia <lia@librechat.ai>
The standalone thinking disclosure and its non-interactive marker were the
one activity row sized to the reader's body text (`fontSize`, 16px at the
default), while tool calls, grouped thoughts and phase summaries all sit at
`tool-status-text` (0.9× the markdown size, 14.4px). The same thought read
at two sizes depending on which surface held it, and at larger settings the
header outgrew the message heading beside it.

Both headers now take `tool-status-text` and the medium weight every other
row label carries. The thought body keeps the body size, as prose should.
* 🫸 fix: Right-Align User Message Images

* 🧪 test: Verify User Image Alignment in Browser

---------

Co-authored-by: Lia <lia@librechat.ai>
…16392)

The live header previewed the sentence the model was still writing, word
by word, so it read as fragments ("This is a simple greeting, not") that
changed before they made sense and were replaced by the next fragment. It
now shows the last sentence that ENDED, holds it for at least a second, and
keeps its generic line until the first sentence finishes. A Latin mark
closes a sentence only before whitespace or the end of the text, so
decimals and version numbers stay whole; CJK marks need no space.

The measurement that let short sentences stream freely until they filled
the row (a ResizeObserver and a per-paint scroll-width read) goes with it:
the line no longer changes per word, so there is nothing to meter.
…s Text (#16393)

An open card whose label has not landed yet kept the live line, so its
sticky header quoted the sentence the reader could already see streaming in
the rows beneath it. Open, the header now names the span by its newest
label: a thought's generated label or the generic thinking line, a batch
label, a call's own line. Reasoning sentences and prose commentary are for
the collapsed header, which stands in for rows the reader cannot see.
…Card (#16394)

* 💭 feat: Bring the Streaming Thought Peek Back Under a Collapsed Live Card

#14546 previewed streaming reasoning as its trailing sentences in a short
fading window under the thought's header. The live fold (#16118) put that
thought inside a collapsed card, which unmounts its rows, so the peek went
with them and one throttled sentence on the header stood for a paragraph
of live reasoning.

A collapsed live card whose tail is a streaming thought now renders the
same peek under its header, outside the fold, in the cursor's place. It
gives way to the rows when the card opens and to the cursor when a call,
not a thought, is at the tail.

* 🧪 test: Watch Live Reasoning Stream Through the Real App

A mock-lane scenario streams three sentences of reasoning at a readable
pace; the spec samples the collapsed card's header and asserts every line
was the generic one or a whole sentence, sees the thought peek under the
card, opens the card mid-stream and checks the header became a title, then
checks the settled thought's header sits at the shared row scale.

The peek strips the thought's tags itself: handed the stream straight from
a live card, it had shown a literal `<think>` before the first words.

* 🎨 style: Sort Activity Phase Imports After Branch Rewrite

---------

Co-authored-by: Lia <lia@librechat.ai>
* 🖊️ fix: Persist Skill Subfile Edits

* fix: Guard skill file revisions and preserve cached views

* fix: Clear unavailable skill files and overlap upload reads

* fix: Recover unavailable skill files and clean up replaced uploads

* fix: Protect agent skill subfiles from concurrent edits

* fix: Keep management skill file writes revision-safe

---------

Co-authored-by: Lia <lia@librechat.ai>
* perf: Collapse Settled Reasoning Text in AnimatedText

AnimatedText re-split and re-classified the entire accumulated reasoning
string on every streamed token and rendered one element per word, making
a long reasoning stream quadratic. Classification is now append-only: the
leading run of words that finished fading is committed as a single text
node and never re-split, and only the unsettled tail is classified.

* perf: Probe the Settled Reasoning Prefix Instead of Rescanning It

The append check compared the whole settled prefix on every token, which
kept the stream quadratic in character comparisons. It now probes the
prefix head and the region at its boundary, and the settled region is
rendered from the current text so a rewrite can never show stale
characters. Adds a long reasoning stream scenario to the mock harness.

* perf: Settle Spaceless Reasoning Runs Before Their Final Segment

A CJK or Thai reasoning stream never reaches whitespace, so the settled
prefix could not advance and the whole run was re-segmented per token.
Inside a spaceless last word everything before its final segment is now
treated as stable; re-segmenting there only changes fade granularity.

* perf: Bound the Unsettled Reasoning Tail by Length

Settling at part boundaries left any run that never reaches whitespace
(unspaced Hangul, a spaceless script without Intl.Segmenter, long URLs)
in the tail, re-classified on every token. The unsettled region is now
capped at 64 characters regardless of segmentation, replacing the
spaceless-specific rule; only non-animated text is ever cut, so a cut
inside a part is invisible. Tests measure the classified length per
render instead of the DOM shape.

* fix: Keep the Settled Reasoning Cut on a Glyph Boundary

The length cap could cut inside a surrogate pair or a combining or ZWJ
sequence, splitting one glyph across two text nodes. The cut now steps
back past continuation code units.

* fix: Cut the Settled Reasoning Prefix at Grapheme Boundaries

Code-unit rules missed emoji modifiers and regional-indicator flags. The
cut now takes the last grapheme boundary from Intl.Segmenter over the
unsettled tail, which starts at a committed cluster boundary and is
bounded by the cap; the code-unit rule remains as the fallback.

* fix: Skip the Reasoning Tail Cap Without a Grapheme Segmenter

The code-unit fallback could still split emoji modifiers and flags. With
no Intl.Segmenter there is no safe cut, so the cap is not applied and the
tail stops at the last word, as it did before.

---------

Co-authored-by: Lia <lia@librechat.ai>
@pull pull Bot locked and limited conversation to collaborators Sep 27, 2026
@pull pull Bot added the ⤵️ pull label Sep 27, 2026
@pull
pull Bot merged commit 3c72c3f into innFactory:main Sep 27, 2026
25 checks passed
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants