mirror of
https://github.com/esengine/DeepSeek-Reasonix.git
synced 2026-11-11 01:51:33 -10:00
8f7e4a0748094de92aa17e534bf54839b2cecaab
946
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
2835c52d2b | fix(desktop): preserve legacy project state on re-add and import | ||
|
|
c2974487dd | fix(desktop): isolate newly registered project slug collisions | ||
|
|
d5c4e196a6 |
Merge pull request #11171 from SivanCola/codex/context-compaction-recovery
Bound compaction waits and preserve context on failure / 限制整理等待并保留失败上下文 |
||
|
|
7e45e66110 |
Preserve provider failure diagnostics across cleanup and restart / 保留收尾与重启后的模型请求失败诊断 (#11152)
* fix: preserve provider failure diagnostics across cleanup and restart Problem: Failed requests could reopen without their failure identity or the transport evidence needed to investigate them. Root cause: Cleanup replaced terminal recovery metadata, HTTP/2 protocol errors fell through classification, and request observations were process-local. Fix: Preserve failure metadata at the shared cleanup boundary, recognize HTTP/2 transport codes without enabling retries, and persist bounded redacted request evidence atomically with turn completion. Localize failure summaries and document additive persistence compatibility. Verification: Full root Go suite, owning-package and focused race tests, desktop history/export regressions, frontend tests and typecheck, Chromium interaction checks, Windows provider cross-compilation, and repolint passed. * fix: align diagnostic contracts and isolate lifecycle test fixtures Problem: CI rejected stale desktop contracts and an error type assertion; Windows lifecycle tests also timed out around filesystem finalization. Root cause: The additive diagnostic field was not regenerated into the host contract. Transport classification used a direct error assertion. Functional checkpoint and cancellation tests unnecessarily coupled live state assertions to multiple durable writes under short completion deadlines. Fix: Regenerate the contract, reject wrapped API errors using errors.As, and use existing in-memory checkpoint/controller fixtures for live semantics. Retain all assertions and dedicated durable maintenance, checkpoint reload, and cold-export coverage without changing production timeouts or CI gates. Verification: Pinned golangci-lint 2.12.2, repolint, focused provider/control tests, three race-test repetitions, checkpoint persistence reload, frontend typecheck, and diff checks passed locally. * fix: structure transport diagnostics and bound failed-turn evidence Problem: Transport classification depended on error wording, UI projections dropped structured diagnostics, successful requests persisted peer addresses, and bounded request history could silently appear complete after eviction. Root cause: Diagnostic identity was converted back to prose across transport and presentation boundaries, while lifetime-wide request eviction lacked bounded per-turn accounting and unsuccessful-turn persistence gating. Fix: Classify exported HTTP/2 types and exact stdlib type/code identities; carry diagnostic fields through all notice/history paths; persist only failed, interrupted, or recovery-required turn evidence; and record per-turn dropped counts and truncation, marking unavailable accounting incomplete. Verification: Typed and real TLS connection/stream/GOAWAY tests, provider/control race checks, cold-export and success-persistence regressions, per-turn loss tests, frontend projection tests, Chromium three-locale interactions, production frontend build, and bundle budgets passed. Preserve model bytes and retry policy. * fix: keep optional diagnostics from blocking terminal commits Problem: Encoding optional provider evidence could prevent required turn-end events from committing. Persisted free-text transport errors duplicated the structured evidence, and generated detail text duplicated HTTP/2 codes. Root cause: Optional diagnostic errors escaped the shared terminal-event builder, while transport prose was retained at multiple presentation boundaries. Fix: Warn and omit failed diagnostic records while preserving required terminal events. Remove transportError and its byte truncation. Leave transport-code rendering to the structured frontend presenter. Update export documentation. Verification: Real invalid-timestamp regression covers failed/interrupted/ recovery-required closures; synchronous and Desktop-send failures retain durable turn/end and cold-export identity. Structured-evidence, no-prose, presentation, and model-context tests pass, as do frontend typecheck and affected suites. |
||
|
|
1405c2a043 |
fix: narrow compaction recovery to the 1.x maintenance scope
Problem: The full progress and retry implementation expands the main-v2 maintenance surface beyond the reported compaction hang and truncation defects. Root cause: The initial change coupled operation deadlines and failure handling to new runtime, wire, persistence, and UI contracts. Fix: Retain one shared five-minute compaction budget and typed failure recovery without automatic lossy truncation. Remove the new observer/progress protocol, UI, retry API, and receipt fields from the final diff. Preserve the existing range-compression behavior and last committed projection. Keep the complete implementation on a separate branch for independent 2.x evaluation. Verification: Focused Agent/Control and race tests, actual SSE heartbeat parsing under virtual time, nested/chunk budgets, queue cancellation, late completion, partial commits, persistence reopening, pinned Go lint, repolint, and diff checks. The complete root suite and current-head CI provide separate broad evidence. |
||
|
|
9c3579256d |
fix: bound context compaction and preserve failed-session state
Problem: Near-full conversations can wait indefinitely for a summary and lose additional model-visible context when summarization fails. Root cause: Auxiliary response buffering hides progress, heartbeat traffic extends transport idle timers, and overflow recovery falls back to lossy truncation without an operation-wide deadline. Fix: Share a cancellable five-minute compaction budget, observe content-free generation progress, remove truncation recovery, preserve atomic save ownership, and expose durable terminal state with session-bound compaction-only retries. Keep initial and switched session runtime bindings available to the Stop UI. Verification: Root Go suite, affected Desktop suites, deterministic and race checks, frontend typecheck/transcript/browser gates, generated contracts, and repository lint. An actual macOS Electron package passed the production 300-second heartbeat timeout, stop, switching, restart, message/draft retention, retry-only behavior, and clean shutdown. Real providers and Windows/Linux native packages remain unverified; an extra unpartitioned Desktop sweep timed out. |
||
|
|
390f996103 |
Merge pull request #11122 from esengine/fix/v1-11010-rotate-typed-key-in-place
fix(setup): rotate a key in place when its providers alone read the variable |
||
|
|
46dd766262 |
Merge pull request #11093 from esengine/fix/v1-9652-review-step-cap
fix(agent): stop capping review sub-agents at eight rounds |
||
|
|
2f7809c10d |
fix(setup): rotate a key in place when its providers alone read the variable
Saving a new key for a provider that already had one always moved the provider to a fresh REASONIX_CONNECTION_*_KEY slot and left the previous value under the old variable. A name the user typed at setup (kept since #11005) therefore went stale on the first rotation, and scripts reading it kept seeing the old key. RotateModelCredentialLocked rewrites the variable in place when the edit targets the user config, the providers being saved are its only readers there before and after the edit, no bot or remote-host setting reads it, and the global .env already holds its value. A project reasonix.toml cannot see the user config's readers, so its providers never rotate. Anything else still gets a private slot. Rollback needs the previous value back. It is written to the global .env under a random REASONIX_ROTATION_*_KEY in the same atomic file write as the new value, so it has exactly the protection every stored key has; the journal records only the two names and a keyed digest of the new value. Cleanup of a failed save and crash recovery restore the previous value only while the variable still holds the digest this edit wrote, then drop the backup; a published edit drops the backup and keeps the new value. Used by the CLI setup commit, CommitConnectionCredential and the desktop's journalled credential edits. Desktop edits that run without a journal keep minting private slots, since they have nothing to restore from. Closes #11010 |
||
|
|
3cb73b3bf4 |
fix(sandbox): keep repository git config and hooks read-only inside the bash sandbox
The host's own git reads the workspace repository's configuration and hooks, so a confined command that can rewrite them can make the host run a program of its choosing. The OS sandbox now denies writes to them while leaving the rest of .git writable. For the repository git itself discovers from each writable root, protected paths are: config, config.worktree, commondir and hooks/ of the gitdir and of the common dir; config, config.worktree and commondir of every existing linked worktree entry (new entries stay creatable); config, config.worktree, commondir and hooks/ of every submodule gitdir under modules/. A .git file is followed to the gitdir it names exactly as git resolves it, and the pointer file and commondir are protected, so a repository cannot aim the protection at a decoy while git reads something else. The .git entry, the gitdir, the common dir, every symlink on the way to them and each writable ancestor are pinned: they cannot be removed, renamed away or replaced by a symlink, while their own mode and times stay writable (touch and chmod on the workspace root work). - macOS: deny rules after every write allowance. Existing worktree and submodule gitdirs (up to 128 each, found without following symlinks) get exact rules and pins; patterns anchored at the common dir cover the rest and gitdirs created later, skipping refs/ and logs/ segments so a branch or tag named config or hooks stays writable. The profile stops growing past the limit. - Linux: pinned directories become their own bind mounts and protected files and hook trees are bound read-only; only real directories and regular files are mount targets. Up to 512 gitdirs per group get exact mounts; past that the whole group directory is bound read-only, so planted HEAD files cannot overflow the argument list. Only existing paths can be mounted and symlinks cannot be pinned. - Windows has no shell sandbox; the guide says so. A protected file with another hard link refuses the confined launch with sandbox.git_metadata_linked (ErrGitMetadataLinked), since no path rule sees a write through the other name. Whenever a confined command's output names a protected path, also on exit 0 (git reports some refused config writes and exits 0), the bash result carries sandbox.git_metadata_protected. Real-sandbox tests cover each protected path, pointer and symlink retargeting, patterns for gitdirs created later, bounded rule size with 2000 submodule gitdirs, ordinary git work, and the git operations that now fail or silently skip their config write. |
||
|
|
3065db7a49 |
fix(serve): changes and approvals require the launch token
With authentication off (still `serve`'s default), reads stay open but every state-changing request needs the per-launch token, as an Authorization bearer or the cookie /auth/token issues, and otherwise refuses with 403 `launch_token_required`. The gate runs inside the host and CSRF guards, and a configured token is honoured so a fronting proxy can inject it. Token mode also accepts the bearer header, and the web UI explains the refusal. The token never reaches the terminal: in both token and none modes serve writes it to a 0600 file under the remote state directory and prints the path, or names the --token-file of a managed launch. Runtime sandboxes and read tools deny that directory and any registered token file. [serve] is read from the user config only, so a project reasonix.toml cannot choose the token, the auth mode, or trust in forwarded headers. |
||
|
|
c2d10fb7e0 |
fix(agent): stop capping review sub-agents at eight rounds
A review, security-review or team-architect sub-agent paused after 8 tool-call rounds whenever the call did not pass max_steps, which is how every skill-run review and most model-dispatched reviews start. The pause message tells the user to raise max_steps, but no setting reaches this budget, so a multi-file review could not finish at all (#9652). Root cause: applyReviewBudget stamped a hardcoded defaultReviewMaxSteps = 8 on review-family children, and PrepareReviewSubagentContext handed the same 8 to skill runners, overriding the step budget the runner had already resolved from the parent. Under the default uncapped parent that turned "no limit" into 8. Fix: the review contract keeps what it is for (the 2048-token cap per response, the structured output format and the parent facts pack) and no longer sets a step budget. Review children take the ordinary child budget: an explicit max_steps wins, otherwise half the parent's cap, or uncapped under an uncapped parent. The skill path keeps the runner's own steps. Test: TestReviewSubagentUnderUncappedParentFinishesPastEightRounds runs a real review task through TaskTool.Execute with a scripted 12-round child; before the fix it fails with "sub-agent: paused after 8 tool-call rounds", after it returns the verdict. TestReviewBudgetKeepsInheritedStepsAndBoundsOutput (steps = 8, want 0 before) and TestReviewSkillKeepsRunnerStepBudget ("runner steps 0 reached the child as 8" before) cover the task and skill budgets. go test ./internal/agent/ ./internal/boot/, go vet, repolint and golangci-lint on both packages pass on Windows. |
||
|
|
d67f2835ac |
ci(release): publish 1.x without deploying or verifying the website
The website is released from its own branch and renders the changelog from that branch's catalog, so the 1.x publication could dispatch Pages and still fail on a changelog page it does not own (v1.39.2: "public changelog returned HTTP 404" after every product surface had shipped). - Postflight now verifies tags, CLI and Desktop releases, npm, the Desktop Stable manifest on dl.reasonix.io and the Homebrew cask, attaches release-event.json, and completes. sync-release-site.sh becomes reconcile-release-publication.sh without the Pages dispatch/watch stages. - The stable manifest check stays: it reads dl.reasonix.io/latest/latest.json, the Desktop updater pointer on R2, not the website. The Homebrew check moves out of the site verifier and reads the tap through the contents API. - Drop the homepage/changelog/hydrated-DOM probes, observe-release-site.sh, release-site-fetch, and the pages.yml dispatches in release-stable.yml and release-desktop.yml together with the actions: write they needed. - Ledger: the site fragment becomes a pointer fragment (stableManifest, homebrew); homepage and changelog surfaces are gone. |
||
|
|
c04f7dd247 |
fix(setup): keep an API key variable name the user types
`reasonix setup` asked for the API key variable name, then saved every new key under a freshly allocated REASONIX_CONNECTION_*_KEY slot, so a typed name was dropped from config.toml and .env without a word. A typed name is now kept when saving under it changes nothing another reader resolves: no other provider, bot or remote-host setting reads it, the credential store holds no value or tombstone for it, and the process environment does not already set it (compared case-insensitively on Windows). The wizard checks that as soon as the name is typed, says what holds it, and asks again; Enter still means a private slot. The commit repeats the check under the edit locks, so a name claimed in between refuses the save with config.ErrCredentialKeyInUse instead of overwriting it. The staging journal records a keyed digest of each staged value, and cleanup and crash recovery remove a slot only while the store still holds that value, so a named slot another writer took over survives recovery. Whether the config still references a slot is read from the parsed config instead of a substring match, so API_KEY is no longer "referenced" by DEEPSEEK_API_KEY and left behind as an orphan. The prompt no longer shows a bracketed default it would not use. Fixes #11002 |
||
|
|
d5c9f2d739 |
feat(release-notes): credit pull request authors and issue fixers in rendered notes
Rendering now resolves every refs number through the GitHub GraphQL API. A pull request gains "by @author". An issue gains "fixed in #M by @author" for each merged pull request GitHub links as closing it (the ClosedEvent closer and closedByPullRequestsReferences; mentions are not fixes); an issue fixed by a direct push stays plain. Bot authors are never credited. A number that names a discussion or nothing stays plain with a warning. The closing Contributors / 贡献者 section lists exactly the credited humans, once each, in first-appearance order. It replaces the old Thanks section, which linked the catalog's contributors (github-actions[bot] included) without mentioning them; that field no longer feeds the notes. Lookups are cached per run, bounded in concurrency, and retried with backoff on 5xx, rate limits and dropped connections. A failure that persists, or an authorisation failure, renders the ref as its plain link and fails the command with a release_credits.* code. Because rendering now reads GitHub, the seal job renders the notes once into the candidate payload (evidence/release-notes.md, recorded as notes.renderedPath and bound to renderedSha256). Promotion publishes those sealed bytes with no network call, so a rename, a newly linked fix or an outage after sealing cannot change or block the release. Candidates sealed before this carry no renderedPath and render offline as before. Every job that renders, and every caller of a reusable workflow that does, gains issues: read and pull-requests: read. |
||
|
|
023896158f |
fix(cli): reasonix review ignores tools and skills the reviewed checkout configures
`reasonix review` runs inside the checkout it reviews, which is untrusted. Two routes let that checkout decide what review executes or asks for: - The tool registry was built from the merged config, so a checkout's reasonix.toml `[tools.search] engine = "rg"` + `rg_path = "tools/rg"` made review's grep execute the checkout's own binary (unsandboxed on Windows), and its `[sandbox]` chose the bash sandbox mode. - The review skill resolved with project scope, so a checkout's `.reasonix/skills/review/SKILL.md` replaced the built-in prompt and its allow-list (e.g. adding web_fetch under an attacker-written prompt). Review now builds its tool registry from the user-only config (config.LoadUserConfigReadOnly) and resolves its skill from a store with no project scope, so only built-in and user-level skills are candidates. Credential env names from the merged config are still registered for env filtering. |
||
|
|
a6cb1f9a28 |
Merge pull request #10845 from esengine/fix/v1-hooks-planner-guardian-review
fix(hooks): run configured hooks in the planner, reasonix review and the desktop try run |
||
|
|
5fd5cb6c6e |
fix(hooks): run configured hooks in the planner, reasonix review and the desktop try run
#10843 gave task, skill, fleet and background subagents their own hook session, but three agents that call tools still ran with no hook runner: - the dual-model planner (plannerOpts in boot.build) runs read-only research tools such as read_file, and its Options carried no Hooks, so a PreToolUse deny on reading a secret did not cover the planner's reads; - `reasonix review` builds its subagent outside boot and passed Options with no Hooks at all; - the desktop settings-page try run (App.TrySubagentProfile) runs read_file, search and policy-wrapped bash in the active workspace, also with no Hooks. Options.Hooks is the whole ToolHooks surface, so each of these agents now runs PreToolUse, PostToolUse and PostToolUseFailure around its tool calls, PostLLMCall on each model turn (its stdout replaces the reasoning stored in that agent's session) and PreCompact before its compaction (its stdout is added to the summary guidance), not only tool hooks. No agent reads hooks while building its prompt or tool schema, so the cache-stable prefix is unchanged. Hook sources and sessions: - Planner: hook.Runner.ForRole("planner") keeps the session's resolved hooks, cwd and spawner, and derives its session ID as "<parent session>:planner" each time a hook fires, so a SetSessionID on the parent (/new, /clear, resume) reaches it and hooks keyed on session_id do not merge sessions. - reasonix review: loads only the user's own hooks, global settings.json and installed plugins. hook.LoadOptions gains SkipProject, which leaves out <root>/.reasonix/settings.json, so reviewing an untrusted checkout never executes a command that checkout configures. Its hooks keep the checkout as cwd, which is what they inspect, and run with NoDefaultCurrentDirectoryInExePath=1 (Runner.WithoutCwdCommandSearch), so on Windows cmd.exe never resolves a bare `python` in a user hook to a python.exe the checkout ships. Each run is its own session, "review:<random>". - Desktop try run: loads project and global hooks the way a chat session in that workspace does, under "try-subagent:<random>". boot.NewCommandHookRunner now takes the hook.LoadOptions to resolve, so a caller states its hook sources. The hook package doc no longer claims project hooks load only for trusted projects. The guardian is left as is. On main-v2 nothing outside internal/guardian calls Session.Review or ReviewVerdict, the only paths that run its agent, so it executes no tool. Wiring it is not a one-liner either: its agent is built inside guardian.NewSession, which would need a new parameter across 29 call sites for a path no production code reaches. Tests: - TestBuildRunsPreToolUseInsidePlanner (through boot.Build): a plan-only turn whose planner calls read_file is blocked by PreToolUse, and the hook sees "<parent>:planner" where <parent> is the ID the UserPromptSubmit hook got. After ClearSession a second turn's planner follows the new parent ID. - TestReviewCommandRunsOnlyUserHooks: with a global deny hook and a project hook, the global one blocks read_file and the project one never runs; two runs get distinct review:<id> sessions. - TestTrySubagentProfileRunsWorkspaceAndUserHooks (desktop): both the project hook and the global deny run for the try run's read_file under a try-subagent:<id> session, and the file contents never reach the model. - TestReviewHooksNeverResolveCommandsAgainstTheCheckout: every review hook spawn carries NoDefaultCurrentDirectoryInExePath=1 with the checkout cwd. - TestForRoleDerivesSessionFromParentAtFireTime and TestWithoutCwdCommandSearchReachesEverySpawnOfItsChildren (internal/hook). Docs: GUIDE.md and GUIDE.zh-CN.md describe which hooks each read-only child runs and under which session ID. Refs #9255 |
||
|
|
c130bd7191 |
Revert "chore(release): the 1.x line stops publishing the CLI to npm, Homebrew, and the CLI pointers (#10757)"
This reverts commit
|
||
|
|
51f6a0ad6d |
fix(desktop): restore each canonical session's own permission preset
Root cause: canonical (v5) sessions had nowhere to keep their permission preset. The legacy BranchMeta sidecar is refused for them, and neither the Session Service projection nor workspace presentation carries a preset, so the value lived only on the tab. OpenSession applied the source surface's live preset to the target session (prepareCanonicalControllerRuntime read it from the outgoing controller). Session B inherited A's danger-full-access, and a surface built from New Chat forced workspace-write onto every session opened into it. The renderer had the same leak one layer up: its composer profile table is keyed by tab, and the push that follows a navigation sent the previous session's preset to the new one. Decision: keep the preset per canonical SessionID in a desktop-owned record (desktop-session-presets.json next to desktop-tabs.json), with no change to the session log schema. Opening a session, and restoring tabs at startup, use only the value recorded for that exact SessionID. A session with no record gets the new-session default. The value on the surface is never carried over. Recording rule: a record is written only by SetPermissionPresetForTab, the one preset setter fenced by session. The caller passes the SessionID of the permission snapshot it read; under the tab's turnStartMu, which also serializes navigation, the call is refused with reasonix_error:permission_session_changed (errPermissionSessionChanged) unless that id equals the live controller's session. The record is keyed by that same session and the file is written outside App.mu. The revision alone cannot fence this: it is per controller, so two freshly opened sessions share a number. SetToolApprovalModeForTab and SetModeForTab name no session and so never record; saving tabs, submissions, composer-profile sync and draft settings never record either. Fix: - sessionPresetStore exposes restore, record and forget. A failed write removes the file, or empties it in place when the directory refuses the removal, so a restart opens every session at the default; the full set is written again on the next record or forget. - The renderer passes the snapshot's sessionId to SetPermissionPresetForTab. On permission_session_changed it re-reads the tab and does not retry the choice on the session the tab now shows. - Whenever a tab's canonical SessionID changes (syncTabSessionIdentity: New Session, clear, legacy continue, rebuild), the tab and its controller take that session's own record or the new-session default. - New Session on a blank tab that is reused drops the blank session's record, resets it to the default, and advances the controller's permission revision (Controller.InvalidatePermissionSnapshots), so a snapshot read before the reuse cannot record a choice for the session handed out as new. - A fork tab starts at the new-session default rather than the source tab's preset. - The canonical open path passes the target session's restored preset to prepareCanonicalControllerRuntime, and startup restore does the same for tabs bound to a SessionID. Legacy tabs keep their existing sidecar path. A session that is permanently deleted drops its record. - docs/desktop-migration inventory regenerated for the new signature and line positions. - Composer profiles in the renderer record which session they were read for (owner). A profile, or a meta snapshot, whose owner belongs to a different session than the tab's current one is discarded, not reconciled. Tests: - TestCanonicalSessionPresetFollowsSessionAcrossNavigationAndRestart and TestRestartDoesNotTrustSurfacePresetForUnrecordedSession fail on origin/main-v2 (session-B / session-A tab preset = "danger-full-access", want "workspace-write"). - TestNewSessionInTabDoesNotInheritSourcePreset: A=full, New Session in the same tab -> the new session is the default, has no record, and stays the default after restart through desktop-tabs.json and after reopening; A is still full. - TestNewSessionOnBlankTabDropsItsEarlierChoice and TestForkStartsAtNewSessionDefault cover the reused blank and fork paths. These three fail without the recording rule above with tab preset = "danger-full-access", want "workspace-write". - TestUnreadablePresetRecordsRestoreDefault: a corrupt record file restores the default. - TestStalePresetChoiceDoesNotLandOnNavigatedSession: snapshot A, open B (same revision), then set full access with A's snapshot -> refused with errPermissionSessionChanged, B not recorded, B stays at the default. The same scenario through the unfenced signature fails with "stale choice for session-A accepted on session-B" and B recorded as danger-full-access. - TestPresetWriteFailureInReadOnlyDirFailsClosed: full access recorded, the config directory made read-only, read-only chosen -> restart does not restore full access. Without the in-place truncation it fails with narrowed to read-only, write failed, restart restored "danger-full-access". - TestSnapshotBeforeBlankSessionReuseIsRefused: snapshot the blank tab, New Session reuses it, set full access with the old snapshot -> refused and nothing recorded. Without the revision advance the set is accepted and recorded as danger-full-access. - The desktop preset tests pin a host that offers every preset (control.SetPresetSandboxForTest), so they assert the same thing on a runner without an OS sandbox, where read-only and workspace-write are not offered. Pinned to "no sandbox", three of them fail with permission preset "read-only" is unavailable: shell sandbox requested but unavailable on this host. - TestUnfencedModeSettersDoNotRecord: SetToolApprovalModeForTab and SetModeForTab leave no record. - frontend permission-preset-session-fence.test.tsx: a navigation between the snapshot and the set neither applies nor records the stale choice, the refusal is not retried, and the tab is re-read. - frontend composer-profile-session-navigation.test.tsx runs the real projection, lifecycle and controller-profile hooks through A -> B -> A. |
||
|
|
aed114c057 |
fix(desktop): start the shell on X11 under Wayland when XWayland is there
Root cause: Electron 44 picks its Wayland Ozone backend by itself in a Wayland session. On drivers whose EGL platform init fails there (Kylin V10 SP1 with the Mali stack in #10369) the GPU process exits and the app dies with 139 before a window appears; the same machine works with --ozone-platform=x11. Nothing in the launch chain chose a backend. The .desktop entry runs reasonix-launcher, which runs reasonix-desktop, whose bootstrapShell builds the Electron argv and starts it detached, so that spawn is the one place that controls the shell's argv. The flag has to be in argv: Chromium selects Ozone before the main script runs. Decision: default to X11 whenever it is available. bootstrapShell prefixes --ozone-platform=x11 on Linux when the session is Wayland (XDG_SESSION_TYPE, else WAYLAND_DISPLAY) and DISPLAY is set. REASONIX_OZONE_PLATFORM=wayland keeps native Wayland, x11 forces X11, auto is the default. A pure Wayland session without DISPLAY and an X11 session are unchanged, and an explicit --ozone-platform or --ozone-platform-hint argument wins. Relaunches through the launcher re-decide; Electron's own relaunches reuse process.argv and keep the flag. Test: TestBootstrapShellChoosesOzonePlatformFromSession drives bootstrapShell with a fake shell that records its argv, over the session table. On origin/main-v2 it fails: shell argv = ["--flag"], want ["--ozone-platform=x11" "--flag"] With the fix all nine cases pass. |
||
|
|
348fff0686 |
Merge remote-tracking branch 'origin/main-v2' into maint/v1-sweep
# Conflicts: # .goreleaser.yaml |
||
|
|
2a8f5f24dc | Merge #9321: docs(hooks): replace macOS-only osascript Stop example with cross-platform command | ||
|
|
0ddd2258fd | Merge #10301: fix(cli): stop the headless run dropping a reasoning-only answer / headless 运行不再丢弃只有推理的回答 | ||
|
|
2c2624afd4 | Merge #10245: fix(agent): detect a prompt the provider silently truncated / 识别被服务端静默截断的提示词 | ||
|
|
1aa4a1fb31 |
chore(release): the 1.x line stops publishing the CLI to npm, Homebrew, and the CLI pointers (#10757)
The Studio line now owns the CLI install channels: npm `reasonix` with its
`@reasonix/cli-*` platform packages, the esengine/homebrew-reasonix cask, and
the R2 CLI update pointers (`cli/{stable,preview}/latest.json`) that
`reasonix upgrade` and the site read through the release gateway. A 1.x
release must never write any of them again.
- release-npm.yml is removed, and release-promote/release-stable no longer
call it or wait on it; the legacy recovery input publish_npm goes with it.
- release.yml no longer hands GoReleaser a tap token, no longer pushes the
prepared cask, no longer advances the CLI channel pointers, and no longer
requires npm `latest` to have moved. It still creates the vX.Y.Z GitHub
release, its notes, the immutable cli/releases/<tag>/latest.json record,
and the Desktop compatibility asset.
- .goreleaser.yaml drops homebrew_casks.
- verify-stable-release-artifacts.sh and the publication ledger stop
requiring npm package identities, npm dist-tags, and the cask version.
- Publisher helpers with no remaining caller are removed:
publish-homebrew-cask, finalize-npm-official-release, resolve-npm-release,
npm/publish-candidate, decide-cli-pointer-update, compare-cli-release-tags.
- release-workflows.test.sh now fails if any workflow holds NPM_TOKEN or
HOMEBREW_TAP_TOKEN, names the tap, runs npm publish or dist-tag, or
writes a CLI channel pointer.
Candidate preparation, tags (including npm-vX.Y.Z), Desktop publication, the
Stable manifest, Pages, and the release-event record are unchanged.
|
||
|
|
438566dd6c |
fix: recover blocked conversation replay (#10731)
Problem: invalid historical tool arguments could permanently block newly created or resumed conversations at the transcript gate. Missing historical images and optional invalid extension replacements could similarly prevent later requests. Root cause: runtime rebuilds consumed provider projections that had discarded host-recorded refusal evidence. Recovery and extension validation occurred too late or used cached failures. Fix: preserve refusal evidence by stable result identity; archive unreplayable calls as quoted historical observations; recover unavailable past image inputs; validate optional extension replacements before adoption; preserve the latest authored request when abbreviating oversized tool results; and provide localized recovery guidance. Canonical session history and healthy provider request bytes remain intact. CI follow-ups: make configuration-error injection portable and register catalog cleanup per App generation after its temporary directory is created, preventing open SQLite handles during Windows cleanup while preserving all behavioral assertions. Verification: full local Go suite, go vet, full golangci-lint, focused race regressions, prompt-cache guards, and the required PR CI checks pass. Synthetic tests cover fresh-session resets, cold recovery, repeated identities, unknown execution outcomes, image loss, extension policies, protected-tail truncation, and cross-platform configuration retry. No CI gates were removed or weakened. |
||
|
|
47087375ac |
Fail fast on provider errors and localize error messages / 取消供应商自动重试并统一错误提示语言 (#10730)
Problem: provider failures keep turns running through nested retries and long waits, and raw exceptions bypass the selected Desktop UI language. Root cause: HTTP, agent sampling and auxiliary requests own overlapping retry loops; independent UI surfaces render backend exceptions directly. Broad net.Error matching also misclassifies request and filesystem failures. Fix: return transport and upstream failures directly while preserving causes, partial output, usage accounting and bounded protocol/context corrections. Centralize locale-aware summaries and expandable original diagnostics. Update legacy assertions and fixture provider hierarchies without removing functional coverage or disabling CI workflows. Verification: root Go suite, focused Desktop checks, all 521 frontend CI commands, production builds and budgets, lint, browser error interactions, and the latest PR CI including cross-platform, race and memory checks passed. This merges the fix into main-v2; it does not publish a release. |
||
|
|
076802e0ad |
fix(cli): stop the headless run dropping a reasoning-only answer
A thinking model can finish a turn with non-empty reasoning and an empty visible message. That shape is accepted on purpose, and the transcript renderer handles it: a "thinking" marker always, the reasoning itself under --show-thinking. The headless sinks have neither. `runOutputSink.Emit` kept only `e.Text`, so a reasoning-only turn left `-p` printing nothing at all and `--output-format json` reporting `result: ""` beside `subtype: "success"`, `is_error: false` and exit 0. A caller cannot tell that apart from a turn that produced nothing, and the run looks successful either way. Measured against a local provider: the turn's whole output was a malformed tool call the model wrote as prose in the reasoning channel instead of emitting as a tool call. 84 characters, the only thing the turn produced, dropped on the floor - the automation contract said success with an empty result. `result` now falls back to the turn's reasoning when the visible message is empty, which is the remedy this repository already applies elsewhere for the same provider shape. `result_from_reasoning` reports the substitution, so the reasoning is not passed off as visible text the model never emitted; it is additive and omitted whenever the model answered normally. The answer and its provenance are one value rather than two fields, because they are only ever correct together: a stale fromReasoning beside a fresh answer would misreport where a later visible answer came from. repolint asked for the same grouping independently, a thirteenth scalar on runOutputSink being over its ceiling. Nothing here reclassifies a reasoning-only turn as an error - that shape stays accepted, and the Plan/Goal side of it is #8023's. --show-thinking is not the lever either: buildRunSink never passes it to this sink, so gating the fallback on it would leave `-p` printing nothing exactly as before. |
||
|
|
2ee6daabac |
fix: refresh desktop migration inventory after sidebar repair
Problem: the main-v2 push CI failed root tests and coverage because the generated Desktop migration inventory was stale after the sidebar repair merged. Root cause: the inventory records source locations and Desktop RPC signatures; extracting workspace removal moved methods, and GetProjectTreeSnapshot now reports read errors. Fix: regenerate both inventory artifacts from the current main-v2 source without changing runtime code or weakening the inventory gate. Verification: go test ./tools/desktopinventory, go run ./tools/repolint, and git diff --check passed. |
||
|
|
c0654a4a5e |
Simplify new-session creation and recovery / 简化新建会话与失败恢复 (#10697)
Problem: New Session could fail after workspace identity reconciliation, delay access to the new input, and display unrelated creation failures. Root cause: Creation compared journal workspace IDs outside the registry transaction; navigation waited for runtime readiness; global recovery duplicated the active request observer. Fix: Resolve directory ownership at reservation and publication, preserve retry identities, expose the persisted input before runtime readiness, and scope recovery feedback to the selected composer. Keep send readiness, input persistence, lifecycle protection, and provider-visible contracts intact. Verification: Creation and workspace interleaving regressions; focused Go race tests; frontend lifecycle and browser scenarios; production build and bundle budgets; native Windows portable-package creation/restart checks; PR cross-platform CI. |
||
|
|
117d6a04c1 |
Fix historical session reconciliation / 修复历史会话展示与生命周期恢复 (#10696)
* Fix historical session reconciliation and ordinary lifecycle Problem: Historical conversations could remain visible but fail to open or archive after upgrades. Root cause: Strict receipt ownership checks had no ordinary discovery reconciliation, and navigation mislabeled underlying failures as missing projects. Fix: Recover proven mappings and absent targets through lifecycle-fenced journals, retain authoritative adoption and tombstones, filter unusable rows, and preserve classified errors. Verification: Catalog and workspace-state tests; desktop E-H, Q-S and history-10-11 partitions; focused race and restart tests; frontend navigation, typecheck, browser loading and production build; Windows cross-compilation. * Index completed historical migration receipts during discovery Problem: Ordinary history discovery repeatedly scanned every migration receipt for each legacy head, causing quadratic metadata lookup cost. Fix: Build a completed-receipt key index once per discovery pass while preserving reviewed-version and interrupted-operation completion evidence. Verification: Focused historical discovery, visibility, and receipt-index tests pass; desktop golangci-lint and repository lint pass. * Use readable sessions in the read-only sidebar fixture Problem: The project-tree read-only test attached eight registry identities without session files and expected them to be visible, contradicting the new unavailable-history contract. Fix: Create real sessions before attaching them while preserving the unmigrated organization and byte-for-byte registry immutability assertions. Separate coverage continues to require missing members to stay hidden. Verification: Reproduced the original assertion locally; focused read-only, organization, and missing-member regressions pass with race detection. Pinned desktop lint and repolint pass. |
||
|
|
3b0ee47e6d |
fix: preserve background gateways through model settings changes / 修复切换配置后后台网关阻塞发送 (#10688)
* fix(model settings): preserve session processes across runtime rebuilds Problem: saving model settings could strand a Desktop session when an independent background gateway kept a job registered, repeatedly rejecting new messages. Root cause: rebuild admission treated every background job as runtime-bound, while replacing a controller would cancel its job manager and kill independent processes. Fix: move background process ownership to a session scope, classify runtime-bound work explicitly, publish rebuilt controllers atomically, and carry structured application status and one-submit validated applied-settings recovery through local and remote Desktop paths. Verification: root Go suite, inventory-partitioned Desktop Go suite, focused race suites, pinned lint, repolint, frontend typecheck/build/tests, browser interaction and Electron smoke passed. Windows shell tests cross-compiled. * fix(desktop): keep remote sends and CI component loading compatible Problem: the frontend CI component runner could not load the recovery CSS import, and a remote submission guard rejected different messages sent in the same tick. Root cause: the component imported CSS in Node test scope, while the guard treated every in-flight send as an unresolved receipt. Fix: include recovery styles through the existing global stylesheet, deduplicate matching in-flight sends, and reserve receipt reuse/blocking for genuinely unknown outcomes. Verification: the failed decision-slots test, remote-session-surface test with same-tick and unknown-result assertions, browser recovery interaction, CSS syntax, production frontend build, TypeScript and repolint passed. * test(frontend): await both file-reference batches before cache assertion Problem: the full frontend CI runner could fail the cached-verdict assertion before the second host batch had settled. Root cause: the test assumed six Promise continuations always covered two async batch applications; that scheduling count varies by runtime. Fix: yield through the host batch completion turn before asserting the unchanged per-candidate cache invariant. Verification: the exact file-reference test passed, and the complete inventory-verified frontend CI runner passed all 516 commands locally. * test(desktop): await catalog fixture shutdown on Windows Problem: The retired projection test can remove a temporary SQLite database while its asynchronous shutdown still holds a Windows file handle.\nRoot cause: stopSessionCatalog has a bounded deadline and may return before Catalog.Close finishes.\nFix: Join the catalog close before advancing and register a cleanup fallback for early test failures.\nVerification: go test -run '^TestMissingProjectDoesNotResurrectDeletedTopic$' -count=1 . (desktop) * chore(desktop): regenerate inventory after base merge Problem: The merged historical-session changes shifted Desktop source locations, leaving the checked-in migration inventory stale.\nRoot cause: The inventory records line hints, and the upstream changes were merged after the prior generation.\nFix: Regenerate both inventory artifacts with the repository tool.\nVerification: go test ./tools/desktopinventory; go run ./tools/desktopinventory -check. |
||
|
|
e934b6a6df |
Repair retained historical session archives / 修复历史会话残留归档 (#10689)
* fix(desktop): reconcile retained historical archives Problem: retained converted sessions could fail archive, remain as ghost rows, or collide with deleted target identities. Root cause: source receipts and pending imports did not distinguish identical retired copies from later source history, and archive could publish an active import before committing its lifecycle. Fix: prove exact conversion ownership and content, repair identical retired receipts, stage changed history directly under an archive transaction, and fence recovery and sibling session operations. Verification: desktop partitioned regression suite, focused archive and damaged-history tests, workspacestate tests, frontend typecheck and tests, repolint, and Windows amd64 cross-compile. * fix(desktop): handle historical proof service shutdown errors Problem: Windows desktop lint rejected unchecked shutdown results for the short-lived historical proof services. Root cause: newly added read-only proof paths deferred Shutdown without handling its error. Fix: close proof services through a shared helper that records cleanup errors in the local service log. Verification: Windows-target golangci-lint v2.12.2 with isolated cache, archive regression tests, repolint, and gofmt. |
||
|
|
a5eba463bc |
fix(desktop): recover interrupted session creation / 自动恢复中断的会话创建 (#10680)
* fix(desktop): recover interrupted manual session creation Problem: pending manual creations can remain starting after a competing process releases its creation lock. Root cause: startup recovery was a one-shot attempt and build notification completion did not establish that an executor had actually stopped. Fix: centralize creation admission and retries, preserve OS-lock ownership through execution and result persistence, join actual build executions on shutdown, and expose transient progress plus bounded diagnostics. Preserve session identity, persisted schema, and unknown payload fields. Verification: native macOS subprocess handoff/exclusion, targeted Go race and shutdown tests, frontend interaction/type checks, Chromium recovery UI, Desktop lint, and Windows cross-compilation passed. The full Desktop sweep retains a separately reproduced baseline activation-ordering failure. Raise one existing test-file allowance by three measured lines to retain pending-attachment source and target isolation assertions. * fix(desktop): claim activation terminal state before background pruning Problem: rapid navigation can cancel an activation that already emitted ready. Root cause: readiness was published while the request remained eligible for supersession until background pruning completed. Fix: group activation bookkeeping and atomically claim the terminal event under App.mu while retaining generation ownership for deferred pruning. Both direct-tab and topic switches preserve cancellation of unfinished requests without cancelling already-completed requests again. Verification: a deterministic channel-gated regression fails on the original base for both switch paths. Activation race tests, ten repeated fast-path and regression runs, Desktop lint, and repository lint pass. * chore(desktop): refresh command inventory after recovery changes Problem: the root CI inventory freshness test rejects the new diagnostic RPC and shifted source locations. Root cause: the independent Desktop contract generators do not regenerate the migration command inventory. Fix: regenerate both inventory artifacts with the existing owner tool. Verification: go test ./tools/desktopinventory passes; the inventory includes ExportManualCreationDiagnostics and retains existing command classifications. * test(desktop): own catalog fixtures before waiting for admission Problem: Windows CLI discovery coverage could fail a three-second publication poll and then leave its SQLite owner running during temporary-dir cleanup. Root cause: a functional discovery test used startup speed as readiness and registered cleanup only after that assertion succeeded. Fix: reuse the existing initial-admission and reconcile-execution barriers; register checked cleanup in the shared fixture before startup. Remove the redundant completion hook and preserve topic identity, ordering, count, and no-migration-marker assertions. No production timeout or platform is changed. Verification: an intentionally failing subprocess proves the old helper leaks its owner and the new helper closes it. Relevant catalog/discovery race tests pass, as do Desktop lint and repository lint. Native Windows CI follows. |
||
|
|
f8b47d3439 |
fix(desktop): simplify running inbox recovery (#10681)
## Summary Restore queue and current-turn guidance for canonical Desktop sessions, and simplify input recovery. ## Problem and root cause Canonical sessions carry a session ID with an empty legacy path. Inbox capture, runtime selection, and receipt lookup still relied on that path, rejecting legitimate running-session messages. A registration failure could then retain a pending marker and stale revision, leaving input locked behind ineffective version choices. ## Changes - Route capture, receipt ownership, runtime selection, and guidance through canonical session identity while preserving generation fences. - Automatically reconcile rejected registrations and lost replies. Release only a registration whose owning renderer never invoked send; uncertain delivery is checked without replay. - Preserve newer edits during late settlement and retain unsent input when another submission is pending. - Replace the three version/recovery actions with automatic recovery and one Retry action when confirmation remains unavailable. - Retire composer recovery-copy creation and display. Keep revision checks, current-input autosave, and queue-item editing conflict handling. Keep the old list RPC as an empty compatibility response; do not change the database schema or delete existing records. - Document the simplified experience in English and Chinese. ## Verification - Focused canonical queue/guidance, composer persistence, deterministic race, lifecycle, and follow-up tests passed; integrated-head queue, race, persistence, wiring, receipt, and stream suites passed. - Desktop inbox and sessionui race tests passed again on the integrated head. - Frontend source/test type checks, hook lint, app-layer checks, and repository lint passed. - All 459 discovery-runner frontend suites passed on the final head, including the unchanged goal-recovery guard. Goal restoration remains suppressed when history changed even though retained text can be edited. - Browser interactions verified automatic unlock after rejection, retained text, lost-response confirmation, and no duplicate enqueue on Retry. - The runtime Chromium scenario initially failed because mock-created tabs omitted binding generations. The mock producer and runtime frames now publish matching identities; all finishing, queue receipt, reconnect, and remote ownership assertions pass locally unchanged. - Production frontend build and bundle budgets passed. Pinned Desktop golangci-lint passed. CI supplies the remaining platform gates. ## Scope and compatibility The removed copy/version-choice behavior is intentional. Existing persisted input and submission records remain readable. Provider stream EOF and historical transcript recovery are separate issues and are not claimed fixed here. Native Windows installer qualification and release packaging are outside this PR. Related work: #10680 concerns manual-session creation recovery; #10657 concerns historical-version recovery. Neither implementation is adopted here. Documentation-impact: updated - bilingual composer controls describe running-turn messages, automatic input recovery, and retired version choices. Cache-impact: none - provider-visible prompts, tools, and message serialization are unchanged. Cache-guard: inbox identity and receipt regressions verify recovery without replay; provider serialization is untouched. |
||
|
|
d5a95e8993 |
fix(desktop): retire legacy drafts and prevent purged topic resurrection (#10677)
Problem: Legacy workspace drafts confuse users, and emptying trash can add new conversation rows that create more sessions when opened. Root cause: The draft surface and replay lifecycle remained connected after manual creation returned to formal sessions. Purge removed canonical topic ownership but left metadata that looked like independent empty topics. Fix: Disconnect draft UI, replay, and restore reads while preserving formal input persistence and exit barriers. Keep minimal purge topic ownership, filter stale metadata, and reject opens that would recreate deleted sessions. Preserve formal sessions on restore conflicts and draft retirement on failed tab-preference writes. Document compatibility and shared-topic behavior. Verification: Frontend 457-suite runner and typechecks passed; focused Go race suites and workspace-registry package passed; repository lint and whitespace checks passed. Full Desktop aggregate run previously timed out; broader cross-platform verification is delegated to PR CI. |
||
|
|
fba4febbb6 |
Unify migrated session identity and lifecycle / 统一新旧会话身份与生命周期 (#10676)
* fix(desktop): keep purged history out of topic placeholders Problem: Permanently deleted history could reappear as a metadata-only topic and fail subsequent archive requests with session not found. Root cause: Sidebar adoption filtering depended on live membership and presentation, both removed by purge, while legacy metadata and source receipts remained. Fix: Derive an immutable topic adoption index from durable committed evidence, preserve minimal topic identity in purge receipts, and share the index across materialized, lazy, and pinned projections. Verification: Workspace state tests with race detection, archive/restore/purge/restart integration coverage, shared-topic and independent-source preservation, pinned/manual-order regressions, previous operation codec round-trip, incremental lint, and diff checks passed. Native packaged Windows/macOS UI was not revalidated. * fix(desktop): unify migrated session identity and lifecycle Problem: A migrated conversation could appear as both a canonical session and a historical source, highlight twice, and reappear after lifecycle operations. Root cause: Physical path normalization changed source hashes while adoption receipts and pending imports retained older keys. Lookup and lifecycle callers did not share compatibility aliases. Fix: Derive immutable source aliases from durable mapping receipts and use them across discovery, navigation, tab restore, import preparation, sidebar projections, and lifecycle guards. Preserve independent head and reviewed-version identities. Persist explicit source-cleanup intent with purge tombstones, clean only unchanged exclusive canonical originals under ownership locks, and retain shared, changed, offline, and multi-head sources. Verification: Focused identity, archive/restore/purge, shared-source protection, writer contention, and process-restart tests passed. Workspace state race tests, filesystem purge tests, incremental lint, and diff checks passed. The complete Desktop CI partition run is in progress; the initial unpartitioned run hit its six-minute package timeout. Packaged Windows and macOS UI has not been revalidated. * fix(desktop): separate migration identity and purge planning owners Problem: The PR failed repository size and complexity checks and lacked the required documentation-impact declaration. Root cause: Source alias lookup and purge-plan encoding grew existing lifecycle coordinators beyond their file budgets, and historical head resolution remained embedded in the generic source resolver. The historical migration guide still implied that all original files survive explicit permanent deletion. Fix: Move source mapping lookup and versioned purge-plan construction into focused owners, extract historical head resolution, and document unified display/lifecycle and guarded source cleanup in both language guides. Preserve all behavior and test coverage without raising budgets or disabling checks. Verification: Repository repolint, the documentation-impact validator, isolated-cache golangci-lint 2.12.2, migration identity/import/head/purge regressions, workspace-state package tests, and diff checks passed. |
||
|
|
3476556ee8 |
Defer historical session loading and add bounded native paging / 延迟历史会话加载并加入有界原生分页 (#10664)
* Improve historical session startup and lazy loading * Fix history preparation retirement across readers Problem: source replacement and immediate reopening could expose a closed SQLite cache to retained history readers. Root cause: reader cancellation did not follow preparation retirement, the last release removed cache ownership before close, and compatibility paging opened a separate owner. Fix: share preparation across both APIs, propagate source cancellation, retain and drain cache-close barriers, and retire unbound borrowers during shutdown. Verification: deterministic shared-reference, replacement, immediate reopen, compatibility cursor and shutdown tests passed with the related history paging race suite. * Index history read fences and extend lazy catalog pages Problem: paging repeatedly cloned the registry and scanned every source mapping, while existing groups, time filters and pinned shells bypassed lazy catalog reads. Root cause: read invalidation lacked immutable reverse indexes and source fences could mix a captured WAL view with live metadata. Fix: share exact-publication lifecycle tokens and one verification per validation; reuse lazy pages for all-session views, frozen time filters and automatic pinned shells; capture source fences from their listing transaction. Document remaining full-materialization and recovery limits. Verification: registry, history lifecycle and catalog paging race suites; deterministic WAL-capture replacement regression; transcript tests; full root Go suite. Desktop full partition sweep remains in progress. * Validate fresh macOS images and join history batch workers * Page native history outlines and resolve direct read anchors * Read schema-one history through cancellable native projections * Handle unused history roots and verify packaged native readers * Page grouped history without materializing every session Problem: group filters expanded all legacy rows and loaded unrelated canonical headers. Disappeared history roots could also be omitted from completeness. Root cause: group membership forced the full snapshot adapter, and listing availability independently skipped absent roots instead of using discovery. Fix: retain immutable catalog pages with source-key membership predicates, admit canonical group and pin members before Stat, and use metadata discovery state for absent roots. Keep source hashes and persisted schemas unchanged. Verification: catalog and Desktop pagination/organization regressions, related race tests, sidebar browser interactions, and stronger packaged legacy reader checks. Root-wide validation and the new packaged group check continue separately. * Share native history preparation with compatibility content readers Problem: Unbound history field requests could enter compatibility repair after native paging had already prepared a read-only view of the same source. Root cause: Paging and content RPCs borrowed different history preparation owners and did not share cancellation and source validation boundaries. Fix: Centralize native pager borrowing and use it for compatibility field reads, including explicit targets, while retaining unsupported-format readers. Verification: Focused paging/content regressions and race tests pass, including Unicode chunk reads, unchanged authoritative bytes, and peer cancellation. The native package harness now exercises unbound content reads as well. * Bound queued history iterators and resolve original compatibility sources Problem: Rotating discovery retained one descriptor per historical root, and cold compatibility RPCs rejected global files after workspace restoration. Root cause: Scan admission had no retained-iterator budget; read validation mistook the destination workspace for the captured historical source root. Fix: Limit queued discovery to eight iterators with a visible-workspace reserve, retaining admitted progress. Share known-root source resolution across cold paging, field reads, and legacy previews outside App.mu. Verification: Catalog full race suite and Desktop focused race tests pass. Tests cover retained progress, priority switching, eventual completion, old global source reads, full Unicode content, and unknown/symlink root rejection. The packaged smoke caught the original directory failure; retain compact page diagnostics so an absent field ref identifies its actual read error. * Filter historical metadata pages without materializing the library Problem: Sidebar text queries bypassed lazy paging and built every legacy session row before returning a small matching page. Root cause: The bounded catalog adapter excluded text requests because SQL case conversion could not preserve display-layer Unicode/title semantics. Fix: Filter bounded catalog batches through the existing Go matching rules inside the retained WAL snapshot. Read captured source identities instead of resolving candidate files and account for retained query text. Verification: Catalog and Desktop focused race tests cover sparse matches, Unicode, cancellation, fixed-cut continuation, source keys and localized titles. Sidebar browser interaction tests pass. Native smoke now also checks text filtering within a group; sparse queries can still scan all metadata. * fix(desktop): admit explicit group targets without scanning history Problem: ordinary group edits enumerated all legacy source pages before applying a workspace organization mutation. Root cause: group commands shared manual reordering's full-source admission. Fix: admit only resolved group targets inside the revision CAS, normalize physical source keys, retain explicit ungrouping fences, and recheck source adoption and lifecycle ownership inside the registry transaction. Verification: organization and group regressions pass with the race detector, including deterministic adoption-before-commit rejection and late preference import. Document the remaining full-source manual and legacy preference paths. * fix(desktop): decouple catalog admission from metadata refresh Problem: catalog startup and filesystem event delivery waited for whole project/topic metadata transactions and restored-path index writes. Root cause: startup and the watch loop synchronously owned these writes, while user edits started independent unjoined metadata goroutines. Fix: register watches before restored identities settle, enqueue restored paths, and coalesce all metadata refresh triggers into one cancellable worker joined by the catalog lifecycle. Publish admission without waiting on writes. Verification: catalog/sidebar regressions and race tests pass. Deterministic coverage holds a real SQLite writer during admission, blocks refreshes to verify coalescing, and checks cancellation and lifecycle wakeup ownership. Whole-projection metadata reconciliation and synchronous database integrity validation remain explicitly documented acceptance gaps. * refactor(history): separate projection readers and preparation stages Split native pager source validation, DAG projection, compatibility windows, catalog paging, and controller usage accounting into focused owners. Remove the unreachable continuation rebind in the empty-key topic branch. Preserve source validation, transaction boundaries, and snapshot ownership without increasing lint baselines. Verification: repository lint; agent, sessioncatalog and session package suites; focused Desktop history/catalog/snapshot/navigation tests; frontend typecheck and transcript suite. * fix(history): serialize discovery progress with catalog writers Problem: the 100k native discovery fixture stalled with SQLITE_BUSY. Root cause: progress publication bypassed the process-local mutation lock while other catalog writers used it. Fix: route progress through the same boundary and preserve its generation predicate and cancellation behavior. Add a production-package scale harness and record the incomplete history.12 result without relaxing its gate. Verification: focused metadata/reconcile/catalog race tests, repository lint, script syntax check; native scale harness smoke and history.12 measurement exposed the failure. A fresh package run is still required. * fix(history): move metadata integrity audits behind startup admission Problem: cached catalog opening still scanned every database page before returning a sidebar page. Fix: open advisory metadata separately from the full background audit while keeping content projection validation synchronous. Corruption revokes the generation, stops its work, closes all read leases, and reopens through the existing validated quarantine path with a monotonic revision floor. Old cursors return stale_cursor; canceled or transient audits cannot quarantine data, and retries remain bounded. Verification: root Go suite; projectiondb and sessioncatalog race suites; Desktop catalog/history/snapshot race regressions; real CHECK-constraint corruption, source preservation, lease closure, late publication, cancellation and retry tests; repository lint. Native package performance acceptance remains pending. * fix(history): avoid redundant discovery and source-wide metadata writes Problem: Large legacy catalogs could finish discovery then immediately restart, while registry refresh rewrote unrelated discovered topics. Root cause: Startup recovery used the full-root invalidation notification alongside watcher-owned discovery; metadata membership reset and orphan cleanup scanned all topics. Fix: Publish only registry metadata after reservation recovery, and use the existing metadata_present flag with an optional partial index to constrain membership refresh. Source mutation retains topic cleanup ownership. Record incomplete history.13 scale evidence and warmup progress. Verification: sessioncatalog suite, focused catalog race tests, Desktop startup/catalog/history race tests, repolint, JavaScript syntax check, and diff whitespace checks passed. New package scale acceptance remains required. * fix(history): bound directory watching and merge undispatched invalidations Problem: Large catalogs continued to restart discovery after the initial scan. Root cause: Catalog monitoring bypassed the platform directory backend and used macOS kqueue registration that enumerates children and opens per-file handles. Unwatched roots were also invalidated on every metadata tick, and queued jobs retained pre-dispatch changes as unnecessary follow-up scans. Fix: Reuse native directory watching, preserve canonical event/access routing, separate watch retries from the rotating audit, and merge invalidations before scan dispatch while retaining genuine in-flight source changes. Verification: Focused catalog queue and Desktop catalog/watcher race tests pass, including 512-file native registration under a 256-descriptor process limit, canonical events, recovery gaps, durable journals and late file discovery. Repolint and diff checks pass. Record the incomplete history.14 scale evidence; the new production package still requires measurement. * fix(history): preserve cold reads when execution recovery fails Problem: lease-blocked native navigation could leave a blank transcript, and cache failures could silently start full compatibility replay. Root cause: passive readiness refresh switched startup-error tabs to controller-dependent Follow; activation failure settled independent history state. Native preparation did not distinguish ordinary errors from explicit unsupported formats, and root invalidation retained native watcher registrations. Fix: coalesce matching cold reads, fence rebuild/reconnect paths, and let only read failures settle history errors. Restrict compatibility fallback to explicit unsupported results, positively identify ancient event envelopes, and remove replaced native watches. Verification: deterministic activation regressions (59 assertions), frontend typecheck/lint, transcript suite, loading browser tests, focused agent/Desktop race tests and Go lint pass. Root full suite previously passed; Desktop default package timeout remains under investigation with exhaustive deterministic shards. Production history.15 failure is documented; new-package acceptance remains pending. * refactor(history): share runtime rebuild subscription handling Problem: the cold-read fence duplicated rebuild handling and exceeded the existing initial bundle budget by 380 bytes. Root cause: single-tab and settings-wide rebuilds maintained equivalent subscription ownership logic in separate branches. Fix: iterate the affected identities through one stop/invalidate/rebuild path, retaining the cold-read and activation guards without changing the asset budget. Verification: activation regressions (59 assertions), prompt lifecycle tests and TypeScript checks pass; the complete production build will verify the unchanged bundle gate. * fix(history): coalesce startup watches before resuming discovery * fix(history): keep startup locale sync out of discovery * feat(history): add shared on-demand native full-text search * feat(history): add bounded discovery source diagnostics * desktop: ignore noisy root watcher writes * desktop: drain native watch backlog before initial history discovery * refactor(history): remove retired startup migration workers * fix(history): coalesce watcher overflow before journal writes * fix(history): replace invalid scans at slice boundaries Problem: large metadata discovery finished obsolete generations before replaying queued root changes, while unchanged rows rewrote topic projections. Root cause: invalidations were consumed only at EOF and discovery always published full upserts. Fix: supersede invalid iterators between slices with bounded coalescing; preserve incomplete journals and recheck unchanged rows transactionally before updating scan presence only. Verification: sessioncatalog/historywork race suites, deterministic restart and projection regressions, Desktop watcher/startup race tests, and sessioncatalog lint pass. Native scale acceptance remains pending. * perf(history): project registry metadata in bounded slices Problem: registry refresh held the catalog writer for the entire registry and rewrote unchanged metadata. Root cause: metadata-only discovery reused the atomic full-projection refresh and a whole-task timeout restarted large observations. Fix: serialize cancellable observations, share background slice admission, compare persisted rows, defer paged retirement until input completes, and bound each writer transaction. Preserve atomic refresh for other catalog modes. Verification: root Go suite, catalog/historywork race suites, Desktop catalog race coverage, frontend typecheck/transcript and loading/sidebar/navigation/scroll browser gates pass. Retain the preceding history.24 package's 90-run native scale evidence separately. * feat(history): resume interrupted checkpoint index preparation * test(history): verify checkpoint preparation across process exit * feat(history): resume streamed legacy display index imports * refactor(history): separate maintenance admission and publication steps * feat(history): resume native event preparation across process exit * fix(history): preserve cold reading windows during passive readiness refresh * test(history): record production scale results and exercise native cache paging * perf(history): load cold refresh proof on demand * perf(history): defer session restoration policy with its reader proof * perf(history): keep restoration policy outside the eager module graph * feat(history): resume DAG preparation across all durable phases * fix(history): include protected windows in resident cache capacity * docs(history): document DAG resume and native cache findings * fix(history): release evicted read bindings without affecting replacements * test(history): qualify native cache resources and startup scale * docs(history): record history.29 cache qualification * test: align history migration fixtures with current schemas Keep the junction migration regression focused on the last pre-invalidation schema and derive the projection schema assertion from the migration ledger. Complete the history hydration Meta fixture so frontend test type checking covers the current contract. * fix: align startup bundle gate with measured producer output Problem: the history startup PR exceeded the raw frontend bundle gate by 275 bytes on the Linux producer and the migration regression comment exceeded repolint's essay budget. Root cause: the earlier budget ceiling was based on a pre-rebase producer measurement, while the test fixture note was four lines at a three-line position limit. Fix: set the raw initial gate to the smallest measured 2056.6 KiB ceiling, keep all compressed and chunk budgets unchanged, and condense the migration note without removing coverage. Verification: node scripts/check-bundle-budget.mjs; go run ./tools/repolint; focused sessioncatalog migration tests; git diff --check. * fix: format desktop catalog runtime Problem: the desktop producer gate rejected one unformatted composite literal in session catalog runtime code. Root cause: the latest history loading changes left the pinned-only field alignment outside gofmt output. Fix: apply the repository formatter without changing behavior. Verification: gofmt -l ., git diff --check, and go run ./tools/repolint. * fix: use passing producer bundle ceiling Problem: the Desktop producer still exceeded the raw startup bundle gate by 112 bytes after the previous measured adjustment. Root cause: the Linux CI build measured 2106070 bytes while the 2056.6 KiB ceiling allowed only 2105958 bytes. Fix: set the raw initial budget to the smallest 2056.8 KiB ceiling that contains the producer result; compressed, chunk, CSS, locale, and residency budgets remain unchanged. Verification: node scripts/check-bundle-budget.mjs, go run ./tools/repolint, gofmt -l ., and git diff --check. * fix: allow Windows history sources to be replaced during paging Problem: Windows pager fencing tests failed when a source or event log was replaced while a display index was being built.\n\nRoot cause: os.Open uses a Windows sharing mode that denies delete and rename, so the deterministic source-change interleaving failed before the pager could validate the new pathname identity.\n\nFix: add a platform-owned replaceable read helper. Windows opens sources with read, write, and delete sharing; Unix keeps the ordinary descriptor. All legacy pager builders, event-kind probing, and paged event/DAG reads use the shared helper.\n\nVerification: go test ./internal/agent -count=1; GOOS=windows GOARCH=amd64 go test -c ./internal/agent; go run ./tools/repolint; go test ./...; git diff --check. * fix: match Windows replaceable read handle flags Problem: the first shared-delete handle still left the DAG source replacement interleaving failing on Windows.\n\nRoot cause: the native source handle did not use the same reparse-aware backup semantics as the repository's proven replaceable regular-file reader.\n\nFix: align the fileops helper flags with the existing Windows repair reader while retaining read, write, and delete sharing.\n\nVerification: GOOS=windows GOARCH=amd64 go test -c ./internal/agent; focused pager tests pass on the host. * fix: grant Windows pager handles replace access Problem: atomic replacement of a DAG event log was still denied on Windows despite delete sharing.\n\nRoot cause: the replacement operation also needs delete access on the existing native source handle.\n\nFix: request DELETE together with GENERIC_READ while retaining read, write, and delete sharing.\n\nVerification: GOOS=windows GOARCH=amd64 go test -c ./internal/agent. * fix: make Windows source replacement regression portable Problem: the pager source-fencing regression used os.Rename(temp, existing) while the target was open, which Windows rejects; granting DELETE access then broke ordinary concurrent rewrites.\n\nRoot cause: the test encoded Unix replacement semantics that are unavailable for an open Windows target, while the production reader must remain compatible with normal writers.\n\nFix: keep the shared-delete read handle at read access and model replacement by moving the old pathname aside before installing the new file. The test still verifies a changed native identity is rejected before publication.\n\nVerification: focused pager source-change tests, GOOS=windows GOARCH=amd64 go test -c ./internal/agent, go run ./tools/repolint, and git diff --check. * fix: join startup cold history on runtime ready Problem A runtime-ready event could start ordinary session hydration while startup cold history was still in flight, issuing a duplicate history read for the same session. Root cause The ready reconciliation only shared the normal session-load owner. Cold startup reads were tracked separately, so the readiness path did not join that owner. Fix Join matching cold-history promises before starting ordinary hydration. After the certified window settles, install the live follower and continue ancillary hydration with the cached window. Keep the failure path on the existing reader and record the measured 163-byte startup bundle increase with the smallest producer-safe budget update. Verification - pnpm exec tsx src/__tests__/ready-event-meta-sync.test.tsx - pnpm typecheck - pnpm test:typecheck - pnpm lint:hooks - pnpm test:transcript - pnpm build - go run ./tools/repolint - git diff --check * Fix protected history windows and catalog context races Problem Navigation could install a transcript before the active tab was pinned, allowing the resident cache budget to evict the just-opened window. Catalog callbacks could also reread the mutable App context while the lifecycle test replaced it, producing a data race. Root cause Active-tab pinning was deferred to a post-paint effect, and the restored catalog watcher used the App context accessor instead of its owning worker context. Fix Publish the active pin at the dispatch boundary before the first history page commits. Let the catalog watcher pass its existing context into historical discovery while retaining the default accessor for synchronous callers. Verification - tab-switch-hydration: 101 passed - desktop TestConcurrentRebuildSessionCatalogCallersShareFailure with -race: 3 runs passed - desktop TestHistorySliceSourceField/cold_index_hit: 100 runs passed - pnpm typecheck and pnpm test:typecheck passed - pnpm lint:hooks and pnpm test:transcript passed - go run ./tools/repolint passed - git diff --check passed * Fix Windows history upgrade acceptance and test cleanup Problem: Windows CI rejected visible upgraded history and could not remove a test source directory. Root cause: the UI acceptance helper required an activation button even after lazy history had rendered, while the test closed its historical service after TempDir cleanup. Fix: accept a healthy visible transcript without activation and close the test app services before removing the source directory. Keep pending and recovery-state rejection covered. Verification: focused desktop Go test, go vet, repolint, golangci-lint, and diff checks passed. Windows PowerShell and installer checks remain for CI. * Align history CI checks with lazy legacy loading Problem: the Desktop CI matrix failed startup worker, cold index, and packaged upgrade checks after history loading changed. Root cause: those checks still assumed startup discovery took the old coordinator lock, equal-timestamp sidecars were trustworthy, and opening a legacy tab automatically migrated it without rewriting JSONL. The current shutdown path snapshots an active legacy tab. Fix: deterministically block the actual startup worker, establish sidecar ordering for the index-hit fixture, and verify legacy path, authored content, registry non-import, restart, and backups in the packaged fixture. Verification: focused desktop tests with repeated and race runs, upgradefixture tests, go vet, repolint, golangci-lint, and diff checks passed. Windows packaged acceptance remains the terminal CI gate. |
||
|
|
9f286c6ee6 |
Fix Windows junction path identity and upgrade recovery / 修复 Windows Junction 路径身份与升级恢复 (#10656)
* fix: resolve Windows junction identities without breaking upgrade locks Problem: Windows users whose existing data directory is a junction cannot start sessions after configuration locks begin requiring physical identity. Recovery also labels runtime-start failures as failed draft saves. Root cause: EvalSymlinks can leave a junction leaf unchanged and reject its children, splitting aliases or failing before the OS lock is acquired. Fix: resolve existing Windows paths through native metadata handles, retain legacy workspace lock spellings, isolate/rebuild derived catalog identities, and separate historical draft submission errors from persistence errors. Add a read-only probe and native regressions to the Windows PR selectors. Preserve current eager session creation and authoritative user data formats. Verification: owning Go suites, host/Windows golangci-lint, repolint, frontend production/test typechecks, draft ownership and recovery regressions, real Chromium recovery interactions, and Windows selector coverage passed. Native Windows execution and complete upgrade qualification remain CI/manual gates; cross-compilation does not establish those results. * fix: preserve native Windows error kinds and verify historical lock spelling Problem: native Windows CI rejected dangling-link and loop error kinds, and the legacy-lock fixture expected the unexpanded runner temporary path. The recovery UI also exceeded an almost exact startup-byte budget. Root cause: os.IsNotExist does not traverse arbitrary wrapping, native reparse exhaustion uses ERROR_CANT_RESOLVE_FILENAME, and EvalSymlinks expands 8.3 ancestors even when it preserves a junction leaf. Fix: use wrapped-error matching and the native reparse-loop code; derive the fixture's legacy key with the historical filesystem algorithm. Keep all rejection and lock assertions. Remove a duplicated browser invocation. Adjust only the raw startup ceiling to the measured producer size: same source/dependencies/commit stamping adds 506 B raw (0.024%) and 120 B gzip; Linux Electron measures 2103802 B, rounded to 2054.5 KiB. Verification: pathidentity, workspacelease and probe tests; host/Windows lint; repolint; bundle budget; same-toolchain base/candidate production builds. All compressed, chunk, CSS, locale and residency budgets remain unchanged. * test: exercise native junction identity across Windows volumes Problem: the reported regression crosses system and data volumes, while the initial regression fixtures only created same-volume junctions. Root cause: filesystem alias tests did not exercise drive-prefix changes. Fix: create a temporary junction from the system temp drive to RUNNER_TEMP and compare entry and missing-tail identities with the physical target. Require the two-volume fixture on GitHub Windows runners. Single-volume local machines still run the unconditional same-volume regression suite. Verification: Windows-target lint and cross-compilation; the native Windows PR lane is required to execute the cross-volume fixture. * Preserve credential ACL recovery and bind artifact consumers to producer Node Problem: native identity resolution blocked credential ACL repair before edit locking, and prepared frontend consumers selected different Node patch versions. Root cause: FILE_READ_ATTRIBUTES requires permissions that legacy read-deny ACLs remove; floating Node majors resolve independently on hosted runners. Fix: use zero-access metadata handles, keep read denials intact until credential recovery, cover linked and unrecorded ACL cases, and propagate the producer Node version to every artifact consumer without weakening verification. Verification: pathidentity/config tests, Windows cross-compilation and golangci-lint, 40 workflow/artifact/selector tests, repolint and diff checks passed. Native Windows execution remains a CI gate. * Bind memory screening consumers to their build Node version Problem: a memory shard rejected the prepared build before running because its floating Node major selected a different patch version. Root cause: the independent app-memory workflow owns a second producer-consumer identity contract. Fix: propagate its producer runtime to shards and aggregation, preserving exact identity validation. Add a workflow regression for both consumers. Verification: 51 workflow and memory protocol tests, repolint, and diff checks passed. * Open Windows identity handles with exact native access rights Problem: CreateFile with zero desired access still fails against legacy credential DENY RX ACLs. Root cause: Win32 adds SYNCHRONIZE and FILE_READ_ATTRIBUTES, as already documented by the credential ACL recovery owner. Fix: open a zero-access NtCreateFile handle that follows reparse points, use the native namespace for long/UNC paths, and translate NTSTATUS failures back to Win32 codes for existing error classification. Verification: Windows compilation and golangci-lint, repolint, and diff checks passed. Existing save/load/revision tests require identity to resolve while content reads remain denied; native CI verifies this contract. * Request valid metadata access for native Windows identity queries Problem: NtCreateFile rejects a zero access mask, breaking ordinary Windows identity queries. Fix: request FILE_READ_ATTRIBUTES without implicit synchronization; when an ACL denies that right, try READ_CONTROL, which file owners retain for security metadata. Neither handle grants content access or mutates the ACL. Preserve NTSTATUS classification and all denial assertions. Verification: Windows compilation and golangci-lint, repolint and diff checks passed; Windows execution is required before qualification. |
||
|
|
2fb74e47d4 |
Clarify MiMo API and Token Plan access and upgrade models / 明确 MiMo 接入方式并升级模型 (#10655)
Problem: region-first filtering hid MiMo API versus Token Plan access and new connections retained the V2.5 model catalog. Root cause: product selection depended on region, MiMo lacked a one-time model migration, and floating CI Node versions made frontend artifact producers and consumers disagree. Fix: select access type before cluster and protocol, clarify independent credentials and usage, default new connections to V2.6, and atomically append models to eligible official connections without changing existing defaults or custom data. Preserve previous-writer TOML compatibility, share repeated frontend defaults within existing budgets, and pin artifact workflows to one exact Node runtime. Verification: root and Desktop suites, previous-reader migration round trips, frontend and browser regressions, unchanged production bundle budgets, cross-platform CI, race checks, Windows packaging, memory screening, CodeQL, and release-control contracts passed. All review threads were clear before merge. |
||
|
|
4fad310c93 |
Restore eager session creation with upgrade-safe input recovery / 回滚新建会话流程并保留升级数据 (#10650)
* fix(desktop): restore eager session creation with durable input recovery Problem: deferred draft creation changed explicit New Session behavior and left upgraded users dependent on draft-only input and recovery paths. Root cause: manual creation, composer persistence, and legacy draft submission shared identities and lifecycle assumptions that no longer match formal sessions. Fix: create formal sessions through durable idempotent operations, persist unsent input separately with revision and receipt fencing, retain legacy drafts as recovery-only data, and retire automatic empty-session cleanup. Repair model, attachment, Goal, and submission wiring while preserving current canonical storage and history formats. Verification: tagged 1.38.3 through 1.38.11 compatibility fixtures; all 11 desktop test partitions; focused race, composer and lifecycle regressions; source and test type checks; repository lint; signed macOS arm64 packaging and isolated restart/image/lifecycle smoke tests. Native Windows and Linux application validation remains pending. * fix: repair rollback CI gates and confine session deletion Problem: rollback CI failed lint and the frontend producer gate, while CodeQL reported existing deletion paths outside a rooted filesystem boundary. Root cause: reserved initialization ignored binding release errors, historical tests used outdated lint syntax, and the raw budget sat below the measured main-v2 baseline. Deletion rebuilt absolute trash and cache paths after validating the session identity. Fix: join release errors, modernize fixture loops without removing assertions, use rooted rename and removal for session deletion, and round only the initial raw bundle gate from 2053.9 to 2054.1 KiB. Same-toolchain main-v2 measures 2103208 bytes versus 2103296 bytes for this branch, an 88-byte increase. Verification: root and Desktop golangci-lint 2.12.2, Desktop Windows lint, reserved initialization and historical fixture regressions, deletion confinement tests, production bundle guards, and repolint passed. No compatibility coverage was removed. * test: preserve historical fixture bytes across Windows checkout Problem: all Windows history partitions failed provenance hashes, and frontend CI still asserted the retired draft-on-new behavior. Root cause: Git autocrlf rewrote JSONL and metadata bytes emitted by tagged writers. A source-regex chrome assertion duplicated navigation coverage while encoding the superseded product contract. Fix: disable text conversion only for the two historical fixture trees, retaining original hashes and offsets. Remove the obsolete regex assertion; formal-creation, legacy recovery, remote navigation, and intent-fencing behavior tests remain. Verification: a core.autocrlf=true checkout preserved all 264 tagged historical file hashes, and app-chrome tests passed. The complete frontend CI runner is running locally; replacement Windows CI will verify the native migration matrix. * test: align recovery assertions and native observation lifecycle Problem: frontend tests still expected active empty-session cleanup and hidden content-free drafts; the native browser smoke timed out reading its initial hidden macOS page. Root cause: those assertions retained the retired draft-first product contract. The native fixture bypassed the observation lease that production host calls acquire before executing DOM scripts. Fix: assert that cleanup cannot be resumed while manual restore remains available, keep settings-only legacy drafts discoverable with workspace isolation, and acquire/release the existing observation lease for the initial hidden-page snapshot and fixture setup. Verification: all 505 frontend CI commands passed across the unchanged completed set and remaining set; Electron typecheck and repolint passed. Native macOS browser smoke passed 100 lifecycle cycles with no remaining pages. Timeouts, product code, and coverage counts are unchanged. |
||
|
|
f26283afb2 |
Add native graphics fault recovery for Desktop (#10644)
Problem: Graphics failures can leave the Desktop interface unusable before users can reach hardware acceleration settings. Root cause: Renderer reloads had no bounded recovery policy, and explicit GPU failure evidence was not retained across launches. Fix: Classify GPU and renderer failures, bound reloads, provide native compatibility restart with draft-loss confirmation, and retain versioned local fault records. Keep acceleration enabled by default and let users choose temporary or persistent software rendering. Preserve newer faults during dialog acknowledgement and rearm prompts after healthy recovery. Verification: Electron unit tests, deterministic recovery and lifecycle regressions, typechecks, shell build, repository lint, CI workflow tests, and real macOS Electron recovery smoke tests passed. Windows recovery smoke coverage is wired into CI; real GPU driver failure reproduction remains unavailable. |
||
|
|
a785237e60 |
fix(transcript): keep the user row ahead of its live output / 修复用户消息被本轮输出插到前面 (#10639)
* fix(transcript): keep a user row ahead of its live output Problem: Streamed reasoning, answer text, and tool calls could render above the user message that started the turn. Later segments of the same turn then continued below the bubble. Root cause: acceptBusiness copied a business record into the follower change before filling an empty TurnID. Export and reload read the buffer, which had the turn identity; the live change did not. Desktop attaches already-mounted output to that turn only when the durable user row carries the same turnId, so the output stayed on the previous formal anchor or ahead of the prompt. Fix: Publish the assigned TurnID with the change record. Place live rows after the submission anchor captured at send time when that user row has no turnId, and move same-turn formal output back behind its user. Keep a resident turnId when a projection update omits it, and stamp the active turn onto a live answer that arrived before the identity did. Verification: go test ./internal/transcript/ -count=1 -run TestAcceptBusinessPublishesTheTurnIDItAssigns desktop/frontend src/__tests__/submission-handoff.test.ts * Preserve sampling order and the offscreen reader during transcript settlement Problem: assigning turn identities could move later live answers ahead of earlier samples and tools. Batched deltas could also clear the newer-history flag while the reader remained on an older page. Root cause: the user anchor overrode every existing formal predecessor, and predecessor lookup ignored newly settled rows while retaining reclaimed ones. The offscreen batch path leaked a temporary history flag into visible state. Fix: preserve surviving and newly formal predecessors within the owning user segment, and keep the reader's history flag while updating its offscreen tail. Document both invariants and add six deterministic regressions. Five fail on the previous PR head; all pass after the fix, including the ordinary-event sibling of the batched stream path. The production raw bundle measures 2102574 B versus 2100795 B on the base (+1779 B, 0.085%). Set only its existing ratchet to 2053.5 KiB, preserving the established 186 B Linux producer difference. Other budgets are unchanged. Verification: transcript Go package; frontend test:typecheck; submission handoff and output-order suites; test:transcript; submission and transcript Chromium suites; production build and check:bundle; repolint; diff check. |
||
|
|
0d7a103fdd |
Upgrade browser runtime and session diagnostics / 升级内置浏览器运行时与会话诊断 (#10635)
* feat(browser): add task-bound runtime and scoped diagnostic evidence Problem: production page injection and invalid captures prevent reliable browser inspection, while session exports lack browser troubleshooting evidence. Root cause: host-coupled page scripts, missing render ownership and mixed viewport/input facts allow stale observations and callbacks across lifecycle changes. Fix: bundle isolated semantic scripts; coordinate rendering, cancellation and identity; validate images; add optional query, wait, responsive, picker, diagnostics, recording and recovery capabilities; export bounded session-owned evidence without changing standing tool schemas. Verification: 279 Electron tests and typecheck; prior full Go and native/package evidence documented in the acceptance report. Native OOPIF CDP stalls and expanded scaled-input routing remain explicit merge blockers; no retry or deadline workaround hides them. * fix(browser): preserve cancelled capability write error chains Problem: capability transport failures lost their underlying cancellation identity, and generated Desktop inventory locations were stale. Root cause: the unknown-outcome wrapper formatted the original error as text instead of wrapping it; browser diagnostic additions shifted inventory source positions. Fix: wrap both the unknown-outcome sentinel and original transport error, and regenerate the existing Desktop inventory. Verification: a server-admission cancellation regression failed before the fix and now passes for pointer, viewport and recording writes; browser tests, root/Desktop golangci-lint and inventory tests pass. * fix(browser): route pointer input through the owning renderer Problem: zoom and native surface changes could drop clicks or send their press and release to different frames. Root cause: root CDP input used asynchronous cross-process hit-test data from the previous surface; DOM geometry alone did not establish input routing readiness. Fix: resolve the current frame in isolated worlds, use direct native input for in-process targets and the owning CDP session for OOPIFs, preserve coordinate scaling, and fence preparation against cancellation and identity changes. Strengthen diagnostic export URL assertions and align the native diagnostic fixture with host observation leases. Verification: 284 Electron tests, TypeScript and production build pass; macOS production fixture passes three scale transitions and 100 mixed-frame lifecycle cycles. Windows ARM64 and Linux ARM64/Xvfb pass the renderer-owned input matrix. Historical CDP stalls remain a separate merge gate. * fix(browser): own iframe sessions for the debugger connection lifetime Problem: adjacent frame observations and actions repeatedly tore down and initialized the same OOPIF protocol domains, exposing world-creation and initialization stalls. Root cause: target attachment ownership was tied to individual operations instead of the page debugger generation; cancellation and shared initialization therefore lacked a common owner. Fix: pool target sessions per connection, retain operation-scoped contexts and object groups, retire failed sessions after their last borrower, and clean late attachments without affecting replacement sessions. Preserve the existing one-second protocol budget and unknown write outcomes. Verification: 287 Electron tests and TypeScript pass; deterministic shared-waiter, cancellation, late-attach and cleanup-order regressions pass. Native macOS validates one attachment across snapshot/ref/input/upload, a fresh attachment after idle reconnect, the scale matrix and 100 mixed-frame lifecycle cycles. Windows ARM64 and Linux ARM64/Xvfb native fixtures pass. * fix(browser): preserve renderer-local pointer coordinates Problem: Scaled iframe clicks could hit a broad button away from its observed centre, and nested same-process descendants used the wrong coordinate origin. Root cause: Child CDP sessions accept renderer-local CSS coordinates, but input reapplied outer Fit scaling and treated the innermost frame as the renderer owner. Fix: Carry the session-owning frame through the operation runtime and dispatch its unscaled traversal point. Native fixtures assert exact received coordinates for same-process descendants inside OOPIFs. Verification: 288 Electron tests, typecheck, macOS 100-cycle native fixture, Windows ARM64 and Linux ARM64 native matrices, and the complete macOS packaged browser toolchain passed. * fix(browser): publish recording completion after cleanup Problem: macOS native CI could reject a new recording immediately after the previous job reported completed. Root cause: WebM validation published terminal status before asynchronous file cleanup released the global recording slot. Fix: Keep cleanup in finalizing and publish the terminal result and artifact at the same synchronous boundary that releases admission. Apply the same invariant to cancellation and interruption. Verification: A deterministic cleanup barrier fails on the prior implementation and passes for completed and cancelled jobs. All 289 Electron tests and TypeScript checks pass; native and packaged validation continue on this head. * test(cli): join controller teardown before removing fixtures Problem: Windows CI failed removing a clear-command fixture because its recovery database was still open. Root cause: The CLI controller owner helper only requested asynchronous Close; a Running poll did not join final persistence and store release. Fix: Wait for the existing Closed signal in the shared test owner, matching control tests, and remove the redundant timed cancellation poll. Verification: Complete internal/cli tests, focused race tests, and Windows ARM64 clear/cancel native test cases passed. Product behavior and test deadlines are unchanged. |
||
|
|
548b541ef0 |
Fix desktop history lifecycle and shutdown recovery / 修复桌面历史会话生命周期与关闭恢复 (#10634)
* fix(desktop): decouple history navigation from runtime startup Problem: Opening a saved canonical conversation can remain unresponsive even when its persisted history is independently readable. Root cause: Canonical topic activation synchronously resumed the execution controller before returning the identity needed by frontend history readers. Deferred surface pruning could also hold the navigation lock while waiting for runtime mutation. Rapid return clicks could reuse an already-cancelled startup that would never publish a controller. Fix: Validate canonical membership and publish its identity through the shared asynchronous topic-open lifecycle. Preserve history access on startup or writer-ownership failure. Do not reuse cancelled startup shells. Acquire async prune locks without waiting for runtime work under the navigation lock, then revalidate the activation generation before removing surfaces. Keep synchronous OpenSession and legacy prune callers on their contracts. Verification: Deterministic regressions reproduce blocked startup, blocked prior prune, and cancelled-startup reselection. Focused desktop race tests, frontend topic activation/navigation tests, transcript tests and Chromium transcript checks pass. Root make lint, repolint and desktop golangci-lint pass. Windows installed-package reproduction remains unverified. * fix(desktop): read committed runtime states for history lists History lists collect status from all open and detached controllers. A fresh sample waiting on an unrelated controller could therefore block another project's history query before its catalog deadline was established. Publish an owned immutable snapshot at the controller's semantic commit boundary and prefer that observation interface in Desktop. Reuse an existing snapshot when building project nodes instead of sampling twice. Preserve the fresh-sampling interface for existing callers and embedders. Cover blocked producers, existing history identity, indexed and fallback lists, open and detached sessions, cancellation, completion, and ownership of mutable observations. The same list regression fails with the old reader. Validation: root go test ./...; full control race suite; 129 Desktop targeted tests/subtests under race; final four-case history regression and published state tests; three frontend lifecycle checks; both module linters; repolint. * fix(desktop): make history lifecycle cancellation and shutdown retryable Problem: History project lists could be blocked by live runtime sampling, Stop could wait behind the same observation path, and shutdown could report success while a session runtime remained active. Root cause: History loading mixed authoritative session identity with mutable runtime observation, while cancellation and service shutdown treated observation or cleanup errors as part of a best-effort path. The frontend also lacked request-stage evidence for distinguishing queueing, backend latency, and stale-result disposal. Fix: Use published runtime snapshots for history and Stop metadata, signal cancellation before observation, preserve failed shutdown checkpoints for retry, and record bounded privacy-safe history-list request stages. Add deterministic cancellation and shutdown regressions plus bilingual runtime-state documentation. Verification: - go test ./... - go test -race . -run 'Test(Shutdown|ConnectionLoss|DetachedShutdown|ReleasedBinding|CancelSession)' -count=1 - go test -race ./internal/control -run 'Test(Cancel|Maintenance|StaleCancelling|PublishedRuntime)' -count=1 - pnpm typecheck, pnpm lint:hooks, pnpm build - go run ./tools/repolint * fix(desktop): reconcile history diagnostics with snapshot pagination Problem: Latest main-v2 snapshot pagination paths had lost request completion diagnostics during merge reconciliation, and the legacy shutdown wrapper did not expose cleanup failures.\n\nRoot cause: The conflict resolution selected the newer snapshot reader without restoring the lifecycle diagnostics and result-returning shutdown helper.\n\nFix: Restore stale/completed project-tree request events for every accepted or discarded page, including preserved complete pages, and route the compatibility shutdown wrapper through the error-aware result path.\n\nVerification: go test -race ./desktop -run 'Test(Shutdown|ConnectionLoss|DetachedShutdown|ReleasedBinding|CancelSession)' -count=1; cd desktop/frontend && pnpm typecheck && pnpm lint:hooks && pnpm build; go run ./tools/repolint * fix(ci): remove flaky recovery observer deadline Problem: PR CI failed in golangci-lint on wrapped runtime-binding errors and Windows intermittently timed out while observing a valid recovery publication.\n\nRoot cause: The shutdown classifier used direct equality instead of errors.Is, while the watchdog regression used a five-second runtime observer deadline that also measured platform-dependent durable I/O latency.\n\nFix: Preserve wrapped released-binding classification with errors.Is and wait for the watchdog's durable terminal event before asserting recovery state, removing the unrelated observer timing budget while retaining all commit, recovery, late-worker, and admission assertions.\n\nVerification: go test -race ./internal/control -run 'Test(TerminationCommitWatchdogRejectsLateWorker|CancellationGraceSealsUncooperativeTurnAndPreservesQueue|RecoverySealDropsLateTranscriptOutput|CancelSessionAcknowledgesBeforeStatusBarrier)' -count=5 -timeout=2m; cd desktop && go test -race . -run 'Test(ShutdownServiceFailurePreservesEvidenceAndRetries|ReleasedBindingWarningDoesNotHideOtherShutdownErrors)' -count=1; golangci-lint run --timeout=5m ./...; go run ./tools/repolint * ci(desktop): retry transient Electron binary fetches Problem: The macOS desktop job failed while Electron's lazy binary download received an HTTP 504 from the GitHub release endpoint, after all product tests had passed.\n\nRoot cause: The CI step treated a transient dependency transport failure as a deterministic Electron diagnostics failure.\n\nFix: Retry only Electron binary installation errors caused by HTTP 429/5xx responses, up to three bounded attempts; all other performance-smoke failures still exit immediately and remain fatal.\n\nVerification: Reproduced the exact 504 failure from the macOS job log, validated the workflow diff with git diff --check, and preserved the three-minute step budget and product assertions. |
||
|
|
b8e2523f09 | fix(desktop): support safe removal of new conversations (#10631) | ||
|
|
1471ef39d9 |
Merge origin/main-v2 into fix/read-snapshot-pagination
Regenerate the combined Desktop host contract and migration inventory to preserve both snapshot pagination and queued guidance operations. Verified frontend build and bundle limits, snapshot and inbox lifecycle tests, Desktop contract tests, repolint, and inventory freshness. |
||
|
|
45374931bd |
Edit and reorder queued guidance / 编辑并调整待处理消息顺序 (#10626)
* feat(desktop): edit and reorder queued guidance Problem: queued guidance could not be safely edited or reordered, and edit errors could strand the user draft. Root cause: the inbox had no versioned edit/move contract or atomic dispatch check; the composer also mixed current-turn guidance with follow-up queue submission. Fix: add versioned store/controller and local/remote queue commands, an inline editor with draft recovery and sorting, explicit current-turn guidance, pause controls, and lifecycle fences for delayed replies. Verification: store/controller/Desktop focused tests, race interleavings, frontend typecheck and queue integration tests, production build, and browser preview interaction checks passed locally. * Fix queue preview test setup and desktop inventory Problem: Initial PR checks failed in frontend Node tests and desktop inventory validation. Root cause: The browser preview assumed Vite env in Node, lazy asset hooks registered after tsx, legacy composer fixtures lacked authoritative queue state, and the generated desktop command inventory was stale. Fix: Guard preview env, preserve optional mock queue capabilities, preload CSS/SVG hooks before tsx, align composer fixtures with next-turn queue semantics, and regenerate inventory. Verification: 433/434 discovered suites passed before the final remote presentation assertion was aligned; the remaining suite passed in isolation. Typecheck, build, focused Go tests, and inventory validation passed. * Keep composer fixtures within repository size budgets Compact the new queue mock state in the existing composer tests without removing assertions. The published head exceeded repolint's existing per-file budget; go run ./tools/repolint and both affected test suites now pass. * Avoid synthetic Inbox bindings in browser mocks Inbox capabilities are supplied by the selected host or preview fixture. Skip the attachment fallback for absent Inbox methods, which also keeps the initial raw bundle within the existing budget. Verified with pnpm build and the composer queue fixture. |
||
|
|
8a6c71437a |
Merge origin/main-v2 into fix/read-snapshot-pagination
Resolve the frontend bundle ratchet from the measured combined build and regenerate the Desktop host contract from the merged Go API. Preserve both snapshot pagination and the latest compaction receipt/provider catalog changes. |