mirror of
https://github.com/esengine/DeepSeek-Reasonix.git
synced 2026-11-07 10:45:59 -10:00
studio
65
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
82de6d9622 | chore(deps): tidy the desktop module for the go group bump | ||
|
|
80ebd88bf7 | fix(deps): bump golang.org/x/net to v0.62.0 for GO-2026-6860 | ||
|
|
148a3eabe7 | build(deps): tidy the desktop module for the same bumps | ||
|
|
8b60e89bee | fix(desktop): record SQLite transitive dependencies | ||
|
|
b0168f37dc | build: use Go 1.26.10 for the crypto/x509 fix (GO-2026-6809) | ||
|
|
751def6d32 | chore(deps): bump the go group with 11 updates, including the desktop module | ||
|
|
68d3238e98 |
chore(deps): bump golang.org/x/mod in /desktop in the go group
Bumps the go group in /desktop with 1 update: [golang.org/x/mod](https://github.com/golang/mod). Updates `golang.org/x/mod` from 0.41.0 to 0.42.0 - [Commits](https://github.com/golang/mod/compare/v0.41.0...v0.42.0) --- updated-dependencies: - dependency-name: golang.org/x/mod dependency-version: 0.42.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: go ... Signed-off-by: dependabot[bot] <[email protected]> |
||
|
|
80835fc83d |
build(deps): bump golang.org/x/sys in /desktop in the go group
Bumps the go group in /desktop with 1 update: [golang.org/x/sys](https://github.com/golang/sys). Updates `golang.org/x/sys` from 0.48.0 to 0.49.0 - [Commits](https://github.com/golang/sys/compare/v0.48.0...v0.49.0) --- updated-dependencies: - dependency-name: golang.org/x/sys dependency-version: 0.49.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: go ... Signed-off-by: dependabot[bot] <[email protected]> |
||
|
|
c8622f7a8c | chore(deps): bump Go toolchain to 1.26.9 and golang.org/x/net to v0.60.0 | ||
|
|
3a51cb1e42 |
build(deps): tidy the desktop module for the go group bumps
desktop/ replaces reasonix with the parent module, so the root bumps raise its graph too, and the Studio desktop and package jobs failed with "updates to go.mod needed". go mod tidy in desktop/ moves only the versions the root already requires. This also covers #11033. |
||
|
|
c163eed8df |
chore(desktop): tidy the desktop module
go mod tidy in desktop/ drops an indirect gopkg.in/yaml.v3 and 32 stale go.sum lines nothing in the module imports any more; the desktop CI job runs the same tidy and failed on the drift. |
||
|
|
393ec8eed8 |
feat(studio): retire the Wails shell
Section 6 of the migration contract bound these into one transaction, because deleting any one first leaves a broken shell rather than a smaller tree: desktop/next and its event pump, remote_pump.go, /rx-replay, the frontend's WAILS_* constants and bus branch, the apiPaths/apiPrefixes whitelist, the vendored go-webview2 fork, and the Wails dependency with its CGO and GTK/WebKitGTK build requirements. They go together here. What allows it now is the condition section 7 named — no exclusive semantics left — plus the one ledger entry that had been recorded as unverified: the release line. studio-v2.11.0 (2026-09-01) through studio-v2.16.0 shipped the Electron bundle seven times, and the crossing from an installed Wails 2.10 was confirmed by the maintainer rather than assumed here. The kernel loses what only that shell reached: update.LocalApplication (a process that is its own application, which Electron's is not), update.Here, appidentity's AppUserModelID application and shortcut repair — Electron states its own appId — and crashreport.CaptureStudioPanic. AppUserModelID itself stays, because Windows toasts still carry it. Two consequences worth stating. The desktop module is now CGO-free, so neither CI nor a contributor installs GTK or WebKitGTK to build it, and the webkit2_41 tag is gone from every command. And what the old shell installed is untouched: winuninstall, the Electron shell's legacy takeover and studio_line's binary name are how an installed 2.10 becomes a 2.11, and none of them are Wails code. The contract becomes docs/STUDIO_SHELL_BOUNDARIES.md: the rules the migration established outlive it, and the ledgers it kept do not. The presentation gaps it tracked are carried over as known gaps, with the host's missing panic capture added to them. |
||
|
|
6e7f189e76 |
fix(ci): the heap corruption's two upgradable causes, and a floor set on the wrong platform
The windows-latest crash is the GC finding a bad pointer while marking: runtime.scanObject in mgcmark_greenteagc.go, under gcDrain. Green Tea is the detector, not the cause — turning it off would move where the corruption surfaces, not stop it, so this does not do that. Two things in that binary can produce it and were both behind. The Go toolchain moves 1.26.6 -> 1.26.8, which carries compiler and runtime fixes; and modernc.org/sqlite moves 1.57.0 -> 1.58.0, taking libc from 1.74.4 to 1.75.6. libc is the only unsafe-heavy code linked into that test binary that is not the standard library, and internal/bot reaches it through historycatalog and projectiondb. Neither upgrade is proven to be the fix: one crash cannot attribute itself, and golang/go#81238 — windows/amd64 exception recovery writing below a goroutine stack — is open, unfixed, and the same class. If it recurs on these versions, that issue is where it goes. covergate was red on a third path this had not read: internal/sandbox is 78.0% on linux against a 78.1% floor, while the same tree measures 78.1% on darwin. The package compiles a different set of files per GOOS, so a floor recorded on one platform can be unreachable on the one the gate runs on. Rather than lower it, three statements nothing reached are now covered — podman's per-user socket, an authority the table does not govern, and a nil context carrying no escape approver — which takes darwin to 78.9% and lifts linux above its floor by the same edges. covergate now names the lines it is short of. A percentage alone left whoever has to restore it measuring a package by hand on a platform they may not be on, which is how this one sat red across three runs. |
||
|
|
85cba5b991 |
fix(ci): the desktop module carries the same advisory bump
go.mod tidy in desktop/ failed because the root module moved x/crypto to v0.56.0 and the nested one still pinned v0.55.0. The dependency is indirect there, so nothing local reads it — and nothing local runs that tidy either, which is why the root gates passed while the desktop job did not. third_party/go-webview2 is left alone: it is a vendored fork with its own module, CI runs only its tests, and tidying it would rewrite a sum nobody asked to move. |
||
|
|
5e1ba919d1 |
chore(deps): update every dependency, and pay what go 1.26 asks
mvdan.cc/sh v3.14.0 requires go 1.26.0, so the directive moves with it, and the directive is what turns modernize's newexpr rule on across the tree. The 75 findings are the price of this upgrade rather than debt it inherited: lint reported 0 issues on the same tree one version back. Inlining the pointer wrappers leaves nine of them with no caller, and two different gates are needed to see that: unused catches the unexported ones, repolint's orphan catches exported tool.IntPtr. The //go:fix inline directives the fixer writes are dropped rather than kept, because govet reads one as a demand to inline every call site -- which would delete names that carry meaning. provider.TemperaturePtr tells an explicit 0 from "not set, use the provider default"; new(0.0) at twelve call sites says neither. Keeping the new(v) body satisfies modernize without spending the name. Go: bubbles, bubbletea, lipgloss, x/ansi, lark, go-runewidth, jsonschema, x/mod, x/net, sqlite, sh; wails 2.13 -> 2.15 in desktop. Frontend: vite 8 on rolldown, typescript 7, plugin-react 6, vitest, electron 44.1.0. Both lockfiles carry the linux and windows native binaries, which CI installs and this machine never does. Verified past every gate CI runs, plus the two it does not: the Electron smoke against a real window, and Studio itself built and launched under wails 2.15. |
||
|
|
ba415e863e |
build(deps): update x/crypto for GO-2026-6303
golang.org/x/crypto v0.54.0 does not enforce the source-address critical option for non-public-key auth callbacks in its ssh package. govulncheck traces the call through sshtest.Server.handleConn, so the scan fails on every branch until both modules move to v0.55.0. |
||
|
|
a58b902acf |
feat(desktop): a status icon for a window that can be closed without leaving
[desktop] close_behavior has been normalized, migrated and editable since before this shell existed, and read by nobody: quit|background was a promise the app did not keep. This is the half that was missing. The close button hides where an icon can bring the window back, and quits where it cannot - no icon, no backgrounding, because a window that vanished with no way back to it would be worse than either. What the icon says is folded in internal/traystate, which owns no OS handle and draws nothing: panes in, one State out, ranked so that being needed outranks being busy. Nothing on the wire announces an answer to an approval, so the fold reads the first thing the run produces afterwards as the resolution - a turn blocked on one produces nothing until it is answered, which is what makes that sound rather than a guess. Background jobs are pulled on a timer instead, because a job announces its life in a sentence and matching those words is the judgement this codebase keeps retiring. Quitting from the menu runs the window's own close sequence rather than a second one. The reason that sequence exists - two processes writing one transcript - does not care which control asked for it. Windows is verified: it builds, and the icon's own encoder is the 22-byte ICO header around a PNG. macOS and Linux compile from here but cannot run: this machine has no CGO toolchain for either, and the thing to check there is whether systray's external loop is happy beside Wails' own on the main thread. |
||
|
|
a33f72f806 |
feat(serve): opening a pane on another machine is one request
The hub gets the endpoint from a hook the host wires, so the two halves meet above it: a window supplies a pool that dials, and a listening server leaves it nil. That default is the point — a kernel that dials onward because a request asked it to is someone else's way into the network behind it. A hold the attach took and the hub refused would be a connection nothing can ever close, so the endpoint gives it back on every path that does not publish a pane. |
||
|
|
24e91001d6 |
fix(build): three edits that reached the branch without their other half
None of these belong to one subject; they share only that the tree does not compile with them in it, and that each arrived inside a commit about something else. - snapshotConflictLogAttrs logged DivergedAt, DiskRole and SnapshotRole. SessionSnapshotConflictError has never carried those fields — not on this branch, not in any commit that touched save.go — so there is no data behind them to keep. They go. - providerSetupSession.setDefaultModel and the setup replay both called Config.SetDefaultModel with a second nil argument. It takes one. - The desktop module imports internal/textutil, which gained a dependency on charmbracelet/x/ansi; desktop/go.sum never learned about it, so the module failed to load its own packages. Cache-impact: none - a deleted log attribute, a dropped argument and a go.sum entry; no provider request, tool schema or prompt prefix is touched Cache-guard: nothing reaches the provider from any of these paths Documentation-impact: none - none of the three was ever reachable behaviour |
||
|
|
908bc3ec0c |
refactor(desktop): Studio is the only shell the module builds
The Wails-bound app and its React frontend are gone. Studio reaches the kernel through internal/serve's HTTP routes instead of bound Go methods, so the shell that replaces ~320k lines is next/ plus the SPA it serves. Three things the deleted tree owned move rather than disappear: the WebView2 fork's patch guard, the macOS dock-icon safe-area assertion, and the Windows ICO the resource stamper needs. The fork itself stays — its mixed-DPI and proxy-isolation patches are Studio's too. CI keeps one desktop Go gate per platform, now built against WebKitGTK 4.1 the way studio-build.sh links; the frontend legs, the Wails CLI pin, and the 4.0 leg went with the app. The desktop release line (release-desktop.yml and the desktop channel in release-stable.yml) is untouched here: it still serves installs that are already out there, and unwiring it is release-pipeline surgery of its own. |
||
|
|
bd88008520 |
chore(deps): close the seven vulnerabilities govulncheck reports
Six are standard library, fixed in go1.26.6: net/url, crypto/tls, net/http, encoding/xml, encoding/asn1, and x/net/idna's Punycode label check. The seventh is golang.org/x/image, fixed in v0.45.0 — both modules use it directly, for webp decoding and for the image compressor. CI pins no Go version of its own, reading go.mod through go-version-file, so the toolchain line is the whole change there. govulncheck locally: 7 affected before, 0 after. |
||
|
|
bf19574d3a |
feat(desktop): add resilient session catalog
Problem: Desktop startup and project-tree queries could synchronously migrate and decode legacy session JSONL, blocking navigation, creation, and shutdown. Root cause: Session discovery, metadata repair, controller admission, and plugin startup shared UI-critical paths and broad lifecycle locks. Fix: - add a disposable SQLite session catalog with incremental reconciliation, repair checkpoints, corruption recovery, and memory fallback - expose revisioned cursor pagination and migrate the desktop project tree to lazy loading - move controller construction outside lifecycle admission and isolate incompatible plugin startup - add session doctor and reindex commands while preserving authoritative JSONL and metadata - retain CGO-free builds with platform-specific fallbacks Verification: - go test ./... - cd desktop && go test ./... - go test -race ./internal/sessioncatalog ./internal/agent - cd desktop && go test -race . - go vet ./... in root and desktop - cd desktop/frontend && pnpm test:all && pnpm build - CGO_ENABLED=0 cross-builds for supported macOS, Windows, and Linux targets |
||
|
|
7577c86326 |
fix(desktop): refresh workspace resources from change events
Problem: Workspace previews and change lists remained stale until turn completion, and the embedded workbench did not expose a reliable refresh fallback. Root cause: WorkspacePanel depended on a turn-level refresh key and had no resource-level invalidation for in-flight agent writes or external filesystem changes. Fix: Add host-only workspace mutation metadata and workspace_changed revisions; add a root-scoped fsnotify hub with bounded coalescing, Git metadata watching, and degraded fallback; refresh frontend resources by revision and expose an embedded refresh action; group task, working-tree, and history changes with regression coverage. Verification: go test ./...; cd desktop && go test ./...; cd desktop && go test -race -run 'TestWorkspaceChangeHub' ./...; cd desktop/frontend && pnpm -s typecheck && pnpm -s lint:hooks && pnpm -s check:css && pnpm -s build; cd desktop/frontend && pnpm -s test:terminal. |
||
|
|
db44780c27 |
chore(deps): batch Dependabot Go/npm upgrades on main-v2
Problem Several open Dependabot PRs stacked overlapping Go and npm updates against older main-v2 tips, making independent merges conflict-prone. Root cause Grouped and per-package bumps touch the same lockfiles across root Go, desktop Go, desktop frontend, and the site package tree. Fix Re-apply the durable version targets from Dependabot PRs #8002, #7825, #7824, #7765, #7602, #6959, and #6795 on current main-v2, regenerating lockfiles with go get/mod tidy, pnpm install, and npm install. Verification - go build ./cmd/reasonix - desktop package compile via go test -c - go test ./internal/provider/... ./internal/control/... - desktop/frontend: pnpm exec tsc --noEmit - site: npm test (58 pass) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> |
||
|
|
bb2606375c |
feat(remote): remove Remote Workbench protocol and projection stack
Problem: Remote Workbench (rpcwire JSON-RPC, schema codegen, Provider Broker, TargetManager, workbench projection in the main window, and system OpenSSH transport) was superseded by the restored SSH Serve web window. The full stack, its build ldflags, and its hidden CLI commands had to disappear. Root cause: The Workbench was added in #6728 and its same-window projection made every controller path conditional on an active remote target. Fix: - Delete internal/remote/{workbench,protocol,protocolgen,broker}, internal/rpcwire, internal/remote/bootstrap/workbench.go, and cmd/remote-protocol-gen. eventwire stays (shared with CLI/serve/desktop). - Delete the desktop workbench layer (workbench_adapter, projection, runtime_adapter, ssh) and the system OpenSSH/AskPass/Job Object code (remote_askpass, remote_ssh_transport, remote_ssh_process). Normal remote SSH keeps using the shared internal/remote.Client. - Remove every activeRemoteWorkbench()/activeWorkbenchTargetIsRemote() gate from app.go, tabs.go, workspace_changes.go, terminal.go, memory_suggestions.go, settings_app.go, and stats_app.go, restoring pure local controller semantics. The local layout_style "workbench" and the shared Wails remote bindings (hosts, SFTP, forwards, serve) are preserved. - remote attach-workspace / runtime-workbench / workbench-build-id stay routable for one release as exit-1 stubs pointing at `reasonix remote connect <host> --open`. - Drop the protocol source-revision ldflag from scripts/desktop-build.sh and update the packaging test; refresh releaseasset comments to describe remote Serve CLI provisioning. Verification: - go test ./... (root) and cd desktop && go test ./... all green - go vet and gofmt clean on both modules; go mod tidy both - Static closure: no workbench/protocol/rpcwire references outside the intentional CLI stubs (frontend convergence lands in the next commit) Co-authored-by: SivanCola <[email protected]> |
||
|
|
0c542ceb7c |
feat(docs): embed version-matched product documentation
Problem:
Reasonix installations could not reliably search the exact documentation and release notes shipped with their build, and dynamic slash-menu filtering could leave stale virtual row gaps.
Root cause:
Documentation lived only as source files without a compiled retrieval identity or deterministic slash-command path. The shared virtual menu also let TanStack Virtual cache rows by numeric index while React used different keys.
Fix:
Embed docs and reviewed release notes, add the read-only docs tool and /docs command across CLI and Desktop, stamp and verify corpus identity in release builds, enforce documentation-impact declarations, and align virtual-list semantic keys across slash and file menus.
Verification:
- go test ./...
- go test -race ./internal/productdocs ./internal/boot ./internal/control ./internal/cli
- cd desktop && go test ./...
- pnpm --dir desktop/frontend build
- ./scripts/cache-guard.sh
- bash scripts/release-workflows.test.sh
- bash scripts/verify-embedded-docs.sh v0.0.0
|
||
|
|
a04460e738 |
fix(desktop): track WebView2 as a direct dependency
Problem: The desktop CI go.mod tidy gate failed because the new Windows Runtime version helper imports go-webview2 directly while the module still classified it as indirect. Root cause: The implementation added the direct import without committing the dependency classification produced by go mod tidy. Fix: Move the existing go-webview2 requirement into the direct dependency block without changing its version, checksum, or local replacement. Verification: go mod tidy is stable; desktop Go tests, race tests, vet, and Windows amd64 test-binary cross-compilation passed. |
||
|
|
41e0c542ca |
fix(desktop): repair native shortcut icons after updates
Problem: Desktop shortcuts and Finder aliases could lose their icons after an update. Root cause: Windows shortcut icons referenced version-scoped executables removed by retention, macOS bundle replacement could leave Finder aliases pointing at an old bundle, and Linux desktop caches could remain stale after package upgrades. Fix: Repair existing Windows and macOS desktop integrations on startup, keep Windows shortcut icons on the stable launcher, and refresh Linux desktop caches from the package post-install script. Verification: - go test ./... - go test -race . -run TestRepairMacDesktopAliases|TestDesktopPackagesPreserveNativePlatformLaunchers - go vet ./... - Windows amd64 and arm64 cross-compilation - Wails macOS arm64 production build - Linux nFPM post-install package smoke test |
||
|
|
4c8001eb16 |
perf(settings): batch model polling with cache, backoff, and async rebuild
Apply performance fixes for model-list polling jank in Settings:
- F1: single-flight dedup + 60s TTL cache (providerModelCache.ts) around
FetchProviderModels; invalidate on key change.
- F2: exponential backoff 1s->60s for failed fetches; skip background
auto-refresh on saveData/slow-2g/2g connections.
- F3: startTransition + useDeferredValue around large model candidate
lists; content-visibility keeps 400+ model rows cheap.
- F4: 30s sessionStorage cooldown for auto-refresh keyed on provider set,
breaking the refreshKey <-> SaveProvider loop.
- F5: rebuild runs in a goroutine after config persist (async, non-blocking
binding response); emit settings:rebuild-{complete,failed} events so the
UI leaves the saving state on completion.
- F6: FetchAllProviderModels batches all providers in one round-trip
(errgroup, 4 concurrent, 15s per-provider timeout); SettingsPanel falls
back to per-provider cached calls on batch failure.
Adds golang.org/x/sync for errgroup (go.mod/go.sum).
|
||
|
|
b8c82972f2 |
Harden Guard repair preview confirmation / 加固 Guard 修复预览确认绑定 (#6973)
* fix(guard): bind repair apply to confirmed preview * Harden Guard preview confirmation binding Problem: non-interactive apply-plan could accept a bare --yes, while preview IDs omitted derived-state contents and full pending-update identity. Root cause: confirmation hashes covered only display previews and were checked once before the plan loop, so state-dependent actions could drift after confirmation. Fix: require a prior preview ID for non-interactive apply-plan, bind opaque file and transaction state, re-check each action immediately before execution, and preserve unrelated multi-action behavior. Update English and Chinese recovery docs. Verification: go test ./internal/repair ./cmd/reasonix-guard; go test -race ./internal/repair; go vet ./...; go test ./...; git diff --check. Co-authored-by: SivanCola <[email protected]> * Serialize confirmed Guard repair mutations Problem: preview IDs were rechecked before mutation, but another process could still replace a target during the final rename window or race on last-repair.json, losing or mixing undo records. Root cause: repair actions lacked a shared transaction lock and per-target cross-process locks, while confirmed snapshot/config bytes were read again after approval and replacement writes could overwrite a concurrent recreation. Fix: serialize repair bookkeeping before canonical target locks, revalidate confirmed state inside the lock and around renames, publish restored content with create-only atomic writes, bind complete pending-update identity, preserve concurrent writers, and keep undo under the same lock order. Add focused coverage for wait/recheck, rename windows, snapshot bytes, unreadable LKG nodes, transaction serialization, and rollback identity. Verification: go test ./internal/fileutil ./internal/config ./internal/repair ./cmd/reasonix-guard; go test -race ./internal/fileutil ./internal/repair; go vet ./...; env -u DEEPSEEK_API_KEY go test ./...; Windows test-binary cross-compilation for internal/repair and cmd/reasonix-guard; git diff --check. Co-authored-by: SivanCola <[email protected]> * Normalize Windows repair lock test paths Problem: the Windows CI test waited for a raw backslash path while the lock hook receives the canonical lower-case slash form, so the test timed out before exercising the recheck. Root cause: the regression test compared platform-specific input text instead of the production lock key representation. Fix: normalize test hook paths with the same Windows case and separator rules used by repair mutation locks. Verification: focused repair lock tests pass on macOS; Windows amd64 test-binary compilation passes. Co-authored-by: SivanCola <[email protected]> * test(jobs): remove StartForSession race Keep the worker blocked while assertions inspect its state and protect job fields with the existing mutex. This prevents the race job from observing concurrent completion writes when validating accepted input. Co-authored-by: PaulDing98 <[email protected]> * fix(repair): bind target identity into Guard preview confirmation Problem: - previewId hashed only exported preview fields and content-based state IDs, so identical reasonix.toml content in two project roots produced the same confirmation and apply-plan --yes could mutate the unconfirmed project. - Mutation locks used Abs/Clean only, so symlink path aliases (and macOS case variants) took different locks for the same real node. - rollback_update bound only the pending transaction JSON and skipped target locks, so a stable pending update could still roll back an unconfirmed live release unit. - When a target was recreated after quarantine rename, the move was not recorded in last-repair, so UndoLastRepair could restore an unrelated prior repair. Root cause: - Confirmation identity omitted canonical target identity; lock keys and rollback binding did not converge on real paths or live release-unit state; post-rename failure paths returned before persisting the already-moved node. Fix: - Embed an irreversible canonical target identity in per-file state IDs used by previewId, while verifying quarantined nodes against the original path identity. - Resolve existing symlink parents and case-fold on Windows/macOS when building mutation-lock keys and target identities. - Bind current release-unit file states into rollback preview state, take shared target locks during prepare/rollback, and re-check under those locks. - Persist the quarantine/move transaction when a concurrent recreate is detected so undo restores the confirmed node and retains the rewrite as redo. Verification: - go test ./internal/repair/ -count=1 - go test -race ./internal/repair/ ./internal/fileutil/ -count=1 - go test ./cmd/reasonix-guard/ -count=1 - go vet ./internal/repair/ * fix(repair): close symlink, app-bundle, and macOS handoff confirmation gaps Problem: - canonicalRepairPath EvalSymlinks'd the full path, so two leaf symlinks sharing a referent produced the same previewId, and renaming a confirmed symlink leaf changed its identity and made apply fail. - App-bundle rollback bound only directory kind/mode for the live .app and omitted the backup tree, so interior executable drift still accepted an old previewId and rolled back. - macOS self-update held mutation locks only while writing the pending transaction; the actual mv/ditto ran in a shell helper that ignored the shared flock, racing Guard rollback into mixed bundles. Root cause: - Leaf nodes are what Lstat/Rename mutate, but identity resolved through them; directory release units need tree digests; handoff and repair used different processes without a shared lock holder over the swap window. Fix: - Resolve only parent-directory symlinks and reattach the original leaf name for mutation locks and preview target identity. - Bind full tree digests for directory release-unit nodes and include backup paths in pending-update preview state. - Replace the macOS shell handoff with a detached self-subprocess that waits for the old PID, then holds LockRepairMutations around the directory swap. - Export LockRepairMutations/Timeout for desktop handoff and add regressions. Verification: - go test ./internal/repair/ -count=1 - go test -race ./internal/repair/ -count=1 - go test ./cmd/reasonix-guard/ -count=1 - cd desktop && go test -count=1 -run TestMacUpdate . - go vet ./internal/repair/ and desktop * fix(repair): secure updater claims and path case locks Problem: The detached macOS updater trusted filesystem paths from command-line arguments, and repair identities folded path case by operating system rather than by the target filesystem. Root cause: The handoff only checked that a pending-update file existed before swapping bundles. Path canonicalization assumed every macOS and Windows filesystem was case-insensitive. Fix: Persist optional handoff paths and owner identity in the pending transaction, claim them under strict pending and target locks, revalidate the staged bundle before swapping, and remove filesystem paths from child arguments. Detect case sensitivity from the actual macOS directory or Windows per-directory flag before folding lock and preview identities. Verification: - go test ./internal/repair/ -count=1 - go test -race ./internal/repair/ ./internal/fileutil/ -count=1 - go test ./cmd/reasonix-guard/ -count=1 - cd desktop && go test -count=1 -run TestMacUpdate . - cd desktop && go test -race -count=1 -run TestMacUpdate . - go vet ./internal/repair/ - cd desktop && go vet . - GOOS=windows GOARCH=amd64 go test -c ./internal/repair * fix(repair): scope case probe helper by platform Problem: The Linux golangci-lint job reported existingRepairPathParent as unused. Root cause: The helper lived in a common file while its only callers were excluded from Linux builds by Darwin and Windows build tags. Fix: Move the helper into a Darwin-or-Windows platform file so it is compiled exactly where the filesystem case probes use it. Verification: - golangci-lint run --timeout=5m - go test ./internal/repair/ -count=1 - cd desktop && go test -count=1 -run TestMacUpdate . - GOOS=windows GOARCH=amd64 go test -c ./internal/repair * fix(repair): preserve verified rollback recovery Problem: - File rollback could install backup bytes that changed after preflight validation. - A failed macOS backup restore still cleared pending recovery state and cleanup material. - NFC and NFD aliases on APFS produced different repair lock and preview identities. Root cause: The staged-copy hash was ignored, macOS rollback cleanup was unconditional, and canonical repair paths handled case aliases without filesystem-aware Unicode normalization. Fix: Verify the staged rollback bytes against the transaction hash before mutating live targets. Make macOS rollback compensating and retain pending, backup, and staging state when restoration fails. Normalize repair identities to NFD only on APFS and HFS while preserving distinct names on other filesystems. Verification: - go test ./internal/repair/ -count=1 - go test -race ./internal/repair/ ./internal/fileutil/ -count=1 - go test ./cmd/reasonix-guard/ -count=1 - cd desktop && go test ./... -count=1 - cd desktop && go test -race -count=1 -run TestMacUpdate . - go vet ./internal/repair/ - cd desktop && go vet . - golangci-lint run --timeout=5m - GOOS=windows GOARCH=amd64 go test -c ./internal/repair * Harden transactional updater handoffs Problem: Repair and self-update paths could continue without a pending-state lock, apply a prepared update after its transaction or release unit changed, or restore an unverified macOS bundle tree. Linux also persisted a CLI sibling that transaction validation rejected. Root cause: Pending transitions used a best-effort unbounded lock, file updaters locked paths without claiming the exact prepared transaction, and app-bundle handoff/rollback validation did not bind both the staged tree and original backup tree across the rename window. Fix: Use bounded fail-closed pending locks and a consistent pending-to-target lock order. Claim Linux and Windows replacements by version, creation identity, platform, and canonical release-unit paths. Bind macOS staging, original, installed, and backup trees; recover rejected handoffs only when the original bundle is still verified; protect backup paths and reject symlink escapes. Add focused concurrency, drift, rollback, compatibility, and cross-platform tests. Verification: - go test ./internal/repair/ ./internal/fileutil/ ./cmd/reasonix-guard/ -count=1 - go test -race ./internal/repair/ ./internal/fileutil/ ./cmd/reasonix-guard/ -count=1 - cd desktop && go test ./... -count=1 - cd desktop && go test -race ./... -count=1 - go vet ./... and cd desktop && go vet ./... - golangci-lint run --timeout=5m ./... - Windows amd64/arm64 test-binary compilation for repair, desktop, and update-helper Co-authored-by: SivanCola <[email protected]> * Complete fail-closed repair and updater transactions Problem: Repair, undo, and self-update entry points could act on state that changed while waiting for locks. File-update completion also needed a crash-safe installed-unit binding, and platform handoffs could execute mutable or incompletely claimed payloads. Root cause: Invocation identity was sampled too late or incompletely, mutable pending state doubled as installed state, cleanup paths did not consistently compare ownership, and Windows/macOS handoffs did not bind every source, backup, staging, and release-unit transition. Fix: Bind every mutating invocation before lock waits and recheck it under a pending-to-target lock order. Persist create-only installed-state sidecars, preserve recovery material on ambiguity, use atomic no-replace publication, validate complete app and file trees, isolate and hold the verified Windows installer through execution, and make rollback/undo crash-retryable. Verification: go test ./internal/repair ./internal/fileutil ./cmd/reasonix-guard -count=1 cd desktop && go test ./... -count=1 Windows amd64 and arm64 update-helper test-binary compilation gofmt -l internal/repair desktop git diff --check Co-authored-by: SivanCola <[email protected]> * Document fail-closed legacy update recovery Problem: Recovery documentation described additive update state as generally backward compatible and a Linux comment still described the obsolete mutable-pending design. Root cause: The text was not updated when installed release-unit state moved to a transaction-unique sidecar and legacy destructive actions began requiring explicit ownership proof. Fix: Document immutable pending state, crash-safe sidecar recovery, and the distinction between readable legacy transactions and authorization for handoff, healthy commit, or app-bundle rollback. Verification: git diff --cached --check Focused repair and updater tests, full race suites, vet, lint, and cross-platform compilation passed against the merged main-v2 baseline. Co-authored-by: SivanCola <[email protected]> * Close updater execution and compensation races Problem: - Generic failure-marker writes could let a stale invocation overwrite a newer transaction marker. - Legacy app rollback could move a symlink backup before rejecting it, and file publish could leave a tampered staged node live. - Windows self-update could execute a helper that changed after transaction prepare, overwrite a concurrently allocated cache path, or add a portable alias that was absent before. - macOS updater commands were resolved through PATH. Root cause: Mutation ownership was not rebound under the pending lock at every public entry point. Helper validation proved only PE architecture, staged-file verification followed symlinks, and helper cache publication used replace semantics. Fix: Rebind generic failure markers after lock acquisition, reject non-directory app backups before mutation, require regular-file staged payloads, compensate failed publication by retaining the rejected node and restoring the verified prior file, and preserve pending recovery state. Bind the Windows helper to the exact prepared release-unit SHA-256, create a unique cache node without replacement, freeze it through process creation, preserve the original portable-alias shape, and use absolute macOS system command paths. Verification: - go test ./... -count=1 - go test -race ./internal/repair ./internal/fileutil ./cmd/reasonix-guard -count=1 - cd desktop && go test ./... -count=1 - cd desktop && go test -race ./... -count=1 - go vet ./... and cd desktop && go vet ./... - golangci-lint 2.12.2 for root and desktop - cache guard: 8/8 pass - Windows amd64/arm64 repair, desktop, and update-helper test-binary compilation - Linux amd64 repair test-binary compilation Co-authored-by: SivanCola <[email protected]> * Tidy desktop updater dependencies Problem: The desktop CI go-mod-tidy gate failed because the updater no longer imports github.com/minio/selfupdate, but the module files still retained that dependency and its stale transitive checksums. Root cause: The transactional updater implementation replaced the selfupdate package without running the desktop module tidy gate afterward. Fix: Remove the unused selfupdate requirement and the transitive checksum entries that are no longer reachable from the desktop module graph. Verification: - cd desktop && go mod tidy (idempotent) - cd desktop && go vet ./... - focused workspace-rebuild timing tests: 10/10 pass - git diff --check Co-authored-by: SivanCola <[email protected]> * Harden prepared repair journals and Windows release-unit signing Problem: - Repair mutations could leave ambiguous undo state after a crash between rename and last-repair.json publication. - Concurrent writers could replace a pending journal or cleanup node after ownership checks, turning cleanup into deletion of unowned material. - Windows portable/installer updates did not bind signed payload identity through packaging, and Linux release-unit publication did not return exact installed-state receipts. Root cause: Undo bookkeeping treated last-repair.json as the only durable intent, cleanup used path-based remove after verification, and Windows packaging trusted unsigned payload layout without a create-only signed manifest contract. Fix: - Introduce create-only pending-repair.json write-ahead journals, promote or discard them under the repair lock, and fail closed on ambiguous recovery. - Displace cleanup targets to unique siblings, re-verify identity, and never unlink a pending journal that changed before cleanup. - Bind Windows signed payload manifests through packaging/signing and require exact installed-state receipts for Linux release-unit publication. Verification: - go test ./internal/repair -count=1 (pass) - focused security coverage for prepared intent, pending cleanup ownership, legacy healthy/rollback fail-closed, and macOS staging cleanup Co-authored-by: SivanCola <[email protected]> * fix(ci): use resolved version for payload manifest Problem: The Windows payload manifest step referenced a nonexistent ver step output, so actionlint rejected the release workflow. Root cause: The build job receives the release version from needs.resolve.outputs.version, but the step used steps.ver.outputs.version. Fix: Bind the signed payload manifest to the resolved release version output. Verification: actionlint v1.7.7 and scripts/release-workflows.test.sh pass. * fix(repair): bind prepared create ownership to published Windows modes Problem: - Windows CI rejected undo after a crash following snapshot create: the restored config file remained because CreatedStateID ownership failed. - Desktop packaging contract tests still expected steps.ver.outputs.version after the release workflow switched to needs.resolve.outputs.version. Root cause: - Prepared create intents predicted FileMode 0o600, but Windows Lstat reports regular files as 0444/0666. Undo then treated the published file as an unowned concurrent write and left it in place. - The packaging test was not updated with the resolved-version workflow fix. Fix: - Predict create-state IDs with platform-published file modes and rebind the pending journal to the exact on-disk node after AtomicCreateFile. - Align the Windows signing packaging contract test with the resolve job version output. Verification: - go test ./internal/repair -count=1 - go test ./internal/repair -run TestPreparedCreateStateIDMatchesAtomicCreate,TestApplyRepairPlanPersistsMissingSnapshotTargetBeforeCreate - cd desktop && go test -run TestWindowsReleaseSignsPayloadBeforeRepackaging . Co-authored-by: SivanCola <[email protected]> * fix(repair): reject prepared create ownership drift Problem: A file replaced between AtomicCreateFile and ownership rebinding could be claimed by the repair transaction and removed by undo. Root cause: The pending journal was rewritten to bind whatever state happened to be present after publication instead of proving it matched the precomputed owned state. Fix: Keep the write-ahead ownership binding immutable and fail closed when the published path differs; add a regression test that preserves a concurrent replacement through undo. Verification: focused crash/create tests, go test ./internal/repair, repair/fileutil race tests, Guard tests, go vet, and Windows repair test cross-compile pass. --------- Co-authored-by: SivanCola <[email protected]> Co-authored-by: PaulDing98 <[email protected]> |
||
|
|
170b23fbde |
feat(desktop): add integrated terminal sessions (#6994)
* feat(desktop): add integrated terminal sessions Add a right-dock xterm terminal with multi-session controls, workspace-relative start directories, and file-tree entry points. Use Unix PTY and Windows ConPTY backends with bounded session counts, output buffering, stale-tab checks, read-only and remote guards, and application shutdown cleanup. Keep shell resolution renderer-safe by accepting only backend-approved shell IDs and reading custom shell paths from user-global config. Refs #6513 Refs #6891 Verification: desktop Go race tests, Windows amd64 cross-compile, frontend terminal tests, full frontend test suite, typecheck, CSS checks, and production build. Co-authored-by: ipanta <[email protected]> * feat(desktop): scope terminal to each chat tab Align the integrated terminal with the chat-scoped Codex workflow. Bind sessions to the active chat tab and workspace, close only that tab's PTYs on tab close, and render the terminal as a responsive bottom drawer. Expose a bounded terminal output snapshot through an explicit user action that adds it to the composer. Do not inject terminal output into provider prompts or bypass the existing agent shell approval path. Verification: desktop terminal race tests, Windows amd64 cross-compile, frontend typecheck, terminal and workspace-layout tests, CSS checks, and production build. Co-authored-by: ipanta <[email protected]> * fix(desktop): harden integrated terminal lifecycle Problem: - Terminal output was lost after switching sessions or reopening the drawer, and concurrent session creation could overwrite renderer state. - Closing a chat tab could race an in-flight terminal start, while Windows coverage did not exercise the real ConPTY path. - The terminal drawer duplicated controls and loaded xterm into the initial application bundle. Root cause: - Output was buffered only while no sink was attached, state updates reused stale workspace snapshots, and the backend did not remember closed tab IDs. - Terminal-specific frontend tests were outside the standard suite, and the Windows executable fixture lacked an .exe suffix. Fix: - Keep bounded per-session output history, clear it only when a session is removed, and merge concurrent session updates atomically. - Tombstone closed tabs before process cleanup, reject late starts, suppress stale output, and distinguish natural exits from removals. - Add backend-approved shell selection, compact horizontal session tabs, lazy terminal loading, Windows ConPTY coverage, and CI wiring. Verification: - Desktop terminal race tests, full desktop tests, and go vet pass. - Windows amd64 test-binary cross-compilation passes. - Full frontend tests, TypeScript type checking, and production bundle checks pass. * fix(desktop): close integrated terminal review gaps Problem: - Read-only transitions and workspace rebinding could leave existing shells alive or allow a slow shell start to register against stale tab state. - First-open, retry, concurrent create, and single-session UI states could lose errors, duplicate requests, or hide the only close control. - Terminal output added to chat could retain terminal control payloads or break its Markdown fence. Root cause: - Terminal lifecycle invalidation was not serialized with tab capability changes, and process exit did not wait for the output reader to drain. - Renderer readiness used replace-style state updates without a retryable error model. - Composer insertion treated raw terminal output as ordinary text. Fix: - Add per-tab generations, capability gates, process-output draining, process-group cleanup coverage, and macOS PTY CI. - Make terminal readiness deduplicated and retryable, preserve concurrent sessions, expose operation errors, and keep bottom-drawer controls usable in all layouts. - Strip ANSI, OSC, C1, and unsafe control sequences, bound inserted output, and choose a safe Markdown fence. Verification: - go test ./... and go vet ./... in desktop/ - go test -race -run 'Test(ResolveTerminal|Terminal|EmptyTerminal|UnixTerminalProcess)' . in desktop/ - Windows amd64 and arm64 cross-builds for desktop/ - pnpm test, test:typecheck, test:remote, test:updater, test:performance, typecheck, test:terminal, and build - go vet ./..., gofmt, YAML parse, and git diff --check --------- Co-authored-by: ipanta <[email protected]> |
||
|
|
03cc774973 |
Fix Remote Workbench release blockers / 修复远程工作台发版阻断 (#6790)
* fix(remote): harden workbench restore and detach Problem: - Physical Windows-to-Linux acceptance exposed null Git pages, a crash restoring sessions after provider catalog changes, and leaked attach/runtime processes after disconnect. Root cause: - Empty pages cloned into nil slices, typed-nil controllers crossed an interface boundary, and runtime children shared the SSH session while proxy teardown waited indefinitely. Fix: - Preserve allocated empty result pages, normalize typed-nil controllers, detach and reap runtime children, and make proxy teardown bounded. - Add regression coverage for clean Git pages, provider removal restore, child reaping, and blocked stdin shutdown. Verification: - go test ./... and desktop go test ./... - go test -race ./... and desktop go test -race ./... - go vet ./... and desktop go vet ./... - pnpm frontend test:all and build - cache guard, protocol generation check, six-platform CLI cross-build, and npm release dry run * fix(deps): update x/text for GO-2026-5970 Problem: - The release CI govulncheck gate detected reachable GO-2026-5970 in golang.org/x/text v0.38.0. Root cause: - Both the root and Desktop modules pinned the vulnerable x/text release used through proxy normalization. Fix: - Upgrade golang.org/x/text to v0.39.0 in both modules and refresh their checksums. Verification: - govulncheck ./... reports no reachable vulnerabilities in both modules. - Root and Desktop full tests, vet, and race suites pass. |
||
|
|
93c82a948a |
Remote Workbench with Local Provider Broker / 使用本机模型的远程工作台 (#6728)
* feat(remote): add rpcwire, RuntimeAPI registry, and protocol codegen Problem: Remote Workbench needs a bidirectional JSON-RPC transport and a single Go registry that generates schema, Schema Hash, and TypeScript DTOs so Desktop and Host negotiate strictly by schema, not by full Build ID hard-fail. Root cause: main-v2 lacked rpcwire and a generative Remote protocol surface. Fix: - Port transport-neutral NDJSON JSON-RPC (rpcwire) with strict frames, concurrent requests, cancel, and size limits (adapted from #6725). - Port RuntimeAPI protocol registry + schema generator; regenerate committed artifacts for main-v2 eventwire. - Mark eventwire large fields externalizable for content-ref offload without changing provider-visible semantics. - Extend directions for Provider Broker (host_to_client_request / client_to_host_notification) and register broker methods in the frozen surface. Verification: - go test ./internal/rpcwire ./internal/remote/protocol ./internal/remote/protocolgen ./internal/eventwire - go run ./cmd/remote-protocol-gen -check -root . Co-authored-by: SivanCola <[email protected]> Co-authored-by: taibai233 <[email protected]> * feat(remote): add Desktop Provider Broker and TargetManager Problem: Workbench Hosts must consume models without holding API keys, and the main window needs Local + one Remote adapters with a single active projection. Root cause: No in-process Broker over rpcwire and no target fencing manager on main-v2. Fix: - Desktop Broker endpoint: catalog, stream open/cancel, chunk/end notifications, concurrency limits, DeepSeek ToolCallReasoning catalog flags, connection-wide stream cancel on close (direction from #6722; fencing ideas from #6725). - TargetManager: permanent Local, optional Remote, last-click-wins fencing, busy remote host-swap rejection, restart reconnect hint only (no auto SSH). Verification: - go test ./internal/remote/broker ./internal/remote/workbench/target Co-authored-by: SivanCola <[email protected]> Co-authored-by: taibai233 <[email protected]> * feat(remote): workbench SSH, attach-workspace, adapters, mirror, trust Problem: Remote Workbench needed end-to-end transport, lifecycle, main-window adapters, workspace-safe files, mirrors, and Provider trust — not Serve HTML or HTTP Gateway tunnels. Root cause: Prior foundation only had protocol/Broker/TargetManager; no SSH stdio path, attach proxy, runtime grace, or UI wiring. Fix: - Windows: system OpenSSH + AskPass + Job Object (adapted from #6725); remote command is `reasonix remote attach-workspace --stdio`. - macOS/Linux: Go SSH session stdio to the same attach entry. - attach-workspace + per-workspace runtime on Unix socket; 5-minute grace after detach; Schema Hash gate on initialize. - Desktop Workbench adapters (connect/disconnect/switch Local, remote RPC, Provider trust prompt); OpenRemoteWorkspace uses workbench path. - File containment (relative refs, no symlink leaves, atomic writes). - Local mirror store with digest validation; restore requires session.jsonl. - Durable Provider trust store (host+fingerprint → allowed refs only). - Frontend workbenchTarget helpers + tests. Verification: - go test ./internal/remote/workbench/... ./internal/remote/broker ./internal/rpcwire ./internal/remote/protocol ./internal/cli - cd desktop && go test -run 'TestRemote|TestWorkbench|TestAskPass|TestOpenRemote' - pnpm exec tsx --test desktop/frontend/src/__tests__/workbench-target.test.ts Co-authored-by: SivanCola <[email protected]> Co-authored-by: taibai233 <[email protected]> * docs(remote): record source PR authors as repository co-contributors Problem: Repository co-contribution attribution requires explicit documentation and commit-level Co-authored-by trailers for the source PR authors (#6722, #6725). Root cause: PR body credits alone do not enter the GitHub contribution graph. Fix: Document both source PR authors, what was adopted, and that GitHub co-author attribution uses Co-authored-by trailers with public noreply emails in REMOTE_WORKBENCH docs (EN + zh-CN). Verification: - docs/REMOTE_WORKBENCH.md contains Repository co-contributors table - every commit on this branch includes both Co-authored-by trailers Co-authored-by: SivanCola <[email protected]> Co-authored-by: taibai233 <[email protected]> * fix(remote): close the workbench broker runtime loop Problem: Remote Workbench still mixed Remote state with the Local controller, left parts of the runtime API empty, and could lose reconnect, operation, history, Git, and provider semantics in real main-window use. Root cause: The first integration established the transport and broker direction but did not fully fence target projections, rebuild controller profiles, persist remote execution state, or cover strict protocol and process lifecycle parity. Fix: - route the existing main window through the Host-owned controller, files, Git, history, catalogs, operations, goals, checkpoints, and slash completion - keep provider credentials on Desktop while forwarding strict provider requests, effort, DeepSeek tool-call reasoning, and reconnect-safe stream generations - harden Build ID/schema negotiation, SSH/AskPass process cleanup, runtime socket ownership, workspace containment, mirror pulls, disconnect fencing, and background target isolation - add generated protocol contracts and regression coverage for no-Host-key broker use, paging, reconnect, overflow, lifecycle, and projection behavior Verification: - env -u DEEPSEEK_API_KEY REASONIX_RELEASE_CACHE_GUARD=1 go test -race ./... - cd desktop && go test ./... && go vet ./... && go build ./... - pnpm --dir desktop/frontend test:all && pnpm --dir desktop/frontend build - go run ./cmd/remote-protocol-gen -check -root . - Windows amd64 cross-compilation for root Remote packages and desktop module - scripts/check-cache-impact.sh Co-authored-by: SivanCola <[email protected]> Co-authored-by: taibai233 <[email protected]> * fix(remote): bind attach to its workspace target Problem:\nCI lint found an overwritten normalized workspace, and initialize could replace the SSH attach target before runtime startup.\n\nRoot cause:\nThe attach bootstrap normalized Options.Workspace but then assigned the client-provided initialize workspace to the same variable without comparing the two.\n\nFix:\nKeep the normalized attach target, normalize the requested workspace separately, and reject mismatches before schema negotiation or runtime startup.\n\nVerification:\n- go test ./internal/remote/workbench/attach ./internal/remote/workbench/runtime\n- go vet ./internal/remote/workbench/attach\n- golangci-lint v2.12.2: 0 issues * fix(desktop): remove the legacy remote child window Problem:\nThe same-window Remote Workbench still carried an unreachable child-window launcher and special shell lifecycle, causing Desktop CI unused-code failures and contradicting the selected product model.\n\nRoot cause:\nThe App bindings were migrated to TargetManager, but the old ticket-based Serve HTML launch path and its window-specific startup branches were left behind.\n\nFix:\nRemove remote-window tickets, spawning, middleware, lifecycle branches, and obsolete helpers so every SSH workbench runs in the existing main window.\n\nVerification:\n- cd desktop && go test ./...\n- desktop and root golangci-lint v2.12.2: 0 issues\n- Windows amd64 Desktop cross-compilation\n- wails generate module * fix(remote): preserve workspace diff parity Problem: After rebasing onto #6733, WorkspaceChangeDetail still executed against the Desktop filesystem while a Remote target was active. Root cause: The frozen RuntimeAPI had no method for the newly added per-file workspace diff query. Fix: Add a strict workspace/changeDetail contract, route Desktop through the Host runtime, and generate bounded Git/checkpoint patches inside the selected remote workspace. Verification: go test ./...; go test -race ./...; cd desktop && go test ./...; pnpm test:all; protocol codegen check; vet/build/lint; Windows cross-compile. Co-authored-by: SivanCola <[email protected]> * Harden Remote provider broker lifecycle Problem: the local Provider Broker became active before the live SSH peer was rebound to its trusted fingerprint, and slow provider consumers could block detach while the Host mutex was held. Root cause: peer validation happened after client.Connect returned, while Host stream delivery performed blocking channel sends inside the global lock. Fix: gate Broker activation on the authenticated live peer identity and move ordered Host delivery into bounded per-stream queues with a sole output pump. Verification: go test -race ./internal/remote/broker; go test ./internal/remote/workbench/client; cd desktop && go test -race . -run 'Test(AuthorizeWorkbenchPeerFailsClosed|Workbench|Remote)' -count=1. * Restore Remote sessions across runtime restarts Problem: Remote sessions existed only in the runtime process map, so a normal detach followed by runtime exit made persisted transcripts invisible on reconnect. Root cause: the Host had no durable mapping between protocol session identities, controller profiles, topics, and transcript paths. Fix: add a per-workspace atomic session registry, lazily rebuild controllers after Broker activation, retain temporarily unavailable records, and persist lifecycle/profile changes without holding the runtime mutex across file I/O. Verification: go test -race ./internal/remote/workbench/runtime -count=1; go test -race ./internal/remote/workbench/runtime -run 'TestRuntime(RestoresSessionRegistryAfterProcessRestart|EarlyShutdownDoesNotOverwriteUnreadRegistry)' -count=20. * Keep Remote projections and provider state current Problem: Remote callbacks stayed bound to a stale local tab, provider metadata and authorization were captured at connect time, and the bounded-handler regression test released its blockers before overload dispatch was observable. Root cause: Local navigation was not serialized with Remote activation, Broker descriptors omitted compaction/pricing fields, provider closures captured immutable config, and the rpcwire test synchronized only handler startup. Fix: Fence visible local navigation and rebind callbacks on Remote activation, propagate complete non-secret provider metadata, refresh config and trust-scoped access for live Broker connections, fail closed on empty authorization, and wait for all overload responses before releasing handlers. Verification: go test -race ./internal/boot ./internal/remote/protocol ./internal/remote/broker ./internal/remote/workbench/client ./internal/remote/workbench/runtime ./internal/rpcwire (cd desktop && go test -race ./...) go test -race ./internal/rpcwire -run TestInboundHandlerConcurrencyIsBoundedWithoutBlockingResponses -count=100 go run ./cmd/remote-protocol-gen -check -root . ./scripts/cache-guard.sh * Constrain restored Remote transcript paths Problem: An edited session registry could name an opaque topic that the wire contract permits but restoration rejected, while a nested transcript path could traverse a directory symlink outside SessionDir. Root cause: Registry validation derived a topic_ prefix from an opaque protocol ID and only performed lexical containment plus a final-component symlink check. Fix: Accept any non-empty opaque topic ID, require runtime transcripts to be direct children of SessionDir, close partially built controllers on restore errors, and isolate fallback socket paths per test run. Verification: go test -race ./internal/remote/workbench/runtime -count=3 * Fence late Remote projection callbacks Problem: A callback or snapshot refresh captured for one local tab could complete after Remote was rebound to another tab and still emit into the stale surface. Root cause: Reactivation marked Remote visible before rebinding callbacks, and callback/refresh completion trusted its captured tab ID instead of the current projection binding. Fix: Bind callbacks before activation, route late events through the current Remote tab under the transition fence, redirect snapshot completion to the live binding, and drop completion while Local is active. Verification: cd desktop && go test -race -run 'TestWorkbenchLateCallbackUsesCurrentProjectionTab|TestSetActiveTabSwitchesRemoteProjectionOnlyForDifferentTab|TestConcurrentActivateTopicSerializesSingleSurfacePruning' -count=20 * Synchronize Remote host-key prompt regression Problem: The full desktop race suite detected an unsynchronized map write in the host-key prompt serialization test. Root cause: The test started the first prompt goroutine before registering the second managed host, allowing the goroutine to read the manager map while the test wrote it. Fix: Register every host before launching concurrent prompts and wait for both prompt goroutines during test cleanup. Verification: cd desktop && go test -race -run '^TestHostKeyPromptsAreSerializedForGlobalDialog$' -count=100 .; cd desktop && go test -race ./... * Wait for queued background task teardown Problem: Windows CI could finish TestBackgroundTaskReturnsBeforeSlotFrees while its background job was still persisting transcript artifacts, causing TempDir cleanup to fail with a non-empty directory. Root cause: The test waited only for provider startup and then slept for 50 milliseconds instead of synchronizing with the job's terminal lifecycle. Fix: Scope the job to the test session, capture its returned job ID, wait for jobs.Done, and close the manager on every exit path before temporary-directory cleanup. Verification: go test -race ./internal/agent -run '^TestBackgroundTaskReturnsBeforeSlotFrees$' -count=100; REASONIX_RELEASE_CACHE_GUARD=1 go test -race ./internal/agent; REASONIX_RELEASE_CACHE_GUARD=1 go test ./...; GOOS=windows GOARCH=amd64 CGO_ENABLED=0 go test -c ./internal/agent; go vet ./internal/agent; golangci-lint run ./internal/agent/... * fix(remote): fall back to server root workspace * fix(remote): close review blockers for workbench v1 Problem: Remote Workbench could silently expose the Host root, strand a session after registry persistence failure, leave local provider streams running after cancellation, and fail the Ubuntu/Windows attach tests. Root cause: One-click workspace resolution used an implicit root fallback; profile changes closed or mutated live controllers before durable state succeeded; Broker delivery lacked an abort path and open cancellation did not reach Desktop; attach readiness and tests depended on connection and timing assumptions that were not portable. Fix: Require explicit workspace selection, roll profile changes back while preserving the old controller, propagate best-effort Broker cancellation with abortable delivery, remove the readiness probe generation race, and use deterministic platform-aware attach tests. Verification: Remote, Broker, attach, runtime, rpcwire, race, Desktop, frontend Remote, TypeScript, production build, protocol generation, cache guard, go vet, and repeated cancellation/attach regressions passed. * fix(remote): make attach startup portable Problem: The Windows CI attach integration test could not observe the in-process AF_UNIX listener and reported that the runtime never became ready. Root cause: Startup readiness was inferred with os.Stat on the socket path, but Windows AF_UNIX does not expose a portable socket file. A separate readiness connection is also unsafe because runtime connections own generations. Fix: Let the real attach connection retry bounded socket dials until the runtime listener accepts it, and return immediately after starting the in-process test runtime. Verification: - attach integration test repeated 50 times - attach race test repeated 30 times - Windows amd64 test binary cross-compiled - go test ./internal/remote/... - remote broker, attach, and runtime race tests * fix(remote): keep runtime sockets portable Problem: Windows CI still could not connect to the in-process workbench runtime even with bounded dial retries. Root cause: The generated AF_UNIX address exceeded the platform path limit when the remote home itself was long, as on Windows hosted runners. Fix: Compact the runtime socket hierarchy while keeping it scoped under the Reasonix home, and add a regression test against the portable AF_UNIX length bound. Verification: - attach integration test repeated 50 times - runtime socket path tests repeated 20 times - attach and runtime race tests - Windows amd64 runtime test binary cross-compiled - go test ./internal/remote/... - protocol generation check and cache guard * fix(desktop): preserve native macOS app ownership Problem: Packaged macOS builds used reasonix-guard as CFBundleExecutable, so the Dock and LaunchServices owned a non-UI parent while the Wails child owned the window. The app could stay running without reopening its window from the Dock. Root cause: The packaging step replaced Wails' native bundle entry point to route startup through Guard. macOS application identity cannot be transferred reliably across that parent-child process boundary. Fix: Keep reasonix-desktop as the macOS bundle executable, retain Guard and the CLI as recovery sidecars, and run the equivalent failed-install, crash-loop rollback, repair, and Safe Mode preflight in Desktop before Wails creates WebView. Update packaging contracts and recovery documentation for the platform-specific launch model. Verification: - env -u DEEPSEEK_API_KEY go test ./... - go test ./... (desktop module) - go test -race -run 'TestDesktopStartupRecovery|TestDesktopPackagesPreserveNativePlatformLaunchers' -count=1 . (desktop module) - go vet ./... (desktop module) - DESKTOP_BUILD_SKIP_DMG=1 scripts/desktop-build.sh darwin/arm64 v0.0.0 - Signed packaged App launched with reasonix-desktop as PID 1 child, one Desktop process, no Guard process, and an onscreen CoreGraphics window * fix(desktop): satisfy startup recovery lint Problem: The Desktop CI job rejected the macOS startup recovery helper because continueLaunch was initialized before every return path overwrote it. Root cause: The named return value carried an unnecessary default assignment that go vet did not flag but golangci-lint's ineffassign check did. Fix: Initialize only the Safe Mode result and let each branch set the continue decision explicitly. Verification: - go test -run 'TestDesktopStartupRecovery|TestDesktopPackagesPreserveNativePlatformLaunchers' -count=1 . - go vet ./... * fix(remote): close workbench runtime safety gaps Problem: Remote Workbench could reject typed client requests before session hydration, execute duplicate mutations more than once, lose the committed target during a failed replacement, race controller lifecycle changes, and expose Local saved-session bindings while Remote was active. Root cause: Client authority hydration preserved serialized zero values, the advertised requestId contract had no runtime ledger or request sequencer, TargetManager stored candidates as committed state, and unsupported Remote history actions silently fell through to Local bindings. Fix: Authoritatively hydrate client-owned fields while preserving retry IDs, serialize runtime requests with a bounded 24-hour mutation replay ledger, fence controller profile transitions, promote replacement targets only after successful connection, and fail closed for unsupported Remote saved-session operations. Verification: - env -u DEEPSEEK_API_KEY go test ./... - env -u DEEPSEEK_API_KEY REASONIX_RELEASE_CACHE_GUARD=1 go test -race ./... - cd desktop && go test ./... && go vet ./... && go build ./... - pnpm --dir desktop/frontend test:remote - go run ./cmd/remote-protocol-gen -check -root . - scripts/check-cache-impact.sh with the live PR body Co-authored-by: taibai233 <[email protected]> * fix(remote): keep session rotations authoritative Problem: Completed turns could retain stale approval or ask prompts, while committed new/clear session rotations could return an error after a registry write failure and leave clients on the old runtime epoch. Root cause: Turn completion only cleared replay events when no prompt remained, session rotation treated the fallible registry write as part of the already-committed controller mutation, and the client did not apply new/clear result epochs. Fix: Clear terminal prompt state, return and adopt the committed rotation epoch while logging registry degradation, reset stale runtime state, and invalidate the previous snapshot. Verification: go test ./...; go test -race ./internal/remote/workbench/runtime ./internal/remote/workbench/client; cd desktop && go test ./...; go vet ./...; go run ./cmd/remote-protocol-gen -check -root . * fix(remote): fence snapshot refreshes and fork cleanup Problem: Remote rewind could report failure after destructive state changes committed, late subscribe responses could restore stale runtime authority, and failed forks left transcript artifacts on disk. Root cause: Post-commit registry errors were treated as rollbackable, subscribe responses had no request-authority fence, and fork rollback removed only the in-memory child session. Fix: Keep committed rewind results authoritative, serialize and revision-fence subscription refreshes with automatic stale retry, validate projection freshness, and remove all artifacts for uncommitted forks. Verification: - go test ./... - go test -race ./internal/remote/workbench/client ./internal/remote/workbench/runtime ./internal/control - go vet ./... - cd desktop && go test ./... - go run ./cmd/remote-protocol-gen -check * fix(remote): serialize projections and capability boundaries Problem: Remote subscribe snapshots could overwrite newer events, unsupported Remote Memory surfaces could reach Local state, and the broker test assumed concurrent notification arrival order. Root cause: Snapshot publication, event callbacks, and authority-changing results had no shared projection boundary; Desktop bindings did not fail closed for frozen capabilities; the test ignored stream sequence numbers. Fix: Linearize subscribe commits with session delivery and mutation results, install the initial Remote projection within that boundary, reject unsupported Remote Memory and Auto Research access, and assert broker chunks by sequence. Verification: go test ./...; go test -race ./internal/remote/...; desktop go test ./...; desktop go vet ./...; broker round-trip with coverage at count 500. * fix(remote): preserve active workbench transport Problem: Uninitialized socket probes could evict the live Desktop connection, target transition events exposed candidates as active, and Remote notification overload silently dropped ordered events. Root cause: Connection ownership was transferred before initialize response commit, candidate identity and active identity shared one event shape, and rpcwire used best-effort concurrent notification dispatch. Fix: Add provisional connection gates with post-write activation and exact daemon build checks; keep transition events on the committed target and fence submits; add ordered bounded notification queues with explicit overflow failure and generation-aware Broker delivery. Verification: go test ./... go test -race ./internal/rpcwire ./internal/remote/broker ./internal/remote/protocol ./internal/remote/workbench/... (cd desktop && go test ./...) (cd desktop/frontend && pnpm test:all) * fix(remote): serialize connection teardown Problem: A client closing immediately after initialize could race the response-commit callback, leaving a rejected gate paired with newly activated connection ownership. Root cause: Provisional connection teardown and post-write activation checked and mutated the wire registry under separate critical sections. Fix: Serialize teardown with connection activation, keep the production notification queue memory bound at 256 entries, and lock the initialize post-write contract with a router regression test. Verification: go test ./internal/remote/protocol ./internal/remote/workbench/runtime go test -race ./internal/remote/workbench/runtime ./internal/remote/protocol ./internal/remote/broker ./internal/rpcwire * test(remote): cover broker-backed tool loop Problem: The no-Host-key integration test only proved a single text response. It did not exercise the critical model-to-Host-tool-to-model loop or DeepSeek reasoning replay across the Desktop Broker. Root cause: The stub Provider always returned final text on its first request, so Host tool execution and the second Broker request were outside the end-to-end coverage. Fix: Make the stub request read_file first, assert the Host result and reasoning/tool-call replay in the second request, then complete the turn and verify projected tool events. Verification: - go test ./internal/remote/workbench/runtime -run '^TestRuntimeControllerUsesDesktopBrokerWithoutHostKey$' -count=1 -v - go test -race ./internal/remote/workbench/runtime -run '^TestRuntimeControllerUsesDesktopBrokerWithoutHostKey$' -count=20 - go test ./internal/remote/workbench/... ./internal/remote/broker ./internal/rpcwire - env -u DEEPSEEK_API_KEY go test ./... * test(remote): add physical Windows Linux acceptance Context: The Remote Workbench release gate needs reproducible evidence from a real Windows OpenSSH client to a Linux Host, including Broker-backed tool execution and reconnect cleanup. Change: Add an opt-in Windows ARM64-compatible acceptance test that exercises the production SSH transport, Linux workspace queries, Desktop Broker tool replay, session authority after reconnect, and OpenSSH process reaping. The fixture is environment-gated and keeps Provider execution on the Desktop side. Verification: - GOOS=windows GOARCH=arm64 CGO_ENABLED=0 go test -c -tags reasonix_remote_integration ./desktop - go test ./... - go test -race ./internal/remote/... * fix(remote): align Desktop and Host build identity Context: The Desktop is built from a nested Go module, so Go does not embed vcs.revision there while the bundled Host CLI built from the root module does. Strict Remote Workbench negotiation consequently rejects matching release artifacts as different builds. Fix: Allow release builds to link a validated shared source revision into the protocol package. Capture the clean repository revision before packaging mutations and inject it into both the Wails Desktop and bundled CLI sidecar, preserving the dirty suffix for local builds. Verification: - go test ./... - go test ./... (desktop module) - go test -race ./internal/remote/... - Windows ARM64 acceptance cross-compile with linked source revision - bash -n scripts/desktop-build.sh * fix(remote): preserve direct SSH credentials Context: The Windows Workbench direct-host branch reduced a saved Host to username, address, and port before starting OpenSSH. Explicit identity_file and proxy_jump settings were silently dropped, so valid direct connections could fail authentication or bypass the required jump path. Fix: Add an injection-safe direct configured transport that preserves identity and ProxyJump as distinct argv values. Route the Windows Workbench direct branch through it while retaining normalized IPv4 and IPv6 target parsing. Verification: - go test ./... (desktop module) - go test -race focused Windows Workbench transport tests - Windows ARM64 acceptance cross-compile * fix(remote): share runtime build identity Context: The detached workspace runtime duplicated Build ID discovery instead of using the shared protocol implementation. Linked source revisions used by nested Desktop release builds were therefore ignored, causing one installed CLI to reject its own runtime as a different build. Fix: Use protocol.CurrentBuildID for production runtime identity while retaining the explicit SourceRevision test seam. This keeps Desktop, attach, and detached runtime negotiation on one validated source of truth. Verification: - go test ./internal/remote/workbench/runtime - go test -race ./internal/remote/workbench/runtime - linked-revision focused regression test * fix(remote): allow initial history page Context: The Desktop history client intentionally requests the latest page without a cursor, and the Host runtime supports that state. The wire DTO nevertheless marked cursor as required, so strict validation rejected every initial session/history request after a successful Remote turn. Fix: Make the history cursor optional, regenerate the frozen protocol artifacts, and cover omission plus strict decoding. Strengthen the physical acceptance fixture to verify both product version and exact source revision before connecting. Verification: - go test ./... - go test ./... (desktop module) - go run ./cmd/remote-protocol-gen -check -root . - Windows ARM64 acceptance cross-compile * fix(remote): validate session mirror snapshots Context: After a completed turn the reconnect snapshot includes the persisted session mirror. That complete object incorrectly populated originalBytes while marked non-truncated, violating the frozen externalized-field contract and turning session/subscribe into an internal error. Fix: Emit complete session mirrors without truncation-only metadata and cover the artifact contract directly. Verification: - go test ./... - go test -race ./internal/remote/workbench/runtime - focused session mirror and runtime integration tests * fix(remote): model session mirror payload Context: Remote reconnect snapshots advertised /mirror/session.jsonl as an externalized content pointer, but SessionSnapshot had no schema field at that path. Strict response validation therefore rejected every post-turn reconnect snapshot before Desktop could fetch the mirror. Fix: Add the mirror placeholder to the frozen snapshot DTO, replace its concrete body with null during marshaling, and keep the body behind the existing bounded contentRef transfer. Regenerate Go/TypeScript protocol artifacts and prove that no session body is inlined on the wire. Verification: - go test ./... - go test ./... (desktop module) - go run ./cmd/remote-protocol-gen -check -root . - Windows ARM64 acceptance cross-compile * test(remote): add live Desktop Broker acceptance * test(remote): require live Broker tool loop --------- Co-authored-by: taibai233 <[email protected]> |
||
|
|
4cec2b0fad |
fix(desktop): isolate WebView2 startup from stale proxies / 隔离 WebView2 启动残留代理 (#6727)
* fix(desktop): isolate WebView2 startup from stale proxies Problem: On Windows, Reasonix can remain invisible when a proxy client exits without clearing an unreachable system proxy. WebView2 stalls before DOM-ready while the Wails window is still StartHidden. Root cause: The WebView2 browser process inherits the Windows system proxy even though the desktop shell loads bundled assets through wails.localhost and remote workspaces through loopback URLs. There was also no visible timeout path when DOM-ready never arrived. Fix: Start the vendored WebView2 Chromium with --no-proxy-server for the embedded and loopback shell, while leaving Reasonix provider, updater, MCP, and SSH traffic on the separate Go proxy configuration. Add a 15-second Windows watchdog that shows the native window and a bilingual recovery prompt when startup remains incomplete. Verification: - cd desktop && go test ./... - cd desktop && go vet ./... - cd desktop && wails generate module - Windows amd64 and arm64 desktop test binaries cross-compiled successfully - Windows amd64 vendored WebView2 edge tests cross-compiled successfully - ./scripts/cache-guard.sh * fix(desktop): preserve proxying for remote images Problem: Isolating the Windows WebView2 shell from the system proxy would also make remote Markdown and Mermaid image resources bypass Reasonix proxy settings. The startup watchdog could additionally fire in remote-window test contexts that do not carry a Wails frontend context. Root cause: Remote image URLs were still loaded by the WebView network stack, while the watchdog was enabled for every Windows WebView process instead of only the primary desktop window. Fix: Route remote Markdown and Mermaid image resources through a bounded, SSRF-guarded backend endpoint that uses the current Reasonix proxy specification, validates raster content, and sanitizes SVG. Scope the Windows startup fallback to the primary window only. Verification: - cd desktop && go test ./... - cd desktop && go vet ./... - cd desktop && go test -race . -run 'RemoteMarkdownImage|WindowsWebView2StartupFallback|AwaitStartupFallback|RemoteWindowLifecycle' -count=1 - cd desktop/frontend && pnpm test - cd desktop/frontend && pnpm typecheck - cd desktop/frontend && pnpm build - Windows amd64 and arm64 desktop test binaries cross-compiled successfully - ./scripts/cache-guard.sh * chore(security): document guarded image fetch Problem: CodeQL cannot infer that the remote Markdown image request is constrained by the custom URL, redirect, DNS, and dial-time guards, so it reports the intentional fetch sink as an uncontrolled critical request. Root cause: The security boundary is implemented across helper functions and a proxy-aware transport rather than a sanitizer recognized by the standard CodeQL data-flow model. Fix: Add the repository-standard scoped CodeQL annotation at the single guarded request sink, with the direct-dial and trusted-proxy DNS boundary documented inline. Verification: - cd desktop && go test . -run RemoteMarkdownImage -count=1 - cd desktop && go vet . * chore(security): format CodeQL suppression Problem: The scoped CodeQL suppression was not recognized because its explanatory text shared the directive line. Root cause: CodeQL requires the codeql[query-id] directive to occupy its own comment line immediately before the guarded alert location. Fix: Keep the security-boundary explanation adjacent to the request and place the exact suppression directive on a separate line. Verification: - git diff --check - gofmt desktop/remote_markdown_image.go * chore(security): add legacy CodeQL suppression Problem: The modern source suppression directive remains visible to the audit but is not honored by the PR request-forgery query post-processing. Root cause: This CodeQL Go query configuration still requires the legacy lgtm query-scoped suppression marker. Fix: Add the exact legacy marker beside the modern directive at the single guarded request sink. Verification: - git diff --check - gofmt desktop/remote_markdown_image.go * chore(security): keep image guard rationale explicit Problem: Neither supported source suppression marker is honored by this repository's PR security summary for the intentional remote-image fetch. Root cause: The query does not model the custom proxy-aware SSRF transport and retains the alert independently of inline post-processing markers. Fix: Remove the ineffective directives while preserving the adjacent security-boundary explanation. The alert will be handled through the repository's auditable code-scanning triage record. Verification: - git diff --check - gofmt desktop/remote_markdown_image.go * fix(desktop): pin proxied markdown image targets Problem: Remote Markdown images skipped destination validation whenever a proxy was selected, valid SVGs with common prologs were rejected, and per-image transports could retain idle connections. Root cause: The image fetcher treated proxy-side DNS as a trust boundary, evaluated dynamic proxy routing more than once, relied on an SVG prefix heuristic, and created keep-alive transports that were never reused. Fix: Resolve every initial and redirected image target to public unicast addresses, select the proxy route once, and dial only vetted IPs through direct, HTTP(S) CONNECT, or SOCKS paths while preserving Host and TLS SNI. Make each transport single-use and close it with the response. Parse SVG roots after safely removing BOM, comments, and directives. Verification: - cd desktop && go test ./... - cd desktop && go test -race . -run 'RemoteMarkdownImage|WindowsWebView2StartupFallback|AwaitStartupFallback|RemoteWindowLifecycle' -count=1 - cd desktop && go vet ./... - frontend Markdown/Mermaid tests and pnpm typecheck - ./scripts/cache-guard.sh - Windows amd64/arm64 desktop and vendored WebView2 test binaries cross-compiled * test(remote): stabilize encrypted identity fallback Problem: The full race job intermittently timed out while testing fallback from a stored passphrase to a per-identity prompt. Root cause: The integration test allowed only 10 seconds for an SSH handshake that performs three CPU-bound passphrase KDF operations, which is too tight under full race-package contention. Fix: Give this single KDF-heavy integration scenario a 30-second context while leaving production timeouts and behavior unchanged. Verification: GOMAXPROCS=2 go test -race ./internal/remote -run '^TestClientFallsBackFromStoredPassphraseToPerIdentityPrompt$' -count=10; go test ./internal/remote -count=3; go vet ./internal/remote. |
||
|
|
65fcd46525 |
Add a Remote SSH module (remote serve + tunnel) / 新增远程 SSH 模块(远端 serve + 隧道) (#6673)
* feat(serve): support --port-file/--token-file/--pid-file for supervised headless serve Extract RunGracefulListener so callers can bind first (--addr 127.0.0.1:0), record the real port, and keep the auth token out of argv. Groundwork for the remote-SSH module's detached remote serve bootstrap. * feat(config): add user-global-only [remote] section for SSH hosts [[remote.hosts]] entries carry connection/forward/workspace settings and name credential env vars (passphrase_env/password_env) instead of secrets, mirroring the provider api_key_env idiom. Like [secrets], loadForRoot pins the section back to the user-global value after the project merge — with a deep copy, because TOML decoding mutates slice backing arrays in place and a shallow pin would alias the project-injected hosts. Remote credential env names flow into CredentialEnvNames so they are filtered from tool subprocess environments. Adds RemoteStateDir/RemoteKnownHostsPath and the internal/store name helpers for the remote-side serve state layout. * feat(remote): SSH transport kernel — dial, auth, host keys, forwards, SFTP New internal/remote package group provides the connection layer for the remote module: - client.go: supervised connection with a single-goroutine state machine, keepalive ([email protected]), full-jitter exponential-backoff reconnect, shared SFTP (generation-checked), and forward re-attach. - auth.go: OpenSSH-like method order (agent, identity file, default identities, password, keyboard-interactive); interactively-obtained secrets are cached in-process so reconnects never re-prompt. - knownhosts.go: system known_hosts read-only + a Reasonix-managed TOFU file; key mismatch is a non-promptable hard error. - dial.go: ProxyJump chains (netclient proxy applies to the first hop only). - forward/: -L (persistent local listener across reconnects) and -R (re-registered on each attach) with typed ErrBindBusy/ErrDuplicateForward. - sftpfs/: list/stat/capped-read-with-text-detection/atomic-write/mkdir/ rename/remove, quarantining the new github.com/pkg/sftp dependency. - netclient/dialer.go: StreamDialer honoring the user's proxy policy (SOCKS5 via x/net/proxy, http/https via CONNECT). - sshconfig.go: ~/.ssh/config import via github.com/kevinburke/ssh_config. - sshtest/: in-process SSH+SFTP server for tests. New deps: github.com/pkg/sftp, github.com/kevinburke/ssh_config (both pure Go). x/* versions kept at main-v2's pins. Cache-impact: none — no prompt/tool/provider path touched. Cache-guard: n/a (new leaf subsystem, not in the system-prompt prefix). * feat(remote): remote serve bootstrap over SSH internal/remote/bootstrap ensures a detached 'reasonix serve' runs for a (host, workspace) pair: reuse a live process (the reconnect path), else detect the remote OS/arch (uname), locate/install/upload reasonix, write a 32-byte token 0600 via SFTP, launch serve bound to 127.0.0.1:0 with --token-file/ --port-file (token never in argv), poll the port file, and persist ServeState JSON. Status/Stop/Logs manage the recorded process. Every remote command operand is POSIX single-quote-escaped (golden test covers '; rm -rf ~'). V1 targets Linux/macOS remotes; cross-platform upload without a matching local binary is a documented limitation directing to serve_install = npm. Cache-impact: none — new leaf subsystem, no prompt/tool/provider path. Cache-guard: n/a. * feat(cli): reasonix remote command group + /remote slash Adds the remote subcommand tree (add/list/remove/import/test/connect/open/ status/forward/serve/fs), mirroring the mcp command shape. Host CRUD writes to the user-global config only (remote hosts are pinned there); connect runs a foreground supervisor that dials, bootstraps remote serve, forwards the serve port with the token in a loopback URL, applies configured -L/-R forwards, and holds the tunnel until Ctrl-C while the remote serve keeps running. Terminal prompts handle key passphrases, passwords, and TOFU host-key confirmation; dialing honors the user's proxy settings via netclient. The /remote slash command shows a read-only host summary. New i18n fields added to all three catalogs. Cache-impact: none — CLI surface + i18n only, no prompt/tool/provider path. Cache-guard: n/a. * feat(desktop): remote SSH Go bindings (remote_app.go) 25 App bindings behind a narrow remoteKernel interface (fake-injected in tests): host CRUD (user-global config), Connect/Disconnect/status, status-driven TOFU host-key confirmation (ConfirmRemoteHostKey resumes a parked dial), SFTP list/read/write-with-mtime-conflict/mkdir/rename/delete, port-forward CRUD, and remote serve EnsureServer/Open/Stop/Status/Logs. desktopRemoteManager wraps internal/remote per host; status/forward/server transitions bridge to remote:status / remote:forwards / remote:server through the asyncRuntimeEmitter (never blocking the kernel). The manager is created lazily on first use and closed in App.shutdown, rejecting any pending host-key prompt. OpenRemoteWorkspace forwards the serve port and opens the tunneled URL via Wails BrowserOpenURL; last workspace/host persist to desktop-remote.json (all-optional fields, backward-compatible). desktop/go.mod gains pkg/sftp + kevinburke/ssh_config via go mod tidy. Cache-impact: none — desktop bindings only, no prompt/tool/provider path. Cache-guard: n/a. * feat(desktop): remote SSH frontend (host manager, explorer, ports, server) - lib/types.ts: Remote* TS mirrors + SettingsTab "remote". - lib/bridge.ts: 25 Remote* AppBindings + browser-dev mock + onRemoteStatus/ onRemoteForwards/onRemoteServer subscribers with a mock fan-out for tests. - store/remote.ts: zustand store; pending_hostkey status drives the global fingerprint dialog and resolves on the next non-pending status. - RemoteHostsPage (Settings > Remote SSH tab): host list with status chip, connect/disconnect/open, add/edit form, ssh_config import picker. - RemotePanel: overlay drawer with Files (lazy tree + textarea editor with mtime-conflict Reload/Overwrite), Ports (live forward rows + add form), and Server (bootstrap phases + open-in-browser + logs) tabs. - RemoteHostKeyDialog: global TOFU modal off the store's pendingFingerprint. - StatusBar remote chip (worst live state, click opens explorer); App.tsx subscribes remote:* app-wide and renders the panel + dialog. - i18n keys added to en / zh / zh-TW (key sets stay aligned). - Editor is a monospace textarea (no CodeMirror/Monaco) per lean-deps. - Tests: src/__tests__/remote-store.test.ts (added to the test script). Cache-impact: none — desktop frontend only. Cache-guard: n/a. * docs: document the Remote SSH module (SPEC, GUIDE, CONFIG_PATHS, CHANGELOG) Adds the internal/remote layout + acyclic dependency note to SPEC 2, a bilingual Remote SSH section to GUIDE.md / GUIDE.zh-CN.md (config, CLI, reconnect/persistence semantics, host-key TOFU, remote-side state layout, desktop UI), the managed known_hosts path to CONFIG_PATHS(.zh-CN), and a CHANGELOG entry. Cache-impact: none — docs only. Cache-guard: n/a. * chore: ignore stray .reasonix state under package dirs A bootstrap test whose RealPath resolved to CWD once wrote a .reasonix/remote/ token under the package dir and it was swept into a commit. The offending file has been scrubbed from history; this guards against recurrence. * fix(remote): CI green — errcheck + cross-platform bind-busy and Windows test skips - client.go: check cl.Wait() return (errcheck lint). - forward: classify WSAEADDRINUSE as bind-busy on Windows (build-tagged addrinuse_windows.go), so ErrBindBusy is reported cross-platform. - sftpfs / bootstrap integration tests skip on Windows: their in-process SFTP server serves the LOCAL filesystem, so on Windows it serves drive paths and the POSIX-remote path translation breaks — a harness limitation, not a product one (V1 remotes are Linux/macOS). Linux/macOS CI covers the flows. Cache-impact: none — test/lint-only + a Windows errno check. Cache-guard: n/a. * test(remote): skip EnsureServe reuse/relaunch/stop tests on Windows at setup TestEnsureServeReusesLiveProcess and siblings pre-write state via pathsFor() (POSIX path.Join) before newFakeConn runs, so on Windows the temp-dir remote home yields a malformed drive path and the setup os.WriteFile fails. Move the Windows skip to the top of each such test (shared skipOnWindows helper) so it fires before any pathsFor/os setup, not just inside the harness ctor. * fix(remote): address review — P1 security / data-integrity / lifecycle Resolves the 14 findings from code review (all verified against the code): Security - Serve token no longer leaks to the remote log: runServe suppresses the token share line under --port-file (supervised), and the launch script creates the log 0600 (umask 077 + chmod). (F1) - ProxyJump no longer sends the target's password to jump hosts: each hop gets its own credential-free AuthOptions with a per-hop cache (persisted on the Client so reconnects don't re-prompt). (F2) - CLI surfaces a misconfigured proxy instead of silently dialing direct; the https-proxy path now TLS-wraps the connection before CONNECT; desktop passes the proxy Dialer it previously omitted. (F14) Data integrity - `remote fs get` streams the full file via sftpfs.Download instead of the 4 MiB-capped preview reader that silently truncated. (F3) - Workspace slug carries an FNV hash of the exact path, so /srv/a-b and /srv/a/b no longer share serve state files. (F10) - Desktop host edit merges: passphrase_env/password_env/forwards (absent from the desktop input) are preserved instead of wiped. (F7) Lifecycle / correctness - Bootstrap gates on `serve --help` advertising --port-file, not a version number (the flags ship in this change, so no released version qualifies). (F4) - Launch uses setsid only when present, so stock macOS (no setsid) works. (F5) - Desktop no longer uploads the GUI binary as the remote CLI (LocalBinary="" makes the upload path fail clearly toward npm). (F6) - Serve tunnel re-binds to the current serve address on every EnsureServer and is torn down on Stop; per-host serveMu serializes concurrent ensures. (F8) - Reuse/Stop verify the pid is actually a reasonix serve (ps args), not just kill -0, so a recycled PID is never mistaken or signalled. (F9) - Desktop connect retries after failure (failed Start drops the managed host) and builds a fully-populated host under the lock so a concurrent Disconnect can always close it. (F11) - SSH handshake is bounded by a conn deadline (ClientConfig.Timeout does not cover NewClientConn), so a banner-less host can't hang the dial or Close. (F12) - ScanSSHConfig returns a non-nil slice so an empty result encodes as [] not null. (F13) Tests: per-hop auth isolation, full-file download vs preview cap, slug collision (/srv/a-b vs /srv/a/b), serve-alive/launch/locate command shape, desktop edit-merge and non-nil scan. go test ./... green in both modules; gofmt/vet clean; Windows cross-build green; cache-guard pass. Cache-impact: none — no prompt/tool/provider/memory path touched. Cache-guard: ./scripts/cache-guard.sh — all cases pass. System-prompt-review: not required — no system-prompt-feeding config field changed. * Harden Remote SSH lifecycle and packaging Problem: Remote SSH could misapply ProxyJump credentials, race connection and service lifecycles, interrupt live forwarding during replacement, and ship Desktop builds without the CLI sidecar required by upload installation. Several UI failures were also silent or left stale state behind. Root cause: Jump aliases shared endpoint-keyed authentication, bootstrap ownership relied on weak state checks without cross-process serialization, and Desktop operations lacked generation-scoped cancellation. Forward replacement stopped the working listener before the replacement was ready, while packaging and updater contracts did not include the CLI binary. Fix: Resolve and authenticate every SSH hop independently, make dialing and Desktop operations context-aware, serialize bootstrap with an atomic remote lock, verify exact process ownership, and make forward replacement failure-atomic. Harden token, SFTP, agent, config, and ssh_config handling; package and update the CLI sidecar on macOS, Windows, and Linux; and surface actionable validation and lifecycle errors in the Desktop UI. Verification: - go test ./... - cd desktop && go test ./... - go test -race for Remote, CLI, store, and Desktop modules - go vet ./... for root and Desktop modules - pnpm test:all and pnpm build - Windows amd64 cross-compilation for Remote, bootstrap, forwarding, SFTP, and CLI packages - clean merge snapshot against latest origin/main-v2 with root, Desktop, and frontend checks passing * Add native Remote SSH workspace windows Problem: Remote SSH was buried in settings, and opening a remote workspace handed the authenticated loopback URL to the system browser instead of preserving a desktop workspace experience. Root cause: The desktop had no window-level Remote SSH control and no isolated Wails launch mode for a second native workspace window. Fix: Add a VS Code-style status entry, host switcher, command actions, and Remote dock. Launch remote workspaces in a lightweight Reasonix process that skips local session ownership, single-instance focus, tray services, and Wails bindings. Pass the loopback URL through a one-shot permission-restricted ticket so the token never appears in process arguments. Verification: - go test ./... - pnpm test:all - pnpm build - wails build -clean - native macOS multi-window smoke test - git diff --check * Tidy Remote SSH module metadata Record the SSH config and SFTP packages as direct dependencies after rebasing onto the latest main-v2 baseline. Remove stale module checksums so the root module remains tidy in CI. * Fix Remote SSH Windows test assumptions Load the fixture SSH config by its explicit path while setting both platform home variables, and use the Windows .exe suffix for the desktop CLI fixture. This keeps the Remote SSH coverage portable across the CI matrix. |
||
|
|
7f00d2c260 |
feat(desktop): Redesign theme experience with official packs / 主题体验改造与官方主题 (#6614)
* feat(desktop): Add Theme Pack V1 engine with theme library / 新增 Theme Pack V1 引擎与主题库 Controlled, non-executable desktop skins: semantic color tokens, density and corner recipes, and an optional local background image with focus and safe area. Includes manifest validation, atomic import with replace confirmation, content-addressed asset serving, live preview with cancel restore, density consumers, transparent layout under backgrounds, contrast warnings, and safe-mode fallback to Graphite. / 受控不可执行的桌面皮肤:语义色令牌、密度 与圆角配方、可选本地背景图(焦点与安全区)。包含清单校验、原子导入与覆盖 确认、内容寻址资源服务、可取消的实时预览、密度消费者、背景下布局透明化、 对比度警告与安全模式回退 Graphite。 * feat(desktop): Redesign theme experience with official packs / 主题体验改造与官方主题 Problem: Appearance settings exposed two competing base-style pickers and applied theme packs immediately on card click, while activeThemeId could store base style ids. Official skins and a gallery/overview IA were still missing. Root cause: Theme Pack V1 layered packs on top of desktop.theme_style without a unified state model or select-then-apply UX, so base styles, official themes, and user packs competed for the same controls. Fix: - Bump desktop-theme-state to schema v2: activeThemeId is official/user only; migrate legacy base-style active ids into desktop.theme_style. - Add GetThemeExperience, ActivateBaseStyle, DisableThemePack, and RestoreGraphiteAppearance; reject base ids in ActivateThemePack. - Embed eight official themes with fixed order, asset routing, and budgets. - Ship AppearanceOverview + ThemeGallery (select ≠ apply, temp preview, immersive detail) driven by themeExperience.ts. - Document the IA and provenance; keep procedural art as placeholders. Verification: - cd desktop && go test ./... - cd desktop/frontend && pnpm typecheck && pnpm test:all && pnpm build - wails generate module (bindings match, no drift) * feat(desktop): complete the theme gallery and editor experience Problem: The theme gallery still had fragmented preview actions, placeholder flagship art, generic base-style previews, and a minimal custom-theme dialog that could not tune semantic colors or independent scenes. Root cause: Theme Pack V1 modeled one background and the gallery treated selection, preview, and persistence as separate interactions without a shared localized editor state. Fix: Upgrade the pack contract compatibly to V2 with independent home and workspace scenes, rebuild the gallery around immediate reversible preview and explicit apply, add localized semantic color and scene controls, replace the eight official artworks, and align base-style previews and actions with the settings UI. Verification: - cd desktop && go test ./... - cd desktop/frontend && pnpm test:all - cd desktop/frontend && pnpm typecheck - cd desktop/frontend && pnpm check:css - cd desktop/frontend && pnpm build - Browser-verified Chinese and English labels, live preview, scene switching, and cancel restoration * fix(desktop): satisfy the full-module formatting gate Problem: The desktop CI job stopped before tests because its repository-wide gofmt check reported main.go and theme_assets.go. Root cause: The earlier theme integration left one struct field with manual alignment and one extra trailing blank line; package tests compile these files but do not enforce gofmt byte formatting. Fix: Run gofmt on both reported files without changing runtime behavior. Verification: - cd desktop && gofmt -l . - cd desktop && go test ./... * fix(desktop): clarify theme scene content placement Problem: The scene editor exposed a vague content-safe-area label without explaining that it controls foreground text and card placement. The desktop CI lint gate also found four empty or unused Theme Pack leftovers. Root cause: The persisted safeArea field was surfaced directly as user-facing copy, while placeholder branches and helpers remained after the Theme Pack integration. Fix: Rename the control to Interface content area, add localized guidance and radio semantics, and remove or complete the four lint findings without changing theme behavior. Verification: - cd desktop && go test ./... - cd desktop && go vet ./... - cd desktop/frontend && pnpm typecheck - cd desktop/frontend && pnpm check:css - cd desktop/frontend && pnpm test:all - cd desktop/frontend && pnpm build - Theme Pack contract: 195 passed - Verified the localized control in the running browser UI * fix(desktop): unify theme confirmation dialogs Problem: Theme deletion, replacement imports, and export-rights checks used window.confirm, which surfaced platform-native dialogs that did not match the Reasonix settings UI. Root cause: The Theme Pack flows lacked a reusable in-app confirmation primitive, so both the gallery and legacy library called the browser API directly. Fix: Add an accessible portal-based Reasonix confirmation dialog with danger styling, safe focus behavior, Escape and backdrop cancellation, and focus restoration. Migrate all six theme confirmation call sites and localize their titles and actions. Verification: - pnpm typecheck - pnpm check:css - pnpm test:all - pnpm build - Browser: delete confirmation stayed in-app, Cancel received focus, Escape restored focus, and the temporary theme was deleted without a native dialog * fix(desktop): restore theme preview and custom fonts Problem: - Custom UI and monospace fonts could no longer be configured from the redesigned appearance page. - Selecting a user theme updated details without starting the advertised global preview. Root cause: - AppearanceOverview discarded both custom font name callbacks and omitted their editors. - ThemeGallery limited non-immersive previews to the catalog tab. Fix: - Restore localized custom font editors and their persistence callbacks. - Preview every non-immersive theme selection, including user packs. - Add focused source-contract coverage for both behaviors. Verification: - pnpm test:all - pnpm build - pnpm check:css - Browser verification of custom font variables, user-pack preview, and Back restoration |
||
|
|
e18d8c4451 | fix(desktop): restore Windows support executable icons | ||
|
|
df94dc709b | fix(desktop): keep WebView2 scale correct after restore | ||
|
|
ed113b4909 | fix(mcp): quarantine tools with invalid schemas | ||
|
|
990c1e33e9 |
chore: bump Go toolchain to 1.26.5
govulncheck now fails every PR: GO-2026-5856 (crypto/tls Encrypted Client Hello privacy leak) and GO-2026-4970 (os root escape via symlink plus trailing slash) are found in go1.26.4 and fixed in go1.26.5. CI resolves the toolchain from go-version-file: go.mod, so bumping the toolchain directive in both modules moves every job to the fixed release. Co-Authored-By: Claude Fable 5 <[email protected]> |
||
|
|
5fe20ebda8 | chore: internalize Windows sandbox backend | ||
|
|
1054c188d3 | Bump windows-sandbox for run-local residue markers | ||
|
|
a8e9421bf9 |
Bump windows-sandbox to survive marker PID reuse
Picks up b79e9a5: the crash-residue marker is keyed by PID alone, so a helper process that drew a crashed predecessor's PID used to skip the stale marker in its run-start sweep (pid == self), append its own lines into the same file, and then delete the whole file on exit — orphaning the predecessor's forbid_read deny or tool-directory grant beyond any future sweep's reach. The library now tracks marker ownership in-process: cleanup only deletes a marker this process wrote, and an unowned file at the run's own PID is consumed at run start as a dead predecessor's residue. The filename stays <pid>.txt so older binaries sharing the marker directory keep parsing PIDs correctly during upgrade windows. |
||
|
|
bd045b0dfd |
Bump windows-sandbox to harden the residue sweep
Picks up the follow-up (3058fd1) that re-checks the system-root invariant at sweep time: the crash-residue marker lives in the user's %TEMP% and is untrusted, so a legacy or planted line naming System32 / Program Files could previously drive the next run's sweep to strip the broad built-in package SIDs from a system directory's factory ACLs. The sweep now refuses system paths independently of the recorder-side filter. |
||
|
|
39bfc0b045 |
Bump windows-sandbox to never mutate system executable dirs
Picks up two upstream fixes: exe-dir contention coverage with the close of the deny-residue window (2a86cf8, 0e09612), and the follow-up that stops grantAppContainerExecutable from snapshotting, granting, or recording crash-residue on system directories (1a3e77e) — previously a crashed elevated run could leave a residue marker that made the next run strip built-in package SIDs from System32 / Program Files ACLs. |
||
|
|
2ecc3bbee0 |
Bump windows-sandbox for per-root lock thread pinning
Pick up the follow-up that pins the per-root lock to one OS thread, so a Go goroutine migrating threads cannot release a Windows mutex from a non-owning thread or re-enter it within the process. Cross-process serialization (each sandboxed command is its own helper process) was already correct; this tightens the within-process semantics the library's unit tests cover. |
||
|
|
ece9c41f8f |
Bump windows-sandbox with ACL lifecycle hardening
Update the native Windows sandbox backend to the release that fixes the mutation-based-isolation failure modes: recursive icacls timeout on large workspaces, residual Low integrity labels after cleanup, concurrent runs corrupting each other's ACL boundary, forbid_read deny residue after a crash, forbid_read entries pointing at files being silently unprotected, and PATH-resolved icacls/taskkill. Correct the docs to match the actual Windows boundary: writable low-integrity commands can still write the narrow set of locations Windows leaves writable to any low-integrity process (e.g. AppData\LocalLow) in addition to the configured roots, concurrent commands on a shared workspace are serialized, forbid_read now covers files, and crash residue is swept by the next run. |
||
|
|
f541bd353a | Bump Windows sandbox test coverage dependency | ||
|
|
a7582e3d20 | Add native Windows sandbox helper | ||
|
|
174ea10844 |
refactor: harden parsers with maintained libraries
Replace several hand-rolled parsers and text processors with maintained libraries while preserving Reasonix-specific policy boundaries and fallbacks. Add focused regression coverage for shell argv policy, HTML extraction, frontmatter decoding, dotenv parsing, glob matching, diff output, grapheme clipping, and optional tree-sitter code indexing. Co-authored-by: July-X <[email protected]> |