65 Commits
Author SHA1 Message Date
esengine 82de6d9622 chore(deps): tidy the desktop module for the go group bump 2026-11-01 04:27:05 -08:00
esengine 80ebd88bf7 fix(deps): bump golang.org/x/net to v0.62.0 for GO-2026-6860 2026-10-27 19:33:39 -07:00
esengine 148a3eabe7 build(deps): tidy the desktop module for the same bumps 2026-10-25 07:05:07 -07:00
pei711 8b60e89bee fix(desktop): record SQLite transitive dependencies 2026-10-21 12:57:02 +08:00
esengine b0168f37dc build: use Go 1.26.10 for the crypto/x509 fix (GO-2026-6809) 2026-10-20 20:01:33 -07:00
esengine 751def6d32 chore(deps): bump the go group with 11 updates, including the desktop module 2026-10-18 06:37:48 -07:00
dependabot[bot] 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]>
2026-10-18 11:52:28 +00:00
dependabot[bot] 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]>
2026-10-11 11:53:12 +00:00
yhh c8622f7a8c chore(deps): bump Go toolchain to 1.26.9 and golang.org/x/net to v0.60.0 2026-10-08 19:27:43 -07:00
esengine 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.
2026-09-27 07:20:11 -07:00
yhh 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.
2026-09-24 04:54:31 -07:00
esengine 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.
2026-09-15 23:41:31 -07:00
esengine 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.
2026-09-06 09:47:46 -07:00
esengine 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.
2026-09-06 09:23:08 -07:00
esengine 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.
2026-08-31 07:49:01 -07:00
esengine 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.
2026-08-29 20:25:55 -07:00
yhh 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.
2026-08-23 20:05:48 -07:00
esengine 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.
2026-08-23 03:25:30 -07:00
esengine 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
2026-08-19 07:56:29 -07:00
esengine 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.
2026-08-17 08:13:37 -07:00
yhh 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.
2026-08-17 05:50:37 -07:00
SivanCola 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
2026-08-10 17:16:26 +08:00
SivanCola 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.
2026-08-10 02:54:04 +08:00
SivanColaanddependabot[bot] 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>
2026-08-09 05:19:41 +08:00
SivanCola 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]>
2026-08-05 03:34:29 +08:00
SivanCola 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 e67b2aa824
2026-08-04 13:20:08 +08:00
SivanCola 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.
2026-08-03 21:36:43 +08:00
SivanCola 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
2026-08-03 17:10:06 +08:00
ClaudeHub Dev 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).
2026-07-31 09:58:27 -03:00
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]>
2026-07-29 16:52:28 +08:00
SivanColaandipanta 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]>
2026-07-29 08:29:28 +08:00
SivanCola 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.
2026-07-22 10:58:58 +08:00
SivanColaandtaibai233 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]>
2026-07-22 02:29:21 +08:00
SivanCola 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.
2026-07-20 20:17:57 +08:00
SivanCola 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.
2026-07-19 21:48:20 +08:00
SivanCola 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
2026-07-18 01:11:36 +08:00
SivanCola e18d8c4451 fix(desktop): restore Windows support executable icons 2026-07-16 10:57:25 +08:00
SivanCola df94dc709b fix(desktop): keep WebView2 scale correct after restore 2026-07-14 19:19:55 +08:00
SivanCola ed113b4909 fix(mcp): quarantine tools with invalid schemas 2026-07-14 12:17:57 +08:00
SivanColaandClaude Fable 5 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]>
2026-07-08 15:51:33 +00:00
SivanCola 5fe20ebda8 chore: internalize Windows sandbox backend 2026-07-06 15:11:47 +08:00
SivanCola 1054c188d3 Bump windows-sandbox for run-local residue markers 2026-07-05 23:54:13 +08:00
SivanCola 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.
2026-07-05 23:06:13 +08:00
SivanCola 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.
2026-07-05 22:09:21 +08:00
SivanCola 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.
2026-07-05 14:48:13 +08:00
SivanCola 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.
2026-07-05 14:44:28 +08:00
SivanCola 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.
2026-07-05 14:44:28 +08:00
SivanCola f541bd353a Bump Windows sandbox test coverage dependency 2026-07-05 14:44:28 +08:00
SivanCola a7582e3d20 Add native Windows sandbox helper 2026-07-05 14:44:28 +08:00
SivanColaandJuly-X 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]>
2026-07-04 01:11:34 +08:00