Repository navigation
[WRONG BRANCH] release: 2.78.0 - #6632
Conversation
…i-parity-foundation # Conflicts: # scripts/test-layout/layout.json # tests/fixtures/test-layout-expected.json
…ty-integrations-maintenance
…i-parity-foundation
…accounts-settings
…ty-integrations-maintenance
…li-parity-observation-api
…6597) * fix(integrations): write DSH routes to the Desktop profile patch it reads DSH 0.1.7+ imports $DSH_HOME/settings.yaml once into the first profile that boots and renames it to settings.yaml.imported. From then on it reads provider routes from the llm-pi-ai row of a profile patch (a top-level YAML list of loader rows) and hot reloads that file. The integration kept writing settings.yaml, so after that first import a catalog refresh, a model change or a disable reached nobody, and status read the renamed file as absent. Declare the Desktop profile's cordis.patch.yml as DSH's currentStore (the seam from #5348), addressed through a leading [id=llm-pi-ai] selector. The home patch is not an alternative: a home row replaces the profile row's whole config, the user's own routes with it. - merge.ts keeps a sequence root, and prunes an element it seeded once only its selector fields remain. - The source-preserving YAML patcher edits one top-level list entry as the block map it holds and restores its "- " and two-space indent byte for byte. DSH's empty `[]` is the only flow form adopted, and a disable that empties the list writes it back. - IntegrationTarget carries its own sourcePreservingYaml, so the legacy file and the store are patched along their own paths, and a coordinated write also holds DSH's config-editor lock (the profile's package.json.lock) whenever the profile directory exists. - Without a Desktop profile the legacy settings.yaml stays the target. - The dashboard accepts the new plan path; GUI text, docs and the integrations structure doc describe the new location. * test(gui): pin the DSH ownership copy to the Desktop profile path The locale-parity fixture still held the settings.yaml sentence in every locale, and the DSH surface test asserted the old llm-pi-ai.providers spelling. Both now follow the copy: the llm-pi-ai row, the Desktop profile patch, and the settings.yaml fallback. * fix(integrations): keep DSH profile refresh order and lock late profiles Refreshing a Desktop profile row that OpenCodex created no longer deletes and re-appends it in the expected merge, so a row DSH appended afterwards no longer turns a routine catalog refresh into an "unsafe" refusal. The created-row provenance is kept so disable still removes it. The profile manifest lock is now chosen after the settings lock is held and re-checked after every revalidation await, so a profile that appears mid-write is locked and revalidated before it is read or written. Removing the last managed row also keeps the file's original final-newline and CRLF convention. * fix(integrations): refuse restoring into a missing DSH profile directory A confirmed restore of a journaled Desktop profile write used to recreate a removed profiles/desktop directory and write it without the profile's package.json.lock. Preview and restore now refuse as unsafe when the declared locked store's directory is missing, and the structure doc records the lock re-probe, row-order, and restore contracts. * docs(structure): fit the DSH store paragraph inside the integrations budget Merging #6577 put structure/clients/integrations.md four lines over its 600-line budget. Tighten the DSH paragraph without dropping any contract. * fix(integrations): report a booted DSH profile without a patch instead of writing settings.yaml With profiles/desktop/package.json present and cordis.patch.yml missing, the target fell back to $DSH_HOME/settings.yaml with no ineffective marker. DSH imported that file when it booted the profile and never reads it again, so the write was lost without a word. A store declaration can now name a missing store the client still reads; DSH's does, and the write is refused as an ineffective one with the remedy (create the patch as `[]`). * docs(integrations): state why a DSH profile without a patch is refused The comments, structure doc, and refusal message said DSH imports settings.yaml once and never reads it again. Upstream DSH's importLegacyDocument renames settings.yaml to settings.yaml.imported and imports it on every startup where it exists. The refusal stays: a block written there would be moved out from under opencodex's ownership record. Only wording changes; behavior and tests' assertions are unchanged. * feat(integrations): show the missing-store remedy in the dashboard A DSH Desktop profile whose cordis.patch.yml is missing is refused as superseded_store, and the CLI names the fix: create the patch containing `[]`. The dashboard only had the generic superseded-store copy, which says the client reads a file opencodex does not write and offers no way out. - The missingStore declaration publishes its empty document (`[]` for DSH) beside the remedy text, and IneffectiveWrite carries it. - Status rows carry supersededReason beside supersededBy, plus missingStoreDocument for a missing store. - A superseded_store plan carries the same two fields. The plan still names no file (the path stays on the status row), and the fingerprint does not change: the bound ineffective-write token already covers this finding. - The GUI accepts the fields only on a superseded_store refusal, the document only for missing-store and only as one short line, and renders copy that names the file and the document in all eleven locales. * fix(integrations): name the missing store in the dialog and decode it in the CLI - The apply dialog covers the page's status notice, so a refused DSH preview now names the patch path as well as `[]`. The path comes from the status row; the plan still names no file. Bulk dialogs, which have no single status row, keep the pathless copy. - The Turkish copy names the legacy settings file explicitly instead of "that file". - The CLI plan decoder from #6542 accepts supersededReason and missingStoreDocument under the same contract as the GUI, and `ocx integration client preview` prints the remedy. Both decoders now reject a non-string reason and a document that is not one short printable-ASCII line, since the CLI echoes it to a terminal. * fix(gui): wrap long missing-store paths in the DSH notice and plan dialog --------- Co-authored-by: JUN <jun@lidgeai.com>
…-list next steps (#6611) * fix(cli): show account health actions, paid-credit consent, and empty-list next steps * fix(cli): never echo an id that fails the selector allowlist in account recovery lines
… in leaf help (#6609) * fix(cli): correct help and CLI reference text and show declared flags in leaf help * test(cli): follow the corrected restart help summary
…te-failed, and ZCode guide text (#6619)
…ss (#6601) * fix(management): keep inference ports independent of management ingress * fix(management): keep Cursor gateway on the bound inference port; cover ingress export Cursor status now prefers the lifecycle-bound public port, then the PID-matched runtime record, then config, so a management-only ingress port can never become the advertised gateway. Adds a real-server regression that exports a client config through hub management ingress and asserts the public bound port, plus Cursor precedence cases, and records resolver ownership and known limitations in ADR-6598. * fix(management): use the live bound port for Desktop provider-change auto-apply autoApplyDesktopBestEffort wrote the Desktop 3P config from config.port, so a CLI override or ephemeral bind could leave Desktop pointing at a port nothing serves. It now uses managementInferencePort like the other management writers; the roster-update fixture asserts the live port reaches the writer. --------- Co-authored-by: Ingwannu <ingwannu@users.noreply.github.com> Co-authored-by: JUN <jun@lidgeai.com>
* fix(responses): normalize native upstream session aliases * test(responses): expect normalized session_id for caller aliases; state alias precedence Canonical ChatGPT egress now fills session_id from session-id/thread-id, so the Claude affinity and Chat affinity wire expectations follow it. Adds a two-alias precedence case and documents exact precedence, the bridges' empty-value filtering, and that aliases are forwarded raw like an explicit session_id. * fix(responses): treat empty session headers as absent when normalizing aliases Upstream auth header selection already drops empty values, so the alias helper now does the same. First attempts and retries rebuilt from raw caller headers pick the same upstream session_id. Docs state the precedence for non-empty headers. * test(responses): cover caller session identity across auth replay --------- Co-authored-by: JUN <jun@lidgeai.com>
… #debug after the GUI regroup (#6612) * fix(gui): keep short sidebars, Claude sidecar rows and Save errors usable; open legacy #debug on Debug Audit follow-up for the GUI merged since v2.77.0 (#6579, #6593, #6596): - A short or zoomed desktop window no longer collapses the sidebar menu to its padding: the menu keeps about three rows and the whole rail scrolls only when it cannot fit, with the language menu kept on screen. - The Claude web search and vision sidecar rows wrap their controls under the copy instead of covering the title (French at 768px) or clipping the model input (390px). - A failed Save on the one-page Claude settings reports in the Save bar, where it was clicked; the bar status wraps instead of sliding under Revert. - Cold-loading a legacy #debug bookmark selects the Debug tab, reading the canonical hash like Connect and Providers do. Findings and plan: devlog/_plan/261005_r4_gui_audit/. * fix(gui): keep the short-window language menu on screen in every engine Review on #6612: the rail backdrop-filter makes the sidebar the containing block of the fixed language menu, so an engine that treats it like an absolute child would scroll and clip it with the rail. In windows 480px tall or less the rail is now opaque, as in the existing no-backdrop-filter fallback, which anchors the menu to the window; the menu is opaque too.
… the dashboard (#6613) * docs: sync Connect, Claude settings and DSH profile-patch guides with the dashboard The integration guides still sent readers to an Integrations tab that is called Connect since #6593, the DSH sections named the legacy settings.yaml mapping as the only owned path after #6522 moved it to the Desktop profile patch, and the Claude GUI sections listed the pre-#6596 order without the Save bar contract. English and the seven translations now match the shipped dashboard. * docs: name the DSH missing-patch refusal and finish the Connect → API Keys rename Review on #6613: a Desktop profile without cordis.patch.yml makes Apply refuse with the create-[] remedy rather than write the patch or fall back to settings.yaml, and the remote-hub, CLI lifecycle and Codex integration pages still pointed at Integrations → API Keys or the Integrations overview.
* fix(test): keep armed test processes out of the real Codex home A local suite run rewrote the real ~/.codex catalog twice (#6529): a convergence ran with OPENCODEX_HOME in the test temp tree and CODEX_HOME unset, so it resolved os.homedir()/.codex, which ignores the preload's HOME on macOS. The real-home guard covered the OpenCodex home and native auth.json, not the catalog, models cache, journal or config.toml. atomicWriteFile now refuses any write whose directory is the real Codex home in an armed test process, and K refuses it before the owner precheck, so a test never gets a catalog permit for it. Refs #6529 * test: probe the unset-CODEX_HOME fallback against the real-home guard Run the probe child with CODEX_HOME absent and HOME/USERPROFILE at the sentinel home, check getCodexHome() resolves there, and check every Codex-home write and K are refused. This is the path the incident took. --------- Co-authored-by: JUN <jun@lidgeai.com>
…ot starve claude.ai (#6511) (#6610) * fix(claude): serve the Desktop picker over HTTP/2 so SSE streams cannot starve claude.ai (#6511) The picker listener terminated claude.ai with an HTTP/1.1-only TLS server. Claude Desktop keeps several messages/stream SSE subscriptions open, each holding one of Chromium's six per-origin HTTP/1.1 connections, so later claude.ai requests queued in the client and timed out before reaching OpenCodex. Blind tunnels negotiate HTTP/2 with Anthropic and multiplex. The listener port is now a TCP front that reads the TLS ClientHello ALPN offer (bounded multi-record reassembly) and splices the untouched connection to an HTTP/2 server when the client offers h2, or to the existing native HTTP/1.1 relay otherwise (WebSockets included). HTTP/2 requests are translated for the HTTP/1.1 upstream (:authority -> Host, cookie crumbs joined) and cancellation follows the underlying stream. Shutdown force-closes every front, bridge, h2 and HTTP/1.1 socket. * fix(claude): bound picker h2 fan-out and refuse unrelayable h2 targets Security review of #6610: an HTTP/2 :method or :path that the HTTP/1.1 client cannot express threw before cleanup was installed, and one connection could open unbounded streams, each dialing upstream. Validate h2 targets and guard request construction (empty 400, fixed log line), advertise maxConcurrentStreams 100 per session and cap in-flight upstream requests at 256 listener-wide (empty 503 without dialing). * fix(claude): refuse picker targets the URL parser rejects Security re-review of #6610: an origin-form h2 path such as //[ passed the target check but threw in new URL() before the refusal boundary. Parse the pathname inside it and answer an empty 400. * fix(claude): do not log a client-cancelled picker request as 502 Hosted CI on #6610: cancelling an h2 HEAD before upstream headers closed upstream as intended, but the teardown error then wrote a 502 log line for a client that had already gone. Ignore upstream errors once the client side closed first. The ALPN test now asserts only that HTTP/1.1 clients never get h2: Bun's native HTTP/1.1 server does not report its ALPN choice.
…#6618) * fix(cli): stop echoing values in claude desktop apply argument errors parseDesktopApplyArgs printed unknown arguments verbatim, so an inline --token=<value> or a stray credential operand reached the terminal. Show options by name only and bare operands as <redacted>. * fix(cli): never echo rejected claude desktop arguments Security review of #6618: option names can still carry values (-tVALUE, dash-prefixed operands) and control characters. Apply errors now count unknown arguments and list the valid options; move/default show a route operand only when it is a plain provider/model id. * fix(cli): keep desktop bind and profile file errors free of operands Security re-review of #6618: bind errors echoed a rejected picker id and an unavailable route, and import/export printed filesystem errors that carry the user-supplied path. Binding errors now omit the id and show a route only as a plain provider/model id; profile file failures report the operation and error code. * fix(cli): drop rejected route operands from desktop errors Security re-review of #6618: a route that only looks like provider/model is still a rejected operand. move/default and bind now say the route is unavailable and point to ocx claude desktop show, without echoing it.
) * fix(gui): keep Claude Desktop role selects inside the Models card A long unavailable stored model plus its status overflowed the role select at 390px (chevron and status clipped) and squeezed the label column to nothing at 768px. Bound the controls column, let the route truncate inside the trigger while the translated status and chevron stay whole, and put the full text in the trigger tooltip. * test(gui): cover the unavailable role choice label and tooltip The route sits in its own truncating span, the translated status in a separate element, and the trigger's tooltip carries the full text.
…ffers Retry (#6623) * fix(gui): start the integration dialog on Close and offer Retry when client status fails Found by the R4 audit of #6597: showModal() focused the invisible backdrop dismiss button, so the dialog opened with no visible focus and Space dismissed it unseen; and a cold status-load failure on a client page had no way to retry. * docs(devlog): record the #6597 GUI audit and the R4 CI plan * test(gui): cover the dialog's initial focus and the client-status Retry
…oxy (#6622) * fix(cli): name ocx start when integration preview finds no running proxy * test(cli): pass preview stopped-proxy cases as object rows
…in argument errors (#6608) * fix(cli): reject arguments to uninstall and redact credential values in argument errors * fix(cli): keep adjacent credential options and option-shaped positionals redacted; cover models leftovers * fix(cli): redact bare leftovers whenever argv carried a credential option * fix(cli): scrub argv credential operands from console diagnostics as a last-line guard * fix(cli): scrub only diagnostics streams and always collect inline credential operands * fix(cli): redact credential-shaped unknown provider subcommands at the output site
…th, update; validate provider add before saving (#6614) * fix(cli): honest exit codes and JSON receipts for config, alias, health, update; validate provider add before saving * fix(cli): name the recovery path when provider changes are refused by config validation
… management refusals (#6615) * fix(cli): name the real cause and next action in restart, update, and management refusals * fix(cli): stop the owning proxy before reinstall advice; scope integration writer detail to the normalized route * fix(cli): render fixed integration recovery guidance instead of writer prose * test(cli): expect the no-request stopped-proxy guidance on Codex login routing
|
Important Review skippedToo many files! This PR contains 644 files, which is 344 over the limit of 300. To get a review, reduce the PR to 300 files or fewer by splitting it into smaller PRs or changing its base branch. Usage-priced reviews support at most 300 files. ⚙️ Run configuration
⛔ Files ignored due to path filters (1)
📒 Files selected for processing (644)
You can disable this status message by setting the
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
✅ Deterministic PR hygiene checks passed. |
⏳ DRAFT
What to do
Its title has been prefixed with |
|
Stable release promotion by the repository owner (lidge-jun), as with #6550. enforce-target's wrong-branch verdict is expected for a maintainer promotion into main. CodeQL reports the whole candidate diff against main as new alerts, as it did on #6550; the same tree passed dev CI and shipped as 2.78.0-preview.20261005. This PR's Cross-platform CI passed at e847a24. Dev pre-move #6631 is merged, so dev (2.79.0) outranks 2.78.0. |
Summary
Promote the verified
devcandidate0511f458f12674f63bf3b629a0523cee246d33a0tomainas stable2.78.0. The candidate already carries version 2.78.0 in all four version sources, so the branch is the candidate plus anoursmerge oforigin/main; its tree equals0511f458f1exactly. The same tree shipped as2.78.0-preview.20261005.2.78.0 contents since v2.77.0 (61 first-parent landings: 14 feat, 37 fix, 6 test, 3 docs, 1 chore): CLI management parity (#6526, #6528, #6535, #6539, #6542, #6545) and CLI UX fixes (#6585, #6607, #6608, #6609, #6611, #6614, #6615, #6619, #6622); native Claude Messages through the Anthropic pool and the native-request preference (#6552, #6559, #6562); catalog ownership/healing (#6553, #6558, #6560, #6563); paid credits after included quota (#6572); Anthropic DNS refresh-intent recovery (#6586); Unix autostart shim private overlay (#6589, guidance #6607/#6619); standalone update restart and Bun readiness (#6556, #6569); Claude Desktop picker over HTTP/2 (#6610) and argument-echo fixes (#6608, #6618); management/inference port separation (#6601), session alias normalization (#6554, #6588); DSH Desktop profile patch and dashboard remedy (#6522, #6597); Ollama replay (#6576), combo errors (#6564), Droid defaults (#6577), Devin (#6557, #6565), reset retry (#6555); GUI: sidebar regroup, theme switch, one-page Claude settings, audit fixes (#6579, #6593, #6596, #6612, #6623, #6625); docs (#6613, #6619). Full record:
devlog/_plan/261005_release_readiness/040_release_readiness.mdin the coordinator worktree.Release authorization: the repository owner asked on 2026-10-05 to release stable 2.78.0 after the preview. Preview
2.78.0-preview.20261005was published from the same tree (preview SHA 04e175f; push CI 37290888855, Service lifecycle 37290889969, release run 37294840779).Upgrade notes: Unix users with an existing Codex autostart shim run
ocx codex-shim installand source the printedcodex-shell-env.sh; consented accounts can now spend credits after the included limit; DSH writes targetprofiles/desktop/cordis.patch.yml(create it with[]if missing); picker users should look forpicker session h2and can fall back withocx claude desktop picker off.GUI changes carried from dev (screenshots from the source PRs):
Verification
0511f458f1: workflow_dispatch Cross-platform CI run 37276383405 success on attempt 2 (attempt 1 failed only windows 7/9 with a known spawn-timeout flake in an untouched test; failed-jobs rerun passed), and Service lifecycle workflow_dispatch run 37279425603 success.devoutranks 2.78.0.git diff 0511f458f1 HEADis empty; the merge commit's tree equals the candidate's.release.yml.Checklist
main(maintainer release promotion)bun scripts/release-version-sources.ts sync 2.78.0reports no change)