100 Commits
Author SHA1 Message Date
Sivan 787886669f docs(ci): clarify carried proof limits and share Windows inventory checks 2026-10-28 11:04:54 +08:00
SivanCola 37bcff7fb9 test(goal): cover whitespace validation without schema changes / 补充目标参数空白校验测试 (#11240)
* fix(goal): align Studio schema with report validation

Problem: schema-valid goal reports can fail execution for missing reasons or
blank verification commands.

Root cause: status-dependent requirements exist only in prose and execution
checks, not in the provider-visible JSON Schema.

Fix: encode conditional reason requirements and nonblank verification entries,
matching the executor's Unicode whitespace handling without relaxing Goal
authority or delivery validation. Update bilingual guidance and schema fixtures.

Verification: schema/executor agreement regressions and golden guards pass,
along with go vet ./..., make lint, and make check. The static schema changes
once on upgrade; system prompt bytes and within-version stability are preserved.

* fix(goal): keep report schema typed and within prefix budgets

The conditional status schemas omitted explicit types, which failed the
built-in provider compatibility gate. The added constraints also exceeded
both schema token budgets by 78 tokens.

Declare string enum and object branch types and trim descriptions whose
semantics are already explained by the tool. Keep every argument constraint
and the original budget ceilings, and refresh the serialized golden files.

Verified update-goal schema/execution tests, enum/type and schema-budget
guards, provider golden guards, go vet ./..., make lint, and make check.

* test(goal): keep whitespace validation coverage without schema changes

Problem: the schema hardening introduced provider compatibility risk through
a top-level anyOf and a new pattern constraint, and reduced model guidance
to fit the prefix budget.

Root cause: matching a general-purpose JSON Schema validator to execution
does not establish acceptance by model providers.

Fix: restore the original schema, descriptions, documentation, and golden
fixtures. Retain focused executor tests for blank reasons and verification
entries, optional completion fields, field diagnostics, and no report on
rejection. Existing error messages already identify the invalid fields.

Verification: update-goal tests, go vet ./..., make lint, and make check pass.
The owning suites retain the baseline ripgrep failure addressed by #11244.
2026-09-29 01:10:18 -07:00
SivanCola 9a9323f3be fix(studio): restore window on macOS Dock activation (#11221)
Problem: Clicking the Dock icon leaves a Studio window hidden after closing to tray.
Root cause: macOS activates the existing process without emitting second-instance, and the shell had no activate listener.
Fix: Route activation through the shared window reveal path and ignore reveal requests during shutdown. Extend native smoke coverage across activation, second-instance, and tray entry points.
Verification: macOS native smoke, Electron unit tests, frontend build, host build, go vet, and repository lint passed.
2026-09-29 12:03:42 +08:00
SivanCola 8c446adaa7 fix(desktop): stabilize native transcript scrollbar dragging / 稳定原生对话滚动条拖拽 (#8586)
* fix(desktop): stabilize native transcript thumb dragging

Problem:
Dragging the workbench native scrollbar thumb across long variable-height transcripts could make the scroll range change under the pointer and jump between distant turns.

Root cause:
React Virtuoso replaced estimated row heights with live measurements while the browser was mapping the native thumb against scrollHeight. Wheel scrolling and thumb dragging shared the same measurement path.

Fix:
Detect primary-button gestures in the real native scrollbar gutter, freeze mounted rows and Virtuoso size accounting only for that gesture, and resume real measurement on pointer release, cancellation, or window blur. Ordinary wheel, trackpad, middle-button, and creation-mode custom scrollbar paths remain unchanged.

Verification:
- pnpm test:transcript
- pnpm build
- Real Chromium transcript scroll stability browser gate

* Stabilize transcript scroll regression gate

Problem: the browser benchmark sampled wheel position before Chromium finished applying the gesture, and pinned tail growth could race Virtuoso's at-bottom callback ordering.\n\nRoot cause: the gate had no render-frame settle condition, while the hook treated every transient atBottom=false notification as user intent even when only the measured scroll range had grown.\n\nFix: wait for real wheel movement to settle, make tail ownership setup deterministic, preserve pinned intent only for layout growth that does not move upward, and keep the shared four-pixel threshold unchanged for ordinary scrolling.\n\nVerification: pnpm test:transcript; pnpm build; transcript scroll stability benchmark passed eight consecutive runs in system Chrome.
2026-08-12 18:56:14 +08:00
SivanCola 9ee45baf70 refactor(desktop): move transcript scrolling to React Virtuoso / 使用 React Virtuoso 接管对话滚动
Replace the transcript's custom measurement and anchor stack with react-virtuoso, removing ~1400 lines of competing state machines that caused multi-screen jump bugs during async row growth.

Changes:
- React Virtuoso exclusively owns virtual measurement and anchor compensation
- Preserve history-prepend position with stable keys and firstItemIndex
- Keep streaming tail-follow, explicit navigation, nested scrolling behind a small Reasonix intent adapter
- Freeze text-selection endpoints before virtual rows are recycled
- Remove superseded scroll manager, height cache, measurement invalidation
- Add deterministic unit and real-Chromium gates for dynamic-height growth, rapid bidirectional scrolling, physical tail pinning, and cross-page selection
- Fix non-wheel upward scroll release during bottomRequest window
- Widen index reconcile search window from 64 to 128 keys with no-overlap fallback

Bundle budget: initial JS gzip grows 16.9 KiB (4.2%) from 402 to 418.9 KiB. This exceptional overrun is locally attributable (entire growth is react-virtuoso) and trades custom scroll arbitration for a maintained library. New gates retain 1% headroom: 423.5 KiB gzip / 2341 KiB raw.

Verification: pnpm test (158 suites pass), pnpm test:transcript-browser (real Chromium gates pass), CI 23/23 green.

Supersedes #8429
Fixes the root cause of multi-screen viewport jumps during streaming

Documentation-impact: none
Cache-impact: none
2026-08-12 15:32:20 +08:00
SivanCola 3dc808ede6 fix(session): v1.24.2 session write authority and recovery root fix / 根治会话写权限与 recovery 误判 (#8457)
* fix(session): bind generation write authority and stop false diverged

Problem:
- Mid-turn autosaves classified same-revision reshapes as diverged and
  forked recovery branches every ~30s (#8294).
- Process-level "I hold a lease" ownership could authorize stale
  controllers after rebind (ABA).
- Empty/missing .jsonl checkpoints with a valid WAL could still enter
  conflict paths instead of healing in place.

Root cause:
- checkSnapshotWrite required digest ownership for same-revision rewrites
  and had no unforgeable write permit bound to controller generation.
- Release did not wait for in-flight authority-guarded saves.

Fix:
- Introduce SessionWriteAuthority issued by SessionLease with generation
  and owner binding; Release waits for active saves.
- Controllers, desktop tabs, CLI, serve, and bot bind authority before
  Ready/autosave; missing/stale authority returns typed errors and never
  forks recovery.
- Owned same-revision rewrite uses authority or digest ancestry; 0-byte
  checkpoints heal from the path's own WAL under the save locks.
- Prefer SaveRewrite for same-revision diverged before recovery fork.

Verification:
- go test ./internal/agent/ -run 'WriteAuthority|SaveSnapshot|SaveConflict|FixedWriter|ZeroByte|Authority'
- go test -race ./internal/agent/ ./internal/control/ focused subsets
- go vet ./internal/agent/ ./internal/control/

Cache-impact: none - no prompt, tool schema, or provider request changes
Documentation-impact: none - internal session save/authority contracts

Fixes #8294
Refs #8342 #7982

* fix(session): bound recovery paths and rebind projection lineage

Problem:
- Depth-cap and continuous conflict ticks created digest-keyed recovery
  files without bound (#8342).
- Projection sidecars dropped across upgrade/model switch (#8390) and
  recovery forks (#8400), snapping the model view to full canonical
  history and re-triggering expensive compaction (#8393).

Root cause:
- Isolated recovery paths were content-digest keyed.
- LoadProjectionSidecar treated lineage key mismatch as total invalidation.
- Recovery forks did not inherit a still-valid parent projection.

Fix:
- One fixed recovery file per {lineage, writer}; in-place rewrite with
  compact event log and preserved in-flight turn markers.
- Split projectionContentValid from lineage gate; rebind matching content
  across key changes and fail closed on prefix mismatch.
- Inherit valid parent projection onto recovery forks; load sidecar on
  commitRecoveredSession.
- Dedup conflict diagnostic log entries by path/revision/outcome.

Verification:
- go test ./internal/agent/ -run 'LoadProjection|SaveRecoveryBranchInherits|SaveRecoveryBranchSkips|SaveConflict'
- go test ./internal/control/ -run 'Snapshot|Recovery|Conflict'

Cache-impact: none - projection body and provider-visible prefix unchanged;
  rebind only rewrites lineage key; removed full-canonical fallback was a
  cache break by construction.
Cache-guard: go test ./internal/agent/ -run 'ProjectionValid|LoadProjectionSidecar|CoveredPrefixHash'
Documentation-impact: none - internal projection/recovery contracts

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

Fixes #8342
Fixes #8111
Fixes #8393
Refs #8390 #8400 #8444 #8430

* fix(catalog): rebuild v3 lineage and reclaim covered recovery copies

Problem:
- 1.24.0/1.24.1 users accumulated covered recovery copies; v1/v2 catalogs
  could be polluted and still listed near-duplicates.
- Open paths could keep pointing at covered ancestors when a unique
  adopted leaf already covers the group.
- Periodic GC waited a full day before clearing upgrade storms.

Root cause:
- Disposable catalog lacked recovery lineage roles and used v2.sqlite
  shared with polluted 1.24.1 data.
- No content-based group classification (covered/adopted/diverged).
- Startup GC used the same long grace as steady-state.

Fix:
- Independent session-catalog/v3.sqlite (v1/v2 left byte-unchanged).
- recovery_group_id / recovery_role / recovery_canonical derived from
  ParentID and content coverage, never filename alone.
- Optional SessionMeta fields for Wails; old clients ignore them.
- Retarget Resume to a unique adopted canonical leaf before Controller
  rebind/authority bind.
- 15-minute startup grace + 20-minute follow-up, then 6h period; only
  covered_copy moves to recoverable .trash after re-checks.

Verification:
- go test ./internal/sessioncatalog/
- go test ./desktop -run 'RecoveryGC|SessionMeta|RecoveryCopy'

Cache-impact: none - catalog is a disposable projection of authoritative
  session files; provider-visible prompts unchanged
Documentation-impact: none - catalog stays internal; release notes follow

Refs #8294 #8425

* fix(desktop): surface diverged recovery branches and v1.24.2 notes

Problem:
- Topics with multiple independent recovery leaves looked like ordinary
  multi-session topics without an explicit choice prompt.
- Release notes needed a clear split between v1.24.1 hygiene and the
  v1.24.2 root-cause fix.

Fix:
- Project tree marks topics with multiple diverged recovery branches
  (non-destructive; no auto-merge/delete).
- Frontend SessionMeta carries optional recoveryGroupId/role/canonical.
- CHANGELOG documents authority/lineage/GC behavior and distinguishes
  v1.24.1 (hide/reclaim copies + Windows flash) from v1.24.2 (stop the
  source, bound real conflicts, repair existing directories).

Verification:
- go test ./desktop -run 'SessionCatalog|Recovery'
- go test ./internal/sessioncatalog/ -run 'Lineage'

Cache-impact: none
Documentation-impact: updated - CHANGELOG Unreleased records v1.24.2
  recovery root-fix scope for operators and release drafting

* fix(acp): bind session write authority with transcript leases

Problem:
- ACP acquired session leases but did not issue a generation-bound write
  authority, leaving saves on the unbound path while other frontends
  required authority for ownership decisions.

Fix:
- After session/new, session/load resume, and recovery lease handoff,
  bind SessionWriteAuthority on the concrete controller.
- Test fakes without *control.Controller stay unbound.

Verification:
- go build ./internal/acp/
- go test ./internal/acp/ -run 'Lease|Recovery|Switch'

Cache-impact: none
Documentation-impact: none - ACP internal lease handoff only

* Fix session authority and recovery handoffs

Problem: stale controller generations could retain write access, recovery callbacks could publish a path without rebinding authority, and process-wide recovery filenames could overwrite independent transcripts.

Root cause: authority validity was not tied to the lease's live generation, frontend handoffs updated leases and controllers in different phases, and canonical recovery promotion lacked full-lineage content proof.

Fix: make authority admission fail closed, bind controller and lease changes atomically across CLI, serve, ACP, bot, and Desktop, isolate recovery lanes per live session, delay startup GC, preserve legacy recovery filename discovery, and promote catalog canonical leaves only after authoritative content coverage checks.

Verification: focused package suites, full Desktop tests, deterministic authority and recovery regressions, go vet, and race tests for agent, control, sessioncatalog, ACP, serve, CLI, and bot.

* Fix session authority handoff review findings

Problem:
The integrated recovery fix still left stale controller generations writable across several runtime handoffs, reused process-wide recovery lanes, and widened the repolint struct-state baseline.

Root cause:
Lease ownership, controller publication, recovery lineage classification, and catalog cleanup were not committed through one fail-closed authority path on every frontend.

Fix:
Bind generation-scoped write authority before publication across Desktop, ACP, Serve, CLI, and Bot; bound recovery files per live session; verify lineage from transcript content; preserve startup/GC compatibility; extract hotspot helpers and restore immutable agentConfig without changing the lint baseline.

Verification:
go test core affected packages; Desktop go test ./...; focused and surface race suites; Desktop recovery/catalog race; pnpm build; go run ./tools/repolint; git diff --check.

* Fix session recovery lint regressions

Remove two superseded helpers and replace the empty same-revision authority branch with its explicit SaveRewrite ownership contract.

Verification: golangci-lint v2.12.2 reports 0 issues; repolint is clean; agent, control, and CLI tests pass.

* Fix ACP recovery lease retirement

* Deliver the diverged-recovery prompt and correct catalog docs

Problem: the diverged-recovery choice prompt never reached the UI, and the
reference docs still named the previous catalog file.

- session_catalog_runtime.go set node.Status = "diverged_recovery", but
  both status whitelists (desktop/tabs.go and projectTreeTopic.ts) accept
  only six values, so the status was dropped and the advertised prompt never
  surfaced.
- The same block appended a hardcoded English "multiple recovery branches"
  label, bypassing the localization used elsewhere in the function and leaking
  English into every non-English locale.
- Four docs/* files still documented session-catalog/v2.sqlite as the live
  catalog, so anyone clearing a corrupt catalog would delete an orphaned file.
- The CHANGELOG described the 15-minute value as a post-startup wait; the code
  uses it as an idle threshold on two early sweeps before falling back to the
  24-hour threshold on the 6-hour ticker.

Root cause: the status value and its label were added at the producer without
registering the value with the shared whitelists or the locale dictionaries.

Fix: register diverged_recovery as a first-class topic status. Add the
topicStatusDivergedRecovery constant, admit it in both whitelists, and drop
the hardcoded label so the frontend renders it from the locale dictionaries
(en, zh, zh-TW). Live runtime states take priority in topicStatus() so a
running topic still reports streaming, and the marker does not block
archiving since it is informational and awaits a user decision. Point the four
docs at v3.sqlite, note that v1/v2 stay for coexistence, and restate the GC
grace accurately. Shorten the startRecoveryGC doc comment to satisfy the
repolint budget resynced by #8503.

Verification:
- go test ./internal/agent/ ./internal/control/ ./internal/sessioncatalog/ -race
- cd desktop && go test .
- npm run typecheck (desktop/frontend)
- make lint (golangci-lint 0 issues; repolint clean, baseline unchanged)
- New TestNormalizeTopicStatusKeepsDivergedRecovery fails without the
  whitelist change.

* desktop: bump raw bundle budget for diverged-recovery i18n

The trilingual projectTree.status.divergedRecovery labels and
ProjectTopicStatus union addition add 0.1 KiB raw JS+CSS. Ratchet the
budget from 2268.5 to 2268.7 KiB (0.2 KiB headroom). Gzip budgets stay
flat — the strings compress well and aren't on the startup path.
2026-08-12 14:18:55 +08:00
SivanCola 6382d24ee1 Fix v1.24.1 recovery catalog and Windows startup / 修复 v1.24.1 恢复会话目录与 Windows 启动闪窗 (#8425)
* fix(desktop): restore recovery-copy catalog and hide Windows git flash

Problem:
- Windows desktop cold-start flashed two git.exe consoles because workspace
  watch probes used raw exec.Command without HideWindow/CREATE_NO_WINDOW.
- Session catalog v1 lost RecoveryCopy, so the project tree flooded with
  redundant recovery "copies" and History could not group or safely clean them.

Root cause:
- workspace_watch gitMetadataDirsForWorkspace bypassed gitcmd.Command.
- Catalog schema/projection never stored recovery_copy; topic stats summed all
  recovery branches; sessionMetaFromCatalog dropped RecoveryCopy; background GC
  used unguarded DeleteSession.

Fix:
- Route both startup rev-parse probes through gitcmd.Command.
- Add SessionRecord.recoveryCopy + schema v4; open disposable v2.sqlite cache.
- Prove RecoveryCopy via agent.RecoveryBranchCoveredByParent at index time.
- Exclude idle recovery copies from tree/stats; keep open/pinned/adopted ones.
- Propagate RecoveryCopy into History; GC via DeleteRecoveryCopy with recheck.
- Focused regression tests for catalog, tree pagination, GC races, and Windows.

Verification:
- go test ./internal/sessioncatalog/ -count=1
- go test -race ./internal/sessioncatalog/ ./internal/agent/ -run Recovery
- desktop focused recovery/catalog/workspace tests (+ -race)
- go run ./tools/repolint

Cache-impact: none - no prompt, tool schema, or provider request changes
Documentation-impact: updated - session catalog cache path v1→v2 docs

* fix(desktop): make recovery cleanup atomic

Problem:
- Adopted recovery branches were added to ordinary topic turns instead of treated as alternate continuations.
- DeleteRecoveryCopy proved coverage before acquiring the parent and branch removal guards, leaving a logical race before the trash move.

Root cause:
- Catalog SQL only distinguished covered copies from all other rows.
- Desktop reused the generic delete path whose coverage check preceded its destructive lock boundary.

Fix:
- Restore the pre-catalog turn rule: max(sum(normal turns), max(adopted recovery turns)).
- Route recovery-copy cleanup through an atomic agent helper that locks the parent and branch, re-proves coverage, and publishes a recoverable trash entry.
- Preserve open/read-only recovery branches instead of detaching them during bulk cleanup.

Verification:
- go test ./...
- go test -race ./...
- desktop go test ./...
- desktop go test -race ./...
- go vet for root and desktop modules
- Windows amd64 desktop test cross-compile
- go run ./tools/repolint

Cache-impact: none - no prompt, tool schema, provider request, or compaction changes.
Documentation-impact: none - behavior completes the recovery catalog contract documented by the parent commit.
2026-08-11 17:48:10 +00:00
SivanCola 42c17ccb29 fix(catalog): make directory and permission tests cross-platform (#8408)
Problem: main-v2 push CI fails on the Windows leg in
projectiondb.TestOpenAppliesLedgerAndPrivatePermissions and
sessioncatalog.TestRebuildFailureKeepsExistingCatalog, introduced by
the sqlite catalogs integration and never executed on Windows because
the PR smoke suite omits these packages.

Root cause: os.ReadDir of a plain file returns ENOTDIR on POSIX but
yields the file itself on Windows (FindFirstFile semantics), so Rebuild
silently treated a file path as an empty directory. The projectiondb
permission assertion assumes POSIX 0600, which os.Chmod cannot express
on Windows (read-only bit only).

Fix: directorySignature stats the path first and rejects non-directories
on every platform; the permission assertion skips on Windows while
keeping the ledger assertions.

Verification: gofmt, go vet (darwin + GOOS=windows), go test -race on
both packages, repolint clean.
2026-08-11 22:44:17 +08:00
SivanCola e56d163636 fix(desktop): unify turn action hover labels / 统一消息操作按钮悬停标签 (#8387)
* fix(desktop): unify turn action hover labels

Problem:
Message turn actions used inconsistent label behavior: Copy collapsed to an icon while Fork, Compress, and Rewind always occupied their full labeled widths.

Root cause:
The three non-copy actions rendered permanent text and disclosure icons without the inline collapsed-label contract used by CopyButton.

Fix:
Wrap Fork, Compress, and Rewind labels in a shared collapsible container. Expand it on hover and keyboard focus, and keep it expanded while a menu is open or Fork is confirming. Preserve disabled and menu semantics.

Verification:
- pnpm exec tsx src/__tests__/turn-actions-rendering.test.ts
- pnpm build
- Real-browser hover, focus, disabled-state, and menu-open checks

* chore: ratchet Message size budget

Problem:
The turn-action markup adds four intentional wrapper lines and raises Message.tsx from 891 to 895 lines, exceeding its baselined 91-line file-size allowance.

Root cause:
The repolint baseline still reflected the file's previous overage and therefore rejected the otherwise verified UI change.

Fix:
Raise only Message.tsx's file-size allowance from 91 to 95, matching the exact four-line increase without relaxing any other repository standard.

Verification:
- go run ./tools/repolint
- pnpm exec tsx src/__tests__/turn-actions-rendering.test.ts
- pnpm build
- git diff --check

* chore(desktop): ratchet raw bundle budget

Problem:
The latest main-v2 merge plus the turn-action label styles produces 2254.9 KiB of initial raw JavaScript and CSS, 0.9 KiB above the 2254 KiB ceiling.

Root cause:
The shared hover/focus label contract is always-available UI CSS and was not represented in the raw startup budget.

Fix:
Raise only the initial raw JavaScript and CSS allowance from 2254 to 2255 KiB and document the turn-action label cost. Keep all gzip and per-chunk budgets unchanged.

Verification:
- pnpm build (2254.9 KiB / 2255.0 KiB)
- go run ./tools/repolint
- pnpm exec tsx src/__tests__/turn-actions-rendering.test.ts
- git diff --check
2026-08-11 20:07:35 +08:00
SivanCola 64f70f3fad feat(provider): exact custom request URLs / 自定义供应商使用精确请求地址 (#8386)
* feat(provider): add exact custom request URLs

Problem:
Custom provider setup mixed base URLs and full request URLs, while legacy chat_url semantics differed across protocols.

Root cause:
The desktop editor reused chat_url for both legacy compatibility behavior and new exact endpoint intent.

Fix:
- add request_url as an explicit exact endpoint field
- preserve legacy chat_url behavior for existing OpenAI, Anthropic, and Responses configs
- simplify the desktop editor to one API address field and migrate only on save
- document and test global and project config round trips

Verification:
- frontend production build and focused editor tests
- focused provider, config, boot, and desktop tests
- desktop go test ./...

* fix(provider): preserve base URL on unchanged endpoint

Problem:
Saving an existing custom provider could rewrite base_url from request_url, changing model discovery and endpoint-derived capabilities even when the user had not changed the API address.

Root cause:
ProviderEditor always derived base_url from the effective request URL instead of preserving the independent stored value for unchanged providers.

Fix:
Preserve the stored base_url when provider kind and effective request URL are unchanged, and derive a new base only for new providers or intentional endpoint/protocol changes. Extract endpoint compatibility logic and provider request URL tests into focused owners, with exact repolint ratchets for the cross-layer contract.

Verification:
- pnpm test:all && pnpm build
- go test ./...
- cd desktop && go test ./...
- go test -race ./internal/provider/openai ./internal/provider/anthropic ./internal/provider/responses
- go vet focused root packages and desktop ./...
- go run ./tools/repolint

* fix(provider): mirror exact URL for legacy OpenAI

Problem:
Older clients ignore request_url and could keep using a stale chat_url after a custom OpenAI-compatible provider was saved with a new exact endpoint.

Root cause:
The desktop editor and save boundary preserved chat_url independently from the authoritative request_url.

Fix:
Mirror the exact OpenAI-compatible request URL into legacy chat_url in both the editor payload and desktop persistence boundary, retain non-OpenAI legacy behavior, and document the downgrade limitation. Add focused downgrade-compatibility coverage and complete the Chinese specification.

Verification:
- pnpm test:all
- pnpm build
- go test ./...
- (cd desktop && go test ./... && go vet ./...)
- go test -race ./internal/provider/openai ./internal/provider/anthropic ./internal/provider/responses
- go vet ./internal/provider/openai ./internal/provider/anthropic ./internal/provider/responses ./internal/config ./internal/boot
- go run ./tools/repolint
- git diff --cached --check

* test(provider): align exact URL copy assertion

Problem:
The provider editor test still asserted the previous English helper copy after the production hint was shortened without changing its meaning.

Root cause:
The final copy-size adjustment and its standalone TSX assertion were not updated together.

Fix:
Assert the current unchanged-URL helper text so the focused editor test covers the shipped copy again.

Verification:
- pnpm exec tsx src/__tests__/provider-editor-model-picker.test.tsx
- pnpm build
- go run ./tools/repolint
- git diff --cached --check
2026-08-11 20:06:17 +08:00
SivanCola 6d262926e0 fix(desktop): streamline task and toolbar controls (#8392)
Problem:
The task monitor exposed duplicate stop and cancel actions with a cramped confirmation flow, while the topic toolbar repeated command and shortcut entry points and placed the workspace control inconsistently.

Root cause:
The desktop UI surfaced backend control aliases as separate primary actions and accumulated utility controls without a clear priority order or complete localization.

Fix:
Merge active-task termination into a single accessible stop flow, localize task state and session-summary labels, remove redundant toolbar entry points, and place session summary before the workspace toggle. Keep the underlying cancel APIs and keyboard/settings access paths intact.

Verification:
- pnpm exec tsx src/components/TaskMonitorPanel.test.tsx
- pnpm exec tsx src/__tests__/app-chrome-tabs.test.ts
- pnpm check:css
- pnpm typecheck
- Browser verification at 127.0.0.1:5174
2026-08-11 19:07:20 +08:00
SivanCola ee17c62901 Unify multi-currency billing across all surfaces / 统一全链路计费与币种 (#8058)
* feat(billing): unify multi-currency cost accounting

Problem:
Reasonix exposed provider prices, wallet balances, and session totals through independent currency paths. Mixed model runs could clear or mislabel costs, and display preferences could mutate regional list prices.

Root cause:
The runtime had no currency-tagged money domain, occurrence-time valuation, or shared quote boundary. Individual sinks accumulated floating-point Pricing.Cost values and inferred display semantics locally.

Fix:
Introduce fixed-point Money, CostQuote, official regional price catalogs, ECB FX snapshots, occurrence-time ledgers, wallet conversion, config v6 migration, and a central quote sink shared by Desktop, Serve, CLI, ACP, Remote, stats, and eventwire. Preserve additive legacy aliases and document the new contracts in English and Chinese.

Verification:
- go test ./...
- (cd desktop && go test ./...)
- (cd desktop/frontend && pnpm typecheck && pnpm test:usage-stats && pnpm build)
- go test -race ./internal/billing ./internal/event ./internal/stats ./internal/boot ./internal/config ./internal/cli ./internal/acp ./internal/serve
- go test -race ./internal/billing ./internal/acp ./internal/eventwire ./internal/event
- golangci-lint run --timeout=5m
- (cd desktop && golangci-lint run --timeout=5m)
- go run ./tools/repolint
- ./scripts/cache-guard.sh
- CACHE_IMPACT_PR_BODY_FILE=/tmp/reasonix-billing-pr-body.md ./scripts/check-cache-impact.sh
- DOCS_IMPACT_PR_BODY_FILE=/tmp/reasonix-billing-pr-body.md ./scripts/check-docs-impact.sh
- git diff --check

* fix(billing): keep cost totals complete and lossless

Problem:
Exact original-currency totals could be marked incomplete when an unused FX valuation was unavailable. CLI and ACP recomputed authoritative totals from a 64-quote sample, and live controllers retained the pre-refresh FX table pointer.

Root cause:
CostQuote completeness conflated cost-fact availability with optional valuation coverage. Run surfaces used bounded quote slices as ledgers, while QuoteContext captured a one-time RateTable snapshot.

Fix:
Make completeness follow the selected shared display total, preserve unrecoverable fact failures, and keep incomplete frontend quotes unavailable. Accumulate CLI and ACP totals through the billing ledger, including restored ACP scalar history, and read the current FXCache snapshot for every quote.

Verification:
- go test ./...
- go test -race ./internal/billing ./internal/event ./internal/boot ./internal/cli ./internal/acp
- (cd desktop && go test ./... && go vet ./...)
- pnpm --dir desktop/frontend typecheck
- pnpm --dir desktop/frontend build
- ./scripts/cache-guard.sh
- go run ./tools/repolint

* refactor: remove runtime FX from billing

Problem: runtime FX downloads and wallet conversion mixed pricing estimates with provider balances and could fabricate cross-currency totals.

Root cause: CostQuote, ledger aggregation, display selection, and wallet rendering shared one currency path and treated display completeness as cost completeness.

Fix: keep fixed-point original facts, restrict new valuations to identity and official regional tables, add explicit cost/display status and currency buckets, remove ECB startup/cache paths, and use exact original wallet balances with runtime-only hints.

Verification: go test ./..., desktop go test ./..., targeted race suites, frontend typecheck/usage-stats/build, cache guard, cache-impact review, and secret scan.

* Fix display-only currency rebinding

Problem: changing the billing display currency still rebuilt Desktop controllers, stale wallet responses could bind a newer session, and two obsolete locale-pricing helpers failed golangci-lint.

Root cause: the display preference reused the general runtime-settings rebuild path, while wallet callbacks validated only the controller pointer and had no tab/session display generation.

Fix: persist the preference without rebuilding, re-select existing ledger valuations across runtime tabs, compare-and-apply wallet hints against controller and telemetry generation, and remove the obsolete locale-pricing paths.

Verification: focused RED/GREEN tests; Desktop focused race tests; go test ./... in root and desktop; go vet; golangci-lint; repolint; cache-impact check.

* Clarify cost display currency settings

Problem: the Desktop setting still used ambiguous pricing terminology and packed the auto-selection rule into the segmented control.

Root cause: the billing refactor renamed the preference but left the surrounding appearance copy and compact option label partially aligned with the old pricing semantics.

Fix: present one cost-display preference, mark auto as recommended, explain wallet, pricing-table, fallback, and mixed-currency behavior in the hint, and align English, Simplified Chinese, and Traditional Chinese copy.

Verification: source contract scan; pnpm typecheck; pnpm build with bundle budgets.

* Shorten cost display currency guidance

Problem: the Desktop cost-display hint wrapped to roughly four lines and visually dominated the setting row.

Root cause: fallback and mixed-currency details duplicated behavior already surfaced by the cost UI.

Fix: retain only the display-only contract and automatic currency-selection order in concise English, Simplified Chinese, and Traditional Chinese copy.

Verification: in-app browser DOM measurement at 904x936; pnpm typecheck; pnpm build with bundle budgets.

* Fix narrow settings control overflow

Problem:
- General settings kept its two-column layout at narrow desktop widths, clipping controls at the modal edge.

Root cause:
- A component-specific 900px override won over the shared 980px responsive rule and sized the row independently of its actual content pane.

Fix:
- Make the general page an inline-size container and stack fields below 620px of available width.
- Align the media-query fallback with the shared 980px breakpoint.
- Add responsive and startup contract coverage.

Verification:
- pnpm test:settings-responsive
- pnpm exec tsx src/__tests__/startup-settings-contract.test.ts
- pnpm check:css
- pnpm typecheck
- pnpm build
- 904x936 browser DOM check: maximum control overflow is 0px
2026-08-11 19:02:51 +08:00
SivanCola ed00eccf32 Add cross-platform Desktop crash diagnostics and runtime attribution / 增加跨平台桌面崩溃诊断与运行时归因 (#8169)
* fix(desktop): add Windows crash attribution diagnostics

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

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

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

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

* refactor(diagnostics): satisfy owner size budgets

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

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

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

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

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

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

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

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

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

* fix(repair): serialize legacy startup claims

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

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

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

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

* fix(diagnostics): close Windows crash attribution gaps

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

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

* fix(diagnostics): address PR review findings

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

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

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

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

* fix(desktop): align raw bundle ratchet

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

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

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

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

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

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

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

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

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

* fix(diagnostics): preserve future lifecycle schemas

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

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

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

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

* fix(diagnostics): correlate WebKit reload events

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

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

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

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

* fix(diagnostics): defer WebKit recovery reload

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

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

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

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

* test(diagnostics): isolate WebKit smoke scenarios

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

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

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

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

* test(diagnostics): keep WebKit smoke local

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

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

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

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

* test(diagnostics): use reloadable WebKit smoke URI

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

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

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

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

* test(diagnostics): allow WebKit process startup

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

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

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

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

* Automate diagnostics D1 migration before worker deploy

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

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

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

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

* Keep diagnostics copy within locale bundle budget

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

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

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

Verification:
- cd desktop/frontend && pnpm build
- node scripts/check-bundle-budget.mjs
- git diff --check
2026-08-11 18:09:41 +08:00
SivanCola bb58eec24e fix(desktop): correct context capacity overflow display / 修复上下文容量条超限显示 (#8307)
* fix(desktop): align context capacity meter with usage

Problem:
The context window meter mixed token composition into a capacity gauge, so the visible fill could disagree with the displayed usage. Over-limit values were also clamped to 100% without an explicit status.

Root cause:
The meter reused token breakdown segments and a single clamped percentage for both physical layout and user-facing text.

Fix:
Render a single capacity fill, preserve the raw percentage for labels and status, cap only the physical fill, keep the compaction marker, and apply the same contract to the context ring popover.

Verification:
- 68 focused context UI assertions
- pnpm typecheck
- pnpm check:css
- pnpm build
- Playwright Chromium DOM, hover, computed-style, accessibility, and console checks

* fix(desktop): preserve just-over-limit context status

Problem:
Context usage that exceeded the window by less than half a percentage point rounded to 100%, so the panel and ring hid the overflow.

Root cause:
The over-limit status was derived from the rounded integer percentage instead of the exact token counts.

Fix:
Keep the existing integer percentage contract, but force the raw value above 100 whenever used tokens exceed the window. Add focused helper, panel, and ring regressions for 1001/1000.

Verification:
- 69 focused context UI assertions
- pnpm build
- In-app Chromium DOM, hover, computed-width, accessibility, and console checks

* fix(desktop): align status bar context overflow

Problem:
The status bar still clamped over-limit context usage to 100%, so it disagreed with the context panel and ring.

Root cause:
The status bar kept a local percentage calculation instead of using the shared raw/display context-window contract.

Fix:
Move contextWindowPercentages into a shared frontend library and use its raw percentage for the status bar label while preserving capped graphical fills.

Verification:
- 114 focused context and status-bar assertions
- pnpm build
- Playwright Chromium DOM, visibility, and console check

* fix(desktop): satisfy context meter lint budget

Problem:
The shared context percentage helper pushed StatusBar.tsx one line beyond its baselined file-size budget, leaving PR #8307's lint job red.

Root cause:
formatRate used a two-branch statement where a behavior-equivalent conditional expression is sufficient.

Fix:
Keep the shared context percentage contract and simplify formatRate without changing its zero or negative denominator behavior.

Verification:
- go run ./tools/repolint
- 54 context panel assertions
- 6 capacity meter assertions
- 9 context ring assertions
- 45 status bar assertions
- pnpm test:typecheck
- pnpm build
2026-08-11 16:07:16 +08:00
SivanCola 717497e25d fix(desktop): externalize WebView2 release smoke (#8333)
Remove test-only WebView2 approval instrumentation from the shipped Desktop executable. Preserve approval behavior coverage in real Chromium and validate the exact Windows production binary externally through UI Automation, including the real composer identity.
2026-08-11 15:43:36 +08:00
SivanCola cc28d83c1b Fix localized Inbox pause recovery / 修复收件箱暂停恢复与本地化 (#8270)
* fix(desktop): localize inbox state errors

Problem:
- Paused Inbox actions surfaced raw English backend errors in localized desktop sessions.
- Non-recovered paused queues had no actionable recovery surface and could still attempt guidance admission.

Root cause:
- Wails transported product-state text instead of stable identifiers, and the Composer rendered Error.message directly.
- Pause state was only propagated for recovered Inbox snapshots.

Fix:
- Encode known Inbox states as stable error codes while preserving Go error unwrapping and unknown diagnostics.
- Localize current and legacy error forms in English, Simplified Chinese, and Traditional Chinese.
- Propagate pause state, disable guidance admission, and expose resume controls for ordinary paused queues.

Verification:
- cd desktop && go test ./... -count=1
- Inbox localization tests: 18 passed
- Inbox recovery interaction tests: 13 passed
- cd desktop/frontend && pnpm build
- Verified the zh-CN pause and resume flow in a real browser

* fix(desktop): localize inbox cancellation failures

Problem:
- Inbox cancellation failures exposed English context or stable transport codes in localized sessions.
- PR checks were blocked by errorlint and ratcheted source-file budgets.

Root cause:
- The controller cancellation path bypassed the shared Inbox error formatter.
- An intentional error identity assertion lacked a scoped errorlint explanation, while added lines exceeded existing budgets.

Fix:
- Localize cancellation context and nested Inbox errors while preserving unknown diagnostics.
- Add formatter and controller regression coverage.
- Keep source budgets unchanged and document the intentional identity assertion.

Verification:
- go run ./tools/repolint
- cd desktop && go test ./... -count=1
- cd desktop && golangci-lint run --timeout=5m ./...
- cd desktop/frontend && pnpm build
- Inbox localization, cancellation reconciliation, and recovery regression tests

* fix(inbox): recover orphaned guidance ownership

Problem:
Durable in-flight guidance could outlive Controller ownership and become an immutable zombie item while the UI kept offering invalid operations.

Root cause:
Same-process snapshots had no ownership reconciliation, and durable claim/active/ack transitions were not serialized against recovery. The guidance shelf also treated every persisted state like a queued item.

Fix:
Recover unowned in-flight records to paused, reviewable uncertain state; serialize the full ownership lifecycle; and expose state-aware disabled or retry actions with localized labels.

Verification:
- go test ./...
- go test -race ./internal/control ./internal/sessioninbox
- cd desktop && go test . -run Inbox\|Steer
- cd desktop/frontend && pnpm test:typecheck && pnpm test && pnpm build
- make lint
- real Chromium recovery/retry interaction in zh-CN

Fixes #8276

* fix(inbox): make retry admission idempotent

Problem:
A recovered item could start as a new turn on retry and then reject a stale frontend steer. Completion also held the inbox admission lock across transcript snapshot I/O.

Root cause:
The steer idempotency path omitted the running state, and completion removed active ownership before persistence, requiring broad serialization.

Fix:
Treat Controller-owned running items as already admitted. Keep active IDs published while snapshotting and hold admission exclusion only for durable acknowledgement and ownership removal.

Verification:
- go test ./...
- go test -race ./internal/control ./internal/sessioninbox
- cd desktop && go test ./...
- frontend typecheck, focused Inbox tests, and production build
- make lint

* fix(inbox): keep durable I/O outside admission lock

Problem:
Inbox snapshots and turn completion held admissionMu while waiting on session inbox disk transactions, allowing bounded file-lock waits and per-item acknowledgement loops to block admission.

Root cause:
Recovery consumed a stale active-ID snapshot, so a broad Controller lock bridged the durable claim-to-ownership and ownership-to-ack windows.

Fix:
Track admitting and active ownership in lock-free registries that Store recovery consults after refreshing its transaction. Keep ownership published through durable acknowledgement, and remove admission locking from snapshot and completion I/O.

Verification:
- go test -race -count=1 ./internal/control ./internal/sessioninbox
- go test ./...
- cd desktop && go test ./...
- go vet ./...
- make lint
- frontend typecheck, Inbox focused tests, and production build

* fix(inbox): keep durable FIFO dispatch live

Problem:
Queued guidance could remain idle after rotation, a rejected steer, a transient dispatch failure, or the previous durable turn completing. The desktop fallback also disabled the queued action while idle.

Root cause:
Inbox dispatch used a one-shot edge guard, so kicks that arrived during a scan or a closed admission gate could be lost. Several gate-release paths did not publish a new kick, and the frontend exposed no FIFO-safe recovery action.

Fix:
Use a level-triggered drain with bounded retry, re-kick after rotation and rejected steering, and expose an idle head-item Send action that reuses the idempotent resume/drain path. Keep later items disabled to preserve FIFO ordering and localize the new states.

Verification:
- go test -race ./...
- go test -race ./internal/control -count=1
- focused inbox race tests with -count=10
- pnpm test:typecheck
- pnpm lint:hooks
- pnpm build
- composer-inbox-recovery.test.tsx

* fix(ci): budget inbox recovery controls

Problem:
The Linux and Windows desktop jobs failed the raw initial asset budget after the inbox recovery UI landed, even though every compressed startup and chunk budget remained green.

Root cause:
The new localized recovery controls add 2.7 KiB to the current main-v2 raw initial bundle beyond its 2246 KiB allowance.

Fix:
Raise only the raw aggregate allowance to 2249 KiB and document the durable inbox recovery contract. This leaves roughly 0.3 KiB of headroom while preserving all gzip, CSS, locale, and largest-chunk limits.

Verification:
- wails generate module
- pnpm --dir frontend build
- pnpm --dir frontend check:bundle
- pnpm --dir frontend test
- go test -race ./... (root and desktop modules)

* refactor(inbox): keep recovery paths within lint budgets

Problem:
Repository lint rejected the queue liveness patch because it expanded two baselined legacy files and pushed TrySteerInboxItem five lines over the function limit.

Root cause:
The drain kick and idle recovery details lived inline in already oversized controller and Composer functions.

Fix:
Move the idle UI kick into composerGuidance, colocate rotation release with inbox dispatch, and extract steer-admission unlock handling. This restores every existing file/function budget without adding lint baselines.

Verification:
- go run ./tools/repolint
- focused inbox race tests with -count=10
- pnpm lint:hooks
- pnpm test:typecheck
- composer-inbox-recovery.test.tsx
2026-08-11 14:49:45 +08:00
SivanCola 175455df43 Keep transient routing out of completion receipts / 避免内部能力路由污染完成回执 (#8317)
* fix(agent): keep transient routing out of completion receipts

Problem:
Ordinary turns could surface an unverified completion receipt whose acceptance criterion exposed the internal capability-route block and invented a required-change check.

Root cause:
The agent kept the provider-composed turn as the completion contract input even though the controller already supplied authenticated raw user text separately.

Fix:
Use the raw user input for live and final completion contracts while preserving the composed provider prompt. Add regression coverage for mutation receipts and advisory turns containing capability routing with delivery-like wording.

Verification:
- go test ./internal/agent ./internal/completion ./internal/taskcontract
- go test ./...
- cd desktop && go test ./...
- git diff --check

* fix(agent): preserve trusted task text in completion contracts

Problem:
The first completion-receipt fix used raw user input unconditionally, which did not preserve Goal scope task text or pristine sub-agent classifier text. Its explanatory comment also exceeded repository lint budgets.

Root cause:
Completion contracts and delivery classification selected their task source independently even though both require the same host-trusted text.

Fix:
Store the already-selected trusted classification input as the per-turn contract input. Add regressions for Goal continuations and host-wrapped sub-agents, and remove the lint-expanding comment.

Verification:
- go run ./tools/repolint
- go test -race ./internal/agent -run TestCompletionContract -count=1
- go test ./internal/agent ./internal/completion ./internal/taskcontract
- go vet ./...
- go test ./...
- cd desktop && go test ./...
- git diff --check

* chore(ci): refresh PR metadata checks

Problem:
The documentation-impact field was added after the pull_request_target event was created, and rerunning that workflow reused the old event payload.

Root cause:
GitHub Actions reruns preserve the original pull request metadata snapshot.

Fix:
Create a new synchronize event so metadata guards evaluate the corrected PR body without rewriting published history.

Verification:
- PR body contains Documentation-impact
- local code and test gates passed at b0fb0fbcd
2026-08-11 14:03:53 +08:00
SivanCola fb965ff5a7 fix(config): avoid repeated DeepSeek migration warnings / 避免重复弹出 DeepSeek 迁移警告 (#8269)
* fix(config): avoid repeated DeepSeek migration warnings

Problem:
Switching or resuming sessions rebuilds the desktop runtime and repeatedly shows a DeepSeek migration warning when an unrelated TOML value is malformed.

Root cause:
The automatic DeepSeek protocol migration decodes the entire user config before the resilient loader runs, so any parse error is mislabeled as a migration failure on every controller build.

Fix:
Let the resilient config loader own malformed-file reporting for automatic migration, preserve the invalid file byte-for-byte, and keep explicit Settings upgrades strict.

Verification:
- go test ./internal/config -count=1
- go test ./internal/boot -run 'TestBuild(MigratesLegacyDeepSeekProtocolWithOneNotice|DoesNotMislabelMalformedConfigAsDeepSeekMigrationFailure)' -count=1
- go test ./... -p 4

* fix(boot): scope malformed config warnings by frontend

Problem: automatic DeepSeek migration parse failures were either shown as duplicate desktop session notices or hidden from headless frontends.

Root cause: malformed TOML recovery belongs to the resilient loader on desktop, but the migration layer is shared by desktop, CLI, serve, and bot builds.

Fix: keep migration parse errors intact, suppress only the duplicate desktop notice when a loader warning exists, preserve headless diagnostics, and move focused regression coverage out of the oversized boot test file.

Verification: go run ./tools/repolint; go vet ./internal/config ./internal/boot; focused race tests for config and boot; go test ./... -p 4.

* fix(desktop): surface runtime config load warnings

Problem:
Malformed config introduced after startup could be silently ignored during desktop session rebuilds.

Root cause:
Boot inferred warning presentation from the usage StatsSource label without transferring resilient-loader warnings to the frontend.

Fix:
Add an explicit warning acceptance callback, publish warnings through the desktop event bridge, deduplicate repeated fingerprints, and fence stale startup responses.

Verification:
- go test -race ./internal/boot
- go test -race . in desktop
- go test ./... -p 4
- go test ./... in desktop
- pnpm build
- go run ./tools/repolint

* fix(desktop): order config warning reloads

Problem: Runtime config warnings could miss project-only damage, and an event queued before a successful reload could reopen a stale banner.

Root cause: Boot delivered warnings only through the DeepSeek migration error path, while desktop warning events and reload snapshots had no shared source ordering.

Fix: Deliver every resilient-loader warning through the explicit frontend callback and assign monotonic build/snapshot revisions so reloads reject older queued events while newer recurrences remain visible.

Verification: go test ./... -p 4; cd desktop && go test ./...; go vet ./... in both modules; pnpm build; race-focused config warning tests; go run ./tools/repolint.
2026-08-11 13:11:23 +08:00
SivanColaandLI Xinyu 2760c09466 fix(desktop): stabilize native transcript scrolling / 修复聊天区原生滚动跳页与卡顿 (#8264)
* fix(desktop): stop trackpad scroll stalls in the chat transcript

Problem: On macOS, two-finger scrolling the chat content area felt sticky
and jumpy—especially over markdown tables—because nested overflow
containers latched the trackpad and virtualizer/stream rewrites fought
native inertia mid-gesture.

Root cause: CSS overflow-x:auto promotes overflow-y to auto, so ordinary
tables/code blocks became vertical scroll ports; virtualizer height
compensation and stream tail-follow could still write scrollTop while the
user was gesturing.

Fix: Add a short user-gesture lock that blocks compensating scroll writes,
promote edge/non-scrollable nested wheels to the transcript with latching
handoff, keep small tables and code blocks out of nested vertical scroll,
and mark large virtual tables for handoff.

Verification: npx tsx scroll-manager, transcript-scroll-session,
nested-scroll-handoff, markdown-table-virtual, typography-overflow-contract,
and transcript-virtualization tests.

* fix(desktop): fold large tables and idle-remeasure after scroll

Problem: Large markdown tables still introduced nested vertical scroll
ports by default, and virtualizer remeasure could jump after a trackpad
gesture settled.

Root cause: Oversized tables mounted a max-height virtual scroller
immediately, and height compensation was deferred only mid-gesture, not
batched once after the hold window.

Fix: Collapse tables above the virtual threshold to a 12-row document-flow
preview with Expand all / Collapse; expanded tables keep the nested
virtual scroller with handoff. After gesture idle, measure once and
reconcile the reading anchor.

Verification: npx tsx markdown-table-virtual, scroll-manager,
transcript-scroll-session, and nested-scroll-handoff tests.

* fix(desktop): stabilize native transcript scrolling

Problem:
Long variable-height transcripts could stall or jump while trackpad inertia, native scrollbar input, or Windows middle-button auto-scroll overlapped virtualizer and resize compensation. Creation scrollbar sensitivity could also change while rows mounted.

Root cause:
Scroll ownership only covered wheel, touch, and keyboard intent; treated unowned native scroll events ambiguously; remeasured the entire virtualizer after every idle gesture; and recomputed custom-scrollbar geometry during a drag. A stale scrollend could also settle a newer gesture.

Fix:
Model native input sessions explicitly, arbitrate controller-owned scroll events, normalize wheel delta modes, guard stale scrollend, freeze custom-scrollbar drag geometry, and measure only real layout invalidations with anchor reconciliation. Split interaction and scrollbar concerns out of Transcript and add real-browser stability coverage.

Verification:
- pnpm --dir desktop/frontend test:transcript
- pnpm --dir desktop/frontend test:transcript-browser
- pnpm --dir desktop/frontend typecheck
- pnpm --dir desktop/frontend test:typecheck
- pnpm --dir desktop/frontend test:motion
- pnpm --dir desktop/frontend build
- go run ./tools/repolint

* fix(desktop): latch manual transcript scroll intent

Problem:
A short upward wheel, trackpad, native scrollbar, or Creation scrollbar movement could remain inside the 80px near-bottom band. After the gesture quiet window, streaming output could therefore re-enable tail-follow and pull the viewport back down.

Root cause:
The scroll manager treated geometric proximity as user intent and recomputed stick mode from the near-bottom threshold after every native scroll event.

Fix:
Latch explicit user detachment across gesture settlement, keep resize and stream repins disabled while detached, and restore tail-follow only at the physical bottom or through an explicit bottom/reset action. Apply the same contract to the Creation scrollbar.

Verification:
- pnpm --dir desktop/frontend test:transcript
- pnpm --dir desktop/frontend test:transcript-browser
- pnpm --dir desktop/frontend test:typecheck
- pnpm --dir desktop/frontend test:motion
- pnpm --dir desktop/frontend build
- go run ./tools/repolint

* fix(desktop): preserve tail follow across scroll ownership

Problem:
- Nested downward handoff at the physical bottom detached streaming tail-follow.
- Passive tail writes rejected during an active gesture were dropped, so final token and row-size growth could leave the viewport behind.

Root cause:
- Nested handoff discarded wheel direction and always released auto-scroll.
- Gesture arbitration had no generation-aware replay for blocked passive owners.

Fix:
- Propagate normalized nested deltas and release only for upward or detached reading intent.
- Replay blocked stream, resize, footer, and row-size repins once after gesture idle using the current scrollHeight.
- Cancel deferred work on manual intent, selection, explicit navigation, and tab generation changes.
- Adapt #8291's row-size ownership, pinned measurement rule, and stream-end fallback into the unified gesture controller.
- Add deterministic and real Chromium regression coverage.

Verification:
- pnpm test:transcript
- pnpm test:transcript-browser
- pnpm test:typecheck
- pnpm test:motion
- pnpm build
- go run ./tools/repolint

Co-authored-by: LI Xinyu <[email protected]>

---------

Co-authored-by: LI Xinyu <[email protected]>
2026-08-11 11:59:43 +08:00
SivanCola 8a39ac1c4b fix(desktop): hide context checkpoint details (#8265)
Problem: the context panel again exposed internal full-history, model-visible, trigger, threshold, and checkpoint diagnostics after the content-driven maintenance rewrite.

Root cause: PR #8244 added a new context checkpoint block and reversed the source contract that had kept internal maintenance details out of the user-facing panel.

Fix: remove the checkpoint detail render path, presentation-only state, styles, and locale strings. Preserve the real compression-distance gauge, backend checkpoint behavior, and maintenance notices. Restore a source contract that prevents the block from returning.

Verification: focused context-maintenance contract test; ESLint; Wails bindings regeneration followed by pnpm build; browser DOM verification.
2026-08-11 08:37:14 +08:00
SivanCola c1a3af4c99 fix(ci): unblock Windows main-v2 full suite before v1.23.0 / 修复 Windows 全量 CI 以便发布 v1.23.0 (#8255)
* fix(ci): unblock Windows main-v2 full suite before v1.23.0

Problem
main-v2 push CI has a red Windows streak that blocks Stable tagging:
internal/agent, internal/boot, and internal/control hit the 3m package
timeout on loaded runners, sessioninbox asserted Unix 0700 modes on
Windows (reports 0777), and cold PowerShell hooks timed out at 60s.

Root cause
The PR smoke suite was already raised to 8m after measured 2.2x runner
variance, but the full push suite still used -timeout=3m with a 10m step
budget. Session-inbox permission checks did not gate on GOOS. Nested
PowerShell spawn shared the default 60s real-spawn budget under -p 4.

Fix
- Raise Windows full push suite to -timeout=8m and timeout-minutes: 20.
- Gate session-inbox directory mode assertion to non-Windows.
- Allow 2m for the nested PowerShell hook spawn under Windows load.

Verification
- go test ./internal/sessioninbox/ -count=1
- GOOS=windows go test -c ./internal/hook/

* fix(sessioninbox): keep store_test under repolint file-size ceiling

Problem
The Windows mode-gate edit pushed store_test.go to 801 lines and failed
repolint test-file-size (800-line ceiling).

Root cause
Extra comment lines around the GOOS permission assertion.

Fix
Collapse the comment to one line so the file stays at 800 lines.

Verification
go run ./tools/repolint

* fix(hook): clamp the Windows hook spawn timeout with max (modernize)

Problem
CI lint (golangci-lint v2.12.2, GOOS=windows leg) fails the Windows hook
test: the manual min clamp is flagged by the modernize linter
(minmax: use max).

Root cause
The 2m floor for the real-spawn budget was written as an if-statement
instead of the max builtin.

Fix
Replace the if/assign pair with max(realSpawnTimeout, 2*time.Minute).

Verification
- GOOS=windows golangci-lint run --timeout=5m ./internal/hook/...: 0 issues
- make lint (CI pin v2.12.2): 0 issues, repolint clean
- go test ./internal/hook/ -count=1: ok
2026-08-11 02:28:29 +08:00
SivanCola 397ed30ec8 feat(agent): content-driven context maintenance (single compact_ratio) / 内容驱动上下文维护(单一 compact_ratio) (#8244)
* feat(agent): content-driven context maintenance with single compact_ratio

Replace multi-threshold prune/snip/native auto maintenance with one
content-driven checkpoint path (stable prefix + one summary + recent
tail) gated solely by compact_ratio (default 0.85). First-visible tool
results are bounded at creation; prune/snip APIs remain no-ops for
call-site compatibility while legacy config/sidecar fields load and
migrate away.

Also align max_output_tokens auto ladder (0=auto; 128K explicit only),
desktop/settings/docs, and force CompactNow planning so mid-size
strict-alternating sessions (e.g. guardian) still reduce tokens.

Verification: go test ./...; desktop go test ./...; frontend test:all;
cache-guard; race on core packages; live seed→resume→continue smoke
(version=1, one summary, ~$0.0012).

* fix(agent): make context projection durable on Windows and clear CI gates

Problem:
- SaveCompactionState fsynced the parent directory after rename. Windows
  returns ERROR_ACCESS_DENIED for directory Sync, so every projection
  install failed and Windows smoke tests went red.
- gofmt, repolint, frontend AgentView fixtures, and required cache/docs
  PR-body lines also blocked CI for #8244.

Root cause:
- Custom atomic write path reimplemented durability with a directory Sync
  that is not portable, instead of using the shared Windows-safe helper.
- Residual format/debt and a missing coldResumePrune fixture field.

Fix:
- Persist sidecars through fileutil.AtomicWriteFile (temp + file fsync +
  replace) without directory Sync.
- gofmt touched Go files; shorten essay comments; extract fold helpers;
  carry residual structural debt in the repolint baseline after reducing
  new essay debt; restore coldResumePrune on settings fixtures.

Verification:
- gofmt clean; go run ./tools/repolint clean
- go test ./internal/agent/ ./internal/config/ ./internal/provider/...
  ./internal/guardian/ ./internal/cli/
- pnpm settings-refresh-snapshot: 91 passed

* chore: nudge PR head refresh after Windows durability fix

Empty commit so GitHub re-resolves the fork head for #8244 after the
merge of main-v2 and the Windows sidecar write fix.

* fix(agent): close #8244 review blockers for content-driven maintenance

Problem:
Review of PR #8244 requested changes for CI failures, threshold under-count,
legacy native lineage drop, non-atomic CAS/write, residual cold-resume UI, and
duplicate context_maintenance notices.

Root cause:
- Threshold used message-only estimates without tools/role projection.
- Sidecar lineage required exact PromptCacheKey and marked restored before
  covered-hash validation.
- Checkpoint install checked transcript outside the install lock and used
  non-strict atomic write with EXDEV copy fallback.
- coldResumePrune UI/API remained after behavior removal; locale gzip grew.
- Maintenance notices ignored operationId on reconnect/replay.
- golangci reported unused prune/summary helpers and minor modernize issues.

Fix:
- Estimate compact trigger from final request shape (messages + tools +
  provider role projection).
- Normalize legacy |context-editing-native-* keys; restore only if projection
  still validates; persist normalized key without version bump/events.
- CAS install under compactionMu; SaveCompactionState uses AtomicWriteFileStrict.
- Remove coldResumePrune settings UI/Wails field/locales; keep config migration.
- Dedupe context_maintenance notices by operationId (bounded FIFO) + tests.
- Delete unused prune/summary helpers; clear golangci; trim locale strings for
  bundle budget; strip SPEC trailing space.

Verification:
- make lint (golangci + repolint) clean
- go test agent/config/control/guardian/provider
- pnpm frontend build (locale budgets pass)
- context-maintenance-notice + settings-refresh-snapshot tests

* fix(desktop): gofmt reasoning_display fixture after cold-prune removal

Desktop CI failed gofmt on reasoning_display_app.go after ColdResumePrune
was dropped from the AgentView fixture literal.

* fix(agent): unblock context-maintenance merge blockers

Problem: PR #8244 still had three merge blockers: emit under
compactionMu (deadlock risk), restore rewriting exact-key
sidecars, and initial raw bundle over budget.

Root cause: commitSummaryProjection held the lock through
synchronous sink.Emit; LoadProjectionSidecar persisted whenever
the key already matched; ContextPanel/TranscriptSelectionMenu
were eagerly imported into the initial chunk.

Fix:
- Unlock before emitContextMaintenance; reentrant sink test
- needsNormalization only for legacy native lineage rewrite;
  log persist errors instead of ignoring them
- Lazy-load ContextPanel and TranscriptSelectionMenu
- Document pre-interceptor threshold estimate + overflow recovery
- CLI/GOAL docs: compact_ratio default 85%

Verification:
- go test ./internal/agent/ (+ race focused tests)
- make lint (repolint baseline essay bump)
- pnpm build: initial raw 2236.8 / 2240 KiB
- context-maintenance-notice test ok

* fix(agent): close remaining context-maintenance plan gaps

Problem: After the merge blockers landed, plan gaps remained: no
parent-dir fsync on strict checkpoint writes, new writers still
mirrored last_*/blocked_* fields, dead CompactionCapabler shell,
and the pre-interceptor threshold contract lacked a regression test.

Root cause: AtomicWriteFileStrict only fsynced the temp file;
LastReceipt was authoritative but writers still dual-wrote mirrors;
provider.CompactionCapabler had no production callers; Prepare never
invoked interceptors by design without an explicit test.

Fix:
- Strict atomic write: fsync parent directory after rename (Windows/
  unsupported FS soft-fail)
- SaveCompactionState / commit / blocked paths: LastReceipt only;
  strip last_trigger/last_mode/token mirrors and top-level blocked_*
- Restore receipt-only blocked sidecars on load
- ContextReport/CLI/status prefer LastReceipt with legacy fallback
- Delete unused CompactionCapabler/NativeCompaction shell
- TestPrepareThresholdSkipsExtensionInterceptors

Verification:
- go test ./internal/agent ./internal/fileutil ./internal/provider
- go test -race focused packages
- make lint (repolint clean)

* docs: drop removed context_editing example from zh GUIDE

Problem: GUIDE.zh-CN still suggested context_editing = "native"
after native Anthropic context editing was removed and startup
migration deletes that key.

Root cause: bilingual GUIDE config sample was not fully synced
with the single compact_ratio local-summary contract.

Fix: remove the obsolete config line from docs/GUIDE.zh-CN.md.
English GUIDE already had no such example.

Verification: grep docs for remaining context_editing user examples
(only SPEC migration notes remain, which correctly describe removal).

* fix(fileutil): never fail strict write after successful rename

Problem: AtomicWriteFileStrict returned parent-dir fsync errors after
rename had already published the new file. commitSummaryProjection
and blocked-receipt writers treated that as "not committed" and
rolled memory back, forking disk (new generation) from process
memory (old generation) and allowing paid re-summary loops.

Root cause: post-publish power-loss durability was modeled as a
hard failure even though the atomic publish already completed.

Fix:
- After successful rename, parent-dir fsync is best-effort only
- Returned error still means "destination not published"
- syncParentDir also covers relative parents (".")
- Agent tests: applied and blocked receipts survive injected
  post-publish dir-sync faults without generation fork / re-summary
- PR body: document AtomicWriteFileStrict + best-effort dir fsync

Verification:
- go test ./internal/agent ./internal/fileutil
- make lint
2026-08-11 01:30:26 +08:00
SivanCola 2507187e97 Add logical cross-message transcript selection / 新增跨消息逻辑选区 (#8224)
* feat(desktop): add logical transcript selection

Problem:
Cross-message transcript selection either lost native endpoints when virtual rows unmounted or retained every selected row in the DOM.

Root cause:
Selection state depended on live DOM ranges, and Markdown rows had no frozen readable-text projection for virtualized content.

Fix:
- promote cross-row pointer drags to UTF-16 logical selection snapshots
- render selection overlays only for mounted rows and keep virtualization bounded
- derive copy text from the Markdown HAST, including LaTeX, code, and tables
- guard copy and Add to Chat against stale tabs and asynchronous results
- add browser, store, projection, cache, and clipboard coverage

Verification:
- pnpm --dir desktop/frontend test:transcript
- pnpm --dir desktop/frontend test:transcript-browser
- pnpm --dir desktop/frontend test:typecheck
- pnpm --dir desktop/frontend test:motion
- pnpm --dir desktop/frontend build
- go run ./tools/repolint

Known baseline:
- test:bench still hits the retained-heap gate on both this branch and main-v2

* fix(desktop): preserve logical selection endpoints

Problem:
Logical transcript selection could slice Markdown at incompatible offsets, miss the final pointer position, or survive pointer cancellation.

Root cause:
The HAST projection inserted different block separators than the DOM adapter, pointerup stopped dragging before applying its coordinates, and pointercancel shared pointerup button filtering.

Fix:
Use one canonical structural newline within Markdown rows, keep source fallbacks in native mode, apply pointerup coordinates before settling, clear canceled gestures independently, and avoid full Transcript rerenders for logical focus frames.

Verification:
- pnpm test:transcript
- pnpm test:transcript-browser
- pnpm test:typecheck
- pnpm test:motion
- pnpm build
- go run ./tools/repolint

* Stabilize logical transcript selection

Problem:
- Cross-page logical selections could lose their overlay after virtual rows committed.
- Long transcripts recomputed layout styles for every cold row estimate.
- Settled selections retained projection pins outside the final selected interval.

Root cause:
- Focus reconciliation could run before the virtualizer committed replacement DOM, overlay observers restarted on every selection snapshot, and cache pins tracked no row positions.

Fix:
- Reconcile focus after virtual DOM mutations and add a bounded overlay stabilization paint.
- Cache one layout snapshot per measurement epoch and reuse it for row estimates.
- Trim frozen rows and release unselected projection pins when logical selection settles.

Verification:
- pnpm test:transcript
- pnpm test:transcript-browser
- pnpm test:typecheck
- pnpm lint:hooks
- pnpm test:motion
- pnpm build
- go run ./tools/repolint

* Fix logical transcript selection races

Problem:
Linux browser runs could lose the logical selection overlay. Streaming tokens also rebuilt every historical text projection, expanded offscreen reasoning was omitted, and edge scrolling kept requesting frames at a loaded-history boundary.

Root cause:
Logical focus reconciliation depended on DOM mutation timing, live content was mixed into the full row projection pass, reasoning membership depended on mounted DOM, and the edge loop rescheduled without confirming movement.

Fix:
Reconcile focus after virtual range commits, split stable and live selection projections, use the structural row model for reasoning membership, stop edge scrolling when no offset is written, and cover each path with deterministic tests.

Verification:
- pnpm test:transcript
- CI=true pnpm test:transcript-browser
- pnpm test:typecheck
- pnpm lint:hooks
- pnpm test:motion
- pnpm build
- go run ./tools/repolint
- pnpm test:bench (all relevant gates pass; existing retained-heap baseline remains above its repository gate)

* Reconcile selection on virtualizer commits

Problem:
Linux desktop CI could finish edge scrolling at the loaded-history boundary with zero logical-selection overlay rectangles.

Root cause:
TanStack direct DOM updates can advance the final virtual window without a React virtualRevision commit, and the boundary path stopped before one last caret remap.

Fix:
Reconcile logical focus from the virtualizer onChange callback and schedule exactly one final focus pass when edge scrolling reaches the loaded-history boundary.

Verification:
- pnpm test:transcript
- CI=true pnpm test:transcript-browser
- pnpm test:typecheck
- pnpm lint:hooks
- pnpm build
- go run ./tools/repolint

* Cover both virtual selection commit paths

Problem:
Linux desktop CI could still finish a logical edge-selection with no visible overlay after a virtual page transition.

Root cause:
TanStack direct-DOM range changes can skip a React commit, while its onChange callback can also run before replacement row DOM commits. Either notification alone leaves one ordering uncovered.

Fix:
Reconcile the logical caret from both the virtualizer callback and the post-commit virtual revision. The existing RAF guard coalesces duplicate notifications, and the loaded-history boundary still schedules only one final focus pass.

Verification:
- pnpm test:transcript
- CI=true pnpm test:transcript-browser
- pnpm test:typecheck
- pnpm lint:hooks
- pnpm build
- go run ./tools/repolint

* Reject stale virtual caret targets

Problem:
Linux Chromium repeatedly completed an upward logical drag with mounted rows but no overlay rectangles.

Root cause:
After transformed virtual rows changed, caretPositionFromPoint could return a still-mounted row that no longer matched the pointer's geometric hit target. Accepting that stale row left the logical focus below the visible window.

Fix:
Resolve the geometric selectable root first and accept caret API offsets only when they belong to that root. Otherwise use the visible root fallback. Remove the redundant post-commit callback and cover the stale-caret case with a deterministic DOM test.

Verification:
- pnpm test:transcript
- CI=true pnpm test:transcript-browser
- pnpm test:typecheck
- pnpm lint:hooks
- pnpm build
- go run ./tools/repolint

* Improve transcript selection CI diagnostics

Problem:
The Linux browser gate reports zero logical-selection overlay rectangles even though the same gesture passes locally, leaving no structural evidence to distinguish caret mapping from Range layout failures.

Root cause:
The benchmark only reported row count and scroll-owner data at the assertion boundary.

Fix:
Record structural, content-free browser diagnostics for the edge hit, caret row, mounted and visible selectable rows, scroll geometry, and rows with positive DOM Range rectangles.

Safety:
No message text, selected text, clipboard data, or row identifiers are logged; production code and behavior are unchanged.

Tests:
- node --check desktop/frontend/bench/transcript-selection.mjs
- CI=true pnpm test:transcript-browser

* Reconcile logical focus after virtual commits

Problem:
On Linux, edge scrolling could mount a new virtual page while logical selection focus remained on the prior page, leaving no selected mounted row for the overlay to paint.

Root cause:
The focus RAF ran in the same frame as TanStack virtualizer updates. Coalescing then discarded the commit-triggered request, so narrow mounted windows could keep the caret one virtual page behind.

Fix:
Use a bounded two-frame reconciliation: the first frame lets the virtualizer commit its direct DOM update and the second resolves the caret against that committed page. Pointerup and cleanup still cancel the tracked frame and apply the exact final focus synchronously.

Safety:
The scheduler retains at most one tracked focus frame, does not add scroll writes, does not log transcript content, and keeps the existing bundle budget.

Tests:
- pnpm test:transcript
- CI=true pnpm test:transcript-browser
- pnpm test:typecheck
- pnpm lint:hooks
- pnpm build
- go run ./tools/repolint

* Make transcript drag depth deterministic

Problem:
The Linux browser test reached a valid logical overlay but copied only 14 turns because a fixed six-second edge-scroll duration covered fewer rows under Ubuntu font and layout metrics.

Root cause:
The gesture depth was time-based even though the assertion was content-depth based.

Fix:
Wait until the active drag reaches the first 30 percent of the loaded transcript scroll range before evaluating and copying. The existing 20+ turn assertion remains unchanged.

Safety:
This changes only the production-build browser benchmark, does not reduce coverage, and does not log transcript or selected text.

Tests:
- node --check desktop/frontend/bench/transcript-selection.mjs
- CI=true pnpm test:transcript-browser

* Settle transcript drag on visible text

Problem:
After reaching the required history depth, the Linux benchmark could stop inside a viewport-filling tool row. The settled selection correctly released its offscreen overscan row, so a visible-overlay assertion had no visible selectable target.

Root cause:
The gesture completion predicate checked scroll depth but not whether selectable transcript text intersected the viewport.

Fix:
Keep edge scrolling until both the 30-percent history depth and a visible selectable body are present before pointerup.

Safety:
The test still requires bounded DOM, a painted dragging and settled overlay, 20+ copied turns, and restoration after scrolling away and back. Product code and privacy behavior are unchanged.

Tests:
- node --check desktop/frontend/bench/transcript-selection.mjs
- CI=true pnpm test:transcript-browser

* Stabilize deep transcript drag sampling

Problem: the browser gate sampled logical selection while edge auto-scroll was still advancing, so machine-dependent row heights could leave the caret on a transient or tool-only row and occasionally cover only 19 turns.

Root cause: a scroll-depth ratio was used as a proxy for the intended selectable endpoint, and the assertion raced the selection focus and overlay animation frames.

Fix: stop edge scrolling at a neutral viewport point, locate a visible deterministic early-turn body using user wheel input, move the live pointer onto it, and wait for the logical overlay before sampling diagnostics.

Safety: production code and selection semantics are unchanged; the gate still requires 20+ turns, bounded virtual DOM, selection-only scroll ownership, overlay restoration, copy parity, and the 2MiB cleanup budget.

Tests: CI=true pnpm test:transcript-browser; pnpm test:transcript; node --check bench/transcript-selection.mjs; git diff --check
2026-08-11 00:23:50 +08:00
SivanCola 0cfda51ca4 chore(ci): remove cross-boundary review gate (#8211)
Problem: The independent cross-boundary review status created recurring red PR statuses and forced every qualifying head through another manual approval cycle.\n\nRoot cause: Protected review workflows published a failing commit status until a non-author maintainer approved the exact head.\n\nFix: Remove the evaluator, workflows, tests, PR template guidance, and CI invocation while retaining a negative contract against stale references.\n\nVerification: Full pull-request CI passed on Linux, macOS, and Windows, including lint, race, CodeQL, desktop builds, and WebView2 smoke.
2026-08-10 19:50:25 +08:00
SivanCola 0139b48287 Merge pull request #8206 from esengine/fix/desktop-transcript-selection-stability
Stabilize transcript scrolling and native selection / 稳定对话滚动与原生跨消息选区
2026-08-10 19:45:04 +08:00
SivanCola 5ecb9e5e37 Merge pull request #7655 from mchenziyi/codex/task-monitor-timer-fix
fix(desktop): freeze terminal task monitor timers
2026-08-10 19:29:13 +08:00
SivanCola 4e36c130a1 Merge pull request #8185 from SivanCola/fix/goal-evidence-progress
fix(goal): bound Runs instead of no-progress pauses / 用有界 Run 替代 Goal 无进展误暂停
2026-08-10 19:05:53 +08:00
SivanCola 1f1e2e3aff Merge pull request #8061 from SivanCola/fix/session-inbox-durability
Persist Session Inbox for durable queued instructions / 持久化会话收件箱以恢复排队指令
2026-08-10 18:03:24 +08:00
SivanCola 8ea06aff23 Merge pull request #8103 from SivanCola/feature/reasoning-display-modes
feat(desktop): add three-mode reasoning display / 桌面端新增思考过程三模式展示
2026-08-10 17:58:55 +08:00
SivanCola b8a37d4ba2 Merge pull request #8156 from SivanCola/fix/tui-scroll-renderer-boundary
Limit legacy TUI scroll clears to Warp / 仅在 Warp 保留 TUI 滚动清屏
2026-08-10 14:51:27 +08:00
SivanCola 96b5d32a76 Merge pull request #8142 from SivanCola/fix/tui-windows-scroll-flicker
Fix Windows transcript scroll flicker / 修复 Windows 会话滚动抖动
2026-08-10 14:09:37 +08:00
SivanCola e0bd12b4c5 Merge pull request #8102 from SivanCola/fix/workspace-live-refresh
fix(desktop): live workspace refresh / 工作区实时刷新
2026-08-10 13:36:50 +08:00
SivanCola 1cfab35a92 feat(provider): add OpenCode Go DeepSeek protocol presets / 新增 OpenCode Go DeepSeek 双协议预设 (#8105)
* feat(provider): add OpenCode Go DeepSeek protocol presets

Problem:
OpenCode Go exposed DeepSeek Flash through Chat Completions, but Reasonix had no dedicated Anthropic Messages or Responses presets with verified server-side web search support.

Root cause:
The curated catalog mixed unrelated Anthropic models, and Responses web-search replay was restricted to the official DeepSeek hostname instead of the enabled wire capability.

Fix:
Add model-scoped Anthropic and stateless Responses presets for DeepSeek Flash, enable verified web search, preserve completed Responses search calls for replay, and update Desktop capability rendering, localization, docs, mocks, and live tests.

Verification:
- go test ./...
- focused Desktop provider preset tests
- frontend typechecks and startup settings contract
- cache impact and cache guard scripts
- authenticated OpenCode Go Anthropic and Responses web-search live tests

* fix(provider): close OpenCode Go review gaps

Problem:
Repository lint rejected the protocol preset additions because they grew several baselined owner and test files. The live probes also stopped after the first web-search turn, leaving Responses replay and Anthropic tool-result compatibility unproven.

Root cause:
OpenCode Go preset data and focused assertions were added inline to already-ratcheted files, while the credential-gated tests did not exercise the provider-issued continuation payloads.

Fix:
Extract the OpenCode Go preset and localized description data into focused files without changing wire-visible configuration. Add a real Responses stateless follow-up and an Anthropic function tool/result round trip that preserves returned reasoning signatures.

Verification:
- go test ./... && go vet ./...
- go test -race ./internal/config ./internal/provider/responses ./internal/provider/anthropic
- go test -tags=live for both OpenCode Go protocol paths
- desktop go test ./... && go vet ./...
- frontend typecheck, settings contract, build, and bundle budgets
- go run ./tools/repolint
- ./scripts/cache-guard.sh
2026-08-10 12:32:39 +08:00
SivanColaandRay-lyy 6376bd4a6d Enforce native motion and WebView2 release gates / 强制执行原生动画与 WebView2 发版门禁 (#8096)
* test(desktop): enforce native motion release gates

Problem:
Native motion regressions could pass aggregate CI even when Element.animate was never exercised, and Windows release builds were signed without a real approval interaction in WebView2.

Root cause:
The focused motion suites were not part of a required check, legacy GSAP easing tokens had no source-level contract guard, and the release workflow never launched the built Wails executable.

Fix:
Add WAAPI contract scanning, required motion CI, a real Wails/WebView2 approval smoke before signing, protected release verification, and an independent review check for large or cross-boundary pull requests.

Verification:
- pnpm test:motion
- pnpm typecheck
- pnpm build
- go test ./... (desktop)
- bash scripts/release-workflows.test.sh
- actionlint for changed workflows
- go run ./tools/repolint

* fix(ci): close native motion release gate gaps

Problem:
- Windows rejected the shared WAAPI easing constants and the required lint job failed ShellCheck.
- Large-PR approval could be stale or evaluated from candidate-controlled workflow code.
- Pull requests did not exercise the real Wails approval flow in WebView2.

Root cause:
- The WAAPI contract matched only POSIX paths, the cross-platform lint loop split an unquoted value, and the review policy was bound to pull_request events without checking review.commit_id.
- The WebView2 smoke existed only in the release workflow.

Fix:
- Normalize contract paths and add a Windows regression fixture.
- Quote the lint target parsing, move review decisions into a protected control plane with current-head status publication, and add policy tests.
- Build and smoke-test the Windows Wails executable in PR CI.

Verification:
- pnpm --dir desktop/frontend test:motion
- pnpm --dir desktop/frontend build
- node --test .github/scripts/cross-boundary-review.test.cjs
- bash scripts/release-workflows.test.sh
- actionlint for all changed workflows
- cd desktop && go test ./...
- go run ./tools/repolint

Co-authored-by: Ray-lyy <[email protected]>

* fix(ci): isolate transcript virtualization from motion gate

Problem:
The native motion gate ran the transcript virtualization suite in three jobs. Its prepend assertion could execute before the virtualizer remounted the anchor, producing a false Windows failure and skipping the real WebView2 smoke.

Root cause:
The suite was grouped by historical proximity rather than ownership, and the assertion waited for one fixed render flush instead of the DOM condition it required.

Fix:
Give transcript virtualization its own test command, run it once in the Linux desktop job, and make the motion contract reject future coupling. Wait for the anchor to remount before asserting, and cover the transcript entrance reset-key contract directly.

Verification:
- pnpm --dir desktop/frontend test:motion
- pnpm --dir desktop/frontend test:transcript (6 consecutive passes)
- pnpm --dir desktop/frontend typecheck
- pnpm --dir desktop/frontend build
- go run ./tools/repolint
- actionlint for changed workflows
- bash scripts/release-workflows.test.sh

* fix(ci): defer WebView2 smoke instrumentation

Problem:
The latest-base Linux Desktop build exceeded the 2,200 KiB raw startup budget by about 0.1 KiB.

Root cause:
The Windows-only approval smoke instrumentation was statically linked into every normal desktop startup while the latest base had little budget headroom.

Fix:
Lazy-load the smoke component only when the native harness injects its flag, and make the motion contract reject a future static App import.

Verification:
- pnpm --dir desktop/frontend test:motion
- pnpm --dir desktop/frontend test:transcript
- pnpm --dir desktop/frontend typecheck
- pnpm --dir desktop/frontend build
- go run ./tools/repolint
- git diff --check

---------

Co-authored-by: Ray-lyy <[email protected]>
2026-08-10 11:38:47 +08:00
SivanCola 24be6e684f Merge pull request #8109 from SivanCola/fix/context-maintenance-idempotency
fix(context): make maintenance projection-idempotent / 上下文维护投影幂等化
2026-08-10 09:13:48 +08:00
SivanCola d541409e26 Merge pull request #8069 from esengine/ci/wails-version-pin
ci(wails): unify the CLI and library version pin / 统一 Wails CLI 与依赖版本锁定
2026-08-10 04:04:05 +08:00
SivanCola 13c072b5c3 Merge pull request #8084 from SivanCola/fix/desktop-archive-reliability
Make desktop topic archive failure-safe / 修复桌面端话题归档卡死
2026-08-10 03:51:41 +08:00
SivanCola 5204882948 Merge pull request #8087 from SivanCola/fix/global-workspace-external-opener
Enable external opener for Global workspaces / 为 Global 工作区启用外部打开按钮
2026-08-10 03:32:06 +08:00
SivanCola d44e1022b7 Merge pull request #8057 from SivanCola/fix/shared-window-output-budget
fix(agent): bound shared-window output budgets / 修复共享窗口输出预算溢出
2026-08-10 03:29:21 +08:00
SivanCola a1e23402e9 Merge pull request #8073 from erphenheimer/fix/8072-windows-shortcut-root-icon
fix(windows): repair legacy root-level shortcut icon and target / 修复旧版根目录快捷方式
2026-08-10 03:25:45 +08:00
SivanCola a25ae9ef24 Merge pull request #8053 from SivanCola/fix/core-data-safety
Harden core data-safety paths / 加固核心数据安全路径
2026-08-10 03:17:57 +08:00
SivanCola 1f93876afe Merge pull request #8077 from SivanCola/feature/native-v2-explicit-resources
fix(plugin): make native v2 resources explicit / 原生 v2 插件资源改为显式声明
2026-08-10 03:15:21 +08:00
SivanCola 303139c612 Merge pull request #8059 from SivanCola/feature/web-cli-handoff
feat(cli): add first-class Web handoff / 增加 CLI Web 会话交接
2026-08-10 03:10:55 +08:00
SivanCola b22ef01193 Merge pull request #8099 from HaoyueQin/feat/status-bar-per-request-tps
fix(desktop): refresh status bar TPS after each provider request / 底部信息栏吞吐速度改为每次请求后更新
2026-08-10 03:03:15 +08:00
SivanCola 53fb380d12 Merge pull request #8051 from SivanCola/fix/session-sidecar-durability
fix(desktop): make session sidecars durable / 持久化并串行化会话 sidecar
2026-08-10 02:06:00 +08:00
SivanCola e3c5affab9 Merge pull request #8065 from SivanCola/fix/disable-composer-spellcheck
Disable composer spellcheck / 禁用输入框拼写检查
2026-08-10 01:53:01 +08:00
SivanCola 95b5ef985c Merge pull request #8076 from SivanCola/feature/workspace-copy-paths
feat(desktop): add workspace path copy actions / 桌面端新增工作区路径复制操作
2026-08-10 01:52:37 +08:00
SivanCola ea57e9bf35 Merge pull request #8088 from SivanCola/feature/compact-ssh-statusbar
Compact idle SSH status / 精简空闲 SSH 状态栏
2026-08-10 01:49:11 +08:00
SivanCola e8f9fa3192 Merge pull request #8080 from SivanCola/fix/desktop-approval-easing
Fix WebView2 animation crashes / 修复 WebView2 动画崩溃
2026-08-10 00:27:11 +08:00
SivanCola d6747df854 Merge pull request #7919 from chengoak/fix/composer-escape-ime-guard-v2
fix(desktop/composer): don't cancel in-flight turn on ESC while IME is composing
2026-08-09 19:17:13 +08:00
SivanCola b691eeba9d Merge pull request #8003 from SivanCola/chore/dependabot-batch-deps
chore(deps): batch open Dependabot upgrades / 批量合并 Dependabot 依赖升级
2026-08-09 18:30:39 +08:00
SivanCola cf9689ad72 Merge pull request #7945 from SivanCola/feature/deepseek-anthropic-provider
Recommend DeepSeek Anthropic access / 推荐 DeepSeek Anthropic 接入
2026-08-09 18:17:33 +08:00
SivanCola 53883c6fe0 Merge pull request #7985 from PMKang/feat/local-path-context-menu
桌面端增强本地文件链接打开体验
2026-08-09 10:55:02 +08:00
SivanCola 209a4673e0 Merge pull request #8007 from SivanCola/fix/goal-context-execution-boundary
fix(goal): harden contextual tool and migration boundaries / Goal/context 工具与迁移边界完整修复
2026-08-09 10:05:57 +08:00
SivanCola 118d10c44c Merge pull request #7982 from SivanCola/fix/session-wal-recovery-7956
fix(session): make crash recovery WAL-aware and conflict-safe / 修复会话恢复回滚与并发覆盖
2026-08-09 09:24:25 +08:00
SivanCola cc2fd2fd2e Merge pull request #8001 from esengine/fix/restore-cancelled-turn-edit-merge
fix(desktop): restore edit actions for cancelled turns / 修复立即取消回合的编辑操作
2026-08-09 08:28:36 +08:00
SivanCola 677ca8db38 Merge pull request #7959 from SivanCola/feature/goal-sole-runtime
feat(goal): make Goal the sole long-task runtime / Goal 成为唯一长任务状态机
2026-08-09 08:27:36 +08:00
SivanCola 9b0fdc5dfc Merge pull request #7988 from SivanCola/fix/desktop-transcript-selection-release
Fix transcript selection release / 修复聊天选区无法释放
2026-08-09 05:30:54 +08:00
SivanCola a41465e9bb Merge pull request #8000 from SivanCola/codex/explicit-compress-tool
feat(agent): add explicit context compression tool / 显式上下文压缩工具
2026-08-09 04:54:36 +08:00
SivanCola c56bc3e106 Merge pull request #7999 from SivanCola/fix/desktop-session-switch-performance
Fix desktop session switching stalls / 修复桌面端会话切换与大历史卡顿
2026-08-09 04:52:48 +08:00
SivanCola 6122d3a0c6 Merge pull request #7981 from SivanCola/fix/skills-settings-consistency
Align Skills settings sources and invocation policy / 统一技能来源与调用策略设置
2026-08-09 04:09:46 +08:00
SivanCola 948cb504d9 Merge pull request #7997 from esengine/fix/site-wordmark-intrinsic-size
Fix horizontal Reasonix wordmark sizing / 修复横向 Reasonix 标识尺寸
2026-08-09 02:47:06 +08:00
SivanCola 146420737c Merge pull request #7984 from esengine/redesign/hero-install
site: redesign hero install with particles, unify fonts, refresh brand assets
2026-08-09 02:13:30 +08:00
SivanCola 69fab57554 Merge pull request #6931 from HaoyueQin/feat/status-bar-throughput-cache-tokens
feat(desktop): add tps throughput, cache token counts, and output tokens to status bar / 底部信息栏新增 tps 吞吐、缓存 token 和输出 token 显示
2026-08-08 23:08:40 +08:00
SivanCola 6c68927728 Merge pull request #7932 from SivanCola/feature/mcp-oauth-sandbox-auth
feat(mcp): add sandbox-safe OAuth and failure-aware routing / 添加沙箱安全的 MCP OAuth 与失败路由
2026-08-08 22:45:39 +08:00
SivanCola a720a3afad Merge pull request #7966 from SivanCola/feature/reasoning-summary-view
Add lazy thinking summaries with a visibility preference / 增加懒加载思考摘要及显示设置
2026-08-08 20:04:35 +08:00
SivanCola d314cee5a5 Merge pull request #7954 from Bernardxu123/main-v2
docs: add animated logo SVG variants
2026-08-08 19:49:06 +08:00
SivanCola e2ef3f43a5 Merge pull request #7942 from esengine/agent/storage-path-settings
Show read-only storage paths and preserve Windows install location / 显示只读存储路径并保留 Windows 安装目录
2026-08-08 17:31:27 +08:00
SivanCola 7ae8c54a5a Merge pull request #7940 from SivanCola/fix/thinking-trace-markdown
Render thinking traces as Markdown without stream lag / 流畅渲染思考过程 Markdown
2026-08-08 15:50:10 +08:00
SivanCola 34dccef8e8 Merge pull request #7903 from SivanCola/fix/kimi-k3-custom-reasoning
Support explicit Kimi K3 reasoning for custom gateways / 支持自定义网关的 Kimi K3 推理
2026-08-08 14:03:39 +08:00
SivanCola 9295946ad2 Merge pull request #7930 from SivanCola/fix/compaction-retry-context-accounting
Fix retry-inflated compaction decisions / 修复重试累计量导致的提前压缩
2026-08-08 13:13:50 +08:00
SivanCola 6e80d410e3 Merge pull request #7925 from SivanCola/fix/windows-taskbar-identity
Fix Windows taskbar identity grouping / 修复 Windows 任务栏图标分组
2026-08-08 12:34:29 +08:00
SivanCola 128b34b63b Merge pull request #7839 from clearnature/feat/output-budget-128k-compaction
feat: raise output budgets to 128K + cache-aware context projection / 提升输出预算与缓存感知惰性压缩
2026-08-07 19:28:59 +08:00
SivanCola 82851c80be Merge pull request #7807 from chuiwenwei/fix/sandbox-available-probe
fix(sandbox): probe sandbox-exec usability instead of PATH presence
2026-08-07 19:13:18 +08:00
SivanCola 6d7a7dff8b Merge pull request #7863 from SivanCola/fix/windows-hook-shebang-all-sources
fix(hooks): normalize Windows POSIX scripts across hook sources / 修复 Windows 多来源 Hook 的 POSIX 脚本执行
2026-08-07 19:04:51 +08:00
SivanCola cfdc5addf9 Merge pull request #7817 from Nath-Vikky/fix/require-visible-subagent-final
fix(agent): prevent stale results after reasoning-only stops
2026-08-07 18:10:41 +08:00
SivanCola c428f9166c Merge pull request #7820 from SivanCola/fix/updater-self-heal-pending
fix(update): self-heal stuck pending updates / 自愈卡住的待更新事务
2026-08-07 17:36:59 +08:00
SivanCola 3c63e430bf Merge pull request #7779 from SivanCola/feature/agent-work-practices
Harden agent runs: work-practice policy, actionable headless block, unhelped SWE-bench measurement / 加固 agent 运行:工作实践策略、可行动的 headless 拦截、不加特殊照顾的 SWE-bench 测量
2026-08-07 17:26:57 +08:00
SivanCola f966dc37f9 Merge pull request #7431 from Lxiny-zy/fix/updater-reconcile-stale-target
fix(updater): quarantine stale self-describing transactions at reconcile
2026-08-07 12:55:05 +08:00
SivanCola 38e4702e87 Merge pull request #7832 from esengine/fix/tui-watchdog-lifecycle
fix(cli): prevent TUI watchdog false kills and recover active stalls
2026-08-07 12:25:15 +08:00
SivanCola a44361deba Merge pull request #7757 from SivanCola/fix/shell-execution-contract
fix(shell): execution contract and structured error presentation / Shell 执行契约与错误结构化呈现
2026-08-06 21:37:56 +08:00
SivanCola 593c074309 Merge pull request #7708 from skyzhao1223/fix/serve-replay-pending-on-sse
fix(serve): replay pending prompts when SSE clients attach
2026-08-06 21:13:40 +08:00
SivanCola 836dd9d16a Merge pull request #7764 from esengine/codex/docs-i18n-cleanup-bundle
docs: consolidate documentation and i18n cleanup
2026-08-06 21:05:58 +08:00
SivanCola 9da4b9e88c Merge pull request #7709 from skyzhao1223/fix/todo-panel-live-refresh
fix(desktop): refresh todo panel from live todo_write events
2026-08-06 20:51:58 +08:00
SivanCola 708ab62bcf Merge pull request #7752 from Nontee22/fix/windows-shortcut-target
fix(desktop): repoint dangling versioned Windows shortcut targets to the launcher
2026-08-06 20:31:36 +08:00
SivanCola cfc6c6f121 Merge pull request #7751 from SivanCola/fix/cache-metrics-accounting-followup
Fix cache accounting, snapshot races, and adaptive TTL churn / 修复缓存计费、快照竞态与 TTL 抖动
2026-08-06 19:47:17 +08:00
SivanCola a4d85f6e2a Merge pull request #7740 from esengine/fix/cli-feedback-integration-de
feat(cli): version metadata, shell completion, MCP source display / 版本元信息、Shell 补全与 MCP 来源展示
2026-08-06 18:45:43 +08:00
SivanCola 102fbcf784 Merge pull request #7737 from SivanCola/feature/codex-stream-interrupt-recovery
fix(agent): replay interrupted streams atomically / 原子重放中断的模型流
2026-08-06 18:28:52 +08:00
SivanCola 6c22ae0a3f fix(desktop): repair Windows uninstall registration / 修复 Windows 卸载注册项 (#7729)
* fix(desktop): repair Windows uninstall registration

Problem:
- Users upgrading from legacy 0.53 builds can retain a stale Apps & features entry whose uninstaller removes the current installation.
- Versioned Windows updates do not refresh the registered display version.

Root cause:
- The legacy Tauri and current Wails installers use different HKCU uninstall-key identities.
- Staged NSIS updates intentionally skip registry writes and left metadata stale.

Fix:
- Reuse and migrate a legacy install root only when its uninstaller is present, then delete the legacy alias only after writing the current entry.
- Reconcile owned Wails registrations after version activation while leaving portable, unrelated, and legacy-only trees untouched.
- Add focused migration, ownership, installer-ordering, and update-handoff coverage.

Verification:
- cd desktop && go test ./...
- GOOS=windows GOARCH=amd64 go test -c ./internal/winuninstall
- GOOS=windows GOARCH=amd64 go test -c ./cmd/update-helper
- git diff --check

* Prevent legacy installer from reclaiming current install

Problem: Re-running the Tauri 0.53 installer after a migration could overwrite the current Reasonix uninstaller and recreate the stale uninstall entry.

Root cause: Tauri persisted its install root in a separate manufacturer/product registry value that the migration left behind.

Fix: Clear only a same-root legacy default value before deleting the old uninstall alias, preserve named values and separate legacy roots, and cover the registry and NSIS ordering contracts.

Verification: desktop go test ./...; focused race test; Windows amd64 test cross-compiles; makensis macro smoke compile; git diff --check.
2026-08-06 17:37:42 +08:00
SivanCola fbab42452a fix(desktop): keep reused blank sessions on the default provider / 修复复用空白会话选错默认供应商 (#7736)
* fix(desktop): align reused blanks with default model

Problem:
New-session actions could reuse an empty tab whose same-named model still belonged to a different provider.

Root cause:
The blank-tab reuse path returned before applying the resolved new-session default, and stale empty-session metadata could override the corrected tab model during startup.

Fix:
Canonicalize new-session refs, retarget ready blank runtimes through the failure-atomic model switch, restart superseded startup generations, and persist the canonical provider/model identity.

Verification:
- cd desktop && go test ./...
- cd desktop && go test -race . -run 'TestEnsureBlankTab(RetargetsReusedSameNameModelToDefaultProvider|RestartsStartingBlankWithDefaultProvider|UsesGlobalSessionDefaultsForModelAndToolApproval|ReusesExistingBlankTab|ReusesPrecreatedBlankBeforeControllerReady)' -count=1
- cd desktop && go vet ./...
- git diff --check

* fix(desktop): serialize blank model persistence

Problem:
A reused blank session could finish writing the default provider after a newer explicit model selection, leaving the live controller and persisted model metadata inconsistent.

Root cause:
The reuse path persisted the default model after SetModelForTab had released runtimeRebuildMu, so the stale completion was outside the model-switch last-click-wins boundary.

Fix:
Persist canonical model refs inside SetModelForTab while the rebuild mutex is held, conditionally repair same-model stale metadata through the same mutex, and remove the unlocked follow-up write.

Verification:
- cd desktop && go test ./...
- cd desktop && go test -race . -run 'TestEnsureBlankTab(RepairsStaleStoredProviderWhenRuntimeAlreadyDefault|ConcurrentModelSwitchKeepsLastSelection|RetargetsReusedSameNameModelToDefaultProvider|RestartsStartingBlankWithDefaultProvider)' -count=1
- cd desktop && go test -race . -run 'TestSetModelForTab(ReusesCurrentSessionLease|WaitsForConcurrentBlankSessionLease|LeaseHeldKeepsCurrentController|ReattachesDetachedRuntime)' -count=1
- cd desktop && go vet ./...
- git diff --check
2026-08-06 17:34:19 +08:00
SivanCola 6af8d1f5b5 fix(goal): remove token hard limits (#7725)
Problem:
Goal mode could pause before the first provider request when the assembled context exceeded an inferred 200k/400k token quota. The request-budget gate could also remain attached after a long Goal and block later ordinary instructions.

Root cause:
The host conflated cumulative autonomous-work safety with per-request context size, enforcing class-derived token ceilings at every provider boundary.

Fix:
- remove provider request-budget admission and token-based Goal pauses
- retain turn/no-progress safeguards and observational token accounting
- migrate legacy budget_tokens sidecars without losing todos
- classify bare fault goals for the write turn quota and update CLI/Desktop/docs

Verification:
- go test ./internal/provider ./internal/agent ./internal/control ./internal/acp
- go test -race ./internal/agent ./internal/control
- git diff --check
2026-08-06 17:12:46 +08:00
SivanCola 5509b36df6 fix(cli): transcript scroll state, wrap cache, and mouse re-enable / 滚动状态机、wrap 缓存与鼠标重启用 (#7706)
* fix(cli): transcript scroll state, wrap cache, and mouse re-enable

Problem
- Opening approval/chooser panels shrank the viewport and flipped AtBottom
  false, which permanently disabled tail-follow until a manual return to
  bottom (#6430). Long sessions with frequent panels looked "broken" (#6978).
- Every streaming commit re-wrapped the entire joined transcript, so long
  sessions paid full O(history) cost on each keystroke of agent output.
- Windows ConPTY dropped mouse tracking after long turns/resize/focus loss;
  wheel then became Up/Down history cycling (#7583). /mouse toggle twice was
  the only recovery.
- Composer cursor coordinates were not clamped to terminal bounds after VS
  Code fullscreen/resize storms (#6282, #7236).

Root cause
- Tail-follow relied on a pre-Update AtBottom snapshot, not an explicit
  followTail/userScrolled mode; modal height changes were misread as user
  scroll. Wrap cache did not exist. Mouse mode was set only in View() without
  re-emitting enable sequences. Cursor position was raw offset math.

Fix
- Explicit scrollFollowMode: wheel/PgUp/scrollbar leave follow; empty Enter,
  Ctrl+End, forceGotoBottom restore it. Height-only modal changes keep
  followTail and re-pin when appropriate.
- Per-block wrap cache with append-only extension; full rebuild on width
  change, mid-history rewrite, or dirty reflow.
- Rate-limited mouse enable on resize, FocusMsg, and TurnDone.
- clampCursorToTerminal on both alt-screen and native-scrollback paths.

Verification
- go test ./internal/cli -count=1
- TestModalOpenDoesNotDisableTailFollow
- TestUserScrollBreaksAndEmptyEnterRestoresFollow
- TestWrapCacheAppendOnlyMatchesFullRebuild
- TestLongTranscriptAppendKeepsFollowAndDoesNotFullRebuildEachTime (2k blocks)
- TestMouseReenableRateLimit, TestClampCursorToTerminal

Refs #6282 #6430 #6978 #7028 #7180 #7236 #7537 #7583 #6563

* fix(cli): suffix wrap invalidation, drag scroll sync, trailing mouse re-enable

Problem
- streamAnswer set transcriptDirty → forceFullWrap rebuilt the entire wrap
  cache on every flushed paragraph, the long-session hot path #7706 claimed
  to remove.
- Scrollbar drag only synced scrollMode on MouseRelease, so agent events
  mid-drag still saw followTail and GotoBottom.
- Mouse re-enable rate limit dropped in-window requests with no trailing
  retry; the last resize in a storm could leave ConPTY without tracking.
- Lint: SA9003 empty branch, unused resetThenEnableMouseTracking/modalOpen.

Root cause
- Collapsed "dirty" with "full rebuild"; gesture state lagged YOffset; leading
  edge only rate limit.

Fix
- invalidateWrapFrom / rewriteTranscriptBlock for live rewrites; Update only
  force-full-wraps on width change or history shrink; suffix path re-wraps
  from wrapBlockCount; feedViewportContent uses SetContentLines.
- dragScrollbar and edge autoScroll sync scrollMode on every YOffset change.
- maybeReenableMouse sets pending + arms trailing timer (mouseReenableMsg);
  controllable mouseNowFn for tests without sleep.
- batchCmds collapses nil; mouse re-enable only on real size change.

Verification
- go test ./internal/cli -count=1
- TestStreamAnswerSuffixInvalidationNotFullRebuild
- TestScrollbarDragMotionSyncsBeforeRelease
- TestMouseReenableTrailingEdge (fake clock)
- BenchmarkStreamAnswerSuffixWrap hist=500/1000/2000 (~const allocs/op)

Refs #6430 #6978 #7583 #6282 #7236

* fix(cli): clear Integration C lint and docs-impact blockers

- Fill empty answerIdx branch with t.Fatal; use wrappedContentString
- Combine fakeNow declare/assign; drop unused mouseReenableSeq generation
- Trailing mouse timer is single-armed pending only (no stale seq)

Verification: go test ./internal/cli -count=1; staticcheck ./internal/cli
2026-08-06 17:02:56 +08:00
SivanCola 835c827dea fix(cli): slash catalog cache and input key contracts / 斜杠补全缓存与输入快捷键契约 (#7690)
* fix(cli): cache slash catalog and harden input key contracts

Problem:
- Every keystroke rebuilt the full slash menu (skills, commands, MCP
  prompts, extension actions), causing lag after / and with many skills
  (#6417, #7090).
- Ctrl+D always quit, even with draft text in the composer (#7638-class).
- Shift+Tab failed on terminals that emit "backtab" (#6660).
- @ completion scanned to end-of-line, ignoring caret position (#6719).

Root cause:
- slashItems() reconstructed skill descriptions on every updateCompletion.
- chatTUI intercepted ctrl+d before the textarea forward-delete binding.
- activeAtToken assumed the caret was at EOF.

Fix:
- Immutable slash catalog rebuilt only when commands/skills/host sources
  change; keystroke filtering uses the cached snapshot.
- Ctrl+D forward-deletes when non-empty; quits only when idle and empty.
- Accept "backtab" as Shift+Tab for plan-mode cycling.
- activeAtToken / acceptCompletion respect caret byte offset; accept
  moves the caret only to the end of the inserted token.
- BenchmarkSlashCompletionKeystroke with 1000 skills (~0.2ms/op locally).

Verification:
- go test ./internal/cli -count=1
- go test ./internal/cli -bench=BenchmarkSlashCompletionKeystroke -benchtime=50x

* fix(cli): correct completion cursor span and slash catalog hot path

Problem
- inputCursorByteOffset treated textarea Cursor.X as content columns, but
  that coordinate includes the "❯ " prompt gutter (+2), so mid-line @ accept
  ate the trailing space (see @foo and more → see @foobar.mdand more).
- accept replaced only [@, cursor), leaving a token suffix (@foobar.mdo).
- slashItems still fingerprinted the full catalog on every keystroke
  (~0.9MB/op heap churn) despite the cached slice.
- Ctrl+D treated whitespace-only input as empty and quit.

Root cause
- Prompt-relative cursor without composerPromptWidth adjustment; incomplete
  token span; fingerprint-based invalidation on the filter hot path;
  TrimSpace emptiness check.

Fix
- Subtract composerPromptWidth and map rune→byte for the caret offset.
- Track replaceFrom/replaceTo for the full token; accept replaces [from,to).
- Rebuild slash catalog only after invalidateSlashCatalog (model switch,
  skill rescan, /reload-cmd); ordinary keystrokes only filter.
- Ctrl+D uses raw input.Value() != "".
- Tests: full-token accept, prompt-gutter cursor, whitespace Ctrl+D,
  shift+tab and backtab encodings; benchmark reports allocs.

Verification
- go test ./internal/cli -count=1
- go test ./internal/cli -bench=BenchmarkSlashCompletionKeystroke -benchmem -benchtime=50x
  → ~0.11ms/op, ~428KB/op, ~78 allocs/op (warm catalog; no fingerprint walk)

Refs #6719

* fix(cli): caret-limited @ query and host-driven catalog invalidation

Problem
- activeAtToken filtered with the full token past the caret (@fo|o → "foo"),
  so mid-token edits matched the trailing half instead of the typed prefix.
- MCP host updates (MCPSurfaceReady, connect/disconnect/remove/import, auth
  clear) refreshed m.host without invalidating the slash catalog, so prompt
  menus stayed stale (slashCatalogOnce remained true).
- SA9003 empty branch in TestCtrlDForwardDeletesWhitespaceOnly blocked lint.
- modeToggleKey lived only in tests and did not exercise production "backtab".

Root cause
- Collapsed filter query and replace span into one slice; host assignment
  paths bypassed catalog invalidation; test-only key helper.

Fix
- activeAtToken returns query = val[at+1:cursor] and end = tokenEnd (full
  replace span unchanged).
- refreshHostAndInvalidateSlashCatalog / setHostAndInvalidateSlashCatalog
  cover all host mutation paths including MCPSurfaceReady and /mcp add|remove.
- modeToggleKey is production; Update handles both encodings via the helper;
  tests drive KeyTab+Shift and Text:"backtab".
- Drop empty if-cmd branch.

Verification
- go test ./internal/cli -count=1
- TestMCPSurfaceReadyInvalidatesSlashCatalog
- TestActiveAtTokenFullSpanAndMidCursor (query fo, span @foo)
- TestShiftTabAndBacktabBothAccepted through Update

Refs #6719
2026-08-06 15:40:27 +08:00
SivanCola 914c691819 Persist private temp files across Bash calls / 让 Bash 临时文件在会话内持久 (#7664)
* fix(sandbox): persist private temp files per session

Problem:
Linux bubblewrap created a fresh tmpfs at /tmp for every Bash call, so agents lost temporary scripts and files between commands.

Root cause:
The sandbox lifetime was per process invocation and had no logical-session temporary resource or process lease ownership.

Fix:
Add a session-private temporary manager with generation rotation, owner and process leases, stale cleanup, cross-platform temp environment propagation, ACP terminal support, and hot-rebuild/sub-agent lifecycle wiring.

Verification:
- go test ./...
- cd desktop && go test ./...
- go test -race ./internal/sessiontemp ./internal/filelock
- git diff --check

* fix(cli): preserve session temp across rebuilds

Problem:
The session-private temp contract was automatic, but the CLI rebuild paths lacked direct lifecycle coverage and the cross-platform guidance did not make the zero-configuration contract explicit.

Root cause:
CLI model/profile rebuilds carried the manager through inline wiring, while Windows PR CI did not execute the real standard-temp cross-call test.

Fix:
Centralize the CLI manager handoff, cover /model, /effort, and /reload manager/generation retention, document automatic TMPDIR/TMP/TEMP injection with portable examples, and add focused Windows CI coverage.

Verification:
- go test ./internal/tool/builtin
- go test ./internal/cli
- go test -race ./internal/cli -run 'TestCLIHotRebuildPathsKeepSessionTemp|TestSessionTempFromCLIControllerHelper' -count=1
- Windows amd64 test-binary cross-compilation for builtin and CLI packages
- git diff --check

* test(windows): cover selected session temp shell

Problem: the Windows regression assumed every host selects PowerShell, but Reasonix prefers Git Bash when it is installed, so CI fed PowerShell syntax to bash.\n\nRoot cause: the test chose command syntax from GOOS instead of the shell resolver used by production.\n\nFix: derive syntax from the resolved shell and explicitly exercise native PowerShell as a second Windows subtest.\n\nVerification: go test -count=1 -run '^TestBashSharesSessionTempAcrossCalls$' ./internal/tool/builtin; git diff --check.
2026-08-06 15:02:39 +08:00
84988e8cdb fix(cli): TUI diagnostics, keyring timeout, Reasonix-home paths / 启动诊断、keyring 超时与 home 路径加固 (#7663)
* fix(cli): harden TUI diagnostics, keyring migration, and home paths

Problem:
- Interactive TUI freezes left zero-byte cli-tui logs and unrecoverable
  terminals on Windows ConPTY and stuck D-Bus startups (#7435, #7507).
- Global hooks/doctor/capdiag could miss the real Reasonix home on Windows
  AppData and REASONIX_HOME isolation (#7411, #7331).
- Legacy keyring migration blocked startup when Secret Service hung.
- #7600's automatic HOME rewrite risked breaking isolation contracts.

Root cause:
- Diagnostics started after heavy work and never flushed milestones/heartbeats.
- Hooks resolved global settings via derived ~/.reasonix rather than the exact
  Reasonix home used by the rest of the product.
- Keyring probes ran unbounded per key with file import ordered after D-Bus.
- Subprocess HOME rewriting conflates service HOME with Reasonix isolation.

Fix:
- Start TUI diagnostics before config/controller work; record milestones and
  flush immediately; 1s heartbeat with 10s stall → goroutine dump + Program.Kill.
- Unify hook/capdiag/doctor on exact ReasonixHomeDir; public paths show
  <reasonix-home>.
- Legacy credential migration: files first, then shared 1s keyring budget with
  empty-success markers; timeout leaves no marker for retry.
- Doctor warns when process HOME ≠ account home without REASONIX_HOME; do not
  auto-rewrite subprocess HOME (#7600 not adopted).

Verification:
- go test ./internal/cli ./internal/config ./internal/hook ./internal/capdiag ./internal/doctor -count=1

Co-authored-by: Zhiyao Wen <[email protected]>
Co-authored-by: zhaotian1 <[email protected]>

* fix(cli): isolate keyring probes and tighten migration markers

Problem:
- In-process keyring timeouts could leave hung D-Bus goroutines and
  collapse found/absent/error/timeout into a single false, risking wrong
  migration-done markers (#7507 audit).
- Keyring markers also skipped legacy credential *file* import.
- doctor HOME warning leaked absolute account paths into shareable reports.
- lint failed on an unused diagnostics mutex; CodeQL flagged SHA-256 of env
  names as secret-like storage.

Root cause:
- Timeout wrappers cannot cancel platform keyring calls in-process.
- skip() treated keyring markers as global migration completion.
- redactHome only knows the process HOME, not the OS account home.

Fix:
- Run legacy keyring batch probes in an isolated helper child with a shared
  1s deadline; parent Kill+Wait on timeout (no parent goroutine leak).
- Four-state outcomes: found / absent / error / timeout; write markers only
  on confirmed absent.
- File import always runs first; markers only filter keyring probes.
- Marker paths use sanitized env names under legacy-keyring-checked/.
- doctor reports HOME mismatch without embedding either path.
- Watchdog WaitGroup drains before closing the diagnostic log file.

Verification:
- go test ./internal/config ./internal/doctor ./internal/cli ./internal/hook ./internal/capdiag -count=1
- go vet ./internal/cli ./internal/config ./internal/doctor

* fix(cli): keyring four-state to metal and secret-free helper

Problem:
- Platform keyring probes still returned (string, bool), so D-Bus/unlock
  failures were treated as absent and could write migration markers.
- The isolated helper printed keyring secrets on stdout, making any
  process that can launch reasonix a credential oracle.
- Watchdog Close waited only 2s, so a stuck Stack/Sync/Kill could write
  a closed diagnostic file.
- Marker names folded distinct keys onto "_" collisions.

Root cause:
- Four-state status stopped at the batch layer; the Secret Service path
  collapsed every error to false.
- Helper was designed as a value RPC instead of an in-child store step.
- Close used a timed wait instead of joining the watchdog fully.

Fix:
- legacyKeyringProbe returns found/absent/error at the platform layer
  (ErrNotFound/empty search → absent; transport/unlock/session errors →
  error).
- Helper stores found secrets via StoreCredentialLines and emits only
  key+status metadata on stdout (no value field).
- Parent never receives secret Values from the helper path; markers only
  on confirmed absent.
- Watchdog WaitGroup is joined fully before closing the log file.
- Marker basenames use base64.RawURLEncoding of the env name.

Verification:
- go test ./internal/config ./internal/cli ./internal/doctor -count=1
- New tests: error does not write marker; helper JSON has no secrets;
  RawURL marker collision-free; four-state in-process outcomes

* fix(cli): drop keyring helper oracle; atomic store-if-absent

Problem:
- The REASONIX_LEGACY_KEYRING_HELPER entrypoint let any process that can
  launch reasonix dump keyring secrets into a caller-controlled
  REASONIX_HOME/.env (disk oracle after stdout was scrubbed).
- Legacy import could overwrite a concurrent settings write or tombstone
  because presence was checked outside the credential lock.

Root cause:
- Migration used an external helper to bound D-Bus hangs, which reopened
  a secret export surface via StoreCredentialLines + controllable home.
- store used unconditional StoreCredentialLines after a TOCTOU check.

Fix:
- Remove the helper entrypoint entirely (main + env dispatch).
- Linux/BSD Secret Service probes use godbus CallWithContext under a
  shared 1s batch deadline so stuck D-Bus calls cancel in-process.
- Found secrets are written only via storeCredentialIfAbsentAndNotCleared
  (same advisory lock re-checks missing/not-cleared before write).
- Channel-forced tests cover user-value and tombstone races without sleep.

Verification:
- go test ./internal/config ./internal/cli ./internal/doctor -count=1
- REASONIX_LEGACY_KEYRING_HELPER=1 reasonix --version is a normal version
  print (no helper path, no oracle home writes)

* fix(cli): private ctx-bound D-Bus keyring and real probe races

Problem:
- dbus.SessionBus left process-global godbus workers that failed goleak
  under Ubuntu/race/coverage.
- GetProperty and session Close used non-context call paths, so a stuck
  bus could still hang CLI past the 1s migration budget.
- Race tests called store-if-absent directly and never forced the
  production probe→store window.

Root cause:
- Shared SessionBus + GetProperty(Background) escaped the batch ctx.
- Channel tests interleaving happened outside lookupLegacyKeyringBatch.

Fix:
- Open a private ConnectSessionBus per probe, race Dial/Auth/Hello
  against the shared ctx, and always Close the connection.
- Resolve Collections and Close sessions only via CallWithContext(ctx).
- Drive store-if-absent races through a blocking probe stub inside
  lookupLegacyKeyringBatch (user write and tombstone cases).
- go mod tidy promotes godbus to a direct dependency.

Verification:
- go test ./internal/config -count=1
- go test -race ./internal/config -count=1
- go test ./internal/control -count=1

* fix(cli): bind keyring bus connect to batch context

Problem:
- ConnectSessionBus had no dbus.WithContext, so a stuck Auth/Hello after
  Dial could keep the connect goroutine and late reaper parked forever
  even though the caller returned at 1s.

Root cause:
- Connection lifecycle was only raced externally; godbus never received
  the migration ctx to Close the transport on deadline.

Fix:
- SessionBusPrivateNoAutoStartup(dbus.WithContext(ctx)) then Auth+Hello,
  so cancel closes the transport and unblocks Auth/Hello I/O.
- Avoid dbus-launch (NoAutoStartup): missing session address fails closed
  instead of hanging in CombinedOutput.
- Outer select still bounds the caller; late *Conn is always closed.
- Document residual: pure Dial hang before newConn remains a godbus limit.

Verification:
- go test ./internal/config -count=1
- go test -race ./internal/config -count=1
- go test ./internal/control -count=1
- GOOS=linux go test ./internal/config -c

* ci: allow Windows smoke runner variance

Problem: The Windows smoke step regularly takes 7-9 minutes and the current run exceeded its 10-minute job budget even though vet and build succeeded.

Root cause: The job-level timeout leaves too little headroom for hosted Windows runner variance.

Fix: Raise only the pull-request Windows smoke job budget to 15 minutes while retaining the per-package 3-minute Go test timeout.

Verification: YAML parse; git diff --check; go test ./internal/cli ./internal/config -count=1.

---------

Co-authored-by: Zhiyao Wen <[email protected]>
Co-authored-by: zhaotian1 <[email protected]>
2026-08-06 14:39:56 +08:00
918d8b1a9a fix(cli): land independent CLI feedback PRs on main-v2 (#7656)
Rebase and verify the small, independently-testable CLI fixes onto latest
main-v2 so they stop rotting behind required checks.

Problem:
- Several high-value contributor PRs were MERGEABLE but BLOCKED: required
  lint/race/test matrix never re-ran against current main-v2.
- Users still hit missing --profile dispatch, machine-session resume,
  usage-receipt clutter, Ctrl+Backspace, Insert copy/paste, and long-session
  rewind list overflow.

Root cause:
- Fork heads drifted from main-v2; reusing their green checks was unsafe.
- Features are small and orthogonal, so they belong as focused landing work
  before the larger integration PRs.

Fix:
- #7621: route bare `--profile` into the interactive session path.
- #7608: resolve `run --resume` against machine session IDs and document QUERY.
- #7587: add `[ui].show_turn_usage` (default true) without disabling accounting.
- #7605: accept Ctrl+Backspace as DeleteWordBackward with Alt+Backspace/Ctrl+W.
- #7262: Ctrl+Insert copies selection without Ctrl+C side effects; Shift+Insert
  pastes via the native clipboard path (SSH-safe).
- #7202: store raw user text in checkpoints and window the rewind picker for
  long sessions.

Verification:
- go test ./internal/cli ./internal/config ./internal/control -count=1
- focused tests for profile, machine resume, show_turn_usage, word motion,
  Insert keys, rewind windowing, and raw checkpoint prompts

Co-authored-by: Hezi-crypto <[email protected]>
Co-authored-by: zhaotian1 <[email protected]>
Co-authored-by: azhonglee <[email protected]>
Co-authored-by: rixzkiye <[email protected]>
Co-authored-by: rongbaichuan <[email protected]>
Co-authored-by: mife <[email protected]>
2026-08-06 11:56:17 +08:00
SivanCola 73bc4e0c1a Merge pull request #7576 from SivanCola/feature/compact-decision-surfaces
feat(desktop): compact decision surfaces and record local receipts / 压缩决策弹窗并记录本地回执
2026-08-05 21:12:39 +08:00
SivanCola 627b05170b Merge pull request #7368 from SivanCola/fix/remote-workbench-reconnect-hardening
test(remote): lock Remote Web recovery after SSH drops
2026-08-05 20:36:29 +08:00
SivanCola 40dd497e11 Merge pull request #7373 from SivanCola/feature/extension-kernel-protocol-v1
Unified extension kernel and Extension Protocol v1 / 统一扩展内核与 Extension Protocol v1
2026-08-05 20:35:58 +08:00
SivanCola 62f38dfec1 Merge pull request #7615 from SivanCola/fix/updater-recovery-webkit-diagnostics
fix(desktop): recover stuck updates and legacy WebKit / 修复更新卡死与旧版 WebKit
2026-08-05 20:08:27 +08:00