mirror of
https://github.com/esengine/DeepSeek-Reasonix.git
synced 2026-11-11 17:10:38 -10:00
8f7e4a0748094de92aa17e534bf54839b2cecaab
159
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
ec866730e0 |
fix(gitcmd): host git never runs repository-configured programs
Every git invocation the host makes goes through gitcmd, and each one must leave the programs a repository's own configuration names unexecuted: filter and merge drivers, textconv and external diff, fsmonitor, hooks, signature verifiers, submodule-boundary programs and lazy-fetch transport. Driver neutralization now covers every subcommand, not diff alone, and takes driver names from git itself rather than from a separate reading of .git/config. - Fixed keys are overridden with -c: core.fsmonitor (empty, not "false", which git before 2.35.2 reads as a hook name), core.hooksPath, log.showSignature, merge.verifySignatures, diff.submodule, gc.auto and maintenance.auto and safe.bareRepository=explicit; submodule.recurse=false for checkout, switch, restore, reset, merge and read-tree (host branch switches and merges never update submodules, whatever the user's global submodule.recurse), and elsewhere only where the repository's own config sets it. GIT_NO_LAZY_FETCH=1 stops a missing object becoming a fetch through the repository's remote (git 2.44+); a historical patch that fails for that reason returns gitcmd.ErrObjectNotLocal, decided by git's own --missing=print listing. - diff, log and show carry --no-ext-diff and --no-textconv; status and diff carry --ignore-submodules=dirty. The subcommand is found past global options, so a call passing -C inside args is hardened too. - Filter and merge drivers defined at local or worktree scope are listed by git itself before each invocation (includes followed) and overridden empty; user and system scope stay live; a name -c cannot address fails closed (ErrRepositoryDrivers). - worktree add uses --no-checkout and populates through gitcmd inside the new worktree, so a conditional include keyed on the new gitdir or branch is seen by the driver listing there rather than run by the add's checkout. The reset takes the worktree-mutation timeout, and a failed reset removes both the worktree and the branch the add created. worktree remove uses --force after the existing hardened clean-tree check, so git does not run status inside the linked worktree with its config. - Launcher ls-remote runs Detached with gitcmd's environment (only PATH is taken from the launcher's declared env), requires an https://, ssh:// or git:// remote (case-insensitive; http:// and local paths are refused), and passes the remote and ref past -- so a leading-dash value is an operand. - worktree git calls with extra environment keep gitcmd's environment. - Repository identity is resolved once, when a session opens: boot runs gitcmd.Open (rev-parse --absolute-git-dir --git-common-dir --show-toplevel) before any command, the controller carries the resulting gitcmd.Repo, and a rebuild of the same workspace passes it on. Every later host call runs through the Repo with GIT_DIR, GIT_COMMON_DIR and GIT_WORK_TREE set, so a nested .git, a commondir file, a rewritten .git file or a damaged HEAD written afterwards cannot substitute another repository or its config; a damaged HEAD fails closed. Threaded through the TUI status line, cli review (resolved once before the review agent runs), the desktop changes panel, change detail, history, commit detail, branch list/checkout/create and tab branch label (the tab session's identity; a tab with no session yet resolves at display), worktree Inspect/Create/RollbackCreate, and the whole merge-back flow: the source and worktree identities are recorded in the merge metadata at creation (the worktree's resolved right after worktree add) in metadata.repo.json beside metadata.json, whose shape older builds decode strictly and so is unchanged; every inspect, auto-commit, merge and cleanup call runs against them, and a worktree without that file resolves them from the roots. InspectMerge finds the allocation root from the managed layout instead of asking git in the worktree. - Repo.Verify applies Open's test to a stored identity; the identities read from metadata.repo.json are verified before the merge flow uses them. - Auto-commit staging uses gitcmd Repo.StageAll: gitlinks present as directories and untracked nested repositories are excluded with :(exclude,literal) pathspecs, because add -A checks a submodule by running git inside it under its own config. A gitlink keeps the commit the index records; an untracked nested repository leaves the worktree unverifiable and the auto-commit refuses. The worktree state token no longer runs git inside a changed nested repository. - Open accepts only a git dir belonging to the work tree that holds the directory: the work tree's own .git (sharing no other common dir) or a linked worktree's admin dir under <common>/worktrees whose gitdir file names that work tree's .git. A .git file or core.worktree pointing at another checkout is ErrNotRepository. Submodule checkouts and --separate-git-dir repositories opened directly are therefore treated as not version-controlled. Regression tests use real git with payload scripts: the gitcmd config-key matrix (signatures, lazy fetch, submodule boundary), the worktree call sites (conditional includes on Create, linked-worktree config on RollbackCreate), the status readout, the launcher's non-URL remote rejection, identity pinning after a nested .git, a commondir file and a damaged HEAD (config origin and filter marker), Open refusing another checkout's git dir (.git file, core.worktree, foreign admin dir), StageAll with nested repositories named like pathspec magic or globs, metadata.json read by a strict older decoder, merge-back without the identity file, StageAll and merge-back auto-commit past a gitlink with its own filter, merge-back after the worktree's .git file is pointed at another git dir, the status line and desktop changes panel of a workspace that later gains its own .git, and a global submodule.recurse=true against host checkout and reset. The list-then-run race is left to a follow-up that denies sandboxed writes to .git config and hooks. |
||
|
|
9083943095 |
fix(plugin): decode an MCP server's code-page stderr before reporting it
On a Chinese Windows, cmd.exe reports a missing command in the console
code page (GBK), not UTF-8. The stdio transport kept the stderr tail as
raw bytes, so an MCP startup failure such as "'read-only-mysql-mcp-server'
is not recognized as an internal or external command" reached the settings
page as U+FFFD mojibake and the user could not see why the server failed.
The stderr tail now goes through encoding.DecodeOutput, the one decoder
for process output. The buffer keeps the last 16 KiB, so only its head
can be cut: tailBuffer records when it dropped bytes and passes
Cut{Head: cut}. The end is never cut, because it is whatever the server
last wrote. Output that is not UTF-8 is decoded from the raw bytes, so a
GBK tail that begins with a CJK character keeps that character.
Tests:
- TestStdioFailureDecodesCodePageStderr starts a real child that writes
GBK to stderr and exits.
- TestStderrTailDecodesTheServersCodePage covers GBK that starts with a
CJK character, both uncut and cut, plus UTF-8 cut mid-character.
All of them fail on origin/main-v2 and pass with this change.
Refs #7857
|
||
|
|
7d9e05a587 |
fix(provider): root object schemas always carry a required array (#10614)
Relays that re-serialize tool schemas into typed structs turn an omitted "required" into null, and strict upstreams then reject the whole request: "Invalid schema for function 'get_goal': null is not of type 'array'". get_goal's schema has no required list, and neither do many MCP tools. CanonicalizeSchema now adds "required": [] to every root object schema that lacks one (including the no-argument fallback), so builtin and MCP tools reach the provider with an explicit array. The effect test checks every tool in the real provider request. Cache impact: the tool schema bytes change once (get_goal gains "required": []), so ToolsHash and PrefixHash move in both golden baselines and the first request after upgrade misses the prefix cache. Goldens regenerated with REASONIX_UPDATE_GOLDEN=1. |
||
|
|
4ba2fde3d1 | Merge #8204: fix(plugin): fail closed when confined MCP loses its sandbox backend | ||
|
|
2f0e350dc7 |
Remove the retired Windows sandbox backend
Windows no longer enforces an OS-level shell sandbox, so the restricted-token and AppContainer backend in internal/winsandbox, the hidden helper subcommand dispatched by the CLI and Desktop entry points, the helper report channel, and the Spec fields only that backend read are deleted. Callers that classified helper failures or issued runner-authorization denial hints are simplified to the ordinary process-launch outcomes. The one part older installs still depend on moves to a leaf package, internal/winaclresidue: the conservative legacy credential DENY RX repair and a sweep of the crash-residue markers that v1.38.8 through v1.38.10 wrote before mutating ACLs. The sweep now runs in the background at process start instead of only before the next sandboxed command, and a marker is deleted only after every recorded ACE removal succeeded so a failed cleanup keeps its evidence. The repair no longer takes the sandbox root lock; live-owner markers still veto it. CI drops the sandbox wait knob and points the Windows credential ACL identity step at the new package. The Windows sandbox architecture document, its evidence directory, and the native verification script are removed with the backend. |
||
|
|
08ca31641a |
Fix Windows path identity and lock layering
Problem: Windows native tests failed every identity-aware file lock with ERROR_NOACCESS, while the first path-identity integration also exceeded repository size, complexity, and layering limits. Root cause: The Windows implementation queried FileCaseSensitiveInfo through GetFileInformationByHandleEx, which is rejected on the CI runner. The utility-layer filelock package also imported the higher-level pathidentity package directly, and several identity workflows remained embedded in oversized owners. Fix: Query per-directory case sensitivity with NtQueryInformationFile, preserving explicit unsupported-information fallbacks. Add an identitylock composition layer that resolves access paths and passes comparison keys into filelock's explicit local-queue API, then migrate production file locks to it. Keep custom session, repair, and workspace lock protocols on explicit keys, and extract path and acquisition logic into focused owner files. Verification: - go run ./tools/repolint - go test -run '^$' ./... - go test -race ./internal/filelock ./internal/identitylock ./internal/workspacelease - Windows amd64 cross-compile for affected root packages |
||
|
|
9ec875a684 |
Stabilize Windows release validation / 稳定 Windows 发版验证 (#10468)
* Stabilize Windows release validation * Respect test file size baseline * Make lazy cache-miss test await publication Problem: The race CI could observe the MCP host client before the lazy registry swap completed, making TestLazyCacheMissAsyncSpawn fail even though the product state machine was still progressing correctly. Root cause: The test polled Host.ServerNames, but host registration occurs before lazySpawn publishes real tools and closes its completion signal. Fix: Drive the advertised second-turn connect call, which waits on the production completion signal, before asserting the registry contents. Verification: - go test -race ./internal/plugin -run '^TestLazyCacheMissAsyncSpawn$' -count=50 - go test -race ./internal/plugin - go run ./tools/repolint - git diff --check * Remove machine-speed budget from transcript test Problem: The Windows control CI failed when a correct transcript history preparation exceeded a test-local five-second deadline on a loaded runner. Root cause: The regression tied the durable-cut contract to filesystem and runner throughput even though adjacent lifecycle coverage already uses the test context and the product operation correctly honors caller cancellation. Fix: Use the test lifecycle context while preserving every ready-state, durable-sequence, page-boundary, and final-message assertion. Verification: - go test ./internal/control -run '^TestTranscriptFollowInitialHistoryIsReadyAndPinnedToDurableCut$' -count=50 - go test ./internal/control - GOOS=windows GOARCH=amd64 CGO_ENABLED=0 go test -c ./internal/control - go run ./tools/repolint - git diff --check |
||
|
|
8f93ea2b2e |
Adopt SessionID-only Desktop persistence / 升级 Desktop SessionID 会话架构 (#10326)
* feat(session): add immutable desktop session headers Problem: Desktop session identity and persistence diverged across tabs, topics, and workspaces. Root cause: Local routing depended on project paths and disposable Topic projections instead of one durable SessionID authority. Fix: Advance the staged SessionID-only Desktop cutover while preserving remote and provider-visible behavior. Verification: Focused owning-package tests cover this stage; the assembled branch is validated by local gates and the pull request CI matrix. * feat(desktop): add durable workspace session registry Problem: Desktop session identity and persistence diverged across tabs, topics, and workspaces. Root cause: Local routing depended on project paths and disposable Topic projections instead of one durable SessionID authority. Fix: Advance the staged SessionID-only Desktop cutover while preserving remote and provider-visible behavior. Verification: Focused owning-package tests cover this stage; the assembled branch is validated by local gates and the pull request CI matrix. * refactor(desktop): use one canonical session service Problem: Desktop session identity and persistence diverged across tabs, topics, and workspaces. Root cause: Local routing depended on project paths and disposable Topic projections instead of one durable SessionID authority. Fix: Advance the staged SessionID-only Desktop cutover while preserving remote and provider-visible behavior. Verification: Focused owning-package tests cover this stage; the assembled branch is validated by local gates and the pull request CI matrix. * feat(desktop): bind canonical sessions to workspaces Problem: Desktop session identity and persistence diverged across tabs, topics, and workspaces. Root cause: Local routing depended on project paths and disposable Topic projections instead of one durable SessionID authority. Fix: Advance the staged SessionID-only Desktop cutover while preserving remote and provider-visible behavior. Verification: Focused owning-package tests cover this stage; the assembled branch is validated by local gates and the pull request CI matrix. * feat(desktop): make session lifecycle registry transactional Problem: Desktop session identity and persistence diverged across tabs, topics, and workspaces. Root cause: Local routing depended on project paths and disposable Topic projections instead of one durable SessionID authority. Fix: Advance the staged SessionID-only Desktop cutover while preserving remote and provider-visible behavior. Verification: Focused owning-package tests cover this stage; the assembled branch is validated by local gates and the pull request CI matrix. * feat(desktop): complete session id lifecycle cutover Problem: Desktop session identity and persistence diverged across tabs, topics, and workspaces. Root cause: Local routing depended on project paths and disposable Topic projections instead of one durable SessionID authority. Fix: Advance the staged SessionID-only Desktop cutover while preserving remote and provider-visible behavior. Verification: Focused owning-package tests cover this stage; the assembled branch is validated by local gates and the pull request CI matrix. * feat(desktop): replace topic sidebar with session browser Problem: Desktop session identity and persistence diverged across tabs, topics, and workspaces. Root cause: Local routing depended on project paths and disposable Topic projections instead of one durable SessionID authority. Fix: Advance the staged SessionID-only Desktop cutover while preserving remote and provider-visible behavior. Verification: Focused owning-package tests cover this stage; the assembled branch is validated by local gates and the pull request CI matrix. * docs(desktop): document session v5 authority Problem: Desktop session identity and persistence diverged across tabs, topics, and workspaces. Root cause: Local routing depended on project paths and disposable Topic projections instead of one durable SessionID authority. Fix: Advance the staged SessionID-only Desktop cutover while preserving remote and provider-visible behavior. Verification: Focused owning-package tests cover this stage; the assembled branch is validated by local gates and the pull request CI matrix. * refactor(desktop): isolate session persistence plumbing Problem: The SessionID cutover added lifecycle, fallback, and persistence logic directly to legacy hotspot files, exceeding incremental repository quality budgets. Root cause: New Desktop session authority state and controller fallback paths were represented as independent App and WorkspaceTab scalars and inline branches. Fix: Group Desktop session state by lifetime, extract controller/model fallback and prune persistence helpers, relocate the focused prune regression test, and ratchet repolint baselines to the new lower aggregate counts. Verification: go test focused Desktop SessionID persistence/migration/fallback cases; go vet for root session/control and Desktop modules; frontend typecheck, hooks lint, workspace browser test, CSS checks; repolint and git diff checks. * fix(frontend): account for SessionRef bridge payload Problem: App memory preparation failed because the SessionID cutover exceeded the initial raw JavaScript and CSS budget by 4.6 KiB, while every compressed and per-chunk budget remained green. Root cause: SessionRef bridge methods plus Workspace Session browser locale and type bindings add a small unavoidable startup payload. Fix: Ratchet only the initial raw payload ceiling from 2434.2 KiB to 2439.0 KiB, preserving 0.2 KiB headroom and leaving gzip, CSS, and chunk limits unchanged. Verification: pnpm build passes all lint, architecture, CSS, typecheck, Vite build, and bundle budget checks at 2438.8 KiB measured raw payload. * fix(desktop): complete SessionRef navigation contract Publish an explicit hydration-ready signal after an exact SessionRef commits, and expose active identity plus bounded expansion in the Workspace Session browser. Move all local browser benchmarks onto one layout-independent session navigation helper, align the browser mock with native hydration semantics, and refresh generated inventory and lint contracts. * fix(bench): await session projection readiness Treat Composer readiness and Workspace Session projection readiness as independent phases before deciding whether a benchmark target requires bounded expansion. * fix(frontend): preserve tab identity on mock session open * fix(session): close projection workers at shutdown * test(desktop): close session services deterministically * fix(desktop): fence canonical sessions by SessionRef Problem: Canonical Desktop tabs could still key caches, hydration and imported workspace membership through compatibility paths. Empty paths could collide, and legacy imports could publish a session before immutable workspace ownership existed. Root cause: SessionRef was not carried through every Meta and clear transition, async fences compared partial identities, and the legacy import publication did not atomically include the v5 SessionHeader. Fix: Use full SessionRef plus generation for frontend cache and hydration identity; return SessionRef from Meta and clear; validate registry attachment against immutable header cwd; publish legacy and paired-preview imports with manifest and header in one atomic directory publication; add targeted identity, import and workspace tests. Merge the latest main-v2 and regenerate protocol and Desktop inventory artifacts. Verification: - go test ./internal/session ./internal/control ./tools/desktopinventory -count=1 - go test ./desktop targeted protocol/session/workspace regressions - go test -race targeted internal/session and desktop identity tests - frontend SessionRef/hydration/cancel suites and test:typecheck - pnpm build with existing bundle budgets - go run ./tools/repolint * fix(session): remove obsolete import wrapper Problem: The SessionHeader import refactor left the old headerless publication wrapper unreachable, and CI's unused check correctly rejected the branch. Root cause: All callers had moved to publishLegacyImportWithHeader, but the compatibility helper remained after the cutover. Fix: Delete the dead wrapper and document the single atomic Header-aware publication path. Verification: - golangci-lint run --timeout=5m ./... - go test ./internal/session targeted import and query tests - git diff --check * fix(session): publish host-owned headers on legacy rebuild import Problem: Desktop model and settings changes that rebuilt a legacy-path session failed with "session header does not match requested ownership", so the workspace could not restart with the corrected root and history stayed unloadable. Root cause: Rebuild imported the legacy transcript through the one entry point that published no immutable SessionHeader. The later create validation compared the requested Desktop ownership against a session directory that carried no header, so every rebuild-imported session was permanently unusable rather than merely unlabelled. Fix: Carry SessionCreateOptions through boot.Options and rebuildWithPrevious so the rebuild import publishes header and manifest in the same atomic transaction as the other two Desktop import entry points. Desktop derives those options from the canonical runtime root, the resume path uses the Header-aware control call, and the superseded headerless rebuild wrapper is deleted now that it has no callers. Header validation reports which field disagrees instead of one opaque message. Verification: - go test ./internal/boot -run TestRebuild -count=1 - cd desktop && go test . -count=1 -run 'TestModelSettingsRunningToolContinuationKeepsOldConnection|TestEnsureBlankTab' * fix(desktop): pin the canonical v5 session root at App construction Problem: Fork creation failed with "publish fork workspace membership: session: nil session query", and canonical sessions could be created without the immutable workspace header the Workspace registry attaches by. Root cause: desktopSessionState left root empty until initializeDesktopSessionRoot ran during startup. Any path that reached the session service before that point - fork membership publication in particular - resolved a nil service and could not query or publish canonical state at all. Fix: Give newDesktopSessionState the production v5 root so a constructed App always owns one canonical root, and keep the legacy-derived fallback only for zero-value Apps in narrow tests. Cover the construction-time invariant directly, and make the fork and v3 catalog fixtures create sessions through real workspace ownership instead of headerless shortcuts. Verification: - cd desktop && go test . -count=1 -run 'TestCreateForkForTab|TestNewAppPinsDesktopV5SessionRootBeforeStartup|TestDesktopSessionServiceIsSharedAcrossWorkspaces|TestDesktopV3CatalogResumeRenameAndDeleteUseSessionIdentity|TestShutdownReleasesCachedSessionOwner' * fix(session): stop cold-query workers before closing live runtimes Problem: TestDesktopSessionServiceIsSharedAcrossWorkspaces failed only on Windows, where the temporary session store could not be removed after shutdown. Root cause: Shutdown ran CloseAll first and closed the query side afterwards. A cold-query worker could open a recovery projection after CloseAll had already collected the runtime set, so that Bolt handle outlived shutdown and kept the file locked on Windows. Fix: Close and join the query workers before CloseAll, so no worker can reopen a projection once live runtimes are being released. Verification: - go test ./internal/session -count=1 - cd desktop && go test . -count=1 -run TestDesktopSessionServiceIsSharedAcrossWorkspaces * fix(desktop): keep the unavailable fork action a single stable control Problem: The app-memory gate reported a persistent three-node DOM drift after GC in every shard, so the memory attribution check never settled. Root cause: An unavailable fork action rendered a second, id-bearing sr-only element whose useId changed on each transcript remount. Tooltip and React lifecycle kept the superseded text nodes referenced briefly, so repeated navigation left three extra nodes behind every settle. Fix: Move the unavailability reason into the button's accessible name and delete the generated description node and its id, so the action is one control with no remount-sensitive identity. Verification: - cd desktop/frontend && node --test src/__tests__/turn-fork-transcript.test.tsx * test(desktop): target the visible new-session action in browser fixtures Problem: The app-settings-motion browser group timed out for 30s waiting for .sidebar__quick-action after the remote-project assertion. Root cause: The fixture hard-coded the old sidebar markup. The Workspace session browser renders the new-session action as .sidebar__new, so the locator could never resolve once that surface replaced the Topic sidebar. Fix: Resolve the new-session action through one shared helper that accepts either visible surface, so layout changes do not silently strand the fixture. Verification: - cd desktop/frontend && pnpm test:app-browser * fix: keep allocation size hints free of arithmetic Problem: CodeQL reported four high-severity "size computation for allocation may overflow" alerts on this branch. Root cause: Three capacity hints added a length to a constant or to a second length. The sum is only a hint, but CodeQL correctly flags unchecked arithmetic that feeds an allocation size. Fix: Size each allocation from the single length that is known to bound it and let append grow the rare extra element. Verification: - go build ./... - go test ./internal/plugin -count=1 * fix(session): make Shutdown release runtimes a client never unbound Problem: desktop-windows-go failed every fork and shared-service test with "TempDir RemoveAll cleanup: ... being used by another process" on the session ownership lock and the recovery Bolt file, alongside "close session service err=session runtime still has client bindings". Root cause: Shutdown is the documented boundary a host must cross before releasing the store root, but it delegated to CloseAll, which refuses a bound runtime and returns without closing it. A binding a client failed to release therefore stranded the writer lease and recovery handles for the rest of the process. POSIX hides this because unlinking an open file succeeds; Windows reports it as an undeletable directory. Fix: Split the refusal policy from the teardown. CloseAll keeps refusing bound runtimes, because tearing one down under a live client is worse than leaving it. Shutdown is terminal: it drops the leaked binding, closes the runtime so the lease and recovery handles are released, and still reports ErrRuntimeBound so the leak stays visible rather than being swallowed. Verification: - go test ./internal/session -count=1 - go test ./internal/session -race -run 'TestService(CloseAll|Shutdown)' -count=1 * fix(desktop): keep workspace runtime activity visible in the session browser Problem: The desktop-browser group failed on "background jobs keep project activity visible" once the Workspace session browser replaced the Topic sidebar in the workbench layout. Root cause: The activity indicator lived inside ProjectTree, so replacing that surface silently dropped the capability: a workspace running background jobs no longer showed activity anywhere in the workbench sidebar. The paired assertion that activity clears also became vacuous, because an element that never renders trivially satisfies a count of zero. Fix: Move the rule to its own owner. RuntimeActivityIndicator matches the runtime store against a workspace identity instead of a ProjectNode, so both sidebars render the same activity state from one implementation, and the indicator class is no longer named after one surface. Both fixtures now assert the shared indicator, so the clearing check has something real to observe. Verification: - cd desktop/frontend && node bench/runtime-state.mjs - pnpm test:typecheck && pnpm lint:hooks && pnpm build * test(desktop): warm every measured fixture before the memory baseline Problem: All three app-memory shards reported post-gc-dom-or-listener-drift: the node count stepped up by exactly 3 on the first windowed round trip and then stayed flat for the remaining 128+ round trips across every phase. Root cause: The prelude warms the geometry and full fixtures with real round trips, but the only windowed visit before the baseline is the safety excursion, which runs with deliberately invalid geometry. The windowed surface therefore reached its first healthy render after the baseline was taken, so its one-time bounded setup was measured as displacement from a baseline that was never warm for that fixture. Per-navigation instrumentation confirmed the step happens once and never repeats, and the same sequence on the legacy sidebar is flat. Fix: Warm the windowed fixture the same way as the other two before sampling. The gate keeps its full accumulation power: every phase still samples against this baseline every 32 round trips, and the drift rule is unchanged. Verification: - cd desktop/frontend && REASONIX_APP_MEMORY_PROFILE=short node bench/app-memory.mjs (baseline 6533 stable, flat through full/windowed/safety/mixed/settled, verdict PASS) |
||
|
|
aec747b866 |
fix(desktop): enforce rooted file boundaries and stable CI shutdown
Problem: CodeQL flagged archive/upload sinks and a shared session-delete input flow. Coverage exposed a test racing the SSE EOF, and the Windows packaged smoke used a window-close signal for a graceful application quit. Root cause: File operations used absolute paths after custom validation, the SSE test did not wait for client-observed EOF, and taskkill could trigger the product's legitimate background-window behavior. Fix: Bind archive extraction and upload staging to os.Root, reject size overflow, validate session basenames with filepath.IsLocal, mark the loopback fixture cookie Secure, synchronize the EOF test, and exercise the packaged shell's real quit lifecycle with child-process cleanup checks. Verification: Rooted-file traversal/symlink/size tests under race; plugin full race/coverage and 100 repeated SSE EOF tests; session-delete HTTP/race tests and Windows cross-compilation; real Chromium fixture login; focused lint and repository checks; 17 packaging tests and a real macOS packaged app quit with both Electron and Go PIDs gone. Final-head CI verifies platform-specific gates. |
||
|
|
f7cce8468f |
fix(mcp): retire subprocesses before cancelling their contexts
Problem: Native Windows qualification intermittently failed to reconnect a single-instance MCP helper, leaving its listening port and temporary files occupied after forced termination. Root cause: SDK teardown cancelled the process context before the transport could offer stdin EOF. The graceful path also retained its Windows Job handle and released bounded instance slots before process retirement. Fix: Retire the current session and endpoint before cancelling their contexts, finish the Windows Job after a graceful exit, and return instance capacity only after cleanup. Cover shutdown and invalidation ordering plus Windows Job and slot retirement. Verification: The ordering regression failed on both old paths and passes 20 race repetitions. Native Windows reconnect failed 3 of 100 repetitions before the fix and passed 100 afterward; native Job and shutdown regressions pass 20 repetitions. Full root Go, plugin/proc race, Desktop MCP tests, root lint and repository lint pass. Final full Desktop qualification is still in progress. |
||
|
|
47ecb44df5 |
fix(doctor): adopt registry regressions and scope MCP aliases
Problem: Skill diagnostics could accept local MCP aliases that runtime preparation does not bind, or report another plugin's tools as ambiguous. Root cause: Diagnostic discovery omitted plugin ownership and reconstructed bindings from display names instead of preserving adapter identities. Fix: Share the runtime ownership filter, retain canonical adapter bindings, and preserve plugin discovery metadata. Adapt PR #9686's registered-builtin and unknown-name regressions with isolated registration. Remove dead code and use the standard HTTP status constant to satisfy lint. Verification: Root tests, affected-package race tests, full golangci-lint, repolint, and whitespace checks pass. Tests exercise real skill preparation, subagent tool scope, plugin ownership, and prefix-transformed metadata. Refs #9686 Co-authored-by: dadgrand <[email protected]> |
||
|
|
871038711d |
fix(mcp): harden Streamable HTTP compatibility / 修复 Streamable HTTP 兼容性 (#9602)
* fix(mcp): harden streamable HTTP compatibility Problem: Stateless Streamable HTTP servers could block startup on a buffered subscriptions/listen request. Transport-level 404 and structured 4xx responses could also poison or misclassify the shared session, while failed protocol negotiation leaked an allocated server session. Root cause: The optional SEP-2575 subscription call remained on the synchronous connect path, and go-sdk v1.7.0 lacked request rejection, stateless subscription 404, and failed-negotiation cleanup fixes. Reasonix also relied only on the SDK session-missing sentinel. Fix: Run Streamable HTTP subscriptions asynchronously with session-scoped cancellation, pin the upstream SDK commit containing the three transport fixes, and narrowly classify explicit JSON-RPC session-loss errors plus sessionless endpoint 404s. Add compatibility coverage for buffered and live subscription streams, stateless 404s, per-call 4xx responses, negotiation cleanup, and legacy sessionless POST-only servers. Verification: - go test ./internal/plugin -count=1 - focused HTTP compatibility tests repeated 30 times - focused go test -race repeated 5 times - go test ./... -count=1 - go mod tidy -diff - git diff --check * fix(mcp): require transport proof before replay Problem: Application-level JSON-RPC errors containing session-loss wording could be mistaken for an expired MCP transport session, allowing a side-effecting tools/call to be replayed. The desktop nested module also retained the previous MCP SDK version and failed CI tidy checks. Root cause: Session recovery trusted server error text without requiring HTTP transport-rejection evidence, and the root SDK bump was not propagated through desktop's replace-based module graph. Fix: Require an SDK transport-rejection marker plus HTTP 404 evidence for structured session-loss recovery, scope sessionless 404 handling to Streamable HTTP, add no-replay regression coverage, and synchronize desktop go.mod/go.sum. Verification: - go test ./... -count=1 - cd desktop && go test ./... -count=1 - focused MCP race tests repeated 5 times - go vet ./... - go run ./tools/repolint - root and desktop go mod tidy -diff - ./scripts/cache-guard.sh - git diff --check * chore(mcp): document exact error-tree traversal Problem: CI errorlint rejected the deliberate direct inspection used to distinguish multiple wrapped JSON-RPC errors. Root cause: errors.As returns only the first matching RPC error, while session recovery must observe both the server error and the separate SDK transport-rejection sentinel. Fix: Document the invariant and scope the errorlint exception to the exact tree-walking helper. Verification: - golangci-lint run --timeout=5m ./... - go run ./tools/repolint - go test ./internal/plugin -count=1 - git diff --check * chore(mcp): format lint directive Problem: The scoped errorlint directive was not gofmt-clean because its explanatory comment required a separating blank comment line. Root cause: The CI formatter enforces gofmt over the complete root module before running tests. Fix: Apply gofmt to the directive comment block. Verification: - root-module gofmt check - golangci-lint run --timeout=5m ./... - go run ./tools/repolint - git diff --check |
||
|
|
22291d4e78 |
feat(agent): land cache-stable use_capability speed and quality controls / 落地缓存稳定的能力代理速度与质量治理 (#9569)
* feat(agent): land cache-stable use_capability speed and quality controls Problem: MCP discovery and review loops inflate tools/list traffic, schema bytes, and automatic architecture review. The 1.33.0 speed-and-quality plan keeps a fixed use_capability proxy, but host validation, parallel read-only dispatch, stop-loss, child review budgets, and observability were incomplete. Root cause: The proxy schema was cache-stable, yet argument validation, scheduling class, review triggers, child sampling caps, facts packs, and list-layer metrics were not wired through. Architecture review fired too broadly, and the review 2048-token cap never reached child provider sampling. Fix: - Validate arguments against the real target schema before MCP tools/call. - Classify list/search/inspect and connected read-only MCP as parallel. - Fail closed when schema generation or safety class changes after scheduling. - Narrow auto independent review to architecture-worthy writes and cap at one first review plus one re-review. - Stamp review children with 8 steps / 2048 output tokens and a parent facts pack, without copying the parent transcript. - Record tool/phase/MCP list metrics and export three-layer list sources. - Keep first-party Tool Search preview plumbing default-off. Verification: - go test ./internal/agent/ ./internal/plugin/ ./internal/taskcontract/ ./internal/cli/ ./internal/tool/ ./internal/boot/ ./internal/eval/replay/ - go test -race on parallel MCP and listChanged tests - go vet ./internal/agent/ ./internal/plugin/ ./internal/cli/ - go run ./tools/repolint * fix(agent): harden capability speed controls Problem - Capability retries, proxy classification, and loop guards could duplicate canceled work, serialize safe reads, or repeat failed decisions. - MCP attribution, review context, native tool search, output validation, and desktop export wiring had correctness and CI gaps. Root cause - Wrapper layers dropped resolved metadata, per-turn state used cumulative or non-atomic accounting, and process-only metrics could not explain session behavior. - Provider support was inferred from names, while two same-stem export modules made TypeScript resolve the wrong implementation. Fix - Fail closed on cancellation and dynamic classification, bound reviews and repeated failures, validate output schemas, index discovery once, and emit session-scoped MCP list observations. - Gate native tool search by concrete first-party Responses capability, preserve host validation, and split lightweight export data from raster renderers. - Add deterministic concurrency, decision, dedupe, Windows path, frontend contract, and wire-format coverage. Verification - go test ./... -count=1 - go test -race ./internal/agent ./internal/plugin -count=1 - golangci-lint run ./... (darwin, linux, windows) - pnpm build - node scripts/run-tests.mjs (236 suites) * refactor(boot): preserve repository lint budgets Problem - The capability review wiring increased line and complexity counts in two already-baselined large files, so the repository lint gate rejected the otherwise clean change. Root cause - Review context preparation and observer binding were inlined at high-complexity call sites. Fix - Move review option preparation into the focused boot helper module and combine capability observer setup behind one call. - Preserve behavior while returning agent.go and boot.go to their prior lint budgets. Verification - go run ./tools/repolint - go test ./internal/agent ./internal/boot -count=1 - golangci-lint run ./... * fix(boot): remove ineffectual review budget result Problem Linux CI reports two ineffectual assignments when review subagent options are prepared. Root cause The extracted helper returned the adjusted step count after it had already embedded that value in agent.Options, while callers never read the returned count again. Fix Narrow the helper contract to return only the rewritten task and completed options, and update both call sites. Verification go test ./internal/boot -count=1 go run ./tools/repolint golangci-lint run --timeout=5m ./... * fix(agent): close speed and quality review gaps Problem: PR #9569 still allowed invalid proxy arguments to reach hooks or remote tools, attributed MCP list activity imprecisely, repeated equivalent decisions and outputs, and used a synthetic performance fixture as release evidence.\n\nRoot cause: Validation, dispatch telemetry, retry semantics, discovery attribution, loop guards, and release qualification were implemented at separate abstraction layers without a single resolved-tool contract.\n\nFix: Validate resolved tools before hooks and dispatch, preserve exact remote-dispatch telemetry, reject MCP application errors from transport retries, deduplicate decisions and outputs, bound inspect responses, attribute live MCP list changes to active sessions, enforce rolling soft budgets, and add a live paired-run release gate.\n\nVerification: go test ./...; targeted Go race tests; go vet ./...; golangci-lint; repolint; desktop and SDK Go tests; frontend build; all 239 frontend suites. * test(desktop): follow composer measurement mirror Problem: The latest main-v2 composer autosize change made the full frontend suite fail seven run-strip height assertions. Root cause: The regression test still mocked scrollHeight on the live textarea even though production now measures an out-of-flow mirror. Fix: Mock the measurement mirror while continuing to assert the live textarea and card geometry. Verification: The focused composer tests and all 239 frontend suites pass; the production build and bundle budgets also pass. * test(desktop): stabilize native tail fixture Problem: The Windows WebView2 smoke failed before its composer assertions when the initial history already exceeded the target row count. Root cause: The prepared multiline draft could shrink the native viewport one paint late while logical ownership remained tail-follow, leaving no jump-bottom control for fixture setup. Fix: Normalize only the logical tail-follow setup to the physical tail, continue requiring the product control in manual-reader mode, and verify the loaded tail after recovery. Verification: node syntax check; desktop Go tests; repolint; local native fixture passed the modified setup and reached native-event sampling. * fix(desktop): preserve tail during hydration resize WebView2 could keep logical tail-follow while a restored multiline draft shrank the transcript during history hydration, leaving the physical viewport 37-74px above the tail. Route those viewport resize events through the existing tail controller only while tail-follow ownership is active. Manual readers remain untouched, and the native smoke now verifies product behavior without a fixture-owned scroll write. Verified with the frontend build, all 239 frontend suites, transcript and composer suites, desktop Go tests, native contract syntax, repolint, and diff checks. * fix(desktop): repair hydrating footer tail gaps The Windows matrix confirmed the restored multiline draft changes the App footer while transcript hydration is active. The footer-height effect discarded that event, and the ResizeObserver could be recreated after the height change, leaving logical tail-follow 37px above the physical tail. Allow footer resize repairs whenever the transcript still owns tail-follow, including during hydration, while continuing to block manual readers. Use the canonical scroll mode for the viewport fallback as well. Verified with transcript and composer suites, frontend build, desktop Go tests, native contract syntax, repolint, and diff checks. * test(boot): isolate provider prefix effects * fix(desktop): pin composer footer tail before paint * fix(desktop): settle staged native footer growth * test(boot): fence history index cleanup * fix(desktop): make transcript tail physically reachable * fix(desktop): use painted WebView2 viewport extent * fix(desktop): learn reachable WebView2 transcript tail * fix(desktop): retain asynchronous WebView2 tail evidence * fix(agent): preserve MCP concurrency policy at runtime Problem: Configured MCP serial/parallel policy disappeared when the session runtime copied plugin entries, allowing explicitly serial stateful servers to be classified and executed concurrently. Root cause: runtimePluginEntry retained only name, source, and auto-start metadata, while both batch classification and the execution gate read Concurrency from that reduced entry. Fix: Normalize and retain Concurrency in the runtime entry. Add regression coverage through both ConfigureServers and UpsertServer so initial assembly and hot updates preserve explicit serial and parallel behavior. Verification: - go test ./internal/agent -run MCP concurrency tests -count=1 - go test -race ./internal/agent -run MCP concurrency tests -count=1 - go test ./... -count=1 - go vet ./... - go run ./tools/repolint |
||
|
|
64f1611e36 |
chore(mcp): extract tool result projection
Problem: The rich-result implementation extended plugin.go beyond its existing repolint file-size debt budget, causing the PR lint job to fail. Root cause: CallToolResult parsing remained in the legacy aggregate plugin file even though the new projection helpers already had a dedicated owner file. Fix: Move the complete text, image, and rich-result projection implementation into toolresult_projection.go without changing behavior or the repository lint baseline. Verification: - go run ./tools/repolint - golangci-lint run --timeout=5m ./internal/plugin/... - go test ./internal/plugin -count=1 - go vet ./internal/plugin |
||
|
|
e462495ca5 |
fix(mcp): project rich tool results safely
Problem: Ordinary MCP tool calls silently dropped structuredContent, resource links, embedded resources, audio, and unknown content blocks before the result reached the model. Root cause: The model-facing CallToolResult adapter only decoded text and direct image blocks. MCP Apps retained the complete raw result, but the normal agent path had no bounded fallback for the remaining standard content types. Fix: Add deterministic, provider-safe rich-result projection with per-item and aggregate budgets. Preserve canonical structured JSON and resource metadata, route embedded images through the existing image channel, summarize audio and binary resources without exposing inline base64 or _meta, and keep the MCP App transcript contract unchanged. Verification: - go test ./internal/plugin -count=1 - go test ./... - go vet ./internal/plugin |
||
|
|
e1958def9f |
fix(mcp): complete Apps compatibility across protocol versions
Merge the latest main-v2 baseline and resolve the release-note, bundle-budget, and repolint conflicts that left PR #9547 unmergeable. Negotiate MCP Apps in both directions, prefer stable nested UI metadata, preserve complete bounded CallToolResult payloads, support unambiguous legacy push elicitation, and cancel App-owned calls with their instance lifecycle. Add protocol-version, metadata, result-shape, and lifecycle coverage and wire the MCP App frontend contract test into CI. Verified with Go 1.26.6 tests and vet for both modules, targeted race tests, frontend production build and protocol tests, golangci-lint including cross-platform tags, govulncheck, repolint, release-note validation, and release workflow contracts. |
||
|
|
4150e2836b |
test(mcp): follow Chrome page ID routing
Problem: The opt-in Chrome DevTools MCP canary failed against the latest server because page-scoped tools now require pageId. Root cause: The test relied on the selected-page side effect and called page-scoped tools without the ID returned by new_page. Fix: Parse the selected page ID from new_page output, validate malformed or ambiguous output, and pass the ID to every page-scoped call. Verification: go test ./internal/plugin -count=1; go test -race ./internal/plugin -run '^TestParseSelectedChromePageID$' -count=1; go vet ./internal/plugin; REASONIX_LIVE_CHROME_MCP=1 go test ./internal/plugin -run '^TestChromeDevtoolsMCPLive$' -v -count=1 -timeout=3m |
||
|
|
fa007b590f | fix(mcp): complete desktop Apps host lifecycle | ||
|
|
5e1bf10c49 |
feat(desktop): MCP Apps sandbox, AppBridge surface, app tool channel
Desktop Apps run in the double-iframe model: each Apps server gets its own 127.0.0.1 loopback origin (cookie/storage/origin isolation between servers) serving only the outer relay page and the validated resource proxy. The relay hardens the channel - instance-nonce binding on the first parent message, event.source checks on every frame, an 8 MiB frame cap, and no RPC before the inner sandboxed frame loads; top navigation, popups, objects, and downloads never load. Resources are decoded with a 4 MiB cap, must be ui:// with an html/mcp-app mime, are served with the declared CSP (deny-all default; only exact http/https origins, no wildcards or credentials), and carry a SHA-256 header so history can refuse to restore UI across a changed upstream. The frontend mounts the official AppBridge (ext-apps 1.7.5 pinned) over PostMessageTransport inside a lazy chunk: app tools/call routes through Controller.MCPAppCallTool (instance-gated same-server/visibility/ generation resolution, hooks around dispatch, nested ToolDispatch/ ToolResult events under the originating call, LocalOnly transcript record - never model context), ui/open-link opens the system browser after the UI confirms the target, and size changes clamp the inline height to the 120-720px band. Unmount closes the bridge and reclaims the host instance. Bundle: ToolCard's expand-to-open surface adds +0.286 KiB gzip initial JS and +1.8 KiB raw (attributed ratchets); the AppBridge dependency stays lazy. repolint carries the new bridge.ts/types.ts wire surface. |
||
|
|
6fc731a2c4 |
feat(mcp): apps tool metadata, visibility split, rich results, instance registry
Parse MCP Apps tool metadata from tools/list _meta: visibility (default model+app) and the ui resource (nested ui.resourceUri with the flat legacy key fallback), carrying the declared CSP directives. The catalog splits into the model catalog (visibility includes model, unchanged for use_capability and provider requests) and a server-private App catalog (visibility includes app); a visibility change flips the catalog fingerprint, and enhanced caches persist both fields. tools/call results from App-capable tools now collect a bounded local presentation (standard CallToolResult + structuredContent + resource identity, 512 KiB cap, inline audio/video and oversized base64 dropped) through a per-call collector on the plan context; the agent attaches it as provider.Message.MCPApp, which projection strips from every provider request - leak tests added for the openai, anthropic and responses wire builders, and provider request bytes stay unchanged for text-only results. Host gains the bounded App Instance Registry: unguessable 24-byte tokens binding server, catalog generation, originating call and resource URI, max 32 live instances with oldest-eviction, reclaimed on server removal. Instance tool resolution refuses cross-server targets, non-App tools, and stale catalog generations. |
||
|
|
24a6c73fb3 |
feat(mcp): interaction broker, MRTR elicitation, form/URL prompts
Wire MCP 2026-07-28 multi-round-trip elicitation end to end. The SDK default MRTR middleware now fulfills server input requests through the new internal/mcpinteraction Broker, which travels with the per-call context (never the shared Host) so concurrent tabs cannot cross wires. Without a broker the request cancels - the model never guesses. internal/control gains the pending-elicitation queue mirroring the ask machinery: register-before-lock visibility, durable MCPInteractionRequest (mcp_interaction wire kind) with synchronous turn-event persistence, PromptAnswered-before-release resolution, and replay on frontend reconnect. The controller implements Broker and installs it on the executor and planner agents. Frontends: desktop renders a shared StructuredForm (flat primitive schemas with defaults/required/bounds) or a URL confirmation card (server + origin shown, browser opens only on click) as a decision surface; serve exposes POST /mcp-interaction; the chat TUI gains a keyboard elicitation card with typing mode. Form values and URL targets never enter logs; decision receipts record only kind and action. E2E tests drive real in-memory SDK sessions: form elicitation retries the original call with the user's answers, decline/cancel still complete the round trip, multiple input requests fulfill in one retry, dangerous URLs cancel before any UI sees them, and the no-broker path cancels. Bundle budgets: +0.2 KiB initial JS gzip (reducer wiring for the new prompt surface), +0.1-0.2 KiB locale chunks (14 labels per locale), +2.0 KiB initial raw; repolint carries the exact new-code footprint on the debt-saturated App.tsx/types.ts/bridge.ts/chat_tui.go/controller.go (no existing debt hidden; deltas equal the added lines). |
||
|
|
7cfda87a16 |
feat(plugin): host capability profiles, cache isolation, capability matrix
Introduce HostProfile (core-v1 / interactive-v1 / desktop-apps-2026-01-26-v1): the semantic client-capability surface fixed at Host creation. Interactive profiles declare form+url elicitation; Desktop additionally declares the io.modelcontextprotocol/ui extension with text/html;profile=mcp-app. The SDK default MRTR middleware replaces the hard-disabled mode, and a no-broker elicitation cancels instead of guessing. Cache identity follows the profile: core-v1 keeps the legacy v2 <slug>.json; capability-declaring profiles write an isolated v3 <slug>.host-<profileHash>.json carrying visibility and UI metadata, and treat the legacy file as a miss so catalogs negotiated under different capabilities never cross-read. Frontends pick their profile: chat TUI and serve interactive-v1, desktop desktop-apps (degrading to interactive when the Apps sandbox listener is unavailable), headless/bots core-v1. Host.CapabilityViews exposes the four-layer matrix (Protocol/Core/Interactive/Apps) over live negotiation state, surfaced via control.MCPCapabilityViews, desktop binding and /mcp status. |
||
|
|
28e6303e48 |
test: make release gates portable on Windows (#9494)
* test: make release gates portable on Windows Problem: The main-v2 release candidate failed Windows CI because path JSON was matched as raw POSIX text, Unix permission bits were asserted on Windows, and active test ledgers were left open during TempDir cleanup. Root cause: The tests encoded platform-specific assumptions and did not close persistent test-only file handles. Fix: Decode event frames before comparing canonical paths, retain the permission assertion only where Unix modes exist, and register ledger cleanup for active-handle tests. Verification: go test -p 4 -timeout=8m ./... go test -race ./internal/serve ./internal/topicstate ./internal/turnevent GOOS=windows GOARCH=amd64 go test -c for all three affected packages go run ./tools/repolint * test: stabilize native scrollbar replay Problem: The Linux transcript browser gate could misclassify a held scrollbar-track auto-repeat as a real thumb drag, then fail when later pointer movement did not reach the physical bottom. Root cause: Thumb discovery checked only an absolute scroll distance after one delayed move, and asynchronous DOM probes interrupted the proven browser-owned pointer transaction before it was completed. Fix: Require two consecutive pointer increments to produce native progress and finish the same physical drag before mutating or inspecting row geometry. Verification: pnpm test:transcript-browser pnpm test:motion pnpm test:transcript pnpm build go run ./tools/repolint * test: target transcript wheel input deterministically Problem: The Linux transcript browser gate could send its hydration wheel event to nested content at the viewport center, leaving the transcript unchanged until the fixture timed out. Root cause: The physical input probe did not verify that its pointer target belonged to the outer transcript reader surface. Fix: Reuse the visible-row padding hit-test before sending the hydration wheel while retaining the manual-mode and physical-displacement assertions. Verification: pnpm test:transcript-browser node --check bench/transcript-scroll-stability.mjs go run ./tools/repolint * test: harden native release fixture activation Problem: The Linux native-scrollbar replay could still accept a short track auto-repeat as thumb motion, while the WebView2 selection fixture could activate its topic before the final virtual row was mounted. Root cause: The headed Chromium probe lacked a sustained third movement check, and the native WebView2 contract assumed one early synthetic click plus the active class guaranteed a settled tail viewport. Fix: Require sustained native progress across a third pointer segment, retry the delayed topic click until active, and hold fixture setup at the physical bottom until the final selection target mounts. Verification: node --check for both browser contracts injected WebView2 contract replay in Chromium at 1200x800 pnpm --dir desktop/frontend test:motion cd desktop && go test ./... go run ./tools/repolint * test: assert cleared OAuth invariant |
||
|
|
9f00da2001 |
Fix MCP CI portability regressions
Problem: The first PR matrix exposed lint regressions and an intermittent macOS startup diagnostic that could miss a short-lived stdio server's stderr. Root cause: Removed transport interfaces left dead test helpers, the roots compatibility path needed an explicit deprecation waiver, and startup read the stderr tail before bounded process shutdown had drained the copy goroutine. Fix: Remove obsolete helpers, satisfy the repository's current lint rules while documenting the legacy roots requirement, and close/reap a failed endpoint before capturing its stderr tail. Verification: - golangci-lint run --timeout=5m - go test ./internal/plugin -run '^TestStdioFailureCapturesStderr$' -count=20 - go test -race ./internal/plugin -run 'Test(StdioFailureCapturesStderr|HTTPTransportCloseCancelsActiveRequest|HTTPTransportEstablishedSessionless404DoesNotRebuild)$' -count=1 - go test ./internal/plugin ./internal/agent -count=1 - go vet ./internal/plugin ./internal/agent - go run ./tools/repolint |
||
|
|
44222afeba |
Harden MCP session failure and shutdown boundaries
Problem: Sessionless HTTP 404 responses were treated as expired sessions, and a non-returning HTTP tool request could outlive bounded session shutdown. Root cause: The SDK uses ErrSessionMissing for every HTTP 404, while the supervisor did not require an established session ID. The SDK also detaches Streamable HTTP from the Connect context and waits for active calls before closing, so CloseIdleConnections could not force active requests to unwind. Fix: Classify a 404 without a session ID as a protocol error with no automatic rebuild. Bind active GET and POST requests to the Reasonix session lifetime, preserve the bounded DELETE cleanup path, and forward idle-connection cleanup through the protected round tripper. Verification: Added deterministic sessionless-404 and active-request shutdown regressions. Ran focused normal and race tests, the JetBrains standalone GET/DELETE regression, repolint, and diff checks. |
||
|
|
67fd53de67 |
Split MCP lifecycle code along owner boundaries
Problem: The completed MCP session migration exceeded repository capacity limits in several long-lived owner files and tests. Root cause: SDK adapters, session diagnostics, argument normalization, and deterministic fixtures were initially colocated with broader product surfaces. Fix: Extract the new responsibilities into focused owner files, group capability and auxiliary refresh state by lifetime, and keep all runtime behavior and public contracts unchanged. Verification: go run ./tools/repolint go test ./... go test -race ./internal/agent/... ./internal/plugin/... ./internal/boot/... cd desktop && go test -race ./... pnpm --dir desktop/frontend test:all pnpm --dir desktop/frontend build |
||
|
|
d33bd3e085 |
Refactor MCP sessions onto the official Go SDK
Problem: Stateful Streamable HTTP servers could lose their pending session before Reasonix opened the listening stream, while the custom transport engine duplicated protocol correlation, cancellation, and recovery logic. Root cause: Reasonix initialized Streamable HTTP with POST requests only and maintained separate request loops for HTTP, SSE, and stdio. Session loss, concurrent rebuilds, pagination, cancellation, OAuth refresh, and cleanup therefore had inconsistent owners. Fix: Adopt modelcontextprotocol/go-sdk v1.7.0 for protocol sessions, add a generation-fenced session supervisor, preserve Reasonix transport security and stdio process isolation, and cover JetBrains activation, recovery, pagination, cancellation, OAuth, schema stability, and bounded close behavior. Verification: - go test ./internal/plugin -count=1 - go test -race ./internal/plugin -count=1 - go test ./... - go vet ./internal/plugin |
||
|
|
4f1cae1061 |
feat(runtime): replace execution modes with adaptive standard policy / 用自适应标准执行替代轻量·均衡·交付模式 (#8866)
* feat(runtime): replace execution modes with adaptive standard policy Problem Reasonix exposed Light/Balanced/Delivery (and legacy TokenMode) as a user-selectable execution mode. That split the product into three workflows, leaked mode labels into UI/ACP/CLI, and let low-risk work pay Delivery ceremony or high-risk work skip closed-loop evidence. Root cause Planner route, verification, review, and completion were branched on AgentPreset/TokenMode/deliveryProfile instead of task risk. Fix Derive one TaskPolicy per turn from intent, risk, structure, and user constraints. Planning, verification, review, and evidence closure follow that policy and may only ratchet upward. New UI/CLI/ACP surfaces no longer offer an execution mode. Deprecated flags, slash commands, ACP options, and Wails SetAgentPreset*/SetTokenMode* stay accepted for one compatibility version as no-ops. New persisted compat fields pin agentPreset=balanced and tokenMode=full. Isolated worktrees keep their on-disk directory and no longer force Delivery. Verification - go test ./... - cd desktop && go test . - go test -race ./internal/control ./internal/acp ./internal/taskpolicy - frontend tsc --noEmit plus Composer/isolated-worktree/send-failed tests * fix(ci): restore desktop bindings and satisfy repolint Problem CI lint failed on comment, file-size, and complexity overruns. Desktop jobs failed TypeScript because Wails still exports deprecated SetAgentPreset*/SetTokenMode* and DeliveryWorktree aliases that AppBindings no longer listed. Root cause The standard-execution change kept the one-version Go compatibility methods bound, but the frontend drift check requires every generated Wails method on AppBindings. The same change also grew several already-baselined files past comment and size budgets. Fix Bind the deprecated Wails methods as no-op aliases in AppBindings and the browser mock. Extract ExecutionPolicyBlock and boot test helpers, split Derive helpers, and compress comments and tests so repolint stays inside existing budgets. Verification - go run ./tools/repolint - go test ./internal/taskpolicy ./internal/acp ./internal/boot ./internal/control ./internal/evidence ./internal/cli ./internal/capability ./internal/agent - cd desktop && go test . -run 'TestLoadTabSessionProfile|TestSetTokenMode|TestSetAgentPreset|TestDeliveryWorktree|TestCreateDelivery|TestIsolated' - desktop/frontend tsc --noEmit - isolated-worktree and composer-profile frontend tests * fix(ci): drop unused mode leftovers and preset UI assertion Problem After repolint went clean, golangci-lint failed on an ineffectual ACP workMode assignment, an unused CLI footer helper, a desktop token-mode snapshot helper, and a strings.Builder modernization in boot tests. The desktop frontend job failed because the change panel no longer renders a Balanced preset label. Root cause Execution-mode removal left unused compatibility helpers and an assignment that was immediately overwritten with the pinned compat value. The workspace-changes test still required the old preset localization. Fix Pin ACP status WorkMode to balanced without reading the unused runtime profile, delete the unused helpers, join the boot transcript with strings.Builder, and assert only the localized verdict on the change panel. Verification - go run ./tools/repolint - golangci-lint run ./internal/acp ./internal/cli ./internal/boot - go test ./internal/acp ./internal/cli ./internal/boot - desktop go test -c - pnpm exec tsx src/__tests__/workspace-changes-errors.test.tsx * fix(runtime): close standard execution review gaps Problem: - Capability routing could run before TaskPolicy was frozen. - Atomic edits could finish without a mutation, and later low-risk edits could hide earlier high-risk changes. - Explicit full verification did not force full planning and review. - Legacy execution modes remained discoverable in CLI, desktop styling, docs, telemetry, and benchmark axes. Root cause: - Policy metadata and evidence checks were evaluated at inconsistent points in the turn lifecycle. - Compatibility controls were registered directly on public surfaces. - Benchmark and documentation cleanup stopped short of the last profile-specific paths. Fix: - Freeze policy before routing, enforce atomic mutation readiness, and ratchet risk across the complete turn ledger. - Promote explicit full verification to full planning and forced review. - Hide one-version compatibility parsing behind non-discoverable adapters and remove dead UI, telemetry, documentation, and benchmark profile surfaces. - Add regression coverage for routing, readiness, risk, compatibility help, and standard benchmark commands. Verification: - go test -p 4 -timeout=8m ./... - go vet ./... - go run ./tools/repolint - ./scripts/cache-guard.sh - go test -race on release-critical packages and repeated plugin refresh coverage - desktop Go vet/tests, frontend pnpm test:all/build, and SDK Go vet/tests/race * test(desktop): tolerate slow Windows autosave Problem: - The Windows desktop job could fail while the asynchronous turn snapshot was still completing. Root cause: - Autosave assertions used a two-second deadline, shorter than legitimate Windows antivirus and indexer delays observed in CI. Fix: - Use a shared ten-second autosave test deadline without changing production retry behavior. Verification: - Autosave persistence test passed 100 consecutive runs. - Autosave persistence test passed 20 race-enabled runs. - Full desktop Go test suite passed. * fix(desktop): reconcile partial trash before restore Problem: A committed topic archive can leave a sidecar at the live path when Windows temporarily blocks one step of the multi-artifact move. An immediate restore then reports the leftover sidecar as an unrelated conflict. Root cause: RestoreSession preflighted destination artifacts without honoring the durable cleanup-pending marker used by committed archive recovery. Fix: Reconcile pending desktop cleanup before restore conflict checks and retain the cleanup-in-progress response when reconciliation cannot finish. Add a deterministic partial-move regression test. Verification: desktop go test ./...; desktop go vet ./...; targeted restore tests x100; targeted race tests x30; repolint; git diff --check. * test(plugin): synchronize no-op refresh cycle Problem: The no-op tool-list refresh test could observe cycleDone after a fast refresh had already completed, causing the race job to report that the cycle never started. Root cause: The controlled transport did not block tools/list in this test, so the refresh goroutine could finish and clear the cycle handle before the test captured it. Fix: Block the controlled tools/list call, capture the active cycle, then release the refresh and assert its no-op behavior. Verification: go test -race ./internal/plugin -run '^TestToolListRefreshNoOpPreservesAdapterGeneration$' -count=1000 go test -race ./internal/plugin/... go run ./tools/repolint golangci-lint run --timeout=5m ./internal/plugin/... * fix(runtime): preserve adaptive risk across absolute paths Problem Deep absolute mutation paths could lose sensitive owner directories during risk classification, allowing a stale low-risk policy to survive the first write. Verification allowlists also recognized only test commands, so vet, lint, and typecheck could bypass an explicit user restriction. Root cause Risk classification truncated absolute paths without workspace context, and parent, child, and background evidence did not share a workspace-aware classifier. The task-policy gate maintained a narrower verifier list than the evidence subsystem. Fix Normalize paths lexically against the owning workspace while preserving deep sensitive owners and conservative out-of-workspace behavior. Propagate host-only workspace context through subagents and background task/fleet evidence without serializing it. Reuse the canonical verification classifier in the pre-execution policy gate. Verification - go test ./internal/evidence ./internal/taskpolicy ./internal/agent ./internal/jobs ./internal/control - go test -race ./internal/evidence ./internal/agent ./internal/jobs ./internal/control - go vet ./internal/evidence ./internal/taskpolicy ./internal/agent ./internal/jobs ./internal/control - go run ./tools/repolint - git diff --check * fix(ci): align latest base contracts Problem: The latest main-v2 reasoning display mode was not represented in the startup settings contract, and the new path helper tripped the modernize linter. Root cause: PR #8732 added the expanded reasoning option after this branch was created, while the path helper retained a manual membership loop. Fix: Require the expanded reasoning option and locale key in the startup contract, and use slices.Contains for exact risk-path segment checks. Verification: pnpm exec tsx src/__tests__/startup-settings-contract.test.ts pnpm test:all golangci-lint run --timeout=5m ./... wails generate module |
||
|
|
b41b4de9df |
fix(mcp): refresh tools after list change notifications / MCP 工具列表变更后自动刷新 (#8673)
* fix(mcp): refresh tools after list change notifications * fix(mcp): bound dynamic tool catalog refreshes Problem: Tool-list notifications could create an unbounded refresh loop, while catalog and execution locks allowed remote MCP calls to stall status reads. Failed refreshes also left adapters executable with stale safety metadata. Root cause: The refresh worker drained a self-replenishing pending bit, and toolsMu covered both remote I/O and adapter dispatch. Notifications were accepted without checking the advertised listChanged capability. Fix: Coalesce notifications into a debounced two-attempt cycle, publish immutable fingerprinted catalogs with generations, separate fetch/status/dispatch lock domains, gate notification support on initialize capabilities, and fail closed while a catalog is stale. Verification: go test ./... -count=1; go test -race ./internal/plugin ./internal/agent -count=1; go vet ./...; go run ./tools/repolint; git diff --check. * fix(mcp): preserve dynamic catalog convergence Problem: A tool catalog refreshed before the session runtime subscribed could leave stale adapters in its registry. Repeated notifications could also exhaust the two-attempt cycle, and refreshes ignored configured timeout budgets. Root cause: Host subscriptions did not replay current catalogs, the refresh worker stopped with a dirty revision after its second successful list, and it imposed the five-second interactive startup wait on background tools/list calls. Fix: Replay existing catalogs before later subscriber deliveries, keep dirty revisions converging under capped exponential backoff, and bound refreshes by the stricter resolved startup or call timeout. Verification: go test ./... -count=1; go test -race ./internal/plugin ./internal/agent -count=1; go vet ./...; go run ./tools/repolint; git diff --check. * fix(mcp): harden dynamic catalog refresh lifecycle Problem: A disabled session could retain replayed shared-Host MCP adapters, generic registry aliases bypassed runtime authorization, catalog resolution could deadlock with a pending refresh writer, and a self-notifying server could refresh forever. Root cause: Runtime inventory changes did not suspend registry prefixes, generic registry routing skipped MCP identity binding, resolve held dispatchMu across a re-entrant catalog callback, and refresh convergence had no attempt ceiling. Fix: Synchronize registry prefix suspension with runtime lifecycle, bind every registry-backed MCP adapter to the current runtime, release the dispatch lease before catalog lookup, and cap each refresh cycle while preserving fail-closed user-driven recovery. Verification: go test ./internal/plugin ./internal/agent -count=1; go test -race ./internal/plugin ./internal/agent -count=1; go test ./... -count=1; go vet ./...; go run ./tools/repolint; git diff --check. * fix(mcp): satisfy refresh loop lint Problem: CI rejects the newly added bounded refresh loops under the Go 1.22 intrange rule. Root cause: The loops used the classic counter form even though the repository requires integer ranges. Fix: Use integer ranges while preserving the four-attempt refresh behavior and test assertions. Verification: - go test ./internal/plugin -run 'TestToolListRefresh' -count=1 - go test ./internal/plugin ./internal/agent -count=1 - go test -race ./internal/plugin ./internal/agent -count=1 - go test ./... -count=1 - go vet ./... - go run ./tools/repolint - golangci-lint run ./... - git diff --check * test(mcp): drain startup cache before adapter mutation Problem: The reader safety-drift test can mutate a remote adapter while Start's detached schema-cache snapshot is still reading it, causing a race on the latest base merge tree. Root cause: The test models metadata drift by directly changing the adapter, but did not first wait for startup persistence to finish. Fix: Drain the host's startup background writes before the test-only adapter mutation. Verification: - go test -race ./internal/plugin -run '^TestReaderIntentRefusesDispatchAfterSafetyDrift$' -count=10 - go test -race ./internal/plugin ./internal/agent -count=1 - go test ./internal/plugin ./internal/agent -count=1 - go test ./... -count=1 - go vet ./... - go run ./tools/repolint - golangci-lint run ./... - git diff --check * test(agent): stabilize background fleet lifecycle assertion Problem: The CI race job can observe no running fleet job in TestBackgroundFleetProgressLifecycleUsesStableIDs even though registration and completion both work correctly. Root cause: The test released both blocked workers before inspecting RunningForSession, allowing the background job to finish and disappear first under runner load. Fix: Assert registration while the provider is still blocked, then release the workers and verify terminal completion. Verification: - GOTOOLCHAIN=go1.26.6 go test -race ./internal/agent -run '^TestBackgroundFleetProgressLifecycleUsesStableIDs$' -count=500 - GOTOOLCHAIN=go1.26.6 go test -race ./internal/agent -count=1 - GOTOOLCHAIN=go1.26.6 go test ./internal/agent -count=1 - go run ./tools/repolint - golangci-lint run ./... - git diff --check --------- Co-authored-by: SivanCola <[email protected]> |
||
|
|
37e9547d38 |
fix(desktop): stabilize rendering, workspace, catalog, and WebView2 / 系统治理桌面端显示稳定性 (#8827)
* fix(desktop): stabilize display ownership and recovery Problem: Desktop conversations could flicker, jump, clip content, retain stale workspace data, hide partially indexed sessions, flash background command windows, and fail to recover cleanly from WebView2 renderer failures. Root cause: Scroll position, workspace freshness, catalog completeness, process visibility, DPI synchronization, and WebView2 recovery each had multiple competing owners or treated incomplete refresh state as empty authoritative state. Fix: Centralize transcript scrolling in a deterministic Virtuoso coordinator; add keyed stale-while-revalidate workspace resources, responsive overlay layout, bounded code views, and secure Markdown media resolution; expose partial catalog projections with per-directory completeness and authoritative persist indexing; and add hidden-by-default Windows commands plus a rebased WebView2 integration with single-owner DPI synchronization and tiered crash recovery. Verification: - pnpm test:all - pnpm test:transcript-browser - pnpm build - go test ./... - cd desktop && go test ./... - go test -race ./internal/sessioncatalog ./internal/history ./internal/control ./internal/proc - cd desktop && go test -race . -count=1 - go run ./tools/repolint - Windows amd64 cross-compilation for desktop, proc, update-helper, windows-resource, and WebView2 edge Co-authored-by: ttmouse <[email protected]> * fix(desktop): close display stability review gaps Problem: The initial stability integration still cleared restored workspace selections, replaced working-tree data on refresh failures, allowed background command constructors outside desktop/ to bypass the Windows hidden-window policy, and could serve a media token after its authorized file was replaced. Critical renderer failures could also be dropped behind auxiliary WebView2 diagnostics, while cold Windows CI combined two slow suites under one timeout. Root cause: Workspace refresh and project restoration still had overlapping reset paths; the process gate covered only the desktop directory; media tokens were bound only to a path; the diagnostics queue treated recovery and telemetry equally; and the Windows job shared one aggregate test budget. Fix: Extract a keyed workspace-changes SWR hook and preserve per-project selections, bind media tokens to the authorized file identity with restrictive response headers, extend proc constructors and static enforcement across desktop-used internal packages, preserve hidden flags for batch commands, prioritize renderer recovery, fix upstream formatting/lint findings, and split the Windows vendor test budget without skipping coverage. Verification: - go test ./... - cd desktop && go test ./... - root concurrency-package race suite - cd desktop && go test -race ./... - golangci-lint on native, Linux, and Windows targets - pnpm test:workspace - pnpm test:transcript - pnpm test:all - pnpm build - go run ./tools/repolint - actionlint .github/workflows/ci.yml - Windows cross-compilation for desktop, hook, and WebView2 edge tests * chore(security): document intentional command boundary * chore(deps): update Go toolchain to 1.26.6 --------- Co-authored-by: ttmouse <[email protected]> |
||
|
|
a09a33ad47 | fix(plugin): fail closed when confined MCP loses its sandbox backend | ||
|
|
7be950ff15 |
merge(main-v2): integrate catalog and runtime updates
Problem: - The integration branch lagged main-v2 and conflicted in Desktop startup, boot wiring, and frontend guardrails. - In-memory shared-cache projections could return SQLITE_LOCKED during concurrent reconcile/write operations. - A timed-out session catalog close could leave its database open, while macOS updater tests shared raw descriptor ownership with production-style helpers. Root cause: - The branch and main evolved the same runtime owners independently. - SQLite shared-cache table locks do not honor busy_timeout across pooled in-memory connections. - Session catalog close completion and test file-descriptor ownership lacked explicit lifetime fences. Fix: - Resolve the merge semantically, preserving catalog task-store and registration-scope behavior alongside upstream config warnings and agent runtime updates. - Use one database/sql connection for memory projections, make session catalog close eventually complete exactly once, and duplicate updater handoff descriptors for exclusive ownership. Verification: - go test and go test -race ./internal/projectiondb ./internal/sessioncatalog - Desktop session catalog regression: 100 ordinary runs and 30 race runs - macOS updater handoff/TempDir regression: 100 ordinary runs and 20 race runs |
||
|
|
f17fceb033 |
fix(catalogs): close projection race and migration gaps
Problem: Stale controller builds could roll back shared MCP instances owned by a newer build. Session tombstones could underfill paginated results and their same-revision refresh event could be ignored. Legacy topic migration markers could also miss same-size or event-log changes. Root cause: Registration scopes tracked creation without transferable instance claims, catalog pagination applied tombstones after SQL limits, and migration completion used only transcript names and sizes while explicit reconciliation still trusted that marker. Fix: Track exact MCP instance claims through scope commit or abort, scan catalog keyset pages until visible limits are satisfied, publish overlay refreshes that accept equal revisions, fingerprint bounded session artifacts and authoritative event logs, and add a marker-bypassing explicit migration path. Verification: go test ./internal/plugin ./internal/sessioncatalog go test -race ./internal/plugin ./internal/sessioncatalog go test ./desktop focused migration, tombstone, restore, clear-session, and registration cases pnpm typecheck npx tsx src/__tests__/project-tree-shell-race.test.ts |
||
|
|
01bc087862 |
fix(desktop): registration scopes, catalog tombstones, migration signatures
Problem: Three P1 merge blockers remained on the catalogs integration head: 1) Host registration journal used a Host-global regJournalActive flag, so sibling hot-adds during a stale build were attributed and rolled back. 2) RemoveSession stored tombstones only after mutex waits; a 150ms desktop context could expire mid-lock, leave SQL undeleted, and ListTopics still returned archived topics (race-mode restore test). 3) Topic migration markers used strict mtime ordering, which failed on Windows coarse timestamps for new CLI sessions after the marker. Root cause: 1) Ownership was time-window based rather than token-based. 2) Query paths trusted SQLite without an immediate removal overlay. 3) Marker validity compared file mtimes instead of directory content. Fix: - Replace the global journal with per-build RegistrationScope tokens propagated via context through EnsureConnected and LazyToolset; abort rejects late registrations; sibling writes without the token are ignored. - Do not hold a Host lock across boot.Build (scopes do not serialize builds). - Record removedPaths before any mutex; filter ListTopics/ListSessions/ GetSession through the tombstone; TryLock durable DELETE and retry async. - Persist migration markers as name+size directory signatures; new sessions invalidate without mtime ordering. - Extract registration scope, removal, migration marker, and legacy migration helpers to keep repolint budgets clean (baseline carry-forward only). Verification: - go test ./internal/plugin/ -run TestRegistration (-race) - go test ./internal/sessioncatalog/ -run 'TestRemoveSession|TestListTopics' (-race) - cd desktop && go test -run 'TestProjectTreeMigratesNewCLISessionAfterProjectDirMarker|TestRestoreGlobalTopicSessionReindexesProjectTree|TestClearSessionForTab' - cd desktop && go test -race -count=3 -run TestRestoreGlobalTopicSessionReindexesProjectTree - go run ./tools/repolint |
||
|
|
8cff50d08b |
fix(desktop): instance-scoped Host rollback and hydrate generics
Problem: Desktop CI failed with TS2305 because hydrateErrorState imported a non-existent Item type from ./types. Host generation-loss rollback still used name-diff snapshots, so a deterministic interleaving where sibling/new-generation resources appear after the snapshot incorrectly deleted those live clients. Root cause: 1. The helper was typed against a concrete Item export that does not exist in types (and created a fragile circular import surface). 2. Name-set diff has no ownership: any server name created after the snapshot was attributed to the lost build, including post-snapshot sibling work. Fix: - Make applyHydrateErrorState / hydratePlaceholderItems generic over TItem so the helper needs no types import. - Assign Host-local Client instance IDs, journal registrations during RunWithRegistrationJournal, and roll back only with RemoveIfInstance. - Controller builds wrap boot.Build in the journal; supersede/generation-loss paths call RollbackRegistration on journaled refs only. - Add deterministic instance-scoped regression tests (including post-journal sibling preservation and same-name newer-generation survival). - Make Client.close nil-safe for transport-less test clients. Verification: - go test ./internal/plugin/ -run 'TestRegistrationJournal|TestRunWithRegistration' -count=1 - go test -race ./internal/plugin/ -run 'TestRegistrationJournal|TestRunWithRegistration' -count=1 - cd desktop && go test -c . && go test -run TestClearSessionForTab -count=1 - cd desktop/frontend && pnpm typecheck |
||
|
|
6db0574e44 |
test: make three CI-red tests deterministic across platforms
Problem: main-v2 push CI has been red since 08-08 15:15. Two Windows legs fail deterministically on permission assertions Windows cannot satisfy, and one race leg fails on a startup-timing assumption that loaded race runners break. Root cause: - oauth_test.go asserted the private-token file mode equals 0600, but Windows has no Unix permission bits and always reports 0666. - autoresearch store_test chmod(0)'d a file expecting LoadTask to reject it; on Windows chmod cannot make a file unreadable. - plugin_test asserted a startup error carried the child's redacted stderr immediately after a 40ms caller-wait deadline; a race-instrumented child takes ~800ms to start, so the failure was snapshotted before its stderr reached the pipe buffer. Fix: - Gate the mode assertion on non-Windows and skip the chmod-based test on Windows; the Unix legs still cover both contracts. - Assert only stage and elapsed on the caller-wait error; stderr redaction is re-asserted whenever the structured record carries stderr. Exit-path stderr plumbing and redaction stay covered by TestStdioFailureCapturesStderr and TestFailureSummaryRedactsCredentials. Verification: go test ./internal/plugin/ ./internal/autoresearch/, go test -race ./internal/plugin/ -count=10 on the affected tests, GOOS=windows go vet both packages, repolint — all clean. |
||
|
|
f4d332b73f |
Merge origin/main-v2 into feature/mcp-oauth-sandbox-auth
Resolve the latest main-v2 controller/runtime changes in favor of the current base implementation while retaining the MCP OAuth and sandbox fixes. |
||
|
|
ba26737fa8 |
fix(mcp): close OAuth eligibility and refresh race gaps
Problem: - Explicit URL and header credentials could be combined with stale OAuth state. - Remote HTTP endpoints and failed semantic IDs could be offered as usable capabilities. - Token refresh held the cross-process state lock during network I/O. Root cause: - OAuth eligibility used incomplete string heuristics and refresh used one lock scope for read, request, and write. Fix: - Parse URL userinfo/query credentials and sensitive headers, restrict OAuth to HTTPS or loopback HTTP, and filter failed/disabled semantic candidates. - Add a process-local refresh gate plus two-phase locked compare-and-apply with generation checks. - Add regression coverage for credentials, transport eligibility, routing status, and clear-during-refresh. Verification: - go test -race ./internal/plugin ./internal/capability ./internal/mcpdiag ./internal/control ./internal/cli - go vet ./... - go run ./tools/repolint - pnpm exec tsx src/__tests__/mcp-oauth-eligibility.test.ts - pnpm typecheck Co-authored-by: Andrei Kiparuk <[email protected]> |
||
|
|
5880af7d65 |
fix(mcp): close OAuth lifecycle races and auth conflicts
Problem: - OAuth authorization held the MCP state lock across browser interaction and could race with removal. - Existing OAuth state could be loaded alongside static MCP credentials. Root cause: - The authorization flow used one lock scope for the entire interactive exchange and had no invalidation generation. - HTTP transport only excluded OAuth when an Authorization header was present. Fix: - Capture and compare a private OAuth generation around interactive authorization, and advance it during removal reconciliation. - Treat all explicit MCP authentication configuration as authoritative when constructing HTTP transports. - Add deterministic regression coverage for browser-lock release, removal invalidation, fallback reconciliation, and static API keys. Verification: - go test -race ./internal/plugin ./internal/mcpdiag - go test ./internal/cli ./internal/control ./internal/capability - go test ./... - cd desktop && go test ./... - git diff --check |
||
|
|
3b66c67cb2 |
fix(mcp): harden OAuth lifecycle routing
Problem: - Failed or disabled cached MCP tools could remain ready after catalog deduplication. - stdio, legacy SSE, and statically authenticated servers could enter the HTTP OAuth flow. - Removing an MCP declaration retained Reasonix OAuth client and token state. Root cause: - Registry-backed MCP tool entries did not inherit host failure state. - Authentication diagnosis and UI actions trusted auth failures before checking transport and configured credentials. - Remove paths did not reconcile private OAuth state with the effective fallback declaration. Fix: - Propagate failed and disabled status through the real catalog assembly path. - Gate native OAuth consistently in diagnostics, CLI, TUI, Desktop, frontend, and the plugin owner. - Reconcile OAuth state under the cross-process lock, preserving only same-resource fallbacks and covering open Desktop workspaces. Verification: - go test ./... - go test -race ./internal/plugin ./internal/capability ./internal/mcpdiag ./internal/cli ./internal/control - cd desktop && go test ./... - cd desktop && go test -race ./... - frontend focused test, typecheck, ESLint, and CSS checks - go vet ./... - go run ./tools/repolint - git diff --check |
||
|
|
1c30748abc |
feat(mcp): add sandbox-safe OAuth authorization
Problem: - Remote MCP OAuth depended on externally injected credentials that sandboxed desktop and CLI sessions could not access. - Failed cached MCP tools could still be preferred by capability routing. Root cause: - Reasonix had no first-party user-initiated OAuth flow or private credential state. - Cached tool entries did not inherit failed or disabled server status. Fix: - Add standards-based OAuth discovery, DCR, PKCE, refresh rotation, proxy-aware HTTP, and resource-bound private state. - Serialize token rotation across processes and prevent stale transports from restoring cleared state. - Add CLI, TUI, and Desktop sign-in and reconnect flows. - Propagate failed and disabled lifecycle state to cached MCP tools. Verification: - go test -race ./internal/plugin ./internal/capability ./internal/boot ./internal/cli - go test ./... - (cd desktop && go test ./...) - (cd desktop/frontend && pnpm exec tsx src/__tests__/capabilities-panel-actions.test.ts) - (cd desktop/frontend && pnpm typecheck) - (cd desktop/frontend && pnpm lint:hooks) - go vet ./... - git diff --check Co-authored-by: andrei-kiparuk <[email protected]> |
||
|
|
3552822776 | feat: add generation-safe runtime composition | ||
|
|
4fbbca6a99 |
refactor(go): adopt modern stdlib idioms and gate them (#7831)
* refactor(go): adopt modern stdlib idioms across kernel and desktop Mechanical sweep with golangci-lint's modernize/intrange/copyloopvar/ usestdlibvars fixers plus hand-fixes for what they could not rewrite: slices.Contains, min/max, slices.Backward, WaitGroup.Go, reflect.TypeFor, maps.Copy, strings.Cut/CutPrefix/SplitSeq, strings.Builder, range-over-int. Net -549 lines with no behavior change. Three loops in session.go stay index-based: slices.Backward yields element copies, so the fixer's rewrite silently dropped writes to DecisionReceipts and ToolCalls (caught by TestDelivery* and TestDependentSameBatch*). All 49 slices.Backward sites were audited for that shape; only these three write through the index, and they carry a nolint with the reason. CaptureAfter's scan loop had an empty body and never ran any logic, so the fixer could not express it. Drop it and the accessor it was the only caller of. omitzero is deliberately untouched: turning omitempty into omitzero changes the JSON written for nested structs, which is a wire-format change to session files and the extension protocol. * build(lint): gate the modern-idiom linters Nothing stopped the codebase from drifting back to pre-1.22 idioms. Both modules are now clean for modernize, intrange, copyloopvar, and usestdlibvars, so enable them rather than leaving the sweep as a one-off. omitzero stays disabled with the reason inline; usetesting, perfsprint, and errorlint still have residue and land separately. |
||
|
|
e1079f2b58 |
fix(errors): compare errors through the unwrap chain, and gate it (#7834)
Every `err == Sentinel` and `err.(*T)` in the tree works today only because the error happens to arrive unwrapped. That is not a property anyone maintains: one `%w` added between producer and check silently stops the branch from firing, and this same change adds 54 of them. So the comparisons are converted first, then the wrapping. Three helpers turned out to be hand-rolled reimplementations of the standard library and are deleted rather than patched: remote.isPassphraseMissing, remote.asKeyError (which open-coded the Unwrap() walk), and forward.containsErr (which open-coded both the single- and multi-error walks). Call sites use errors.As/errors.Is. Two assertions deliberately keep `!=`: explainError and AnnotateToolSchemaError must return the *same* error object, and errors.Is would still pass if they started wrapping it. Both carry a nolint naming that. errorlint is enabled in .golangci.yml; both modules are clean under it. |
||
|
|
c969509d66 |
style: strip section-banner decoration from comments (#7840)
312 banner separators across 63 files, the thing REASONIX.md bans outright and nothing has ever checked. 64 were pure rules with no content and are gone; the other 248 carried a label inside the dashes, so the decoration is stripped and the label kept -- `// --- session/resume ---` becomes `// session/resume`, which still orients a reader in a 1000-line test file without drawing a line across it. The repolint ceiling for this rule drops 312 -> 0, so it is now zero-tolerance rather than merely frozen. |
||
|
|
f6be547f49 |
feat(cli): version metadata, shell completion, MCP source display
Problem - reasonix --version only printed a bare version string, which is hard to correlate with source builds and user bug reports. - No first-party bash/zsh/fish completion script existed. - /mcp status did not show which config plane registered a server/tool. Root cause - Version path was a one-line println; no completion registry; ServerStatus omitted Spec.ConfigSource. Fix - BuildInfo with version --verbose / --json (version, git commit, build time UTC, target, go). --version/-v stay single-line. No config path, no CST. - reasonix completion bash|zsh|fish from a shared registry with Run dispatch drift guard (adapted from #7425). - Plumb ConfigSource into ServerStatus and render source= on /mcp status and tool lists (from #6175 scope: display only). - Makefile ldflags inject version, gitCommit, buildTimeUTC. Verification - go test ./internal/cli -count=1 - go test ./cmd/reasonix ./internal/plugin -count=1 - TestBuildInfo*, TestCLICompletion*, TestRenderMCPStatusShowsConfigSource Refs #6476 #7425 #6175 #6578 #6833 Session lease / REASONIX_HOME / bash session-allow already covered on main-v2; no --home flag added. Co-authored-by: h4rK8s <[email protected]> Co-authored-by: Zhiyao Wen <[email protected]> |
||
|
|
4a989f4d6a |
fix(mcp): flush startup stderr before reporting
Problem: The Linux race job intermittently observed an initialize timeout before the stdio stderr copy goroutine had flushed, so structured startup diagnostics omitted the expected redacted stderr tail. Root cause: Startup failure paths sampled the bounded stderr buffer before closing and reaping the transport, even though transport close is the synchronization point for the copy goroutine. Fix: Close the failed transport first, then read and redact startup stderr for initialize and both tools/list paths. Verification: - go test -race ./internal/plugin -run ^TestStartupFailureReportsStageElapsedAndRedactedStderr$ -count=20 - go test -race ./internal/plugin ./internal/control ./internal/agent -count=1 - go test -p 4 ./... -count=1 - go vet ./internal/plugin - ./scripts/cache-guard.sh - git diff --check |
||
|
|
e94e549799 |
fix(mcp): fence background startup removal
Problem: A server removal could race the narrow gap between a background handshake finishing tools/list and publishing its client, allowing the removed server to reappear. Root cause: EnsureConnectedInBackground registered a deferred generation but did not pass that generation into the final Host publication check. Fix: Carry the generation through the shared connection path, retain configured server names in connection status, and cover removal during an in-flight background startup. Verification: - go test ./internal/plugin -count=1 - go test -race ./internal/plugin ./internal/control ./internal/agent -count=1 - go test -p 4 ./... -count=1 - git diff --check |
||
|
|
5c16cc1408 |
fix(mcp): keep slow startup alive in background
Problem: Cold MCP servers that exceeded the short interactive wait were canceled and restarted on every retry, obscuring the real startup stage and source. Root cause: The caller wait context also owned the shared initialize and tools/list lifecycle, so a five-second timeout terminated otherwise healthy cold starts. Fix: Separate the session-owned startup lifecycle from the caller wait, add bounded global and per-server startup timeouts, preserve pinned provider-visible tool schemas, and expose redacted structured diagnostics. Verification: - go test -p 4 ./... -count=1 - go test -race ./internal/plugin ./internal/control ./internal/agent -count=1 - ./scripts/cache-guard.sh - go vet ./internal/plugin ./internal/config ./internal/boot ./internal/capdiag ./internal/control ./internal/agent ./internal/installsource - git diff --check |
||
|
|
2c1df5b4f9 |
Trust configured MCP and Hooks by default / 默认信任已配置的 MCP 与 Hooks (#6889)
* fix(mcp): trust and surface project servers Problem: Project-provided MCP servers were blocked behind a separate launch authorization flow and were not clearly distinguished from global servers in Desktop or CLI management views. Root cause: Project scope was coupled to launch approval, while configured-server lookup and presentation did not preserve project provenance consistently. Fix: Treat project MCP configuration as user-authorized by default, preserve workspace-scoped activation and relative working directories, load project config for reconnects, and group project, global, and managed servers consistently across Desktop and CLI. Verification: - ./scripts/cache-guard.sh - go test ./... - cd desktop && go test ./... - pnpm build - pnpm exec tsx src/__tests__/capabilities-panel-actions.test.ts * fix(mcp): preserve config scopes and precedence Problem: Global MCP installs and project declarations shared persistence paths, so Desktop and CLI actions could write the wrong config, store activation against the wrong workspace, or remove same-name declarations across scopes. Root cause: Runtime config merging preserved the effective entry but persistence call sites did not consistently carry its owning source or the controller workspace. Fix: Write explicit installs to the user-global config, keep project edits in reasonix.toml or .mcp.json, apply deterministic project-over-global precedence, and restore the next same-name fallback after removing an override. Align Desktop, CLI, TUI, imports, Planner-facing runtime state, and documentation with the same rules. Verification: - go test ./... - go vet ./... - cd desktop && go test ./... - cd desktop && go vet ./... - cd desktop/frontend && pnpm exec tsx src/__tests__/capabilities-panel-actions.test.ts - cd desktop/frontend && pnpm run typecheck - cd desktop/frontend && pnpm build - ./scripts/cache-guard.sh - git diff --check * test(mcp): satisfy staticcheck in handler Problem: The PR lint job rejected the new MCP test handler with staticcheck QF1003. Root cause: The handler used an if/else-if chain on the same request method value. Fix: Use a tagged switch for initialize and tools/list responses without changing test behavior. Verification: - go test ./internal/control - git diff --check * feat: trust configured project hooks by default Problem: Project hooks required a separate workspace trust action even though configured MCP servers and installed plugin hooks already use low-friction trusted defaults. This added a second confirmation model and left Desktop, CLI, diagnostics, and documentation describing a trust state users should not need to manage. Root cause: Hook loading and inspection were gated by a persistent trust.json store, with that state propagated through boot, slash commands, Wails settings, diagnostics, and compatibility payloads. Fix: Load project hooks automatically whenever a project root is present, while preserving Safe Mode as the emergency disable path. Remove the trust store and user-facing trust controls, and keep legacy CLI commands, Wails methods, LoadOptions.Trusted, and trusted_project JSON as backward-compatible no-ops or enabled values. Update tests, diagnostics, localization, and documentation to describe automatic loading. Verification: - env -u DEEPSEEK_API_KEY go test ./... - cd desktop && go test ./... - pnpm typecheck - pnpm check:css - pnpm exec tsx src/__tests__/diagnostics-settings.test.tsx - ./scripts/cache-guard.sh - Desktop browser validation for project Hooks - git diff --check * fix: keep MCP auth cleanup in the active workspace Problem: The TUI clear-auth action followed the process working directory instead of the active controller workspace, and Desktop CI was blocked by an orphaned helper. Root cause: Authentication cleanup bypassed the workspace selection used by the MCP manager, while the removed project-trust flow left an unused Desktop path helper behind. Fix: Centralize TUI MCP workspace selection, add a cross-workspace regression test, remove the unused helper, and document the executable repository-config boundary with Safe Mode recovery guidance in English and Chinese. Verification: go test ./internal/cli; go vet ./...; go test ./...; cd desktop and run go vet ./..., go build ./..., and go test ./...; ./scripts/cache-guard.sh; git diff --check. * fix(mcp): preserve advertised cold-cache connect calls Problem: A provider could call a cold-cache MCP connect placeholder that had been advertised at request serialization time, but background schema discovery could replace it before dispatch and produce an unknown-tool failure. Root cause: Tool calls are resolved against the current registry after the provider returns, while cache-miss discovery intentionally swaps the synthetic connect placeholder for the server's real tools. Fix: Recognize a stale synthetic connect call as completed when the same MCP namespace now exposes real tools, and let already-resolved lazy placeholders report the same controlled result. Keep genuine unknown calls unchanged and add regressions at both dispatch and lazy-lifecycle boundaries. Verification: - go test ./... - go test -race ./internal/plugin ./internal/agent - go vet ./internal/plugin ./internal/agent - ./scripts/cache-guard.sh - delayed cold-cache LongCat MCP integration test |