Skip to content

chore(reconcile): integrate upstream v1.1.0 with fork behaviors retained - #24

Merged
rschlaefli merged 117 commits into
mainfrom
chore/reconcile-upstream-v1.1.0
Sep 17, 2026
Merged

rschlaefli merged 117 commits into
mainfrom
chore/reconcile-upstream-v1.1.0

Conversation

@rschlaefli

@rschlaefli rschlaefli commented Sep 17, 2026

Copy link
Copy Markdown
Member

Merges upstream LibreChat-AI/code-interpreter v1.1.0 (tag b35c503fd2fe, chart 0.3.1, appVersion 2.0.0) into the UZH fork while replaying every fork behavior onto the new upstream tree. Nothing in current fork behavior is dropped; the merge delta is 108 upstream commits since the PR #17 reconcile point (2c7fb8fc).

The fork is still required after this release: v1.1.0 absorbs none of the ten fork behaviors. Upstream remains single-issuer JWT trust, raw Winston/Pino logging, five-attempt Redis reconnect, event-only job completion, single-namespace Helm, chart-owned secrets/HPA, hook-based package-init, and has no image-publication workflow. The fork is active production GitOps infrastructure (df-cloud Argo Applications pinned to UZH GHCR images), so the reconciliation keeps those contracts while adopting upstream's new BYOM/remote-worker system, hosted apps, PTC cancellation, workspace tools and admission capacity, artifact delivery/truncation, express 5 / nanoid 5, chart 0.3.1, and the new security fixes (bounded upload memory, input-file limits into guests, credential ACL).

What changed

Merge and conflict resolution. Eight files conflicted: api/Dockerfile, docker/Dockerfile.worker-sandbox, launcher/Dockerfile, service/rollup.config.js, service/tsconfig.json, service/src/auth/librechat-jwt.ts, service/src/queue.ts, service/src/service/programmatic-router.ts. The Dockerfiles adopt upstream's docker/rootfs-setup.c, guest-dns.sh and hosted-app launcher, and keep the fork's chmod 0555 on both spec-guard and sandbox-rootfs-setup; rollup/tsconfig take the union including ../shared/operational-log.ts. Helm templates, values.yaml, ci.yml, egress-ledger.ts and the logger sinks auto-merged and were verified rather than replayed.

Adopted upstream capabilities. The BYOM remote-worker system and worker admission, hosted-app control plane, PTC cancellation across processes, confined local workspace tools, artifact delivery/truncation reporting, express 5 / nanoid 5, chart 0.3.1, and the security fixes above.

Retained fork behaviors (ten logical patches, ledger in docs/fork/patches.md). Exact-SHA UZH GHCR images; readable spec-guard roots (0555); split/hardened untrusted sandbox namespace; ceded ownership to external controllers; Argo-safe PVC package-init (de-hooked to sync-wave -5, marked for retirement review after rollout); BullMQ poll fallback composed into the new cancellation-aware waiter; egress-ledger indefinite reconnect (narrowed to egress-ledger.ts, since the queue-client finite-retry concern is resolved upstream); issuer-scoped JWT trust table extended with code_worker_id; values-free operational logs; nonfatal telemetry (PR #23).

Final-review fixes (commit 53ed390)

One integrated final review (correctness, maintainability, security) returned PASS_WITH_CONCERNS with three required fixes; all three are addressed in 53ed390 with covering tests:

  • Completion event channel (service/src/queue-wait.ts) — the event channel now resolves from the QueueEvents payload instead of re-fetching a possibly-evicted job, and a poll failure grants one in-flight event poll interval before propagating. Without this, handleBlocking could surface a successful execution as an error when a concurrent completion evicted the job.
  • Fallback poll lifecycle (service/src/job-cancellation.ts, service/src/service/programmatic-router.ts) — the fallback poller now receives an abort signal owned by waitForJobWithCancellation and stops on the first terminal outcome; retention restored to removeOnComplete.count: 100 at both enqueue sites, which the merge had silently reverted to upstream's 1 and which left the replay fallback inert.
  • code_worker_id bounding (service/src/auth/librechat-jwt.ts) — a multi-entry trust table must now declare codeWorkerIdPrefixes for its external entry and the claim is verified against them; a single-entry table stays unconstrained for backward compatibility. This closes a cross-issuer bridge-worker routing gap that only appears with CODEAPI_BRIDGE_DYNAMIC_WORKERS=true and two or more trust entries.

A focused confirmation pass over 53ed390 (Claude Opus 5, trusted read-only) confirmed fixes 1 and 3 and found one regression plus two unpinned behaviors; all are closed in a0ebd3c:

  • TypeScript regression — the completion handler had been typed with the result type, which produced two TS2345 errors against BullMQ's string declaration; the field is now declared as BullMQ does and converted through unknown.
  • Unpinned abort contract — removing either fallbackAbortController.abort() call left the suite green, so the fallback poller's lifecycle is now covered: it runs while the wait is pending, stops once completion wins, aborts when subscription registration fails, and a rejecting poller cannot override the completion outcome.
  • Prefix isolationcodeWorkerIdPrefixes had no cross-entry uniqueness check, so two external entries could declare duplicate or nested prefixes and each admit the other's bridge workers. Overlapping prefixes are now rejected at config time, mirroring the existing keyId and external-source checks.
  • Shadowed parameter — the fallback poller's parameter no longer shadows the client-disconnect signal in programmatic-router.ts.

Two residual, non-required findings are recorded as follow-ups, not slices: internal principal sources are not de-duplicated across trust entries (inherited pre-fix), and Redis TLS keeps rejectUnauthorized: false upstream-inherited. The inherited 7 packages/code failures, tests/kvm_guest_dns.sh failure, and the trailing blank line in service/src/minio-client.ts are byte-identical to pristine v1.1.0 and are not regressions from this package.

Review and validation

Local verification is green at the pushed head:

  • service: 1076 pass / 0 fail (11 Redis skips); bun run build OK
  • api: 481 pass / 0 fail; bun run build OK
  • helm lint OK; chart 0.3.1 render verified for STG+PRD ownership (no chart Secret/HPA in external mode; package-init Job + PVC only in pvc mode, at sync-wave -5)
  • deployment-config tests pass: block_root_package_delivery.sh, sandbox_runner_healthcheck.sh, bridge_pairing_rollout.sh, release-versioning.sh, compose-bridge-config.cjs
  • spec-guard compile + smoke PASS in ubuntu:24.04; docker buildx --check clean on the api and worker-sandbox Dockerfiles
  • git diff --check clean apart from the inherited v1.1.0 blank-line-at-EOF
Verification receipts (commands and revisions)

Tested revision: 53ed390 code head (docs-only 5e49e0d on top). Reused evidence: packages/code failure set is byte-identical to pristine v1.1.0 under container; tests/kvm_guest_dns.sh fails identically on pristine v1.1.0. A single bridge/store.test.ts DNSException: getaddrinfo ENOTFOUND flake reproduces at the pre-fix commit and is a missing local redis-server, not a CI concern. Planner gate recorded as blocked (native planner route failed on an account usage limit), not passed; plan is docs/project/2026-09-17-upstream-v1.1.0-integration-plan.md.

Out of scope

Deployment, image publication, GitOps promotion, and any cluster change are excluded. Merging into main, closing superseded dependabot PRs, and the PR #21 re-derivation are separately gated decisions, not part of this package.

upman and others added 30 commits August 25, 2026 20:05
* fix(codeapi): add session cache recovery utility

* fix(codeapi): accept emitted session keys
…AI#71)

Source: ClickHouse/ai@573e859e52c5e8566843556438bdc2e479f011b4

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
* feat: add outbound stateful code bridge

* test: cover remote bridge startup policy

* fix: harden remote bridge lifecycle fencing

* fix: harden remote bridge assignment lifecycle

* fix: close remote bridge commit races

* fix: surface shutdown workspace quarantine

* fix: fail closed across bridge lifecycle gaps

* fix: persist stateful settlement commit barriers

* fix: bound bridge control-plane timing

* fix: recover abandoned bridge leases

* fix: fence stateful bridge workspaces

* fix: recover bridge lease read failures

* fix: add safe workspace fence recovery

* fix: bound bridge liveness timers

* fix: bound bridge cleanup recovery

* fix: preserve bridge deadline fencing

* fix: acknowledge bridge lease delivery

* fix: close bridge deadline gaps

* fix: harden bridge recovery edges

* fix: preserve bridge rejection recovery

* fix: fence bridge lease recovery

* fix: bound bridge control lifetimes

* fix: retain bridge recovery ownership

* fix: isolate bridge control state

* fix: bound bridge workspace reset

* fix: validate bridge deployment inputs

* fix: close bridge registration and deadline races

* fix: anchor bridge lease freshness

* fix: preserve bridge response status
* feat: add outbound stateful code bridge

* test: cover remote bridge startup policy

* fix: harden remote bridge lifecycle fencing

* feat: add secure code worker pairing

* fix: harden paired worker lifecycle

* fix: require paired auth on hardened APIs

* fix: harden bridge pairing startup policy

* fix: preserve bridge fencing through pairing

* fix: distinguish assignment settlement conflicts

* fix: harden remote bridge assignment lifecycle

* fix: close remote bridge commit races

* fix: surface shutdown workspace quarantine

* fix: fail closed across bridge lifecycle gaps

* fix: persist stateful settlement commit barriers

* fix: bound bridge control-plane timing

* fix: recover abandoned bridge leases

* fix: fence stateful bridge workspaces

* fix: recover bridge lease read failures

* fix: add safe workspace fence recovery

* fix: bound bridge liveness timers

* fix: bound bridge cleanup recovery

* fix: preserve bridge deadline fencing

* fix: acknowledge bridge lease delivery

* fix: close bridge deadline gaps

* fix: harden bridge recovery edges

* fix: preserve bridge rejection recovery

* fix: fence bridge lease recovery

* fix: bound bridge control lifetimes

* fix: retain bridge recovery ownership

* fix: isolate bridge control state

* fix: bound bridge workspace reset

* fix: validate bridge deployment inputs

* fix: close bridge registration and deadline races

* fix: anchor bridge lease freshness

* fix: preserve bridge response status

* fix: harden paired bridge lifecycle

* fix: sustain paired credentials in flight

* fix: fence credential refresh deadlines

* fix: fence sandbox start at deadline

* fix: complete paired worker revocation

* fix: make worker revocation atomic

* fix(bridge): fence queued registration after revoke
* feat: add outbound stateful code bridge

* test: cover remote bridge startup policy

* fix: harden remote bridge lifecycle fencing

* feat: add secure code worker pairing

* fix: harden paired worker lifecycle

* fix: require paired auth on hardened APIs

* fix: harden bridge pairing startup policy

* fix: preserve bridge fencing through pairing

* fix: distinguish assignment settlement conflicts

* feat: add principal-bound bridge workers

* fix: authenticate principal worker routing

* fix: fence bridge identity and backend routing

* fix: fence bridge redemption and queue routing

* fix: preserve legacy routing and assignment auth

* fix: preserve long-lived bridge assignments

* fix: persist replay queue backend

* fix: fence bridge replay and credential rotation

* fix: address principal worker review findings

* fix: reconcile principal workers with bridge fencing

* fix: fence principal worker lifecycle transitions

* fix: fence bridge settlement ownership

* fix: fence bridge leases to active principals
* feat: add outbound stateful code bridge

* test: cover remote bridge startup policy

* fix: harden remote bridge lifecycle fencing

* feat: add secure code worker pairing

* fix: harden paired worker lifecycle

* fix: require paired auth on hardened APIs

* fix: harden bridge pairing startup policy

* fix: preserve bridge fencing through pairing

* fix: distinguish assignment settlement conflicts

* feat: add principal-bound bridge workers

* fix: authenticate principal worker routing

* fix: fence bridge identity and backend routing

* fix: fence bridge redemption and queue routing

* fix: preserve legacy routing and assignment auth

* fix: preserve long-lived bridge assignments

* fix: persist replay queue backend

* fix: fence bridge replay and credential rotation

* fix: address principal worker review findings

* fix: reconcile principal workers with bridge fencing

* fix: fence principal worker lifecycle transitions

* fix: fence bridge settlement ownership

* fix: fence bridge leases to active principals

* fix: invalidate pending worker pairings on revoke

* fix: package bridge protocol in API image

* fix: fence mixed-version pairing revocation

* fix: redeem valid legacy pairing codes

* fix: harden pairing rollout compatibility

* fix: make pairing revocation atomic

* fix: preserve pairing identity across rollouts

* fix: reopen pairing cleanup after rollbacks

* fix: bound legacy pairing migration scans

* fix: retry interrupted pairing migrations

* fix: harden pairing migration cleanup

* fix: make pairing cleanup recoverable

* fix: preserve pairing recovery across lifecycle rollout

* fix: drain API pods before pairing rollback

* fix: fence rollback reentry and pairing epochs

* fix: close rollback verification gaps

* fix: keep rollback drain on one cluster

* fix: bound rollback recovery triggers
…ibreChat-AI#65)

The /upload and /upload/batch endpoints in router.ts construct busboy
without specifying defCharset or defParamCharset. Busboy defaults to
Latin-1 (ISO-8859-1) for multipart parameter decoding, which causes
non-ASCII filenames (e.g. Japanese characters) to be garbled.

file-server.ts already sets defCharset: 'utf8' and
defParamCharset: 'utf8' (lines 336-337), but router.ts was missing
the same options — an asymmetry that surfaces as mojibake when
uploading files with non-ASCII names through the API gateway.

Add both options to the two busboy() calls in router.ts for
consistency with file-server.ts.
…lization (LibreChat-AI#75)

* fix: large tool inputs break bash PTC replay and pending-call serialization

* fix: reject multi-document tool input and invalid PTC byte-cap env values

* fix: validate floored PTC byte caps to avoid a zero-cap collapse

---------

Co-authored-by: kenzaelk98 <254484110+kenzaelk98@users.noreply.github.com>
* feat: Add Stateful Runtime Supervisor 🧰

* fix: harden runtime supervisor lifecycle
* feat: add Docker runtime supervisor

* fix: preserve Docker runtime lifecycle safety

* fix: close interrupted Docker lifecycle paths
* feat: add Docker supervisor CLI mode

* fix: allow image-free Docker workspace reset
Deployments currently have to track main, which advances whenever an
internal snapshot is merged. Cut versioned tags instead, each carrying
the packaged Helm chart so a deployment can pin one.

The tag is the app version and must match helm/codeapi/Chart.yaml
appVersion, so a deployed chart cannot report a version no release ever
carried. `latest` moves only for the highest stable tag, and the chart is
packaged before the tag is created so a rate-limited subchart pull leaves
the version unused and the run retryable.

Closes LibreChat-AI#63
* feat: add local NsJail runtime profile

* fix: refresh stale local runtimes

* fix: surface local runtime state loss
* fix(lambda-microvm): give each PTC replay iteration a distinct clientToken

PTC replay reuses one executionId across every stateless iteration, but
the launch clientToken was derived from that executionId alone. Each
iteration relaunches with a changed sandbox payload (a fresh
_ptc_history.json), so AWS rejected the second launch with "The provided
clientToken was used with different request parameters" and LibreChat
surfaced the generic "Bash programmatic execution failed" (LibreChat-AI#59).

Fold the launch inputs and the per-iteration request body into the token
so each distinct launch gets a distinct token while an identical retry
stays idempotent. Reuses runtimeSessionLaunchRequestFingerprint rather
than restating the launch inputs.

Reported with a working patch by @snapydziuba.

* fix(lambda-microvm): key the stateless launch token to the queued job

Addresses codex review on LibreChat-AI#82.

Hashing the request body made the token move between attempts of the same
job: workers.ts rebuilds the request on every attempt with a fresh egress
grant (random IV and sandbox session id) and a re-signed manifest, so a
replacement worker taking over a stalled job would derive a different
token, launch a second VM, and leave the accepted one burning capacity
until its maximum duration expired.

Use the queued job id instead. Each PTC replay iteration is enqueued as
its own job, so it is distinct per iteration and stable across attempts of
the same job -- and it carries no capability-bearing material. The launch
configuration stays in the digest so a worker with a different config
cannot reuse another's token.
* fix: preserve uploaded filenames without s3 metadata

* fix: preserve filenames through hardened egress
Guest clock drift past the 30s execution-manifest tolerance makes every
/v1/exec fail with "not_yet_valid" while both health endpoints keep
reporting healthy, so the stack looks fine while nothing runs (LibreChat-AI#37).

The healthcheck already detects this, but it stays disabled unless an
orchestrator opts in, and the Compose files never did -- only the Helm
chart set it. Opt in there too, at the same 10s the chart uses.

The 2s probe timeout keeps the check inside both files' healthcheck
timeouts (3s and 5s) and leaves headroom under the 30s tolerance.
* fix(helm): resolve Bitnami subcharts over OCI

Bitnami distributes charts OCI-only. The classic charts.bitnami.com index
still lists redis 24.1.0 and minio 17.0.21, but resolves them to an oci://
download URL that HTTP-repository getters cannot follow, so FluxCD's
source-controller fails dependency resolution outright with
'unsupported protocol scheme "oci"' (#21).

Point both dependencies at the OCI registry directly. Requires Helm >= 3.8.

Reported by @meroo36.

* docs(helm): require Helm >= 3.8 for OCI subchart resolution

Addresses codex review on LibreChat-AI#83.

The README's "Helm 3.x" prerequisite and setup-local.sh's existence-only
check both allowed 3.0-3.7, where OCI dependency references are not
resolved without an experimental flag -- so the documented setup flow
would fail at dependency resolution rather than with a clear message.

State the real minimum, and reject older Helm in setup-local.sh before it
gets that far. Also drop the classic bitnami repo registration, which the
OCI references no longer use; verified 'helm dependency update' resolves
both subcharts with that repo removed from the local Helm config.

* chore(helm): bump chart to 0.3.1 for the dependency source change

Addresses codex review on LibreChat-AI#83.

Changing where the subcharts resolve from is a chart-level change, and
Chart.yaml's own version comment asks for a bump. Leaving 0.3.0 in place
lets consumers reconciling on chart version treat the corrected chart as
the already-seen 0.3.0 artifact and keep the broken HTTP dependency
metadata. Matches 4b72e9d, which bumped the chart for the same reason.
* feat: add networkless BYOM file relay

* fix: harden managed file relay

* fix: fence relay lifecycle races

* fix: order relay handoffs by registration

* fix: gate relay workers on runtime readiness

* fix: preserve legacy marker listings

* fix: recover reclaimed relay staging
* 🗑️ fix: Make Code Environment File Deletion Work

Object deletion has never removed anything, and the failure was silent at
every layer.

The client (LibreChat `deleteCodeEnvFile`) issues DELETE against
`/v1/sessions/:session_id/objects/:fileId`, the file-server's own path,
which is not exposed on `/v1` — only GET is mounted there. Every deletion
404'd, and a 404 is indistinguishable from "already gone", so the caller
cleared its state and the bucket only ever grew (13 GiB / 29k objects on a
six-week-old deployment, per danny-avila/LibreChat#15511).

Mount DELETE on that path as an alias of `/v1/files/:session_id/:fileId`,
so deployments running a client older than LibreChat v0.8.6 — before the
fallback to `/files/...` landed — delete successfully. Pass the
file-server's 404 through instead of collapsing it into a 500: a 500 reads
as retryable, and a client sweeping its retention window re-issues the same
DELETE hourly, forever, for an object that no longer exists.

Correcting the route is not sufficient on its own. `sessionAuth` authorizes
deletion against `session:<session_id>`, whose `SESSION_CACHE_TTL` is 24h
and is not refreshed by use, so an object was deletable only for the day
following upload and stranded permanently after that — unreadable, unusable
as an execution input, and undeletable through every route. Clients are
typically far outside that window when they get there; LibreChat's default
retention is 30 days.

Record ownership twice: `session:<id>` stays the hot-path cache bounding
read access, and a durable `session-owner:<id>` record (`SESSION_OWNER_TTL`,
90 days, never shorter than the cache TTL) backs deletion once the cache key
has lapsed. The fallback applies to DELETE only — reads keep the window they
have always had — and a live cache key naming a different owner remains
authoritative, so a re-registered session is never deletable by its previous
owner.

The recovery script restores both records, so a rehydrated session stays
deletable rather than stranding again a day later.

* fix: Close Codex review findings on session ownership

Four P2 findings from the review of daae56c:

- The 404 deletion path cleared the upload key with a bare `await` inside
  the catch block. A Redis failure there rejects with no handler above it,
  and Express 4 does not forward async rejections, so the request would hang
  instead of answering 404. Make the cleanup best effort and log it.

- The blocking PTC path discarded the registration promise with `void`,
  preserving the previous fire-and-forget behavior. That now spans two keys:
  a partial write (cache key stored, durable record refused by a Redis ACL
  scoped to `session:*`) would produce exactly the undeletable files this
  change exists to prevent. Await it; the caller turns a rejection into a
  500 before anything is enqueued.

- Recovery treated a durable owner record naming someone else as a log line
  while still counting the session as restored or matching, so an apply
  could exit 0 having recovered nothing usable. Reconcile the owner record
  before touching the cache key and report the disagreement as a conflict,
  in dry run as well as apply. The cache key is no longer restored for those
  sessions either — the manifest's claimant should not get a day of access
  the service never granted it.

- `SET NX` cannot extend an expiry, so a matching owner record could carry
  less remaining TTL than the cache key being restored and lapse first,
  stranding the session again just as recovery reported success. Top up the
  expiry when it is shorter than the target, leaving longer ones alone.

* fix: Settle session ownership before recovery writes anything

Two findings from the review of 816dbb5:

- Reconciling the durable owner record first meant creating it before the
  live cache key had been consulted. For a session whose durable record was
  absent and whose cache key named a different owner, recovery wrote a
  durable record for the manifest's claimant, then reported the cache
  conflict and moved on — leaving the record behind. It outlives the cache
  key by design, so once that expired, `sessionAuth` would authorize the
  manifest owner to delete the real owner's files.

  Split the read from the write. The durable record is now inspected
  read-only up front, where a disagreement still settles the session before
  anything is written, and is created or extended only once the cache key
  has been confirmed to name the same owner.

- `/exec` registered ownership before entering the route's `try`. Express 4
  does not forward a rejected async handler to the error middleware, so a
  Redis failure there would hang the request rather than answering. Guard it
  and return a controlled 500.

* fix: Harden recovery's durable owner handling

Three findings from the review of 47b36e0, all in the recovery script:

- A `SET NX` that lost the race to a key which then expired before the
  follow-up read left no record and no conflict, and the session was
  reported as recovered while its durable half did not exist. Retry once,
  and report anything past that as missing so an apply exits nonzero
  instead of claiming success.

- When the owner commit conflicted on a session whose cache key this run
  had just created, the cache key stayed. That grant authorizes reads and
  deletes for its full TTL while the durable record names somebody else, so
  roll it back. A durable record that merely could not be created is left
  alone: the session is no worse off than before the run, and removing the
  grant would leave the operator with nothing.

- A dry run reported a session whose cache key already matched as
  `matching` even when its owner record was absent or short-lived, hiding
  the work an apply would do and inviting operators to skip it. Pending
  owner repairs now count as missing.

* fix: Answer Redis failures instead of hanging on them

Two findings from the review of d1de664:

- `sessionAuth` awaited the ownership lookup unguarded. Express 4 does not
  forward a rejected async middleware, so an unavailable Redis — or an ACL
  granting `session:*` but not `session-owner:*` — would hang a DELETE
  rather than answering it. Catch and return a controlled 500.

- The recovery script's TTL top-up read, extended and returned across three
  round trips, reporting success on evidence it had not rechecked. The
  record can lapse in between, in which case it is now created fresh, or
  name somebody else, in which case the session is a conflict. Extending a
  record that turns out to belong to another owner prolongs a claim the
  service itself wrote and grants nothing new, but reporting the session as
  recovered on that basis would not be true.
* feat(code): add confined local workspace tools

* fix(code): confine workspace text search

* fix(code): center bounded search previews

* fix(code): tighten workspace result semantics

* fix(code): harden workspace text search

* fix(code): normalize workspace text boundaries
* feat(code): dispatch local workspace tools

* fix(code): fence workspace tool completion

* fix(code): drain workspace cancellation before settlement

* fix(code): drain active cancellation responses

* fix(code): preserve drained cancellation fences

* fix(code): Fall back from invalid workspace names
* feat(code): add workspace tool API

* docs(code): clarify repository-optional workspaces

* fix(code): validate workspace API boundaries

* fix(code): Harden workspace API validation

* fix(code): Preserve workspace search scope

* fix(code): close workspace API lifecycle gaps
* feat(code): list attached workspace files

* fix: preserve canonical workspace listings

* fix: preserve exact UTF-8 workspace paths

* fix: preserve workspace listing policy
* feat: add dedicated Lambda hosted-app runner

* fix: serialize hosted app cgroup cleanup

* fix: serialize hosted app workspace lifecycle

* fix: fence hosted app replacement cleanup

* fix: preserve hosted app lifecycle invariants

* fix: complete hosted app workspace coordination
…t-AI#94)

Redo of the Dependabot bump, which only updated `api/package.json` and
`api/package-lock.json`:

  - `api/bun.lock` is the lockfile CI and `api/Dockerfile` actually
    install from (`bun ci` / `bun install --frozen-lockfile`), and it was
    left on express 4, so every api job failed with "lockfile had
    changes, but lockfile is frozen".
  - The `path-to-regexp: 0.1.13` override pinned express 4's router
    dependency. Express 5 routes through `router@2`, which needs
    path-to-regexp ^8, so the override had to go rather than be carried
    forward.
  - qs stayed on 6.15.3: it already satisfied express 5's `qs@^6.14.0`,
    so resolution never moved it to the 6.16.0 the bump was for.

Both lockfiles are regenerated and agree on express 5.2.1, qs 6.16.0,
path-to-regexp 8.4.2 and router 2.2.0.

The api's express surface is v5-clean: all route paths are literal (no
wildcards or optional params for path-to-regexp 8 to reject), `req.query`
is never read (so the query-parser default change is inert), and the only
`req.body` reads are behind a JSON parser, an `application/json` guard, or
already undefined-tolerant. `@types/express` was already ^5.0.0, so the
types now match the runtime instead of being a version ahead of it.

Co-authored-by: Danny Avila <danny@librechat.ai>
* feat(code): add opt-in workspace mutations

* fix(code): fence workspace mutation commits

* fix(code): harden workspace replacement invariants

* fix(code): close workspace mutation lifecycle gaps

* fix(code): revalidate edits after metadata restore

* fix(code): persist mutation quarantine across restarts

* fix(code): fence uncertain mutation executor failures

* fix(code): close durable mutation guard races

* fix(code): bind durable guards to workspace roots

* fix(code): harden mutation durability

* fix(code): fence concurrent workspace mutations
danny-avila and others added 26 commits September 14, 2026 01:27
* feat: run PTC in selected BYOM workspaces

* fix: harden native workspace PTC replay

* fix: preserve replay isolation and bridge limits

* test: tolerate hosts without filesystem cloning

* test: surface copy-on-write clone faults

* fix: harden native workspace PTC admission

* fix: close native replay effect and finalization boundaries
* feat: report truncated output artifacts

* fix: classify omitted artifacts precisely

* fix: preserve artifact scan invariants

* fix: bound depth truncation probes

* fix: bound capped directory enumeration

* fix: constrain truncation probes across the job

* fix: stop exhausted artifact probes
* fix: cancel replay jobs across API and worker processes

* fix: drain worker cancellation watches promptly

* fix: close programmatic cancellation races

* fix: preserve cancellation response ordering

* fix: close distributed cancellation races

* fix: harden cancellation under concurrent load

* fix: make cancellation ownership durable through completion

* fix: recover durable replay outcomes across lost replies

* fix: return atomic cancellation outcomes with aligned retention

* fix: commit native results inside the workspace mutation fence

* fix: claim programmatic execution before stalled-job redelivery
* fix: report deleted persisted files

* fix: reconcile deletions across code runtimes

* fix: preserve protected session inputs

* fix: classify reserved runtime paths
* feat: declare named worker project environments

* fix: preserve environment trust and negotiated action boundaries

* Harden environment loading and executor identity

* Protect environment root traversal and exact config bytes

* Reject self-controlled environment root aliases

* Check filesystem identities at environment trust boundaries

* Validate environment containment across Linux mount aliases

* Handle stacked mounts conservatively without blocking unrelated paths
* fix: Allow Trusted Own-Root Environment Symlinks

* fix: Check Alias Parent Ownership by Filesystem Identity

* fix: Enforce Parent Ownership Across Every Environment Path
* fix: Retain Quarantine After Failed Environment Setup

* test: Run Native Environment Setup Lifecycle in CI

* fix: Document and Verify Local Setup Quarantine Recovery
* fix: decouple repository release versions

* ci: automate releases after successful main builds
* feat: add bounded local project inventory

* fix: report incomplete Git metadata reads

* fix: preserve incomplete discovery and remote identities

* fix: finalize discovery budgets and nested remote identities

* fix(code): stop project traversal at filesystem budget boundaries
Merge LibreChat-AI/code-interpreter v1.1.0 (b35c503, chart 0.3.1) into the
UZH fork. Resolves the eight conflicted files:

- Dockerfiles: adopt upstream docker/rootfs-setup.c, guest-dns.sh and the
  hosted-app launcher; keep the UZH chmod 0555 read permission on both
  spec-guard and sandbox-rootfs-setup.
- service/tsconfig.json and rollup.config.js: union upstream packages/code/src
  inputs with the fork shared/operational-log.ts policy.
- service/src/queue.ts: keep the queue-wait import and export surface alongside
  upstream jobCancellationRegistry, closeQueueConnections and redisReconnectDelay.
- service/src/service/programmatic-router.ts: adopt upstream cancellation-aware
  waits; add the additive BullMQ poll fallback via a never-rejecting
  fallbackCompletion channel in waitForJobWithCancellation, and keep the fork
  events+poll race on the blocking path.
- service/src/auth/librechat-jwt.ts: keep the issuer trust table, principal
  sources and tenant namespacing while adopting upstream code_worker_id.
- service/src/service/exec-timeout.test.ts: expose waitForJobFinished in the
  queue mock to match the fork router contract.

service: 1076 pass / 0 fail. api: 481 pass / 0 fail.
Record the v1.1.0 re-audit basis (fork f5bf3b4, upstream tag b35c503, merge base 2c7fb8f), narrow the egress-ledger patch to the singleton ledger now that upstream reconnects queue clients indefinitely, add the PR #23 nonfatal-telemetry patch, extend the spec-guard and PVC package-init sections with their v1.1.0 evidence and retirement review, and record the values-free logging re-inventory.
Resolve the completion-event channel from its QueueEvents payload instead of re-reading a possibly-evicted job, and give an in-flight event one poll interval of grace before honoring a poll failure so a completed job is never reported as missing. Restore the poll fallback's retention depth in programmatic-router.ts (removeOnComplete.count 1 -> 100, as router.ts already had), own the fallback poller's abort signal in waitForJobWithCancellation so it stops when any other outcome settles, and bound the upstream code_worker_id claim per trust entry via codeWorkerIdPrefixes so an external issuer cannot name another issuer's bridge worker in a multi-entry trust table.
…view reports

Keeps plan-hardening and review reports under docs/project/_local/ out of history, and records the executed slices with their verification receipts.
The focused confirmation review of 53ed390 confirmed the three required
fixes but found one regression and two unpinned behaviors:

- queue-wait.ts declared the QueueEvents payload with the typed result, which
  introduced two TS2345 errors against BullMQ's string declaration. Declare the
  field as BullMQ does and convert through unknown instead.
- The fallback poller's abort contract had no test coverage: removing either
  abort call left the suite green. Cover the pending/aborted lifecycle, the
  registration-failure abort, and the additive rejection contract.
- codeWorkerIdPrefixes had no cross-entry uniqueness check, so two external
  entries could declare duplicate or nested prefixes and each admit the other's
  bridge workers. Reject overlapping prefixes at config time, mirroring the
  existing keyId and external-source checks.
- Rename the fallback poller's parameter so it no longer shadows the
  client-disconnect signal in programmatic-router.ts.
@rschlaefli
rschlaefli marked this pull request as ready for review September 17, 2026 19:05
@rschlaefli
rschlaefli merged commit 55840f3 into main Sep 17, 2026
10 checks passed
@rschlaefli
rschlaefli deleted the chore/reconcile-upstream-v1.1.0 branch September 17, 2026 19:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

9 participants