* feat: surface interrupt-steer escalation on waiting messages
The interrupt & steer feature shipped reachable only through the
composer chord, the send-button hovercard, and the composer button; a
message already waiting (queued for after the run, or steered and
parked at the next tool boundary) had no path to it. Both waiting
surfaces now carry one:
- Queued rows get an icon-only ZapOff escalation button beside the
existing Steer primary. It routes through sendQueuedNow, which now
takes a preempt option on its live-run path. The tooltip teaches the
composer chord, derived through resolveComposerKeyDown so a rebound
or yielded chord is never advertised.
- In-flight steer bubbles get an "Interrupt now" overflow entry with
the same race rules as Edit: reclaim first, and only a `reclaimed`
outcome resubmits (via retrySteer with preempt, swapping the chip
for an interrupting one). `applied` and run-ended-mid-reclaim
outcomes stop at the existing informational toasts, so the words can
never land twice. Not offered on a steer already preempting.
- Every during-run overflow menu gains an "Always interrupt instead"
toggle for steerInterruptsByDefault, next to the existing steer/queue
default toggle. MenuEntry supports disabled for the new entries.
Only one interrupt can be unresolved at a time: while one preempt is
pending (or the run is paused on approval, where the server 409s),
every escalation control disables instead of racing the same seal.
Ten new tests across both surfaces; 381 green in the affected suites.
* fix: lock escalation across its reclaim window, keep the paused control visible, label as steer
Codex round 1, all three findings.
P2, escalation race. The single-interrupt invariant had a window between
clicking "Interrupt now" and the reclaim resolving, where no preempt
chip existed for the chip-derived gate to see: two bubbles escalated
back-to-back could both resubmit. A shared escalating flag (Jotai,
per-conversation) now covers the window and disables every escalation
control on both surfaces, and a fresh recheck before resubmitting
catches an interrupt armed elsewhere meanwhile (composer chord, queued
row); those words re-home to the queue with an informational toast
instead of breaking the invariant.
P2, unreachable paused state. canSteer is defined as
hasRealConvoId && !pausedOnApproval, so gating the button on canSteer
removed it exactly when it was meant to render disabled; the test only
passed on an impossible stub combination. The render gate is now
duringRunActive && (canSteer || pausedOnApproval), and the test uses the
real invariant.
P2, label semantics. "Interrupt & send now" borrowed the name of the
hard-abort action; this one preserves the partial answer and steers.
Renamed to "Interrupt & steer now" (com_ui_interrupt_steer_now).
Both behavior fixes counterfactually verified; 384 tests green across
the affected suites.
* fix: disable bubble escalation while the run cannot accept a steer
Codex round 2, one P2. Answer mode (ask_user_question) sets
duringRunActive false while pausedOnApproval stays false, since that
flag only detects approval-bearing tool calls. The bubble's escalation
entry stayed enabled there, so clicking it cancelled a healthy waiting
steer and the preempt resubmission bounced off RUN_PAUSED, degrading
the words to the queue. The entry now also disables on
!duringRunActive, matching the queued-row control's gate.
Counterfactually verified: reverting the gate fails the new
answer-mode test.
* fix: recheck live run state after the reclaim, not just at the click
Codex round 3, one P2, and it is the round-1 recheck principle applied
one level deeper: the entry-time disable cannot see a run that pauses
(tool approval, answer mode) while the reclaim round-trip is in flight,
and the .then closure held the render's stale steering controls, so the
resubmit would fire into a RUN_PAUSED rejection after the reclaim had
already surrendered the steer's boundary slot.
The escalation continuation now reads the LIVE controls through a
latest-ref: if the run can no longer accept a steer, the words re-home
to the queue with an informational toast instead of resubmitting, and
the resubmit itself also goes through the live controls.
Counterfactually verified: reading the stale closure instead of the ref
fails the new mid-reclaim pause test.
* refactor: make escalation one atomic server-side arm, in place
Codex round 4: four P2s, every one an interleaving of the same window —
escalation as reclaim-then-repost is a compound, non-atomic operation
whose continuation must revalidate the world (FIFO position lost, ref
assigned too late, no run fence, competing bubble actions). Rounds 1-3
patched that window with a lock and rechecks; round 4 shows the window
itself is the defect, so this removes it instead of guarding it again.
Escalation is now POST /chat/steer/arm: the server flips preempt on the
EXISTING queued item in one atomic store op (new IJobStore.armSteer; a
decode-patch-encode LSET Lua on Redis, an in-place mutation in memory),
fenced to the validated generation and refused once the queue closes.
The handler mirrors the steer POST's preempt contract exactly: durable
flag gated on the owner's recorded capability, volatile requestPreempt
fire-and-forget because the durable flag is the truth resume/handover
re-arm from.
By construction this resolves all four findings: FIFO survives (the
item never moves; the whole queue still drains in instruction order at
the seal), there is no continuation to hold stale controls, the store
op is fenced to the original run, and a competing Edit/Queue/Cancel
either beats the arm (armed:false, chip untouched) or operates on the
armed item, whose cancel already disarms.
The client escalation entry becomes one mutation: armed:true relabels
the chip in place (same steerId, same position), PREEMPT_UNSUPPORTED
and lost races toast honestly, and the round 1-3 machinery — the
escalating lock atom, the latest-ref, the post-reclaim rechecks and
their two toast strings — is deleted rather than extended.
Verified: 7 new handler tests on the real in-memory manager (including
FIFO preservation and the stale-generation fence), 2 Redis integration
tests against real Redis (in-place arm keeps order and every field;
missing/stale/closed all refuse), client suites 396 green.
* fix: decide capability inside the atomic arm, neutralize the lost-race toast
Codex round 5, both findings, both edges of the new arm design rather
than its mechanism.
P2, capability TOCTOU. A HITL resume on a rolling deploy rewrites
preemptCapable for the SAME generation, so the handler's read could go
stale between validation and the flag flip, arming a steer the live
owner cannot seal. armSteer now returns armed | missing | incapable,
with the owner's live capability part of the same atomic predicate as
the generation fence (HGET preemptCapable inside the Lua; the flat job
field, not a metadata blob — the in-memory store reads the same field).
The handler's pre-check is deleted rather than kept alongside; the
store predicate is the single source. New handler test rewrites the
capability after queueing and expects PREEMPT_UNSUPPORTED with the item
left unflagged; the Redis guards test now asserts the incapable refusal
against real Redis.
P2, ambiguous toast. armed:false covers injected, cancelled, re-homed,
and run-over alike, so telling the user the message "already reached
the agent" claimed one specific outcome. The lost-race branch now uses
a neutral message (com_ui_steer_arm_lost_race) and defers to the events
for what actually happened.
* fix: flip the escalation lock synchronously before the arm request
Codex round 6, one P2. Round 4 deleted the escalating flag along with
the reclaim continuation it guarded, but that left the one-interrupt
gate blind during the arm request's own round trip: the chip-derived
check cannot see an arm until its response relabels the chip, so on a
slow connection two bubbles could both arm before either response
landed. Double-arm is harmless server-side now (the run seals once and
drains the whole queue in order), but every escalation control
advertises "one interrupt at a time" by disabling, and the controls
must tell the truth.
The per-conversation escalating flag returns as a pure UX gate: set
synchronously at click, before the mutation, cleared on settlement, and
folded into interruptPending on both surfaces. Unlike its round 1-3
ancestor there is no continuation behind it to guard and no recheck to
pair with it.
Counterfactually verified: without the synchronous set, the two-bubble
race test arms twice. 207 tests green across the Chat Input suites.
* test(e2e): cover escalation of waiting messages through the real seal
Three mock-harness tests on E2E_SLOW_REPLY, a 160-chunk stream with no
tool boundary, so an in-thread steer part can ONLY come from a genuine
mid-stream seal — which makes each test a behavioral proof rather than
a UI check:
- Queued row escalation: the ZapOff button turns a waiting queued
message into a preempt-armed steer (202 echoes preempt: true) that
seals and injects, where the sibling steering.spec test proves the
unescalated path waits for run end instead.
- Bubble in-place arm: an ordinary steer (202 with no preempt echo)
waits as a bubble, POST /chat/steer/arm answers armed: true, the
bubble relabels in place (same single bubble, same text, escalation
no longer offered on reopen), and the armed steer seals mid-stream.
- Always-interrupt toggle: flipped from a waiting row's overflow menu,
plain Enter now produces a preempt: true steer that seals in the SAME
run, and the menu offers the way back. An afterEach clears the
localStorage preference so a mid-test failure cannot leak
preempt-by-default into the rest of the serial suite.
All three verified locally through the full harness (real backend, mock
LLM, seeded DB): 3 passed in 27s.
* feat: dedicated escalation arrow + shortcut, menu split into actions and preferences
The escalation was still half-hidden: the bubble only offered it inside
the overflow menu, and the tooltip taught the composer chord, which does
a different thing (interrupts with typed text, not this chip). Three
changes make it a first-class command:
- A shared EscalateNowButton (circular arrow, ghost-bordered like the
composer's interrupt control) is always visible on BOTH surfaces:
beside each queued row's Steer primary and on every waiting steer
bubble next to its menu. It disappears once a steer is interrupting.
- A dedicated registry shortcut, escalateSteer (Cmd/Ctrl+Shift+.),
editing-allowed and rebindable like every other action. Deliberately
NOT an Enter chord: the composer owns every Enter chord, and the
yield design rests on no default binding using Enter besides submit.
Its handler clicks the newest enabled arrow control (bubbles beat
queued rows), so the shortcut can never diverge from the button, and
the arrow's tooltip teaches THIS command via the registry display.
- The overflow menus separate one-off actions from sticky behavior
changes: Edit, Cancel, Queue, then a smaller "Preferences" section
holding the queueing and always-interrupt toggles, each with the
standard InfoHoverCard reusing the Settings panel's descriptions.
"Interrupt & steer now" leaves the menu entirely.
386 client tests green, including a menu-structure test locking the
order and the absence of the escalation entry; bubble escalation tests
drive the visible arrow. The e2e spec's bubble test now clicks the
arrow, and a fourth test drives the dedicated shortcut end to end
through a real mid-stream seal.
* style: bind the escalation arrow to its message (variant A anatomy)
Two same-weight circles in a row read as one control group, leaving the
arrow's ownership ambiguous, and a floating arrow stops meaning anything
once several messages stack. The shared control now carries variant A's
anatomy: a thin divider binds a small SOLID arrow (filled, inverted) to
the message region on its left, and the menu ellipsis stays a bare
glyph, so the two affordances can no longer blur together — and the
divider+arrow pairing repeats cleanly per chip at N messages.
* chore: drop the unused within import CI lint caught
* fix: advertise the escalation shortcut only while the control is live
Codex on the e2e head, one P2: the tooltip appended the chord hint even
while the button was disabled, advertising a shortcut that does nothing
during an approval pause. The flagged control (InterruptNowButton) was
since replaced by the shared EscalateNowButton, which inherited the
pattern; the successor now omits the chord whenever the control is
disabled, matching the rule the during-run hovercard already follows.
* fix: harden steer escalation lifecycle and recovery
* test(e2e): disambiguate accessible steer preferences
* test: align abort persistence coverage with prerequisites
* chore(i18n): remove obsolete steer race message
* chore: normalize imports across steering changes
* test: exercise stream integration on Redis Cluster
* test: scope HITL checkpoints to generation
* test: fix cluster cleanup and locale policy
* fix: keep escalation visible during ask pauses
* fix: fence recovery downgrade and stale predecessors
* fix: require generation owner abort acknowledgement
* fix: validate delayed preempt arms
* test: align final escalation fixtures
* fix: preserve in-memory predecessor abort handoff
* fix: restore controls for recovered queued messages
* test: cover recovered queue controls
* fix: close final steering review gaps
Bitnami moved versioned image tags from docker.io/bitnami to
docker.io/bitnamilegacy on 2025-08-28, which causes ImagePullBackOff
on a fresh Helm install of the LibreChat chart. Override the MongoDB
subchart image repository to bitnamilegacy/mongodb so installs
succeed out of the box.
Fixes#13031
* ✨ v0.8.3
* chore: Bump package versions and update configuration
- Updated package versions for @librechat/api (1.7.25), @librechat/client (0.4.54), librechat-data-provider (0.8.302), and @librechat/data-schemas (0.0.38).
- Incremented configuration version in librechat.example.yaml to 1.3.6.
* feat: Add OpenRouter headers to OpenAI configuration
- Introduced 'X-OpenRouter-Title' and 'X-OpenRouter-Categories' headers in the OpenAI configuration for enhanced compatibility with OpenRouter services.
- Updated related tests to ensure the new headers are correctly included in the configuration responses.
* chore: Update package versions and dependencies
- Bumped versions for several dependencies including @eslint/eslintrc to 3.3.4, axios to 1.13.5, express to 5.2.1, and lodash to 4.17.23.
- Updated @librechat/backend and @librechat/frontend versions to 0.8.3.
- Added new dependencies: turbo and mammoth.
- Adjusted various other dependencies to their latest versions for improved compatibility and performance.
- Changed image references from `ghcr.io` to `registry.librechat.ai` across multiple Docker and Helm files, ensuring consistency in image sourcing.
- Updated `deploy-compose.yml`, `docker-compose.override.yml.example`, `docker-compose.yml`, `rag.yml`, and various Helm chart files to reflect the new registry.
* 🔧 chore: Update configuration version to 1.3.4 in librechat.example.yaml and data-provider config.ts
- Bumped the configuration version in both librechat.example.yaml and data-provider/src/config.ts to 1.3.4.
- Added new options for creating prompts and agents in the interface section of the YAML configuration.
- Updated capabilities list in the endpoints section to include 'deferred_tools'.
* 🔧 chore: Bump version to 0.8.3-rc1 across multiple packages and update related configurations
- Updated version to 0.8.3-rc1 in bun.lock, package.json, and various package.json files for frontend, backend, and data provider.
- Adjusted Dockerfile and Dockerfile.multi to reflect the new version.
- Incremented version for @librechat/api from 1.7.22 to 1.7.23 and for @librechat/client from 0.4.51 to 0.4.52.
- Updated appVersion in helm Chart.yaml to 0.8.3-rc1.
- Enhanced test configuration to align with the new version.
* 🔧 chore: Update version to 0.8.300 across multiple packages
- Bumped version to 0.8.300 in bun.lock, package-lock.json, and package.json for the data provider.
- Ensured consistency in versioning across the frontend, backend, and data provider packages.
* 🔧 chore: Bump package versions in bun.lock
- Updated version for @librechat/api from 1.7.22 to 1.7.23.
- Incremented version for @librechat/client from 0.4.51 to 0.4.52.
- Bumped version for @librechat/data-schemas from 0.0.35 to 0.0.36.
Add Bitnami Redis as an optional Helm sub-chart dependency, following the
same pattern used by MongoDB and Meilisearch. When enabled, USE_REDIS and
REDIS_URI are auto-wired into the LibreChat ConfigMap.
- Add redis dependency (Bitnami 24.1.3, Redis 8.4) to Chart.yaml
- Add redis config section to values.yaml (disabled by default)
- Auto-wire USE_REDIS and REDIS_URI in configmap-env.yaml with dig
checks to allow user overrides via configEnv
- Bump chart version to 1.10.0
Co-authored-by: Feng Lu <feng.lu@kindredgroup.com>
Co-authored-by: Danny Avila <danny@librechat.ai>
* 🐳 feat: Expose enableServiceLinks in Helm Deployment templates (#11740)
Allow users to disable Kubernetes service link injection via enableServiceLinks
in both LibreChat and RAG API Helm charts. This prevents pod startup failures
caused by "argument list too long" errors in namespaces with many services.
* Update helm/librechat/templates/deployment.yaml
* Update helm/librechat-rag-api/templates/rag-deployment.yaml
* set enableServiceLinks default to true
---------
Co-authored-by: Ganesh Bhat <ganesh.bhat@fullscript.com>
- Removed `forcePrompt` parameter from various configuration files including `librechat.example.yaml`, `initialize.js`, `values.yaml`, and `initialize.ts`.
- This change simplifies the configuration by eliminating unused options, enhancing clarity and maintainability across the codebase.
* chore: Update version from v0.8.2-rc3 to v0.8.2 across multiple files
* chore: Update package versions for @librechat/api to 1.7.22 and @librechat/client to 0.4.51
* chore: Bump version of librechat-data-provider from 0.8.230 to 0.8.231
* chore: Bump version of @librechat/data-schemas to 0.0.35
* chore: bump config version to 1.3.2
* chore: bump config version to 1.3.3
* docs: Update README to include new features for resumable streams and enhanced platform capabilities
* ✨ v0.8.2-rc2
* chore: Update package versions in bun.lock and package-lock.json
- Bumped versions for @librechat/api (1.7.10 to 1.7.20), @librechat/client (0.4.3 to 0.4.4), librechat-data-provider (0.8.210 to 0.8.220), and @librechat/data-schemas (0.0.32 to 0.0.33) in relevant files.
* 🗑️ chore: Remove unused Legacy Provider clients and related helpers
* Deleted OpenAIClient and GoogleClient files along with their associated tests.
* Removed references to these clients in the clients index file.
* Cleaned up typedefs by removing the OpenAISpecClient export.
* Updated chat controllers to use the OpenAI SDK directly instead of the removed client classes.
* chore/remove-openapi-specs
* 🗑️ chore: Remove unused mergeSort and misc utility functions
* Deleted mergeSort.js and misc.js files as they are no longer needed.
* Removed references to cleanUpPrimaryKeyValue in messages.js and adjusted related logic.
* Updated mongoMeili.ts to eliminate local implementations of removed functions.
* chore: remove legacy endpoints
* chore: remove all plugins endpoint related code
* chore: remove unused prompt handling code and clean up imports
* Deleted handleInputs.js and instructions.js files as they are no longer needed.
* Removed references to these files in the prompts index.js.
* Updated docker-compose.yml to simplify reverse proxy configuration.
* chore: remove unused LightningIcon import from Icons.tsx
* chore: clean up translation.json by removing deprecated and unused keys
* chore: update Jest configuration and remove unused mock file
* Simplified the setupFiles array in jest.config.js by removing the fetchEventSource mock.
* Deleted the fetchEventSource.js mock file as it is no longer needed.
* fix: simplify endpoint type check in Landing and ConversationStarters components
* Updated the endpoint type check to use strict equality for better clarity and performance.
* Ensured consistency in the handling of the azureOpenAI endpoint across both components.
* chore: remove unused dependencies from package.json and package-lock.json
* chore: remove legacy EditController, associated routes and imports
* chore: update banResponse logic to refine request handling for banned users
* chore: remove unused validateEndpoint middleware and its references
* chore: remove unused 'res' parameter from initializeClient in multiple endpoint files
* chore: remove unused 'isSmallScreen' prop from BookmarkNav and NewChat components; clean up imports in ArchivedChatsTable and useSetIndexOptions hooks; enhance localization in PromptVersions
* chore: remove unused import of Constants and TMessage from MobileNav; retain only necessary QueryKeys import
* chore: remove unused TResPlugin type and related references; clean up imports in types and schemas
* v0.8.1
* fix: GitHub workflows for OIDC trusted publishing
- Added permissions for OIDC trusted publishing in client, data-provider, and data-schemas workflows.
- Updated npm installation to support OIDC in all workflows.
- Changed npm publish commands to include `--provenance` for better package integrity.
- Updated repository URLs in package.json files for client, data-provider, and data-schemas to remove `git+` prefix.
* ✨ v0.8.1-rc2
- Updated version numbers in Dockerfile, Dockerfile.multi, package.json, and various package.json files for client, api, and data-provider.
- Adjusted appVersion in Chart.yaml and constants in config.ts to reflect the new version.
- Incremented versions for @librechat/api, @librechat/client, and librechat-data-provider packages.
* chore: Update Chart version to 1.9.3
- Incremented the chart version in Chart.yaml to reflect the latest changes.
* Possibility to add extra env values to the deployment
* Fix: Custom environment variables should be placed after the predefined environment variables
* ✨ v0.8.0
* 🔧 chore: Update config version to 1.3.0
* 🔧 chore: Bump @librechat/api version to 1.4.1
* 🔧 chore: Update @librechat/client version to 0.3.1
* 🔧 chore: Bump librechat-data-provider version to 0.8.020
* 🔧 chore: Bump @librechat/data-schemas version to 0.0.23
* Add support for extra secrets and config maps in helm deployment template
* expose single env field in values field instead of extra secret and config map fields
* a small fix
Add ability to configure hostAliases in Helm chart to redirect traffic to proxy servers or custom endpoints via /etc/hosts entries.
Co-authored-by: Feng Lu <feng.lu@kindredgroup.com>
This PR adds DNS configuration support to the LibreChat Helm chart, enabling users to redirect traffic to proxy servers or use custom DNS settings.
## What's Changed
- Added dnsPolicy and dnsConfig fields to deployment.yaml template
- Added DNS configuration options to values.yaml with comprehensive examples
- Created documentation and example configurations
## Use Cases
- Redirect AI service traffic (AWS Bedrock, OpenAI, etc.) to proxy servers
- Use corporate DNS servers for name resolution
- Control traffic routing through custom DNS configurations
- Enforce traffic through security gateways
## Configuration Example
```yaml
dnsPolicy: "None"
dnsConfig:
nameservers:
- "10.0.0.10" # Custom DNS server for redirections
searches:
- "svc.cluster.local"
options:
- name: ndots
value: "2"
```
## Testing Results
✅ Successfully tested with Docker Compose environment
✅ DNS resolution correctly redirects to configured IPs
✅ HTTP requests properly routed to proxy servers
✅ Tested with multiple domains (AWS Bedrock, OpenAI, SageMaker)
Test output:
- bedrock-runtime.us-east-1.amazonaws.com -> 172.25.0.10 ✓
- api.openai.com -> 172.25.0.10 ✓
- sagemaker-runtime.us-east-1.amazonaws.com -> 172.25.0.10 ✓
All DNS redirects working correctly with proxy server receiving traffic.
## Documentation
- Added comprehensive DNS_CONFIGURATION.md guide
- Included examples for common use cases
- Provided troubleshooting steps
## Backward Compatibility
This change is fully backward compatible. If dnsPolicy and dnsConfig are not specified, the default Kubernetes DNS behavior is maintained.
Fixes #[issue_number]
Co-authored-by: LibreChat User <user@example.com>
- Update MongoDB chart dependency from v16.3.0 to v16.5.45
- Add explicit containerPort configuration with fallback to service.port
- Standardize port references in health probes to use explicit port numbers
- Add targetPort and containerPort fields to service configuration
- Include service type options as inline comment for better clarity
These changes improve the Helm chart's port management flexibility and
bring the MongoDB dependency up to date with the latest stable version.
Co-authored-by: Jerum Hubbert <jerum.hubbert@scientificgames.com>