Repository navigation
fix(desktop): recover a session the engine no longer accepts - #3
Merged
Merged
Conversation
The desktop window could strand on the engine's 401 denial page. The health probe counts a 401 as a live engine, so no host restart ever followed, and the plain text body carried no way back into the application. A browser document navigation now receives a minimal HTML page whose script reports the denial to the shell through the `dshDesktopBoot` bridge. The shell keeps the engine URL the host reported readiness with and navigates the window back to it, which re-mints the cookie from the launch token only the shell holds. Every other request keeps the plain text denial, and no denial response carries a token. No other CI lane compiles the Rust shell, so a new `windows-rust-check` job does, and it joins the PR verdict.
There was a problem hiding this comment.
Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.
An organization owner can choose the repository organization as the billing source for reviews requested by members with a paid Copilot license. Learn about Copilot code review billing.
The foreign session readers stamp every converted header with the catalog's current version, but the manifest never listed the package that publishes it. Node then only resolved the import when some other consumer had already put a copy where the walk up from this package would find it, so a profile that mounted session-controller without that accident failed the row outright with ERR_MODULE_NOT_FOUND and took the whole composition down with it. The dependency joins the manifest, and the lockfile records the link.
The Codex and Claude converters each walked their own file with the same loop, accumulating the same header fields and building the same event envelope, so every fix to one had to be made twice and one of the two already drifted: the id fallback split the path on `/` only, which on Windows turned a rollout file name into a whole absolute path. Both readers now share the record walk, the session accumulator and the event envelope, with the narrowing helpers they both reached for lifted into `json-records`. The shared fallback takes the base name and strips the suffix, and `node:sqlite` moves inside the functions that open the store so a process that never reads a ZCode session never loads the experimental module. The new spec pins what each writer contributes to a converted session, what a writer leaves out, and the abort and fallback paths.
Sibling rows mount concurrently, so the settings section holding the user's
default model can still be installing when a client's first session/new or
session/resume lands. Reading the selection straight away then found nothing,
the agent reached the loop with no model selected, and the loop failed on the
`{{model}}` placeholder it had no value for.
Both entry points now await the settled tree first, the same barrier the
headless runner already applies before it reads a default.
tauri-build asserts every externalBin path exists and copies it beside the crate output before a single crate compiles, and the only thing that ever puts a real Node sidecar there is prepare:tauri-runtime, which needs the whole workspace payload the check deliberately never installs. The lane therefore failed before it could compile anything. A check never launches the shell, so a stand-in file satisfies the assertion and keeps the lane free of pnpm.
The verify-export-jsdoc gate reads every exported name in a package API, so `asObject` reached the static lane without the `@param` and `@returns` lines the contract requires. The helper is shared by the foreign-format adapters this branch introduced, so the omission lands on the branch rather than on the package it came from. Describe the narrowing in the same terms the adapters use: the raw field value arrives as whatever JSON type the writer emitted, and the object comes back empty when the value is not one.
The sidecar stand-in let the shell check compile, and tauri-build then stopped on the next assertion: `bundle.resources` maps `staging/runtime` into the bundle, and only prepare:tauri-runtime ever puts the payload there. That script needs the whole workspace payload the check deliberately never installs, so the lane stages the directory instead and stays free of pnpm. The real prepare step clears entries it does not own before it writes, so a directory left behind by a check cannot outlive the next real staging run.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
The desktop window could suddenly strand on the engine's plain text 401 page,
"alpha web authentication required; reopen the URL printed by alpha web."
The health probe counts a 401 as a live engine (
ENGINE_UNAUTHORIZED), sothe watchdog never replaces the host, and the plain text body carries no way
back into the application. Only a manual restart recovers.
Approach
packages/client/connection/src/browser-auth.ts:writeUnauthorizedanswers a browser document navigation (
sec-fetch-dest: document, notHEAD) with a minimal HTML page carrying the same copy plus an inlinescript that calls
unauthorized(location.href)only whenglobalThis.dshDesktopBoot?.unauthorizedexists. Probes, scripts, andcommand-line callers keep the plain text 401. No denial response carries a
token.
apps/desktop/src-tauri/src/lib.rs: thedshDesktopBootbridge gainsunauthorized(href), which emits thealpha-unauthorizedwindow event.The shell listens for it, reads the engine URL it recorded when the host
reported readiness (that URL carries the launch token of the host still
serving), and navigates the main window back to it. The navigation
re-mints the cookie and the application reloads.
.github/workflows/ci.yml: a newwindows-rust-checkjob runscargo check --lockedon the desktop shell and joins the PR verdict joball-checks-passed. No other CI lane compiled the Rust crate.Verification
pnpm exec vitest run packages/client/connection: 12 files / 117 testspass.
browser-auth.tsat 100% statements / branches /functions / lines, satisfying the connection layer's per-file gate.
pnpm exec vitest run packages/host/frontend-static: 1 passed.pnpm run doc-sync: 41 passed, 0 failed (translation pairing, docbudgets, md-wrap, md-links, doc-graphs, doc-typecheck, site build).
pnpm run typecheck:contracts-ready: exit 0; the pre-push hook re-ran it.pnpm exec oxlint packages/client/connection: clean.lint:contracts-readyreports thesame 10 errors in
packages/api/session-controller, unrelated to thischange.
CI job.