Skip to content

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

Merged
pull[bot] merged 14 commits into
innFactory:mainfrom
danny-avila:main
Aug 25, 2026
Merged

[pull] main from danny-avila:main#231
pull[bot] merged 14 commits into
innFactory:mainfrom
danny-avila:main

Conversation

@pull

@pull pull Bot commented Aug 25, 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 14 commits August 25, 2026 06:26
…#15186)

* 📌 fix: Keep the Settled Turn Mounted Through Final Content Compaction

The agent aggregator writes content parts at provider-source indexes, so
the streamed array is sparse wherever a step produced nothing; the final
SSE event carries the persisted, compacted array. Adopting it verbatim
shifted every part after a hole, re-keying every index-derived React
identity: the settled message remounted wholesale, activity-phase groups
replayed their fold-in entrance, code panes re-highlighted, and the
thread visibly snapped up and down at the end of every tool-calling run.

finalHandler now pairs the compacted parts with their streamed
counterparts in order and stamps each with the index it streamed at
(`streamedIndex`, client-only); render keys read the stamp while all
coordinate logic (edit indexes, phase bounds, cursor) stays on the live
compacted positions the server persisted. Phase-segment keys also anchor
to their first defined part instead of the segment ordinal, since
phantom hole-only segments vanish at compaction and shifted every
segment after them.

* 🔁 fix: Carry Identity Stamps Through Re-Delivered Finals and Parallel Attribution

Codex round 1, both real:
- P1: a later final event can re-deliver an already-settled message as a
  fresh compact array (Assistants runMessages resync); index-aligned
  pairing returned it unstamped, wiping the previous settle's stamps and
  re-keying the older turn all over again. The pairing now carries the
  matched current part's stamp forward, so a settled turn keeps its keys
  through every subsequent final.
- P2: ParallelContentRenderer's sequential stretches invoked
  renderResumeAttribution with only the live index, so steer attribution
  nodes in parallel content still re-keyed at the swap. The stable key
  index now threads through both call sites; getPartKeyIndex moves to
  utils/messages beside the stamp writer it reads.

* 🧿 fix: Require Content Agreement Before Pairing Streamed Identity

Codex round 2 (P2, real): hide_sequential_outputs runs omit intermediate
parts from the final array, so a type-only match could hand the retained
output an omitted intermediate's identity — transferring its key and any
UI state. Non-tool pairing now requires content agreement: mutual-prefix
text for TEXT/THINK/ACTIVITY_LABEL (one side extending the other is the
same part observed at two moments), the Open Responses phase for TEXT,
and the label kind for activity labels — a blank reservation still pairs
with its filled label. Ambiguous shapes fall back to the pre-stamp full
re-key, which is honest for a final that visibly removes parts.

* 🪢 fix: Refuse Stamping When the Server Removed Content; Strip Stamps on Edited Reruns

Codex round 3, two of three real:
- Prefix agreement alone still mis-paired when an omitted intermediate
  happened to prefix the retained output. Pairing now also requires that
  no substantial streamed part is left over: leftovers mean the server
  removed content (hide_sequential_outputs), so every in-order pairing
  is suspect and the message re-keys plainly instead.
- An edited resubmission clones the settled (stamped) prefix and appends
  the rerun's parts at the prefix length; a retained stamp at or above
  that length collides with an appended part's key. The clone now strips
  the client-only stamps, reverting the retained prefix to physical
  identity for the rerun.

The third finding (content-segment keys under late-phase recovery) is
declined with rationale on the PR: user expansion overrides survive via
the message-wide expansion map with stable group ids, recovery is a
genuine restructure at the moment a phase materializes, and first-child
anchoring is the only choice stable under the two high-frequency events
(streaming appends and final compaction).
)

The timer sat at the footer's flush left while everything around it is
inset 6px: the streaming dot pads (24 − 12) / 2 to center on the size-6
header icon's axis, and the hover-button glyphs that replace the timer
sit behind their own p-1.5. The same ps-1.5 inline-start inset lines the
reading up with the dot above it and the glyphs that follow it — measured
in the live app: timer x 382, dot x 382, first settled glyph x 382.
Resolve the transactions config from the request and forward it to both
abort write paths, so `transactions.enabled: false` is honored when a
generation is stopped.
* 🧵 feat: Background Execution Toggles for Actions & Plugin Tools

* 🩹 fix: Resolve action background opt-in across encoded-domain forms and scope it per action

* 🧹 refactor: Resolve action domain in a single pass

* 🧩 fix: Merge Normalized Action Background Options

* 🪢 fix: Reconcile Action Background Aliases

* 🧭 fix: Harden Action Background Compatibility

* 🕰️ test: Allow Settled Task TTL Expiry

* 🧬 fix: Merge Refreshed Action Tool Registrations
Adds helmet's CSP-independent headers (HSTS, X-Frame-Options,
X-Content-Type-Options, COOP, CORP, Referrer-Policy) on every response,
with contentSecurityPolicy explicitly disabled. Every header that can
break a deployment is configurable, so there is no allow-list to go
stale the way #7377's hardcoded CSP directives did.

HSTS includeSubDomains defaults off rather than matching helmet's
on-by-default: it would otherwise pin every sibling subdomain to HTTPS
for a year in every visitor's browser, and undoing that requires
serving max-age=0 from each affected host.
…ce (#15201)

Both floated over the chat on surfaces one step too close to it — the
question popover on surface-secondary, the subagent thread panel on the
chat's own surface-primary. Both now use surface-primary-alt, the
conversation-list sidebar's role, verified in the running app: popover,
panel, and sidebar all resolve to the same computed background in dark
(rgb 23,23,23) and light (rgb 247,247,248). Inline question cards keep
surface-secondary deliberately — that is the tool-record family's
surface, and settled questions collapse into that family.
* 🧾 fix: Honor Disabled Transactions on the Assistants Usage Path

Thread the resolved transactions config through `recordUsage` from each of
its callers, so `transactions.enabled: false` is honored on the assistants
token spend path.

* 🧾 fix: Thread the transactions config through the vision-request caller

Address review: `ToolService.processVisionRequest` also records usage without
the resolved config, and `recordUsage`'s documented return type did not match
the function.

* 🧾 fix: Set the resolved transactions config after the usage spread

- provider usage could carry a `transactions` key that overwrote the trusted value
- matches the ordering the other `recordUsage` callers already use
)

`useGetModelsQuery` seeds from a static fallback config, so `modelsQuery.data`
describes a hardcoded model list both before the mounted fetch resolves and
after it fails outright. The agent builder read that seed as authoritative and
offered models the active server configuration never exposed.

Blank the catalogue until the mounted fetch actually succeeds, surface the
failure in the model panel instead of silently falling back to the seed, and
refuse to create an agent against a provider/model pair the resolved catalogue
does not offer.

Also wires the builder's orphaned `htmlFor` labels to the controls they name.
* 🛡️ feat: Configurable Baseline HTTP Security Headers

Adds helmet's CSP-independent headers (HSTS, X-Frame-Options,
X-Content-Type-Options, COOP, CORP, Referrer-Policy) on every response,
with contentSecurityPolicy explicitly disabled. Every header that can
break a deployment is configurable, so there is no allow-list to go
stale the way #7377's hardcoded CSP directives did.

HSTS includeSubDomains defaults off rather than matching helmet's
on-by-default: it would otherwise pin every sibling subdomain to HTTPS
for a year in every visitor's browser, and undoing that requires
serving max-age=0 from each affected host.

* 🛡️ feat: Nonce-Based Content Security Policy for the SPA Shell

Adds an opt-in, per-response nonce CSP on the HTML response, resolved once
at startup so each request only mints a nonce and concatenates the header.
Report-only by default, since that is the rollout step #7377 skipped.

Rebase and correctness pass over #13226:

- Styles carry no nonce. A nonce in style-src makes browsers ignore
  'unsafe-inline', which would have blocked the <style> element the theme
  script injects at runtime, plus every style third-party components inject.
- frame-ancestors 'self' is now a default rather than opt-in, so enabling
  CSP actually covers the clickjacking half of #7110.
- CSP_SCRIPT_SRC_EXTRA now drops 'strict-dynamic', which would otherwise
  make browsers ignore the very hosts the operator configured.
- Nonce stamping runs after the query-devtools bootstrap injection so that
  injected script is covered too.

* fix: replace frame-ancestors instead of merging it

Merging the configured value into the default turned a deliberate
CSP_FRAME_ANCESTORS='none' into `frame-ancestors 'self' 'none'`, which
browsers resolve back to 'self'. Also bail out if the serialized policy
somehow lacks the nonce slot rather than emitting a header the shell
cannot match.

* fix: address Codex review findings on the CSP defaults

All five were real against LibreChat's actual runtime:

- CSP_REPORT_ONLY now only enforces on an explicit false/off/0/no. A typo
  or `1` previously fell through isEnabled() to enforcing, turning a
  config slip into a blocked SPA. Shares the parse helper with
  headers.ts via a new security/env.ts.
- Module preloads are stamped. A production client/dist/index.html
  carries 32 parser-inserted `<link rel="modulepreload">` tags, which
  'strict-dynamic' does not cover and 'self' cannot rescue.
- Stale nonce attributes are replaced rather than preserved; only the
  current response's nonce is authorized.
- worker-src allows data:, which Monaco's default CDN loader needs to
  bootstrap its workers (there is no loader.config() in the client).
- script-src allows 'wasm-unsafe-eval' for the HEIC upload path, which
  compiles WebAssembly through heic-to. Narrower than 'unsafe-eval'.

Verified against the real built shell: 4 scripts and all 32 preloads
nonced, stylesheets/icons/manifest and <style> untouched.

* fix: address second Codex round on CSP rollout controls

- SECURITY_HEADERS=false now disables CSP too. It is documented as the
  global kill switch, and an operator reaching for it to recover a shell
  broken by an enforcing policy must not be left with that policy on.
- The SPA shell is forced to `no-store` while CSP is enabled, ignoring
  INDEX_CACHE_CONTROL/INDEX_PRAGMA/INDEX_EXPIRES and warning when they
  are set. A cacheable shell pins one nonce across page loads and users,
  which is the whole thing a nonce policy defends against.
- Added CSP_ALLOW_WASM and CSP_ALLOW_DATA_WORKERS. The previous commit's
  .env.example claimed CSP_ADDITIONAL_DIRECTIVES could drop
  'wasm-unsafe-eval' and data:, but merging only ever appends sources, so
  the documented hardening step was impossible. These toggles make it real.
The event subagent group's child rows are raw buttons with no text color
of their own, and the section root set none either, so the agent name
labels inherited straight from the unthemed black body color — invisible
on the dark surface (and silently off-token in light mode: pure black
where --text-primary is 33 33 33). The slug and status lines carried
explicit text-text-secondary, which is why only the names vanished.

Root gets text-text-primary, matching SubagentActivity and
SubagentThreadPanel, so every descendant inherits the theme role and the
rows' secondary lines keep their explicit overrides. Verified against
the live cascade: the label computes rgb(0,0,0) in both modes today and
the token color (236/236/236 dark, 33/33/33 light) with the root themed.
* 🛑 fix: Separate Agent Event Backpressure From User Bans

* fix: Address Agent Event Review Findings

* fix: Mirror Case-Insensitive Agent Control Routing
…5204)

* ⏳ fix: Anchor Resumed Elapsed Time at the Generation's Real Start

A reload emptied the Recoil anchor, so the indicator fell back to its
mount time and visibly reset to 0s over a run that had been generating
for much longer. The stream status the resume path already reads carries
the server-recorded generation start; the fill now prefers it, so a
reattached run reports real elapsed time. The fill remains fill-only:
a same-session reattach keeps its original ask baseline, and the
indicator's existing clamp absorbs any client/server clock skew.

* 🕰️ fix: Rebuild the Resumed Baseline From the Server-Computed Age

Codex round 1: anchoring at the server's raw createdAt compares two
clocks — a client behind the server froze the resumed reading at 0s for
the skew, one ahead inflated it. The status route now also reports the
generation's age computed on its own clock, and the client rebuilds a
clock-local anchor as Date.now() minus that age, so each machine only
ever compares to itself. Raw createdAt stays as the fallback for an
older server mid-rolling-deploy.

* 📥 fix: Compute Elapsed Age in TypeScript, Anchor It at Status Receipt

Codex round 2: the elapsed computation moves into packages/api as
getGenerationElapsedMs — the route now just delegates, keeping the
response contract type-checked and the /api surface a thin wrapper —
and the client subtracts the age from the moment the status response
arrived (dataUpdatedAt) rather than from apply time, so a slow history
fetch between receipt and apply can no longer shrink the reading.
Declined with rationale: a shared clock source across replicas — the
residual is inter-replica NTP drift, milliseconds against the
minutes-scale client skew this PR eliminates, and the helper gives any
future shared-clock upgrade a single home.
@pull pull Bot locked and limited conversation to collaborators Aug 25, 2026
@pull pull Bot added the ⤵️ pull label Aug 25, 2026
@pull
pull Bot merged commit 6d499ba into innFactory:main Aug 25, 2026
1 check 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.

4 participants