Skip to content

[pull] main from danny-avila:main - #251

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

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

Conversation

@pull

@pull pull Bot commented Sep 8, 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 7, 2026 07:56
#15691 paired every read of `AgentCapabilities.execute_code` / `file_search`
with its role grant, and skipped `execute_code` from the resend-file priming
when `RUN_CODE` was denied. It left the `file_search` half: the grant was not
available inside `initializeAgent`, so a role denied `FILE_SEARCH` still had
its prior-turn search files re-hydrated on every resend.

Priming is not free. The files are fetched, `updateFilesUsage` bumps their
counters, and `primeResources` builds `tool_resources.file_search` — all for
a tool the loader is about to drop. A deployment with the capability off paid
the same cost.

`InitializeAgentParams.fileSearchAvailable` carries the capability AND the
grant, resolved from the same single `resolveToolRoleGrants` read that already
answers `codeEnvAvailable`, so pairing the second flag costs no extra role
lookup. Every initializer that resolves grants forwards it: the chat path and
its handoff, added-convo and discovery hand-offs, both API routes, and the
OpenAI-compatible embedder route. Absent leaves priming unconditional, so an
embedder that resolves no grant keeps its current behavior.

The loop that selected the resend tool resources becomes
`resolveResendToolResources`, which makes both halves of the gate testable
rather than reachable only through a full agent initialization.

The memory agent is untouched: it passes no `conversationId`, so it never
reaches this priming.

Co-authored-by: Claude <noreply@anthropic.com>
* fix: Bound Code Environment Status Polling

* refactor: Move Code Limiters Into API Package

* fix: enforce status polling boundaries
* fix: Require Platform Grants for Code Workers

* fix: Separate Platform Scope From Tenant Identity
* feat: Add conversation code approval modes

* refactor: Reuse shared code approval types

* fix: Scope approval selector to stateful agents

* fix: harden code approval state propagation

* fix: fail closed during approval discovery

* test: cover approval mode acceptance

* test: use visible approval mode label

* test: drive approval menu by keyboard

* test: expose accept edits in BYOM acceptance

* fix: read approval mode atomically on send

* fix: align code modes with endpoint policy

* fix: initialize code limiters at startup
…ck Icon (#15706)

* 🕓 style: Move the Queued-Message Hint Into a Hover Tooltip on the Clock Icon

"Sends when this response finishes" rendered as a caption row under the
queued chips for as long as a run was pending, costing composer height
for a single reassurance. The clock icon of each queued row now carries
that sentence as a hover tooltip and as its accessible name, and the
caption row is gone. Nothing else about the row changes: warning states
keep their triangle, and the hint disappears with the run as before.

* ⌨️ fix: Give the Queued-Hint Clock an Explicit Tab Stop and Focus Ring

The tooltip anchor was already focusable through Ariakit's Focusable
default, and opens on focus-visible, but nothing said so and no ring
showed where focus was. Make the tab stop explicit, add the same
focus-visible ring the row's buttons use, and prove the keyboard path
with a test. The hover test now allows for Ariakit's show timer on a
loaded CI shard, which is what failed the first run. Codex round 1 (P2)
on #15706.

* 🧪 test: Move the Pointer Between Coordinates Before Expecting the Queued Hint Tooltip

Ariakit's hover detector treats a pointer as moving only when consecutive
mousemove events differ in screen coordinates; its NODE_ENV=test shortcut
does not apply under the CI runner's NODE_ENV, which is why the hover test
passed locally and failed in the shard. Two moves at distinct coordinates
make the test environment-independent; the keyboard test was already
deterministic.
* fix: Clean Up MCP OAuth Teardown State

* fix: Harden OAuth Teardown Boundaries

* fix: harden MCP OAuth teardown races

* fix: avoid flow cache dependency cycle

* fix: bind OAuth teardown to credential generations

* test: cover active OAuth server fixtures

* style: sort OAuth cleanup imports

* fix: discard mismatched OAuth token snapshots

* fix: fence OAuth teardown across callback races

* test: align OAuth callback fixtures with server fencing

* test: complete OAuth callback server fixtures

* fix: version OAuth flow settlement

* fix: fence OAuth teardown snapshots

* test: align OAuth teardown fixtures

* test: complete OAuth snapshot fixtures

* fix: snapshot OAuth deletion lifecycle

* test: bind OAuth revocation snapshots

* test: assert per-record OAuth fence

* fix: harden MCP OAuth teardown consistency

* test: expect snapshotted OAuth readers
* fix: fence concurrent MCP OAuth teardown

* fix: hold MCP refresh teardown fences

* fix: suppress OAuth fallback during teardown

* fix: retire MCP connections before OAuth cleanup

* fix: annotate refresh teardown tenant scope

* fix: fence OAuth teardown across replicas

* test: provide OAuth teardown leases in controller mocks

* fix: fail safely around OAuth teardown leases

* fix: complete OAuth lease lifecycle handling

* fix: preserve OAuth single-flight admission
* fix: recover direct OpenID MCP bearer sessions

* fix: harden direct bearer recovery lifecycle

* fix: close direct bearer lifecycle races

* test: align direct bearer checks with current dev

* fix: unify direct bearer recovery budget

* fix: infer direct bearer recovery from placeholder

* fix: preserve bearer precedence and recovery budget

* fix: propagate bearer recovery across joiners

* fix: preserve direct bearer fallback without session provider

* fix: close direct bearer recovery race windows

* chore: sort bearer recovery test imports

* fix: align bearer recovery with OAuth cleanup lifecycle

* fix: fence request-scoped MCP creation during config mutations

* fix: retain suspended MCP catalog authentication outcomes

* fix: cancel MCP bearer recovery and reuse refreshed credentials

* fix: close MCP bearer recovery lifecycle boundaries

* fix: preserve ordered catalog authentication rejection

* fix: isolate bearer recovery across request and transport boundaries

* fix: preserve shared recovery ownership and credential precedence

* fix: wait for recovery borrowers and validated OpenID publication

* fix: observe the validated browser publication flight

* fix: cancel abandoned OpenID publication followers

* fix: fence refresh grant admission after lease acquisition
* 🗜️ feat: Manual Context Compaction as a Summarize-Only Turn

Adds a user-triggered "Compact context" action that summarizes the active
branch on demand. The compaction is an ordinary agent turn: the client
submits it through the normal chat pipeline with `compact: true`, the
controller hangs it off the branch's leaf instead of creating a user
message, and the agent client runs the graph summarize-only (the SDK's new
`summarizeOnly` input), so the summary streams into the response
placeholder under the leaf exactly like the automatic detour's and is
persisted as the boundary every later turn starts from.

Nothing new is needed for job registration, billing, abort, persistence,
or the usage snapshot: the run emits the same summary step and post-summary
context snapshot the automatic path already emits, and the response message
carries the summary part plus the `summaryUsedTokens` baseline the gauge
reads. The controller validates the request (existing conversation, a real
leaf, summarization enabled, no edit/regenerate/continue combination) and
answers 409 when the leaf is already a bare summary.

Client-side the submission borrows the regenerate shape — no new user
bubble, the placeholder parents onto the leaf — while the wire payload
drops `isRegenerate` so the server never treats it as a regenerated user
turn. The action sits in the context-usage popover, gated by the same
`summarization.enabled` switch as the automatic detour.

* 🗜️ fix: Localize a Compaction That Cannot Run

A compaction the graph could not attempt (summarization disabled, the
instructions already exceeding the budget, or an explicit recency window
covering the whole conversation) rejects with the SDK's
`ManualSummarizationSkippedError`, which rendered as the generic admin
error. It now maps to a typed `compaction_skipped` payload with the reason,
and a run that ends with neither a summary nor a recorded error fails as
`compaction_failed`; the Error component localizes both. A run that already
recorded why it stopped keeps that error part instead of failing twice.

* 🗜️ fix: Anchor a Compaction on the Leaf Everywhere It Is Read

Review round 1.

- The client sent the leaf's parent as `parentMessageId`, which the
  controller treats as the anchor, so the summary would have skipped the
  latest reply and landed as its sibling. The leaf's own id is now both the
  placeholder's parent and the server-side anchor.
- A compaction has no user message of its own, and every consumer of the
  job's user-message slot now knows it: the error path parents its row on
  the loaded anchor and never upserts the leaf, and the job records the
  regenerate shape so a reconnecting client rebuilds the turn under the
  leaf instead of recreating it as a user message.
- The controller no longer reads the anchor before the branch is loaded;
  `BaseClient` validates it against the history it loads anyway and
  answers 404 for a missing leaf or a typed `nothing_to_summarize` for a
  finished compaction. That predicate is shared through the data provider
  and ignores a summary still streaming or one that failed, so an
  interrupted compaction can be retried.
- A compaction never consumes files staged in the composer, the action is
  shown only when the server advertises `compactionEnabled`, and the
  in-flight marker is feature-local Jotai state rather than a read of the
  Recoil submission store.

* 🗜️ fix: Keep a Compaction Turn to Its Summary

Review round 2, plus the class it belongs to. A compaction submits no user
turn, so nothing that keys off one may run: the memory pass no longer runs
over the history (it would have spent a model call and could repeat memory
writes), and skill primes are no longer injected into the transcript the
run is about to summarize.

The client's in-flight marker is also reconciled on mount: a compaction
that finished while the view was away left the marker set, and the next
ordinary turn in that conversation would have shown as compacting.

* 🧹 chore: Sort Imports in the Compaction Hook Spec

* 🔧 chore: Update @librechat/agents dependency to version 3.8.1 in package-lock.json and package.json files
* 🧐 feat: Review Pending Tool Actions in the Composer (#15714)

* feat: add interactive approval review to the composer

* fix: Harden interactive approval boundaries

* fix: Bound every approval display label

* test: exercise composer approval review in BYOM acceptance

* fix: reveal all directional approval controls

* fix: canonicalize code approval targets across replicas

* style: sort resumable approval test imports

* test: Exercise Coexisting Approval Surfaces

* Revert "🧐 feat: Review Pending Tool Actions in the Composer (#15714)"

This reverts commit 5f79ded.

* Reapply "🧐 feat: Review Pending Tool Actions in the Composer (#15714)"

This reverts commit 52c7d9c.

* chore: bump agents sdk to v3.8.2
* 🔒 fix: Isolate Checkpoint Cleanup by Generation Ownership

* 🔒 fix: Harden Checkpoint Cleanup Ownership

* 🔒 fix: Retain Checkpoint Cleanup Evidence

* 🔒 fix: Persist checkpoint cleanup receipts

* 🔒 fix: Bound checkpoint cleanup receipt lifecycle

* fix: Bind checkpoint cleanup to durable ownership and deletion intent

* fix: Bind event actor checkpoint storage to authenticated owners

* fix: Resolve exact actor checkpoint ownership before lifecycle cleanup

* fix: Preserve actor storage compatibility with durable ownership and prune work

* fix: Defer checkpoint erasure until conversation deletion commits

* fix: Reclaim orphan actor ownership and batch deletion lookups

* fix: bind actor checkpoint ownership to payload rows

* fix: isolate actor readers and await account persistence

* fix: gate owned suspensions and drain complete account state

* fix: unify checkpoint deletion lifecycle and storage authority

* fix: Replay checkpoint deletion from durable owner state

* fix: retain checkpoint cleanup identity before topology loss

* fix: complete checkpoint catalog capture and retirement lifecycle
* feat: Enable Full Access for BYOM Coding 🛫

* test: Preserve endpoint policy under Full Access

* fix: Gate Full Access on expected root agent metadata
The retry navigation reserves its width in the message footer whether or not
hover has revealed it, so a response with siblings rendered the elapsed reading
indented past the column the streaming dot had just vacated. Rendering the
reading ahead of the navigation in all three row renderers puts it back on that
column start, in the streaming footer and in the flat thread alike.
The code approval selector was the only chat control built on the shadcn
`DropdownMenu` primitives — a borderless ghost button and default radio
dots beside a row of bordered composer pills, with per-item text styled
by hand.

- Rebuild the control on the composer's own menu language (Ariakit menu +
  `TooltipAnchor`), matching `MCPSelect`: a pill trigger with a rotating
  chevron, and a `bg-presentation` popover whose options carry an icon,
  a description and a check on the selected row, as the model and MCP
  menus already do.
- Extract the pill appearance into `composerControlClasses()` beside
  `composerSurfaceClasses()` in `@librechat/client`, and consume it from
  `CheckboxButton` and `MCPSelect`, which each carried their own copy of
  the same border/radius/height/elevation decision.
- Replace the raised hand with a pair that reads as one question — a
  document with a question mark for "ask", a document with a pen for
  "accept edits" — leaving the shield to the pending review button that
  sits in the same row.

Roles, keyboard behaviour and the stored `codeApprovalMode` are
unchanged: the trigger keeps its `code-approval-mode` test id and
`aria-expanded`, and the options stay `menuitemradio`.
@pull pull Bot locked and limited conversation to collaborators Sep 8, 2026
@pull pull Bot added the ⤵️ pull label Sep 8, 2026
@pull
pull Bot merged commit f50c40e into innFactory:main Sep 8, 2026
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