Commit Graph
33 Commits
Author SHA1 Message Date
yhh 394da0a313 refactor: the kernel's packages live in the layer they belong to
internal/ was 141 packages side by side, so nothing but a reader's memory
said which were foundations and which assembled the rest. Each now sits in a
layer directory, and the directory is the declaration:

  base      L0  foundation with no domain in it
  contract  L1  types and interfaces the layers above agree on
  model, tools, safety, ext, state, platform
            L2  subsystems, peers of one another
  runtime   L3  the turn engine
  session   L4  the controller
  assembly  L5  the composition root
  frontend  L6  cli, serve, acp and the host adapters

Only paths change: package names, code and behaviour do not. Imports,
repolint and coverage baselines, sensitive paths, CI package lists and docs
follow the new paths; release notes keep the paths they were written with,
and sample paths inside tests and fixtures keep theirs.

repolint gains layer-order: a package may import its own layer and those
below it, read from where it lives. The 47 imports that point upward today
are recorded as that rule's opening debt (-allow-widen, a new rule's first
baseline) and can only go down. Most of it is config, which still reaches
into eight subsystems and is the next thing to take apart; the rest is
event, history and a few tools reaching into runtime for types that belong
in contract.
2026-09-23 18:28:21 -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
yhh c5a3c85c4c feat(studio): make Electron the Studio launch path
`make studio` now builds the SPA and the kernel and opens the Electron
window. The path it replaces was macOS-only by construction -- a bare Go
binary is not a GUI app to LaunchServices, so the script bundled one
before launching and refused to run anywhere else. Electron's own dev
build is already an .app, so one script serves every platform.

Three things had to be true first, and none of them were.

The window carried no icon at all, so the taskbar drew Electron's. It is
named per platform now: .ico on Windows, where the taskbar reads the
small sizes out of it and a PNG would be one bitmap scaled; a PNG on
Linux; and nothing on macOS, which takes the icon from the bundle and
ignores what a window says.

`build:host` wrote bin/reasonix-studio-host with no extension, which
Windows will not spawn. `go build -o bin/` names the binary after the
package and adds .exe where it belongs.

That failure arrived as "the kernel sent no handshake" twenty seconds
later, because firstLine listened for exit and a program that cannot be
started emits error and never exits. It says what happened now, in
milliseconds. An error a caller must tell apart carries an identity, and
"could not be started" is not "said nothing".

Also fixes a test that could only pass on POSIX: it built an expectation
with a /tmp literal and compared it against path.join, which answers in
backslashes on Windows.

pnpm test 11/11.
2026-08-31 20:04:26 -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
SivanCola ed00eccf32 Add cross-platform Desktop crash diagnostics and runtime attribution / 增加跨平台桌面崩溃诊断与运行时归因 (#8169)
* fix(desktop): add Windows crash attribution diagnostics

Problem: Windows LTSC 2019 abrupt exits could not be attributed reliably, test telemetry could reach production, and Diagnostics counted lifecycle events rather than affected installations.

Root cause: legacy startup state was replayable, WebView2 failures were inferred from log text without native process details, and the crash Worker lacked build-, runtime-, and installation-level aggregation.

Fix: fail-close Desktop test endpoints, consume legacy state atomically, add lifecycle v2 and native WebView2 Args2/Args3 observation, and extend privacy-bounded telemetry, D1 rollups, Diagnostics filters, and bilingual release runbooks.

Verification: go test ./... in root and desktop; focused race tests; Worker typecheck and 86 tests; frontend tests and production build; fresh and migrated SQLite schema validation; Windows amd64 Desktop and vendored WebView2 cross-compilation; git diff --check.

* refactor(diagnostics): satisfy owner size budgets

Problem: the initial diagnostics implementation exceeded the repository's incremental file and function size budgets in Desktop startup, Worker ingestion, and dashboard rendering owners.

Root cause: lifecycle initialization, installation aggregation, and group-detail rendering were added directly to already baselined entry-point files.

Fix: move lifecycle setup into its tracker owner, extract diagnostics-v2 storage and query policy, and isolate crash-group rendering without changing wire contracts or runtime behavior.

Verification: repolint; Worker typecheck and 86 tests; focused Desktop and repair tests; Windows amd64 Desktop cross-compilation; the unrelated transient updater test passed three consecutive focused reruns.

* test(repair): use WaitGroup.Go for claim race

Problem: the current PR lint rejected the manual WaitGroup Add/go/Done pattern in the concurrent legacy-state claim regression test.

Root cause: the test used the legacy goroutine setup even though the repository modernize rules require sync.WaitGroup.Go.

Fix: use WaitGroup.Go while preserving the explicit ready barrier and deterministic interleaving.

Verification: go test ./internal/repair; go test -race ./internal/repair; go run ./tools/repolint; git diff --check.

* fix(repair): serialize legacy startup claims

Problem: Windows CI observed zero reports when concurrent launches tried to consume one dead legacy startup record.

Root cause: concurrent pre-claim reads can keep Windows file handles open long enough for every rename attempt to fail.

Fix: acquire the existing non-blocking cross-process file lock before reading and atomically renaming the legacy record, so exactly one observer owns the claim path.

Verification: 100 concurrent-claim repetitions; 20 race-enabled repetitions; full internal/repair normal and race suites; Windows amd64 test compilation; repolint; git diff --check.

* fix(diagnostics): close Windows crash attribution gaps

Start lifecycle tracking after the Wails single-instance gate and serialize anonymous install identity creation across concurrent callers.

Wait for WebView2 navigation completion before recording renderer recovery, commit crash group/sample/aggregate writes in one D1 batch, and align runtime CLI schema and diagnostics ordering.

* fix(diagnostics): address PR review findings

Problem:
- Renderer recovery could consume an unrelated navigation and recoverable COM errors could terminate the desktop.
- Per-install daily rows overwrote WebView2 dimensions, relabeling earlier GPU and runtime events.
- The PR exceeded repository size gates and did not execute vendored patch tests in Windows CI.

Root cause:
- Recovery completion was not correlated with NavigationStarting IDs, and shared fatal error handling was reused.
- Mutable dimension columns and event counts lived in the same deduplicated installation row.

Fix:
- Correlate renderer reloads by navigation ID, keep recovery errors non-fatal, and default failed kind reads to unknown.
- Add immutable event-dimension facts for filters and distributions while retaining installation deduplication.
- Split diagnostics tests, trim size regressions, and run vendored WebView2 tests in Windows CI.

Verification:
- go test ./...
- targeted Desktop and internal/repair race tests
- Worker tests and typecheck
- Frontend 146-suite test run
- repolint, actionlint, gofmt, and Windows cross-compile

* fix(desktop): align raw bundle ratchet

Problem:
The Desktop CI inherited a main-v2 raw startup bundle size of 2204.8 KiB while the ratchet still allowed only 2203 KiB, so frontend builds stopped before all Desktop checks.

Root cause:
Recent always-loaded desktop contracts already moved the baseline beyond the old threshold, and this PR adds the required diagnostics privacy attribution copy without materially changing gzip size.

Fix:
Raise only the raw aggregate allowance to a tightly measured 2205 KiB and document the native diagnostics copy. Keep gzip, CSS, locale, and largest-chunk limits unchanged.

Verification:
- pnpm build
- go run ./tools/repolint
- git diff --check

* feat(desktop): add cross-platform crash diagnostics

Problem: Desktop runtime failures lacked reliable ownership, lifecycle attribution, and affected-install analytics across Windows and Linux.

Root cause: legacy startup records could replay, WebView2 diagnostics were platform-specific, and Worker aggregation mixed recovery outcomes into unstable groups.

Fix: add fail-open primary ownership and lifecycle v2, generic Web Runtime reports, native WebView2 and WebKitGTK observers, privacy-safe platform dimensions, additive D1 aggregates, and cross-platform Dashboard filters.

Verification: go test and race suites, go vet, golangci-lint, Worker typecheck/tests, frontend tests/build/bundle budgets, repolint, actionlint, and Windows amd64 cross-builds.

* fix(diagnostics): preserve future lifecycle schemas

Problem:
A future lifecycle schema that removes v2 PID or phase fields could be treated as an invalid v2 record and pruned after retention.

Root cause:
The consumer validated v2-specific fields before checking the schema version.

Fix:
Reject unknown schemas before applying v2 validation or cleanup, and cover an aged future record without current fields.

Verification:
- cd desktop && go test . -run '^TestDesktopLifecycle'
- cd desktop && go test -race . -run '^TestDesktopLifecycle'
- git diff --check

* fix(diagnostics): correlate WebKit reload events

Problem:
The native WebKitGTK recovery smoke classified a successful renderer reload as failed and could not reach the cooldown assertion.

Root cause:
WebKit can emit load-failed for the terminated navigation after web-process-terminated. The recovery state treated every pending load failure as belonging to the new reload.

Fix:
Track whether the recovery reload has emitted LOAD_STARTED and only accept load-failed and LOAD_FINISHED after that boundary. Reset the marker on every terminal path and extend the source contract test.

Verification:
GOCACHE=/tmp/reasonix-pr8169-go-cache go test . -run '^(TestWebKit|TestWebRuntime)'
git diff --check

* fix(diagnostics): defer WebKit recovery reload

Problem:
WebKitGTK 4.1 still timed out the success recovery smoke after load events were scoped to the recovery navigation.

Root cause:
Starting webkit_web_view_reload synchronously inside the web-process-terminated signal callback is unreliable while WebKit is still dispatching process teardown.

Fix:
Schedule the single recovery reload on the GTK main context's idle queue, retain the WebView until dispatch, and guard the helper with the pending recovery owner. Update the source contract test to enforce this deferred abnormal-only path.

Verification:
GOCACHE=/tmp/reasonix-pr8169-go-cache go test . -run '^(TestWebKit|TestWebRuntime)'
git diff --check

* test(diagnostics): isolate WebKit smoke scenarios

Problem:
After the deferred reload made the success smoke pass, the next scenario aborted inside WebKitGTK while constructing another test WebView in the same process.

Root cause:
The smoke harness repeatedly destroyed and recreated WebViews that share WebKitGTK's process-global default context. That cross-scenario lifecycle does not match the single-WebView Desktop process and leaves asynchronous teardown active between cases.

Fix:
Run success, failure, timeout, and cooldown scenarios in isolated test subprocesses. Keep the cooldown pair in one child and one WebView so repeated native termination and single-reload behavior remain covered.

Verification:
GOCACHE=/tmp/reasonix-pr8169-go-cache go test . -run '^(TestWebKit|TestWebRuntime)'
git diff --check

* test(diagnostics): keep WebKit smoke local

Problem:
The isolated success and cooldown smoke scenarios still reported reload failure while failure and timeout behaved correctly.

Root cause:
The test HTML used https://reasonix.invalid as its base URI. Reloading the terminated view attempted to resolve that unreachable URI, so the smoke measured a synthetic network failure instead of native process recovery.

Fix:
Load the smoke HTML with a NULL base URI, which WebKitGTK maps to local about:blank content and requires no network access.

Verification:
GOCACHE=/tmp/reasonix-pr8169-go-cache go test . -run '^(TestWebKit|TestWebRuntime)'
git diff --check

* test(diagnostics): use reloadable WebKit smoke URI

Problem:
The native success and cooldown scenarios still timed out with load_html content even after removing the unreachable base URI.

Root cause:
A transient document created by load_html does not provide a stable reloadable resource after its WebProcess is terminated on the tested WebKitGTK runtime.

Fix:
Load a fully local data URI before terminating the WebProcess. This provides a stable current URI for the same production reload call without adding network or filesystem I/O.

Verification:
GOCACHE=/tmp/reasonix-pr8169-go-cache go test . -run '^(TestWebKit|TestWebRuntime)'
git diff --check

* test(diagnostics): allow WebKit process startup

Problem:
The native smoke used a one-second recovery timeout and classified cold WebProcess startup under Xvfb as a failed reload.

Root cause:
The smoke-only timeout was substantially shorter than observed headless WebKitGTK process creation, while production correctly allows thirty seconds.

Fix:
Raise only the tagged smoke timeout to five seconds. Keep the production timeout at thirty seconds and the smoke safety timeout at fifteen seconds.

Verification:
GOCACHE=/tmp/reasonix-pr8169-go-cache go test . -run '^(TestWebKit|TestWebRuntime)'
git diff --check

* Automate diagnostics D1 migration before worker deploy

Problem:
The diagnostics-v2 schema had to be applied manually even though the production Cloudflare credential is available only to Actions. Deploying the new Worker first would make ingestion depend on missing D1 columns and tables.

Root cause:
The crash Worker workflow validated and deployed code but had no guarded database migration stage.

Fix:
Add a fail-closed schema inspector that skips a complete migration, applies the SQL only when entirely absent, rejects partial state, records the current D1 Time Travel bookmark, and verifies the final schema. Run it after tests and before Worker deployment.

Verification:
- npm test (96 tests)
- npm run typecheck
- node --check scripts/apply-diagnostics-v2.mjs
- bash scripts/release-workflows.test.sh
- actionlint v1.7.7
- git diff --check

* Keep diagnostics copy within locale bundle budget

Problem:
Merging the latest main-v2 bundle ratchets made the expanded Chinese telemetry disclosure exceed both locale budgets.

Root cause:
The cross-platform disclosure repeated verbose phrases for the anonymous identifier, runtime fields, abnormal-exit payload, and privacy exclusions.

Fix:
Compact the Simplified and Traditional Chinese copy while preserving upload timing, diagnostic dimensions, and excluded sensitive data. Keep the existing bundle budgets unchanged.

Verification:
- cd desktop/frontend && pnpm build
- node scripts/check-bundle-budget.mjs
- git diff --check
2026-08-11 18:09:41 +08:00
奚疑andSivanCola 288392f778 chore(desktop): harden build toolchain contracts (#8098)
Problem:
Non-interactive Wails builds could abort while pnpm asked to recreate node_modules, and local, CI, release, and documentation toolchain versions could drift.

Root cause:
The Wails frontend install hook kept pnpm purge confirmation enabled, release jobs still used Node 22, and the repository had no cross-file guard for the Node and pnpm contract.

Fix:
Make the pnpm purge path non-interactive, align Desktop builds on Node 24 and pnpm 10, reuse the shared .wails-version installer from #8069, separate CLI and Desktop build instructions, and enforce the contract in the release workflow test lane.

Verification:
- node scripts/check-desktop-build-contract.mjs
- bash scripts/check-wails-pin.sh
- bash scripts/check-wails-pin.test.sh
- bash scripts/release-workflows.test.sh
- actionlint .github/workflows/release-desktop.yml
- Node 24 engine-strict frozen install and frontend production build
- make lint
- go vet ./...
- go test ./internal/tool/builtin/ ./internal/boot/
- make wails-install and a full Wails macOS build

Co-authored-by: SivanCola <[email protected]>
2026-08-10 11:20:42 +08:00
SivanCola 641f7993fd fix(ci): enforce the shared Wails version everywhere
Problem:
Local production builds and contributor setup could still bypass the shared Wails pin, so the old CLI/library skew remained possible even while the workflow-only guard passed.

Root cause:
The guard inspected only two workflow files, prod_test checked command presence instead of the installed CLI version, and the desktop guide recommended @latest.

Fix:
Add a make-based pinned installer, make prod_test repair and verify version mismatches, reject repository-wide hard-coded Wails installs, move the maintenance guidance out of the cache-stable prompt, and cover each drift path with regression tests.

Verification:
- make lint
- bash scripts/release-workflows.test.sh
- make wails-install installs v2.13.0 in an isolated GOBIN
- wails build -platform darwin/arm64 -clean -skipbindings -nopackage

Co-authored-by: SivanCola <[email protected]>
2026-08-10 03:06:01 +08:00
YHH 86b28aaf84 docs(desktop): correct the Windows signing state (#7475)
The section claimed no Authenticode certificate exists. Stable Windows
builds have been signed through SignPath since desktop-v1.14.1, and the
release fails if any payload binary is unsigned — the stale text sent at
least one reporter chasing signing as the cause of an unrelated network
problem. macOS is still unsigned; say so separately.
2026-08-04 21:00:04 +08:00
SivanCola 4b2ef3dabb feat(desktop): add native crash diagnostics / 增加桌面原生崩溃诊断 (#6900)
* feat(desktop): add native crash diagnostics

Problem:
Windows users could report that the desktop closed and would not reopen, but native process exits, UI-thread stalls, WebView2 failures, and window-restore hangs left too little durable evidence for analysis.

Root cause:
The desktop only persisted panics from explicitly wrapped goroutines in one overwrite-prone file. Startup tracking stopped distinguishing a healthy process that later vanished, and Windows native/WebView2/window-restore failure paths were not connected to the diagnostic pipeline.

Fix:
Capture Go runtime fatal output, retain a bounded backward-compatible report queue, heartbeat startup lifecycle state, and attribute abnormal exits to coarse uptime, install, and update buckets. Add a Windows Wails-window watchdog, WebView2 process-failure classification, and durable window-restore journaling. Extend aggregate metric ingestion and the health dashboard, while redacting panic values, secrets, and personal path segments and documenting the telemetry behavior.

Verification:
- cd desktop && go test ./...
- cd desktop && go test -race . -run focused crash queue tests
- go test -race ./internal/repair -run TestStartupTracker
- Windows amd64 cross-compile of the desktop test binary
- desktop frontend typecheck, focused settings tests, and production build
- crash-report worker: 42 tests and TypeScript typecheck
- git diff --check

* test(desktop): make hang assertions platform-aware

Problem: Windows desktop CI expected macOS crash labels and metric buckets even though the watchdog correctly emits Windows-specific diagnostics.\n\nFix: derive the expected label, error type, and metric bucket from the same platform identity helpers used by the implementation.\n\nVerification: targeted macOS tests pass; Windows amd64 test binary cross-compiles successfully.

* fix(desktop): remove crash watchdog stalls

Problem: the Windows UI watchdog allocated a permanent syscall callback on every one-second heartbeat, and the window-restore journal used a crash-safe fsync/retry path immediately before showing the window.

Root cause: Windows callbacks cannot be released and the Go runtime has a finite callback table; AtomicWriteFile can also wait on antivirus-held rename locks.

Fix: register one serialized EnumWindows callback for the process, keep the restore journal best-effort without fsync/retry, and add long-run Windows plus journal regression coverage.

Verification: go test ./... (desktop); targeted race tests; go vet ./...; Windows amd64 cross-compile; cache guard.

* fix(desktop): make crash diagnostics process-safe

Problem:
Window restore diagnostics recorded success after Wails only queued UI-thread work, and routine duplicate launches shared the primary process's fatal-output file.

Root cause:
The restore timeout watched the WindowShow call instead of native window state, while fatal output used one process-global path before Wails enforced its single-instance lock.

Fix:
Wait asynchronously for the Windows window to become visible and non-minimised, fence superseded restore attempts, and retain the journal until a real acknowledgement. Store fatal output and structured-panic markers per PID, process only dead owners, and keep backward-compatible legacy dump ingestion.

Verification:
- cd desktop && go test ./...
- cd desktop && go test -race .
- cd desktop && go vet ./...
- Windows amd64 desktop test cross-compile
- ./scripts/cache-guard.sh
- git diff --check
2026-07-24 23:04:59 +08:00
SivanCola 035f92897f fix(desktop): support authorized Debian package updates / 支持 Debian 安装包应用内授权更新 (#6866)
* fix(desktop): support authorized Debian package updates

Problem:
Ubuntu users who installed the desktop .deb could not update in app because the updater could not replace root-owned system files.

Root cause:
Linux installs were treated uniformly as writable portable layouts, and the release manifest exposed only the tarball updater artifact.

Fix:
Detect dpkg-owned installs, publish signed native .deb metadata, and install upgrades through a packaged Polkit helper that re-verifies ownership, signatures, package identity, architecture, and version before delegating to apt. Preserve the portable tarball path and add user-facing progress, metrics, tests, and recovery documentation.

Verification:
- cd desktop && go test ./...
- cd desktop && go vet ./...
- pnpm --dir desktop/frontend test:typecheck
- pnpm --dir desktop/frontend test:updater
- pnpm --dir workers/crash-report typecheck
- pnpm --dir workers/crash-report test
- Linux amd64 helper test binary cross-compilation
- git diff --check

* fix(desktop): recover from updater mode changes

Problem:
The desktop CI failed on two unused updater helpers, and the UI could remain stuck in downloading or authorizing when install mode or manifest availability changed between updater steps.

Root cause:
The backend opened the manual download page and returned nil after reclassification, while the frontend treated nil as an in-flight operation with no terminal state. Two obsolete helper functions also remained after the refactor.

Fix:
Return and emit a typed manual-update error before opening the download page, preserve the manual fallback action in the shared updater state, add authorization/install/cancellation/reclassification coverage, and remove the unused functions.

Verification:
- cd desktop && go test ./...
- cd desktop && go vet ./...
- pnpm --dir desktop/frontend test:typecheck
- pnpm --dir desktop/frontend test:updater
- pnpm --dir desktop/frontend build
- pnpm --dir workers/crash-report typecheck
- pnpm --dir workers/crash-report test
- Linux amd64 update-helper test binary and production build
- git diff --check

* Fix Linux package smoke checks under pipefail

Problem:
Linux .deb contract smoke checks could fail on valid packages when the producer emitted more data after grep found its match.

Root cause:
The build script combined set -o pipefail with grep -q. Early grep exit could close the pipe and make dpkg-deb terminate with SIGPIPE, turning a successful contract match into a failed pipeline.

Fix:
Consume the complete dpkg-deb output with non-quiet grep redirected to /dev/null, and add a packaging guard that rejects the unsafe early-exit patterns.

Verification:
- cd desktop && go test ./...
- cd desktop && go test -run TestDesktopPackagesPreserveNativePlatformLaunchers -count=1 .
- pnpm --dir desktop/frontend test:updater
- bash -n scripts/desktop-build.sh
- git diff --check
2026-07-23 23:22:44 +08:00
ef29d046fd feat(desktop): add line numbers and search to file preview (#6557) / 为文件预览添加行号边栏与搜索功能 (#6574)
* feat(desktop): add line numbers, virtual scroll, and search to file preview (#6557)

Replace the default HljsCode editor with LineNumberCode, which adds: - Line-number gutter (right-aligned, selectable via CSS user-select) - Virtual scrolling via @tanstack/react-virtual for files > 100 lines - Ctrl+F search bar with Codex-style controls: N of M counter, prev/next arrows, case-sensitive (Aa) and whole-word (ab) toggles - Inline <mark> highlighting of matched text within syntax-highlighted lines - Row dimming for non-matching lines during search

Applies to both the workspace file preview (WorkspacePanel) and chat tool output (ToolCard read_file / read_session), where the existing %*d arrow prefix is stripped before rendering to avoid double line numbers.

Also raise filePreviewLimit from 256 KB to 2 MB so the workspace panel can show complete files.

* Fix file preview search regressions

Problem:
The file preview viewer intercepted find globally, corrupted escaped-code searches, dropped copy controls, and lost multiline lexer state.

Root cause:
The line-number viewer became the default CodeViewer, reused a stateful regex across lines, and searched serialized highlight.js HTML.

Fix:
Scope the enhanced viewer to workspace previews, preserve original tool output, map raw match offsets onto balanced highlighted rows, restore copy controls, and keep the shortcut local to the focused viewer.

Verification:
- npm test
- npm run build
- Browser verification of workspace line numbers, scoped find, and copy layout
- git diff --check

Co-authored-by: SivanCola <[email protected]>

* Document workspace preview limits

Problem:
The desktop editor documentation still described obsolete PlainCode seams, and localized truncation notices still reported the previous 256 KB preview limit.

Root cause:
The file preview implementation and backend limit changed without updating the developer documentation or user-facing locale strings.

Fix:
Document the opt-in searchable line-number viewer and 2 MiB preview behavior, update all supported locale notices, and add a contract test that keeps the backend limit and translations aligned.

Verification:
- tsx src/__tests__/workspace-preview-css.test.ts (21 passed)
- tsc --noEmit
- npm run check:css
- npm run build
- git diff --check

Co-authored-by: SivanCola <[email protected]>

* Credit #6570 workspace preview contribution

Context:
PR #6570 independently established the workspace-only opt-in line-number approach that is retained and extended in PR #6574.

Attribution:
Credit the adopted scope boundary, adaptive gutter design, and non-selectable line-number behavior to the source PR contributor.

Verification:
- Compared both PRs against the latest main-v2
- Confirmed PR #6574 preserves the adopted workspace-only rendering boundary
- git diff --check

Co-authored-by: youuoypi <[email protected]>
Co-authored-by: SivanCola <[email protected]>

* fix(desktop): harden file preview search

Problem:
The file preview search skipped the first result, rebuilt the entire highlighted document on each query, and exposed untranslated or incomplete controls.

Root cause:
Search navigation advanced before the pending query committed, while matching and markup generation were coupled across the full source.

Fix:
Debounce and cap matching, inject marks only into rendered lines, use a plain-text fallback for large files, reveal the first result, and localize and label the search controls.

Verification:
- npm test
- npm run build
- go test ./...
- in-app browser search regression

Co-authored-by: SivanCola <[email protected]>

---------

Co-authored-by: SivanCola <[email protected]>
Co-authored-by: youuoypi <[email protected]>
2026-07-22 00:10:31 +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 c966d02796 Remove the Memory v5 execution compiler / 彻底移除 Memory v5 执行编译器 (#6615)
* Remove the Memory v5 execution compiler

Delete the Memory v5 execution compiler end to end: the
internal/memorycompiler package, the agent turn-start compilation and
injection gate, the LLM/heuristic task classifier apparatus, the
/memory-v5 and `reasonix config memory-v5` commands and completions,
the control/boot/config/doctor/event/eventwire wiring, the desktop
settings toggle and Wails binding, compiler metrics buckets, and the
compiler-derived memory suggestions bridge. Update GUIDE (en/zh-CN),
SPEC, the example config, the desktop README, and the site docs.

The feature delivered no provider-visible value on the default observe
path while costing per-turn state I/O and a recurring bug family
(#5316, #5329, #5342, #5468, #5486, #5596): the compact mode replaced
the user turn with an execution contract that over-planned simple
requests into runaway tool loops.

Backward compatibility:
- [agent].memory_compiler is retired via a one-time config migration
  (same pattern as redact_tool_output) with a boot notice; old configs
  load cleanly either way because unknown TOML keys are ignored.
- Legacy transcripts from releases up to v1.17.x still display
  normally: <memory-compiler-execution> blocks keep unwrapping to the
  original prompt for previews, titles, history replay, and the
  desktop transcript (internal/agent/preview.go,
  memoryCompilerDisplay.ts, memoryCitationVisibility.ts), and
  persisted compiler memory citations still render.
- The delivery evidence gate keeps its task-vs-chat heuristic, moved
  to internal/agent/task_heuristic.go with its regression table.
- On-disk compiler state directories are simply orphaned; nothing
  reads or writes them anymore.

Cache-impact: none. The compiler never touched the system prompt,
tool schemas, or provider request serialization; observe mode added no
provider-visible bytes, and compact injection was opt-in. Removing it
cannot shift the stable prefix. Verified by the release cache-hit
guard (all 8 scenarios >= 90% tail average).
Cache-guard: scripts/cache-guard.sh pass (tail_avg 92-95 across
plain-dialogue, long-dialogue, mixed-message-sizes, and tool-loop
scenarios).
System-prompt-review: no system prompt or tool schema text changed;
prompt-construction call sites in internal/agent and internal/boot
only lost the user-turn replacement branch.

* Make retired-key config migrations TOML-multiline-safe; drop unused metric buckets

Address review findings on #6615:

1. The retired-key config migrations (memory_compiler, redact_tool_output,
   agent step limits, MCP tier) scanned line-by-line without understanding
   TOML multiline strings, so a `memory_compiler = ...` example line quoted
   inside a multiline `system_prompt = """..."""` was deleted from the
   prompt and the config rewritten. All four strippers now share
   stripTOMLKeyLines, which tracks multiline basic/literal string state
   (honouring single-line strings and comments so their quote delimiters
   cannot open a multiline state) and never treats lines inside a multiline
   string as section headers or key assignments. The scanner is
   deliberately conservative: on any ambiguity it keeps lines rather than
   deleting them, and a dropped line must itself leave the scanner outside
   a multiline string. Regression tests cover the reported reproduction
   (multiline system_prompt survives byte-for-byte), literal strings,
   section headers inside strings, single-line triple-quoted values,
   comments containing triple quotes, and the real-key-next-to-string case,
   plus a full MigrateLegacyMemoryCompilerForRoot test.

2. Remove tokenSizeBucket and memorySizeBucket from desktop/metrics_app.go;
   they lost their only caller with observeMemoryCompilerStats and failed
   the desktop golangci-lint (unused) check.

Cache-impact: none; config-file migration and desktop metrics helpers are
not provider-visible surfaces.
Cache-guard: n/a beyond the base PR's passing run; no prompt construction,
tool schema, or serialization path is touched by this commit.
System-prompt-review: the migration now provably preserves user
system_prompt content (multiline regression tests); no prompt text changed.
2026-07-17 20:37:38 +08:00
SivanCola 97c746625a fix(release): keep desktop as latest release 2026-07-06 12:23:43 +08:00
SivanCola 0a1839adc9 test(desktop): add faster desktop test lane 2026-07-04 22:31:10 +08:00
SivanColaandmyipanta 06634777c1 fix: route desktop updater via release gateway
Co-authored-by: myipanta <[email protected]>
2026-07-03 18:41:09 +08:00
SivanCola 13a86545d3 Complete memory compiler issue controls
Co-authored-by: SivanCola <[email protected]>
2026-06-30 21:06:36 +08:00
GTC2080 6a0b5a94a6 fix(desktop): disable Linux webview GPU without DRI 2026-06-25 14:39:49 +10:00
SivanCola 5b78ad1889 Merge main-v2 into Memory v5 compiler PR 2026-06-25 04:01:51 +08:00
SivanCola 75a32c5331 Add CLI Memory v5 toggle commands 2026-06-25 03:50:22 +08:00
SivanCola a93440ff3e Document Memory v5 usage and metrics 2026-06-25 03:27:43 +08:00
Zhuocheng-Lang 11b6c52bf8 fix(desktop): apply NVIDIA Wayland explicit-sync workaround for WebKitGTK
On KDE Plasma Wayland with NVIDIA GPUs, the WebKitGTK webview crashes at
startup (Error 71: Protocol error) due to an upstream WebKit explicit-sync
bug (WebKit #280210, #317089). ANGLE advertises wp_linux_drm_syncobj but
fails to set an acquire point before committing the buffer, violating the
Wayland protocol.

Set __NV_DISABLE_EXPLICIT_SYNC=1 automatically when all three conditions
are true: Wayland session detected, NVIDIA GPU present (/sys/module/nvidia),
and the user hasn't already set the variable. This preserves GPU acceleration
(unlike WEBKIT_DISABLE_DMABUF_RENDERER=1) and keeps the native Wayland
session (unlike GDK_BACKEND=x11).

Closes #4700.
2026-06-22 21:32:56 +08:00
Sivan 4cc7c89b70 Fix anchored popover scroll positioning 2026-06-17 11:15:32 +08:00
SivanCola bc75807e05 Add desktop update flow and settings refinements
Co-authored-by: SivanCola <[email protected]>
2026-06-17 03:29:09 +08:00
SivanCola 72d54e3cb9 feat(desktop): performance-pressure diagnostics reporting (#4322)
Add a user-initiated performance-pressure report prompt when the desktop observes long tasks, event-loop lag, or high JS heap pressure. Captures coarse diagnostic context (timing, heap, visibility, network, breadcrumbs, version, OS), scrubs it through the existing crash-report pipeline as kind: performance, and lets the Worker/dashboard ingest it with no DB migration.

Follow-up to #4285. Worker must be deployed before the desktop release that sends performance reports (old kind enum rejects them).
2026-06-14 20:41:57 +08:00
YHHandreasonix c58bcc3a06 feat(desktop): anonymous launch ping, crash device info, /stats page (#3985)
Crash reports alone answer "what broke" but not "for whom" or "how many
are even running this". Three additions, all on the existing worker:

- Launch ping: one POST per start with a random install id + version +
  OS facts. Gated on new desktop.telemetry config (default on, toggle in
  Settings > Updates, disclosed in desktop/README.md); dev builds never
  ping. Carries no conversation, key, or file data.
- Crash reports now attach coarse device facts (OS version, CPU model,
  cores, RAM) so "only crashes on X" patterns are visible.
- Worker grows /v1/ping (per-day install dedup with opens counter) and a
  Basic-auth /stats page (daily actives, version/platform breakdown,
  recent crash groups) so day-to-day reading needs no SQL.

Co-authored-by: reasonix <[email protected]>
2026-06-11 15:53:08 +08:00
Sivan 99e11643dd fix(desktop): harden windows webview startup 2026-06-10 17:41:38 +08:00
YHH a5be38700c build(desktop): ship a macOS .dmg for first-time install (#3216)
Ship a macOS .dmg (drag-to-Applications) for first-time install alongside the .zip updater channel; point the site's macOS download at it. The .dmg is named -universal so cmd/sign's manifest substring match skips it.
2026-06-05 01:19:14 -07:00
YHHandreasonix 8e65d02464 fix(desktop): link Linux release against WebKitGTK 4.1 (#3155)
The Linux artifact linked libwebkit2gtk-4.0.so.37, which Ubuntu 24.04+
and Fedora 40+ no longer ship, so the app failed to start with a missing
shared-object error. Build with -tags webkit2_41 and install the 4.1 dev
package; 4.1 is available from ubuntu-22.04 on, so the runner is unchanged.

Closes #3154

Co-authored-by: reasonix <[email protected]>
2026-06-04 18:26:29 -07:00
YHHandreasonix ddefd1c348 docs(desktop): document the WebKitGTK 4.1 build tag for Fedora et al. (#2944)
The desktop build defaults to webkit2gtk-4.0 (Wails' `!webkit2_41` cgo
pkg-config). Distros that ship only 4.1 — Fedora 40+, Ubuntu 24.04+, Arch —
have no webkit2gtk-4.0 package, so `wails build` fails to compile (#2939).
Document the `-tags webkit2_41` build/dev invocation and the Fedora deps.

Closes #2939

Co-authored-by: reasonix <[email protected]>
2026-06-03 06:55:06 -07:00
YHH 55d408dda3 feat(desktop): use a fresh signing key; add genkey/verify (#2467)
The v1 minisign private key wasn't recoverable (no local backup, and a GitHub
secret can't be read back), so the desktop updater uses a brand-new minisign
key pair rather than reusing v1's. v2 is an independent update line, so a fresh
key is cleaner regardless.

- internal/update: embed the new public key (ID AF12CA46F4A9EBB0).
- cmd/sign: add `genkey <dir>` (generate a key pair) and `verify <file>`
  (check a .minisig against the embedded key — a sign/verify self-test).
- README: update the verification public key.

The private key and password live only in ~/.reasonix-keys locally; set them as
the MINISIGN_PRIVATE_KEY / MINISIGN_PASSWORD secrets before the first release.
2026-05-31 06:57:56 -07:00
YHH 93ab240e72 feat(desktop): add release pipeline and self-updater (#2466)
The Wails desktop had no CI release and no updater (Tauri's updater plugin
is gone in the Go rewrite). Add both, reusing the v1 R2/GitHub distribution.

- release-desktop.yml: desktop-v* tag → 4 native-runner matrix → minisign
  sign → latest.json manifest → GitHub release → R2 mirror. The CLI's v*
  release line is untouched.
- internal/update + cmd/sign: shared manifest type, embedded public key, and
  minisign verify — used by both the CI signer and the updater, so the sign
  and verify paths share one definition.
- updater.go / updater_app.go: CheckUpdate/ApplyUpdate over the two-endpoint
  manifest. Linux and Windows update in place (selfupdate / per-user NSIS);
  macOS falls back to the download page (unsigned bundle vs. Gatekeeper).
- Frontend: an update banner (startup auto-check) plus a Settings "Software
  update" section, with en/zh strings.
- Per-user NSIS template (project.nsi) committed; README documents the
  unsigned first-launch steps and minisign verification.

The first CI run needs MINISIGN_PRIVATE_KEY / MINISIGN_PASSWORD secrets
(the v1 minisign key, converted to the standard format).
2026-05-31 06:45:47 -07:00
esengine 507103a725 feat(desktop): Wails + React desktop shell
A native desktop window around the kernel. desktop/ is a nested Go module
(reasonix/desktop, replace reasonix => ../) so the CGO/WebKit build never
touches the CLI's CGO_ENABLED=0 single-static-binary guarantee, while
still importing the reasonix/internal/* kernel and reusing internal/boot.
App binds the control.Controller's command surface to the webview; an
eventSink forwards the typed event stream via runtime.EventsEmit — the
desktop analogue of the serve transport.

Frontend (Vite + React + TS): an event-stream reducer hook, compact
collapsible tool cards (lucide icons, status glyphs, inline highlighted
diffs for edit_file), react-markdown + remark-gfm, highlight.js behind a
lazy editor seam reserved for Monaco/CodeMirror, a model/context/cwd
status line, and an approval modal. A browser dev mock streams a canned
turn so the UI runs without the shell. Multi-platform adaptation baked in
(Linux WebKitGTK GPU policy, macOS inset titlebar drag region,
prefers-color-scheme).
2026-05-29 21:34:19 -07:00