Skip to content

fix(windows): find provider CLIs from User PATH - #7362

Closed
AMDphreak wants to merge 4 commits into
pingdotgg:mainfrom
AMDphreak:fix/windows-cursor-agent-path
Closed

fix(windows): find provider CLIs from User PATH#7362
AMDphreak wants to merge 4 commits into
pingdotgg:mainfrom
AMDphreak:fix/windows-cursor-agent-path

Conversation

@AMDphreak

@AMDphreak AMDphreak commented Aug 18, 2026

Copy link
Copy Markdown

Summary

Hi! On Windows, Settings → Providers reports every local CLI as missing (cursor-agent, opencode, grok, codex, …) even when those binaries are on User PATH and a new PowerShell window can run them. Restarting T3 does not help.

This is Windows-only. Electron inherits a stale process PATH, and the PowerShell probe used [Environment]::GetEnvironmentVariable('PATH') (process env), not User/Machine registry. macOS/Linux login-shell probing is a different path.

This PR tries to make User PATH visible to every provider detector:

  • Read HKCU/HKLM Path via reg.exe (does not depend on spawning PowerShell)
  • Merge that persistent User+Machine PATH in both the server fixPath path and the desktop shell
  • Also fix the PowerShell capture to merge Machine + User + Process, for probes that still use it

Fixes #7360

Happy to adjust anything that doesn’t fit the project’s taste.

Test plan

  • vp test run packages/shared/src/shell.test.ts apps/desktop/src/shell/DesktopShellEnvironment.test.ts
  • On Windows desktop: with opencode / grok / codex / cursor-agent on User PATH, launch T3 from a stale process PATH; those providers should become installed/ready without binaryPath overrides

Made with Cursor (Auto).


Note

Medium Risk
Windows PATH merge order changes which binaries resolve for every provider probe; Hornet adds a new HTTP-backed runtime path but is isolated to the new driver.

Overview
Fixes Windows provider CLI detection when Electron inherits a stale process PATH. The shared shell layer now reads persistent User and Machine Path from the registry via reg.exe, exposes it through WindowsPersistentPath, and merges that ahead of shell/inherited segments in both resolveWindowsEnvironment and the desktop DesktopShellEnvironment install path. PowerShell env capture for PATH now joins Process, Machine, and User (not process-only), with Windows-specific helpers moved/consolidated in @t3tools/shared/shell. resolveKnownWindowsCliDirs also adds %LOCALAPPDATA%\cursor-agent. Tests cover registry parsing and stale-PATH scenarios.

In the same change set, adds a built-in hornet provider: HornetSettings (default serverUrl), HTTP HornetAdapter against /api/provider/*, health probing, driver registration, contracts/model aliases, heuristic HornetTextGeneration, and web picker/icon/docs for six built-in drivers.

Reviewed by Cursor Bugbot for commit 8e7b59d. Bugbot is set up for automated code reviews on this repo. Configure here.

Note

Fix Windows User PATH resolution for provider CLIs and add hornet provider driver

  • Windows environment capture now reads persistent User and Machine PATH from the registry via readWindowsUserAndMachinePath in shell.ts, merging them ahead of known CLI dirs and no-profile PATH so GUI-launched processes find provider CLIs installed in User PATH.
  • Adds cursor-agent to known Windows CLI dirs in resolveKnownWindowsCliDirs.
  • Introduces a full hornet built-in provider: HornetDriver in HornetDriver.ts, an HTTP adapter with session/turn streaming in HornetAdapter.ts, provider status probing in HornetProvider.ts, heuristic text generation in HornetTextGeneration.ts, settings schema in settings.ts, model defaults/aliases in model.ts, and web UI icon + provider picker entry.
  • Registers hornet in BUILT_IN_DRIVERS and extends RuntimeEventRawSource with hornet.http.
  • Behavioral Change: resolveWindowsEnvironment and installWindowsEnvironment now prepend registry User/Machine PATH entries to the merged PATH, which may change command resolution order on Windows for GUI-launched desktop processes.

Macroscope summarized 8e7b59d.

Electron often inherits a stale User PATH, so Settings still reports cursor-agent missing after the irm install. Read Machine+User PATH and include the official cursor-agent directory.

Co-authored-by: Cursor <cursoragent@cursor.com>
@coderabbitai

coderabbitai Bot commented Aug 18, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 4e8b8783-bcf1-462e-9a2e-bf271eae3b5b

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Warning

Your free Security trial is over. An organization admin can activate Security or dismiss this notice.


Comment @coderabbitai help to get the list of available commands.

@github-actions github-actions Bot added vouch:unvouched PR author is not yet trusted in the VOUCHED list. size:M 30-99 changed lines (additions + deletions). labels Aug 18, 2026
Comment thread packages/shared/src/shell.ts Outdated
Comment thread packages/shared/src/shell.ts
@macroscopeapp

macroscopeapp Bot commented Aug 18, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Not approved

Macroscope's review found this PR not approvable — This PR combines a Windows-wide PATH-resolution change with a substantial, enabled-by-default Hornet HTTP provider and model/settings integration. The new runtime path has unresolved lifecycle, cancellation, model-routing, health-status, and UI integration risks that require human review.

Not approved because:

  • 4 blocking correctness issues found at or above your repo's Minimum Blocking Severity

Adjust the Minimum Blocking Severity for this repo — including turning it Off — in Settings. You can add or adjust custom eligibility rules. Learn more.

Electron's process PATH and the PowerShell one-arg probe miss User/Machine registry Path, so every Settings detector stays Not found. Read HKCU/HKLM Path via reg.exe and merge it in desktop and server PATH repair.

Co-authored-by: Cursor <cursoragent@cursor.com>
@AMDphreak AMDphreak changed the title fix(windows): find Cursor CLI after the official installer fix(windows): find provider CLIs from User PATH Aug 18, 2026
@github-actions github-actions Bot added size:L 100-499 changed lines (additions + deletions). and removed size:M 30-99 changed lines (additions + deletions). labels Aug 18, 2026
sheehanmunim added a commit to munimtechnologies/mtcode that referenced this pull request Aug 18, 2026
…istent PATH

Electron on Windows inherits a stale PATH, so freshly installed
provider CLIs (cursor-agent, grok, codex) showed as missing until
relaunch; read HKCU/HKLM persistent PATH as a fallback.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
cursoragent and others added 2 commits August 25, 2026 09:59
PowerShell PATH capture joined Machine, User, then Process, so profile
scripts that prepend toolchain dirs (fnm, etc.) lost to system installs.
Join Process first, then Machine and User as supplements.

Co-authored-by: Ryan Johnson <AMDphreak@users.noreply.github.com>
Wire contracts, server adapter, and web icon so t3code can use Hornet as an interim multi-machine provider while desktop runs hornet serve.

Co-authored-by: Cursor <cursoragent@cursor.com>
@github-actions github-actions Bot added size:XL 500-999 changed lines (additions + deletions). and removed size:L 100-499 changed lines (additions + deletions). labels Aug 30, 2026

@macroscopeapp macroscopeapp Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Effect service conventions review of the changed TypeScript. One finding in the new Hornet adapter: an error detail built from the stringified underlying cause.

Posted via Macroscope — Effect Service Conventions

new ProviderAdapterProcessError({
provider: PROVIDER,
threadId,
detail: `Hornet request failed: ${String(cause)}`,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

detail here is just a stringified cause, so ProviderAdapterProcessError.message ends up derived from the underlying failure instead of stable structural attributes (and String(cause) can splice raw request/URL text into a caller-visible message). The real error is already preserved as cause; consider deriving detail from the structural context (path) only.

Suggested change
detail: `Hornet request failed: ${String(cause)}`,
detail: `Hornet ${path} request failed.`,

Posted via Macroscope — Effect Service Conventions

})),
),

rollbackThread: (threadId, numTurns) =>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Medium Layers/HornetAdapter.ts:338

rollbackThread reports success after truncating only ctx.turns, but the Hornet session remains unchanged. Subsequent sendTurn calls still post the same ctx.nodeId, so Hornet uses the discarded turns and replies with stale context despite the application reporting a rollback. This must invoke Hornet's rollback operation (and update the session cursor if required), or return an explicit unsupported error.

🤖 Copy this AI Prompt to have your agent fix this:
In file @apps/server/src/provider/Layers/HornetAdapter.ts around line 338:

`rollbackThread` reports success after truncating only `ctx.turns`, but the Hornet session remains unchanged. Subsequent `sendTurn` calls still post the same `ctx.nodeId`, so Hornet uses the discarded turns and replies with stale context despite the application reporting a rollback. This must invoke Hornet's rollback operation (and update the session cursor if required), or return an explicit unsupported error.

raw: { source: "hornet.http", method: "turn.started", payload: { turnId } },
});

const result = yield* postJson<{

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Medium Layers/HornetAdapter.ts:233

When postJson("/api/provider/turn", ...) fails, readThread still returns the empty turn appended at line 221, so rollback and checkpoint logic treat a failed request as conversation history. Wrap the request failure path to remove that turn and publish turn.aborted before propagating the error.

🤖 Copy this AI Prompt to have your agent fix this:
In file @apps/server/src/provider/Layers/HornetAdapter.ts around line 233:

When `postJson("/api/provider/turn", ...)` fails, `readThread` still returns the empty turn appended at line 221, so rollback and checkpoint logic treat a failed request as conversation history. Wrap the request failure path to remove that turn and publish `turn.aborted` before propagating the error.

Comment on lines +149 to +151
const body = yield* http.execute(HttpClientRequest.get(`${baseUrl}/api/health`)).pipe(
Effect.flatMap((response) => response.json),
Effect.timeout("3 seconds"),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Medium Layers/HornetProvider.ts:149

A non-2xx response containing { "ok": true } is published as ready and authenticated, so an erroring Hornet server is shown as usable. HttpClient.execute exposes the HTTP response independently of its status, but this code trusts the JSON body without checking response.status; require a successful status before parsing and accepting ok.

      const body = yield* http.execute(HttpClientRequest.get(`${baseUrl}/api/health`)).pipe(
-      Effect.flatMap((response) => response.json),
+      Effect.flatMap((response) =>
+        response.status >= 200 && response.status < 300 ? response.json : Effect.succeed(null),
+      ),
🤖 Copy this AI Prompt to have your agent fix this:
In file @apps/server/src/provider/Layers/HornetProvider.ts around lines 149-151:

A non-2xx response containing `{ "ok": true }` is published as `ready` and `authenticated`, so an erroring Hornet server is shown as usable. `HttpClient.execute` exposes the HTTP response independently of its status, but this code trusts the JSON body without checking `response.status`; require a successful status before parsing and accepting `ok`.

respondToRequest: () => Effect.void,
respondToUserInput: () => Effect.void,

stopSession: (threadId) =>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Medium Layers/HornetAdapter.ts:311

stopSession and interruptTurn do not cancel in-flight sendTurn work, so a request that passed requireSession before either operation still publishes content.delta and turn.completed after the session or turn was stopped. Track active turn fibers/requests and invalidate the session or turn before publishing completion events.

🤖 Copy this AI Prompt to have your agent fix this:
In file @apps/server/src/provider/Layers/HornetAdapter.ts around line 311:

`stopSession` and `interruptTurn` do not cancel in-flight `sendTurn` work, so a request that passed `requireSession` before either operation still publishes `content.delta` and `turn.completed` after the session or turn was stopped. Track active turn fibers/requests and invalidate the session or turn before publishing completion events.

@macroscopeapp macroscopeapp Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One finding: the new hornet provider is exposed in the chat provider picker but is not registered in the web settings provider metadata, so it cannot be configured from Settings → Providers.

Posted via Macroscope — UI Consistency

Comment on lines +55 to +60
{
value: ProviderDriverKind.make("hornet"),
label: "Hornet",
available: true,
pickerSidebarBadge: "new",
},

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hornet is now offered in the provider picker, but there is no matching entry in PROVIDER_CLIENT_DEFINITIONS (apps/web/src/components/settings/providerDriverMeta.ts), unlike every other available option (codex, claudeAgent, opencode, cursor, grok).

Because ProviderSettingsPanel derives PROVIDER_SETTINGS from DRIVER_OPTIONS, Settings → Providers renders no Hornet default row at all, AddProviderInstanceDialog cannot create a Hornet instance, and any instance that does exist falls through the unknown/fork-driver path in ProviderInstanceCard: raw hornet label, no icon, no Early Access badge (the server snapshot advertises one), no settings form — so serverUrl/binaryPath are uneditable — and no models section.

Suggested fix: add a Hornet definition next to the grok entry in providerDriverMeta.ts (importing HornetSettings and HornetIcon):

  {
    value: ProviderDriverKind.make("hornet"),
    label: "Hornet",
    icon: HornetIcon,
    badgeLabel: "Early Access",
    settingsSchema: HornetSettings,
  },

Posted via Macroscope — UI Consistency

@cursor cursor Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using high effort and found 4 potential issues.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Want fixes drafted automatically? Bugbot Autofix can create code changes for findings. A team admin can enable Autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 8e7b59d. Configure here.

input: text,
},
input.threadId,
);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Failed Hornet turns stay open

High Severity

sendTurn publishes turn.started before POST /api/provider/turn. If that request fails, the effect errors and no turn.completed, turn.failed, or turn.aborted event is emitted. Ingestion and checkpointing already observed the start, so the thread can remain in progress after the adapter failure is recovered.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 8e7b59d. Configure here.

});
}),
),
),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interrupt cannot stop Hornet turns

High Severity

interruptTurn only publishes turn.aborted. It does not cancel the in-flight POST /api/provider/turn, and that request still publishes content.delta plus turn.completed when it returns. Stop therefore races with a later completion, so the UI can show an aborted turn that then finishes with the full assistant text.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 8e7b59d. Configure here.

{
sessionId: ctx.nodeId,
input: text,
},

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Selected Hornet model is dropped

Medium Severity

The adapter advertises sessionModelSwitch: "unsupported" and the contracts layer adds Hornet defaults plus aliases, but neither startSession nor sendTurn forwards modelSelection. Every turn hits the desk with only sessionId and input, so picker choices such as GLM, MiniMax, or Kimi never reach Hornet.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 8e7b59d. Configure here.

label: "Hornet",
available: true,
pickerSidebarBadge: "new",
},

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hornet omitted from settings catalog

Medium Severity

Hornet is registered as a built-in driver and added to the chat picker, but PROVIDER_CLIENT_DEFINITIONS in providerDriverMeta.ts was not updated. Settings therefore has no Hornet schema, icon, or add-instance option, so serverUrl and binaryPath cannot be edited in the UI even though every install hydrates a default Hornet instance.

Additional Locations (2)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 8e7b59d. Configure here.

@t3dotgg

t3dotgg commented Sep 4, 2026

Copy link
Copy Markdown
Member

Note

🤖 GPT-6 Astra (preview) responding on behalf of Theo

This was closed as part of an automated cleanup pass. If you believe it was closed in error, reply here and we will get it reopened.

Closing this combined PR in favor of #8465 for Windows provider discovery. The PATH cases are recorded there for review. This branch also enables a separate Hornet provider, with unresolved stop and failure behavior, which does not belong in the PATH fix. Windows discovery work continues in the retained PR.

@t3dotgg t3dotgg closed this Sep 4, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XL 500-999 changed lines (additions + deletions). vouch:unvouched PR author is not yet trusted in the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Windows reports every provider CLI not found even when they are on User PATH

3 participants