Skip to content

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

Merged
pull[bot] merged 7 commits into
innFactory:mainfrom
danny-avila:main
Sep 2, 2026
Merged

pull[bot] merged 7 commits into
innFactory:mainfrom
danny-avila:main

Conversation

@pull

@pull pull Bot commented Sep 2, 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 : )

* feat: add safe default approvals for BYOM code

* fix: preserve safe BYOM approval baseline
* 🔁 fix: Recover Keyv Redis Client From READONLY Replica After Failover

When a Sentinel failover demotes the master a pod is connected to, the
ioredis client recovers through `reconnectOnError`, but the Keyv/node-redis
client never does: `socket.reconnectStrategy` only runs when the socket
closes, and a demoted replica keeps existing sockets open while rejecting
every write with `READONLY`. Every cache write through the Keyv client
(USER_PRINCIPALS, MCP catalog, server configs) then fails until restart.

- Add a READONLY recovery hook for the standalone node-redis client that
  tears the socket down and reconnects, re-resolving to the current master
- Debounce attempts with `REDIS_READONLY_RECOVERY_INTERVAL` (5s default)
  and retry on the next error, since a reconnect issued before the
  topology settles can land on the demoted node again
- Route all three surfaces where READONLY replies appear through the hook:
  the client error event, the Keyv cache error funnel, and Lua scripts
  evaluated via `evalKeyvRedisScript`, which reject straight to callers
- Skip cluster clients, which follow MOVED to the owning master already
- Cover the hook with a fake RESP server so real node-redis and Keyv code
  paths are exercised against a node whose role flips mid-connection

Fixes #15500

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WNbgMeR2aZDyhsqAy4L8gF

* 🛟 fix: Harden READONLY Recovery Retry, Clear Path, and Clock

Address review findings on the READONLY failover recovery:

- Retry on the next error of any kind after a failed reconnect. A failed
  `connect()` leaves the client closed, so later failures surface as
  ClientClosedError rather than READONLY and the hook never fired again
- Route the namespace `clear()` override through recovery. It deletes
  through the raw client, bypassing the Keyv error funnel, so a READONLY
  rejection there previously left the pod stranded
- Debounce on an injectable monotonic clock (`performance.now`) instead of
  `Date.now`, so wall-clock steps cannot suppress or duplicate attempts;
  the interval test drives the clock instead of sleeping
- Reuse `isReadonlyReplicaError` in the ioredis `reconnectOnError` hook so
  READONLY detection has one definition
- Document the in-flight command rejection that a socket teardown implies

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WNbgMeR2aZDyhsqAy4L8gF

* 🛟 fix: Gate READONLY Recovery on Client State

Address the second review pass on the READONLY failover recovery:

- Act only on a client the hook can reason about: a READONLY reply on a
  ready socket, or a client its own failed reconnect left closed. While
  node-redis runs its own reconnect loop (open but not ready) the hook
  stays out, since a second `connect()` would start a concurrent loop
  that leaks sockets into one shared reply decoder
- Clear the pending retry whenever an error arrives on a ready client, so
  a client that Keyv auto-connected after a failed attempt is never torn
  down by a later unrelated error
- Drop the client error-event wiring: node-redis rejects command error
  replies straight to the command promise and emits `error` only from its
  socket and connect loop, so that listener could never see a READONLY
  reply and only fed connect-loop errors back into the hook
- Cover the connect-loop, self-recovered, and Keyv auto-connect outage
  cases, and a rejected namespace clear, with a SCAN-aware RESP server
- Add the new export to the `redisClients` mocks in the cacheFactory specs
Queue a message during a run — queue, not steer — and the row is server-owned:
`useQueueDrain` deliberately declines the boundary (`serverOwnsBoundary`) and
the backend admits the successor itself. So the client has to hear about that
run through the active-job list, exactly like a background-tool continuation.

It cannot. Finishing a run empties the list — the client removes its own job
optimistically the moment FINAL lands — and the poll is disabled on an empty
list, so it goes quiet at precisely the moment the successor is admitted. With
the tab already focused there is no focus refetch either, which is why this
reproduces on desktop and not only on mobile. The announcement never arrives,
so the re-arm never runs and the pane sits on the previous turn until a reload.

Keep asking for a bounded window after the last known job ends. The window is
module-scoped rather than per-observer because this is one shared query and a
component mounting mid-handover should inherit the grace, not restart it.

This is the other half of the previous commits: without the re-arm the
announcement lands on a pane that still reads its finished submission as an
attachment, and without this the announcement never lands at all. Neither half
is observable alone.
* feat: add audit-only content filter action

* refactor: align filter action typing and tests

* fix: preserve audit-only filter behavior

* fix: enforce audit mode across content paths

* fix: honor audit mode for canonical files
* test: add mobile chat performance benchmark

* fix: isolate mobile benchmark measurements
* feat: add configurable BYOM permissions

* test: cover BYOM permission settings live

* fix: harden BYOM permission settings

* perf: reuse principals for environment discovery

* chore: normalize BYOM settings style

* fix: Align BYOM Discovery and Admission

* fix: Harden BYOM Permission Boundaries

* fix: Align BYOM Skill Approval Routing

* fix: Preserve Lazy BYOM Approval Capabilities

* perf: Parallelize Cached Environment Reads
Claude Fable 5.1 (`claude-fable-5-1`, GA 2026-09-01) succeeds Claude Fable 5
at the same input/output prices, with a 1M context window and 128K max output.
The Mythos-class helpers added for Fable 5 already match it — `isMythosClassModel`
tests `claude-(?:fable|mythos)[-.]?\d`, so adaptive thinking, summarized-display
opt-in, sampling omission, 1M context, the 128K output clamp, prompt caching,
the Vertex multi-region gate, and the Bedrock PDF exemption all cover 5.1
without a code change. What was missing was registration and pricing.

Cache reads are the one rate that differs, and the substring matcher would have
silently gotten it wrong: `claude-fable-5-1` falls through to the `claude-fable-5`
key, which prices cache hits at the usual 0.1x base input ($1/MTok). Fable 5.1
and Mythos 5.1 price them at 0.025x ($0.25/MTok), so every cache read would have
billed 4x — on exactly the long agentic sessions this model is built for.

- Register `claude-fable-5-1` in the shared Anthropic list and
  `global.anthropic.claude-fable-5-1` in the Bedrock list
- Add 1M context / 128K output entries for `claude-fable-5-1` and
  `claude-mythos-5-1`
- Add $10/$50 rates and `{ write: 12.5, read: 0.25 }` cache rates for both
- Update the `.env.example` and Vertex `librechat.example.yaml` examples
- Test that 5.1 resolves to its own key rather than collapsing onto 5.0, across
  token maps, pricing, cache rates, and Mythos-class detection

The three documented breaking changes from Fable 5 need no handling here:
LibreChat never sends a forced `tool_choice` on the Anthropic path (the agents
SDK omits the param when unset, so the API default `auto` applies); thinking
blocks an earlier model can't read are dropped server-side, unbilled; and
history is rebuilt from stored content parts rather than replayed signed
thinking blocks, so the prefix-binding check has nothing to reject.

Refs #15506
@pull pull Bot locked and limited conversation to collaborators Sep 2, 2026
@pull pull Bot added the ⤵️ pull label Sep 2, 2026
@pull
pull Bot merged commit d5b2a85 into innFactory:main Sep 2, 2026
21 of 22 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.

1 participant