Commit Graph
185 Commits
Author SHA1 Message Date
Mikołaj Kondratek 141a61fd45 fix: thread proxy/CA-aware fetch into the SDK inference path (#11462)
* fix: thread proxy/CA-aware fetch into the SDK inference path

The main agent loop did not receive the host's proxy/CA-aware fetch, so
on JetBrains and the CLI inference over a corporate proxy or to a
self-signed/private-CA endpoint failed with "unable to get local issuer
certificate". This regressed at the SDK cutover: the pre-SDK CLI
(2.18.0) constructed provider clients with a proxy-aware fetch directly,
while the SDK agent loop fell back to bare global fetch (CLINE-2353).

Two layers:
- App (cline-session-factory.ts): always build CoreSessionConfig.
  providerConfig and carry the proxy-aware fetch from @/shared/net, not
  just for Bedrock. In VSCode this fetch is global fetch, so behavior is
  unchanged there; in the standalone (JetBrains) build it is undici with
  EnvHttpProxyAgent.
- SDK (handler-factory.ts): forward providerConfig.fetch into
  createGateway both as the top-level fallback fetch and per provider, so
  the gateway's provider clients use it. Passing undefined is a no-op
  (registry resolves config?.fetch ?? defaults?.fetch ?? fallbackFetch),
  so other SDK consumers are unaffected.

The SDK change covers every host that supplies a fetch; the app change
covers VSCode and JetBrains. The CLI builds its session config through a
separate path (apps/cli) that does not yet wire a proxy-aware fetch, so
CLINE-2353 on the CLI surface is addressed in a follow-up.

Adds a handler-factory unit test asserting the host fetch is forwarded
to createGateway at both the top level and per provider.

* fix: deterministically install proxy dispatcher in standalone core

The proxy/CA-aware undici dispatcher is installed as a side effect of
loading @/shared/net (it calls setGlobalDispatcher with EnvHttpProxyAgent
in the standalone build). The standalone entry cline-core.ts did not
import that module, so the dispatcher was only installed incidentally
when some other transitively-imported module happened to pull it in. A
future change to the import graph could silently drop proxy/CA support on
JetBrains.

Import @/shared/net for its side effect, first, so the install is
deterministic and runs before any network use (CLINE-2353).

Standalone-only hardening; VSCode uses global fetch and is unaffected.
2026-06-24 14:11:33 +09:00
Saoud Rizwan 7578a1de36 fix(vscode): fix duplicate tool row when changing plan/act mode during pending tool approval (#11437)
* fix(vscode): suppress duplicate tool row when a mode change clears a pending approval

Switching plan/act while a tool approval was pending duplicated the
approval row in chat. clearPending resolved the pending approval as
denied, which unblocks the core; the core then emits the denied tool
call's content_start/content_end events before the mode coordinator's
abort lands. The interactive deny paths record the denial in the
message translator state so those events are suppressed, but
clearPending skipped that step, so the translator rendered the events
as a fresh say:tool row next to the still-visible approval ask.

clearPending now records the denial through recordDeniedToolApproval
before resolving, mirroring resolvePendingToolApproval. This covers all
clearPending callers: mode changes, task cancel, and task clear.

* refactor(vscode): trim the clearPending denial fix to its minimal shape

Keep clearPending's original structure, only inserting the denial
recording before the resolve. Drop the end-to-end suppression test:
translator suppression for recorded denials is already covered by
message-translator-approval-denial.test.ts, and the clearPending
recording is covered by the extended unit assertion.
2026-06-24 14:11:33 +09:00
Saoud Rizwan 5aa60cf116 fix(vscode): restore aggressive pin-to-bottom auto scroll in chat view (#11436)
* fix(webview): restore aggressive pin-to-bottom auto scroll in chat view

The auto-scroll effect only fired on groupedMessages.length changes, but in
the SDK-migrated extension new content can appear in the chat without the
message list length changing:

- The Thinking placeholder row is driven by turnState alone (e.g. the plan
  to act switch auto-continues the task with no new message), and it was
  appended to the rendered list inside MessagesArea where the scroll hook
  never saw it.
- New tool messages merge into the trailing tool group, and the thinking
  placeholder gets swapped for a real reasoning row at constant length.

Fixes:
- Lift the thinking placeholder computation out of MessagesArea into a new
  useDisplayedGroupedMessages hook so ChatView feeds the same list to both
  Virtuoso and useScrollBehavior; the placeholder appearing now pins to
  bottom like a real message.
- Key the pin effect on the tail message ts (skipping the placeholder) in
  addition to list length, covering in-place tail changes.
- Re-engage auto scroll when turnState.phase transitions into streaming. In
  the old extension every turn start was accompanied by a user send/button
  click that reset disableAutoScrollRef; turnState-driven turn starts like
  plan to act auto-continue have no webview-side action, so handle it in
  the scroll hook.

* refactor(webview): replace scroll fix with minimal single-file version

Same three behaviors as the previous commit (pin when the thinking
placeholder appears, pin on in-place tail changes, re-engage auto scroll
when a turn starts streaming) but implemented as two small effects in
MessagesArea, which already has both the rendered list and scrollBehavior
in scope. Reverts the useDisplayedGroupedMessages hook extraction and the
ChatView/useScrollBehavior changes; net diff vs the base branch is now
one file.
2026-06-24 14:11:33 +09:00
Dominic Cooney b62e572ea9 test(vscode): exercise full SDK structured edit flow in file-edit e2e (#11442)
* test(vscode): exercise full SDK structured edit flow in diff.test.ts e2e (ENG-2042)

The SDK runtime executes structured (OpenAI-format) tool calls instead of parsing XML-style tool syntax out of assistant text. Teach the e2e mock server to stream an editor tool call for edit_request (arguments split across deltas to exercise fragment reassembly), answer the SDK's follow-up tool-result request (role:'tool' message) with turn-ending completion text, and remove the classic XML-era EDIT_REQUEST/REPLACE_REQUEST responses.

diff.test.ts now covers the full approval flow: approval ask row -> Save -> editor tool writes the file -> completion text, verifying the edit on disk and restoring the git-tracked fixture afterwards. The old 'test.ts: Original <-> Cline's Changes' diff-tab assertions are unreachable under the SDK executor architecture (the editor executor writes via Node fs and does not route through DiffViewProvider); this behavioral difference is documented in the test file.

* test(vscode): address review feedback on diff.test.ts e2e

- Scope the mock server's tool-result follow-up detection to edit_request conversations so tool results from other (future) scenarios don't mis-route to EDIT_REQUEST_COMPLETE.

- Move the fixture readFileSync inside the try block and guard the finally restore, so a failed read doesn't bypass cleanup attribution or write undefined back to the fixture.

* docs(vscode): rephrase diff e2e comments to describe current behavior

Comments described historical behavior (XML-style tool-call parsing that predates the SDK runtime), which is confusing to readers of the current code. Rephrase them to describe the code as it exists now.

* test(vscode): rename diff.test.ts to file-edit.test.ts and drop duplicated preamble

The test no longer touches a diff editor (the SDK editor executor writes files directly after approval), so the 'Diff Editor' name was misleading. Rename the file and describe block to match what it asserts: the file-edit approval flow.

Drop the first half of the test (send hello, wait, New Task, check history), which duplicated chat.test.ts, and the mock server's 500ms delay that existed only to support an 'API Request...' visibility assertion that no longer exists.
2026-06-24 14:11:33 +09:00
Robin Newhouse 268462ddcf fix(vscode): stabilize SDK e2e login flow (#11441) 2026-06-24 14:11:33 +09:00
Dominic Cooney 072e237d01 fix(vscode): persist skill disable to SKILL.md frontmatter so the model honors it (ENG-1995) (#11294)
* fix(vscode): persist skill disable to SKILL.md frontmatter so the model honors it (ENG-1995)

The VS Code skill toggle only updated extension state (globalSkillsToggles /
localSkillsToggles), but the SDK builds the model's skill list and the `skills`
tool from each SKILL.md's frontmatter `disabled` flag. As a result, disabling a
skill in the sidebar left it fully available to the model, including in new
tasks.

toggleSkill now also writes the `disabled` flag to the skill's SKILL.md
frontmatter (no-op for remote skills, which have no backing file), via new
helpers updateSkillMarkdownDisabledState / setSkillDisabledInFrontmatter in
skills.ts. Adds unit tests for both helpers.

* fix(vscode): don't rewrite skills with malformed frontmatter (ENG-1995)

parseYamlFrontmatter fails open on invalid YAML, returning the full original
document as the body. updateSkillMarkdownDisabledState would then prepend a
second `---` block on a disable, corrupting the file. Bail out and leave the
file untouched when frontmatter fails to parse. Adds tests for the malformed
disable/enable cases.

Addresses Greptile review feedback on #11294.

* test(vscode): assert malformed-skill fixture is actually invalid YAML (ENG-1995)

Add a guard test that parseYamlFrontmatter reports hadFrontmatter and a
parseError for the shared malformed fixture, so the two "leave file untouched"
tests can't silently pass via a different code path if the fixture ever became
valid YAML.

Addresses Greptile review feedback on #11294.

* fix(vscode): resolve @cline/shared/storage subpath in mocha unit-test compile

The CommonJS mocha unit-test runner uses classic "node" moduleResolution,
which does not read the `exports` subpath maps in @cline/* package
manifests, so `@cline/shared/storage` (imported by
src/sdk/telemetry-settings-sync.ts) failed with TS2307 when test files
transitively reach the SDK adapter. Mirror the explicit paths mapping
already added to tsconfig.test.json for the integration-test compile.

* fix(vscode): restore E2E mock auth in SDK auth service so e2e tests can sign in

The SDK migration replaced classic AuthService (which swapped in
AuthServiceMock under E2E_TEST) with sdk/auth-service.ts, losing the
mock path. "Login to Cline" then invoked the real SDK OAuth flow and
opened a native browser dialog the Playwright tests cannot interact
with, so helper.signin() never authenticated and chat.test.ts +
diff.test.ts failed on every platform (the failures also reproduce on
the base branch).

- auth-service.ts: under E2E_TEST=true (and CLINE_ENVIRONMENT=local),
  exchange the well-known test code with the local mock API server and
  persist credentials to providers.json — no browser. Replaces classic
  AuthServiceMock (see origin/main src/services/auth/AuthServiceMock.ts).
- chat.test.ts/diff.test.ts: wait for the mock turn to complete before
  clicking New Task; SDK history is persisted at turn end, so navigating
  mid-turn races the write and Recent never shows.
- diff.test.ts: the footer Start New Task button only appears for
  attempt_completion turns under SDK TurnState; use the header New Task
  button like chat.test.ts.
2026-06-24 14:11:32 +09:00
Saoud Rizwan 31cff1f75e fix(vscode): auto-continue the task when switching from plan to act (#11401)
* fix(vscode): enforce stop-before-start ordering for same-id session restarts

The app reuses the taskId as the sessionId whenever it replaces or
resumes a session (mode/MCP rebuilds, follow-up resume, history
restore), but the old session's stop ran fire-and-forget, and core
cleanup is keyed by sessionId across multiple awaits. A stop still in
flight when the same-id replacement started could tear down the live
successor: late sessions-map deletes, a late 'ended' emission, or a
stalled status write landing on the replacement.

Adopt the sequencing invariant the CLI has always used: never start a
same-id session while its stop is in flight. SdkSessionLifecycle tracks
in-flight stops in a pendingStops map keyed by sessionId, and
startNewSession awaits the pending stop for a reused id before starting
(with a log line so a wedged stop is diagnosable). Fresh-id starts
never wait. fireAndForgetSend additionally captures the ActiveSession
by object identity at send time so a send settling after a same-id
replacement cannot flip the successor's run state.

* fix(vscode): auto-continue the task when switching from plan to act

In plan mode, the model's switch_to_act_mode tool call flipped the toggle
but ended the run as aborted: the beforeModel stop hook fired after
turn-started, leaving a dangling api_req_started spinner rendered as
'API Request Cancelled', and nothing continued the task after the
act-mode rebuild. Manually toggling after a presented plan had the same
dead end.

The tool now declares lifecycle.completesRun so the run ends cleanly
after the tool result, and the queued mode change rebuilds the session
and auto-continues with a hidden continuation prompt. A manual plan to
act toggle auto-continues only when the agent is idle after presenting
its plan (not running and awaiting_followup; a pending ask_question
blocks mid-run so it cannot false-positive). Composer content rides
along: typed text becomes the continuation, attachments are forwarded
and echoed, attachment-only toggles count as consumed. The RPC reports
consumption only after the send was actually handed to the session, and
the webview then clears only the exact submitted content, so failures
and racing input never lose composer state. Failures before the send
undo the optimistic running flip, report an error phase, and roll the
mode back when the session was never replaced.

Hidden prompts (the act continuation and the pre-existing task
resumption prompt) shifted editMessageAndRegenerate's visible-to-SDK
user message ordinal mapping; the new sdk-user-message-mapping module
skips them in their persisted user_input-wrapped shape, counts
attachment-only messages (which have visible bubbles), ignores
tool-result rows, and attachment-only resumes now echo a bubble to keep
both transcripts aligned. Follow-ups sent during a rebuild wait on
waitForPendingRebuild instead of resuming a parallel session that the
rebuild would kill.

The plan-mode system prompt and tool description require explicit user
approval in a message sent after the plan was presented, preventing the
model from self-escalating to act mode.

* fix(vscode): move the turn phase to error when a task resume fails

askResponse optimistically sets the turn phase to streaming before
delegating to the followup coordinator, but the coordinator's resume
catch only posted an error row, leaving the footer stuck on
Thinking/Cancel. Resume failures (auth errors, session start errors)
now report back via onResumeFailed so the controller can set the phase
to error.
2026-06-24 14:11:32 +09:00
Saoud Rizwan 9a981f81d7 fix(webview): use consistent reasoning selector component in extension provider settings (#11399)
* fix(webview): use themed components and reasoning selector in generic provider settings

The catalog-backed GenericProviderSettings path (deepseek, gemini, mistral,
and other migrated providers) rendered its model picker with raw unstyled
HTML select/input/button elements, unlike every other provider which uses
the VS Code webview-ui-toolkit components. Swap ModelPickerWithManualEntry
to VSCodeDropdown/VSCodeOption/VSCodeTextField/VSCodeButton, reusing the
DropdownContainer and re-init key workaround from common/ModelSelector.

Also render ReasoningEffortSelector in GenericProviderSettings when the
selected model's catalog info has supportsReasoning, persisting the effort
through the provider config reasoning patch, matching ClineModelPicker.
This is driven by the catalog capability flag rather than provider id.

* fix(webview): re-sync custom model id field after async config hydration

The controlled customModelId state was initialized once at mount, but the
provider config and model catalog both hydrate asynchronously, so the lazy
initializer could capture a placeholder value and leave the custom model
text field stale once the committed selection loaded. Sync the field via an
effect keyed on the committed model id and its in-list status, depending on
derived values rather than the models object whose identity can change
every render while the catalog loads.
2026-06-24 14:11:32 +09:00
Robin NewhouseandCursor d8b1ce54d4 fix(vscode): expand remote workflow/skill slash commands before send ENG-2036 (#11388)
* fix(vscode): expand remote workflow/skill slash commands before send

The SDK-backed extension sent `/workflow` text to the model verbatim, so
remote-config workflows never ran. Expansion is host-driven (the agent loop
never auto-expands), and the controller's pre-send path did none — matching
the CLI's `buildUserInputMessage`, resolve slash commands via a
controller-owned UserInstructionConfigService that watches the workspace
(including `.cline/remote-config/`), refreshed after each remote-config sync.

Fixes ENG-2036.

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

* fix(vscode): guard instruction watcher against post-dispose race

Reject in ensureUserInstructionService when the controller is already
disposed so a slash-command resolution that yielded across dispose() can't
resurrect a file watcher that nothing will stop. Also log the post-expansion
length handed to parseMentions. Addresses Greptile review.

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

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-24 14:11:32 +09:00
Max Paulus 🥪 6bc771ba9d include optional deps so that CI passes 2026-06-24 14:11:32 +09:00
Max Paulus 🥪 87338cacd1 fix broken tests 2026-06-24 14:11:32 +09:00
Max Paulus 🥪 827a4a2616 bump sdk version 2026-06-24 14:11:32 +09:00
Max Paulus 🥪 13d6fe3876 add vertex support to extension 2026-06-24 14:11:32 +09:00
Mikołaj Kondratek 82c3d8d771 fix(sdk): make model-not-found API errors actionable in the webview (#11378)
When a provider returns a model-not-found error (e.g. Anthropic's HTTP 404
for a retired model such as claude-3-haiku-20240307), the SDK strips the
status and delivers only the terse body, which collapses to the bare label
"model: <id>". reshapeErrorForWebview fell through to returning that raw
string, so ErrorRow rendered a label-like fragment in red with no hint that
the model is gone or how to recover.

Detect these in the plain-text branch of reshapeErrorForWebview and rewrite
them into a sentence that names the model and tells the user to switch models
in API Configuration settings, then retry. The model switch is framed as a
precondition rather than a parallel option so users don't loop on Retry.

Detection is text-based because the HTTP status is unavailable at this point.
The keyword match is anchored to the word "model" with a not-found signal in
the same sentence, so unrelated errors that merely mention a model (plan
gating, deprecated features) are left untouched. Adds tests for the bare
label form, a generic "does not exist" form, and two negative cases (plan
gating and an auth error mentioning a model) that must pass through unchanged.
2026-06-24 14:11:32 +09:00
Max Paulus 🥪 ee0c63a216 fix telemtry opt flag migration 2026-06-24 14:11:31 +09:00
Dominic Cooney 11e668ca23 fix(vscode): resolve @cline/shared/storage subpath in test compile + vitest
The CommonJS integration-test tsconfig (moduleResolution: node) and the
vitest config did not resolve the @cline/shared/storage exports subpath
imported by src/sdk/SdkController.ts, breaking 'compile-tests' (TS2307)
and 3 vitest SDK suites. Add explicit path/alias mappings to the built
dist so both resolve without changing module emit. Compile-time/test-only;
emitted JS still uses the real package specifier.
2026-06-24 14:11:31 +09:00
Max Paulus 🥪 50a4bfb435 migrate telemetry value in extension 2026-06-24 14:11:31 +09:00
Max Paulus 🥪 2c53d36e6d bump sdk version 2026-06-24 14:11:31 +09:00
Max Paulus 🥪 e4ab360c3c fix claude-code setting loading/persistence 2026-06-24 14:11:31 +09:00
Max Paulus 🥪 5363b2a6d2 fix task history delete 2026-06-24 14:11:31 +09:00
Max Paulus 🥪 51d3865ec5 fix model selector not showing most up to date model in providers.json 2026-06-24 14:11:31 +09:00
Max Paulus 🥪 2a070cddf5 fix ui test 2026-06-24 14:11:31 +09:00
Max Paulus 🥪 401154a42e fix ci checks 2026-06-24 14:11:31 +09:00
Robin Newhouse 3b02d5162e refactor(vscode): remove MCP marketplace ENG-1591 (#11217)
* refactor(vscode): remove MCP marketplace

* test(vscode): clarify MCP marketplace removal test

* docs: update MCP server controls docs
2026-06-24 14:11:31 +09:00
Max Paulus 🥪 710cceab90 fix anthropic provider settings persistence 2026-06-24 14:11:30 +09:00
Max Paulus 🥪 a469377d57 remove baseUrl from providers.json when unchecking box in ui 2026-06-24 14:11:30 +09:00
Max Paulus 🥪 1689abf479 fix ollama and lmtudio settings persistence 2026-06-24 14:11:30 +09:00
Max Paulus 🥪 abd84b80cb fix openrouter apikey persist to providers.json 2026-06-24 14:11:30 +09:00
Max Paulus 🥪 9d4aa03d4d fix vscodelm provider settings persist 2026-06-24 14:11:30 +09:00
Max Paulus 🥪 91077fd49e persist bedrock settings to providers.json 2026-06-24 14:11:30 +09:00
Max Paulus 🥪 6aa4c33918 don't block user input when hasNoUsableProvider == true 2026-06-24 14:11:30 +09:00
Mikołaj Kondratek aa8e11ad47 fix(bedrock): treat profile/IAM/credential-chain auth as a usable provider (#11313)
The inline "Sign in to Cline or add an API key" gate appeared and disabled
chat for Amazon Bedrock users who configured AWS Credentials (access key +
secret), an AWS profile, or relied on the default AWS credential chain, even
though the provider was fully usable (issue #11270).

hasUsableProvider() decided Bedrock usability solely via resolveApiKey(),
which maps bedrock -> awsBedrockApiKey. Bedrock's three non-API-key auth
modes leave that field empty, so buildBedrockProviderConfig() would build a
working session while the gate reported the provider unusable. The Cline
login state is irrelevant here: the gate is computed for the active-mode
provider, and the "Sign in to Cline" button is just one of two generic
remedies, which is what made the symptom look like a logged-out state.

Add a Bedrock branch that classifies usability per auth mode, reusing
resolveBedrockAuthentication() so the gate and the session builder agree on
what each mode means:
- api-key: usable only when awsBedrockApiKey is non-blank (unchanged, now
  also rejects whitespace-only keys)
- profile / iam / default credential chain: usable, deferring credential
  resolution to request time (mirrors buildBedrockProviderConfig and the
  existing keyless-provider philosophy)

Manually verified on a real setup across all four auth modes: pre-fix the
gate blocked chat for access-key and profile auth; post-fix the gate clears
and chat works. API-key mode was never gated incorrectly.

Tests: add Bedrock coverage for every auth mode, including api-key with a
blank and with an unset key (both not usable), the SigV4 repro, profile
(explicit/inferred/awsUseProfile), the bare credential-chain config, and
plan-mode resolution plus plan/act isolation.
2026-06-24 14:11:30 +09:00
Ara ca128cfd9f Fix SDK task size in delete tooltip (#11277)
* fix: show SDK task size in delete tooltip

* fix: address SDK task size review feedback

* fix: simplify SDK task size caching
2026-06-24 14:11:30 +09:00
Max Paulus 🥪 78b1f30133 remove unused code/files
fix broken webview build

remove unused code/files

fix broken webview build
2026-06-24 14:11:30 +09:00
Max Paulus 🥪 1f31738b32 delete unused files 2026-06-24 14:11:29 +09:00
Max Paulus 🥪 6eeee7c4e6 Add edit and regenerate for VS Code chat messages
Allow user feedback messages in the VS Code extension to be edited inline and regenerated from that point. Adds a TaskService RPC, truncates persisted SDK history before the selected visible user prompt, and starts a new session with the edited prompt. Also ensures the regenerated active task appears in extension history while SDK history catches up.
2026-06-24 14:11:29 +09:00
Max Paulus 🥪 e88e0c4994 fix webview-ui tests 2026-06-24 14:11:29 +09:00
Max Paulus 🥪 4258b338df show model list if possible for openai compatible 2026-06-24 14:11:29 +09:00
Max Paulus 🥪 7b24026291 fix onboarding model selection not persisting 2026-06-24 14:11:29 +09:00
Max Paulus 🥪 9a6c972b02 remove provider-specific views and just use genericprovidersettings.tsx 2026-06-24 14:11:29 +09:00
Max Paulus 🥪 fae824bff0 dry up duplicate code and create useProviderModelSelection 2026-06-24 14:11:29 +09:00
Max Paulus 🥪 9011051f76 dry up provider api key logic 2026-06-24 14:11:29 +09:00
Max Paulus 🥪 c4068823a4 dry up some duplicate code 2026-06-24 14:11:29 +09:00
Max Paulus 🥪 323492108d fix onboarding models 2026-06-24 14:11:28 +09:00
Max Paulus 🥪 58b41fbaf5 fix failing biome/lint 2026-06-24 14:11:28 +09:00
Mikołaj Kondratek 3df2d6c780 Remove unused import 2026-06-24 14:11:28 +09:00
Mikołaj Kondratek 60b1ab4d57 fix(sdk): drop dead autoContinue branch in mode rebuild
cf25cd66a ("make extension plan mode more similar to CLI") removed the
file-level ACT_MODE_CONTINUATION_PROMPT constant and stopped passing the
autoContinue / continuationPrompt options when rebuilding a session for
a mode change, but left the corresponding block inside
rebuildSessionForMode in place. The block still references the deleted
constant, so tsc fails on the SDK migration branch with TS2304: Cannot
find name "ACT_MODE_CONTINUATION_PROMPT".

No caller passes options to rebuildSessionForMode anymore, so the block
is dead. Drop the block and narrow the signature to take only newMode.
Existing tests already invoke rebuildSessionForMode(<mode>) with no
second argument and assert that fireAndForgetSend is not called on a
mode rebuild, so they keep passing.
2026-06-24 14:11:28 +09:00
Mikołaj Kondratek 0755b58da1 fix(terminal): capture standalone terminal output on Windows and harden PowerShell command handling (#11133)
* fix(terminal): surface standalone terminal spawn diagnostics

Add Logger calls at every chokepoint of the standalone terminal pipeline
so the (currently silent) failure modes around JetBrains-hosted
cline-core become debuggable from cline-core-service.log.

Lines added, all using the existing Logger facility (no new
dependencies, no behavioral changes):

* StandaloneTerminalProcess.run() now logs:
  - `[StandaloneTerminalProcess] run() entered: shell=… cwd=… args=…`
    on entry, before the try block;
  - `[StandaloneTerminalProcess] spawned pid=… for shell=…` right
    after child_process.spawn returns;
  - `[StandaloneTerminalProcess] close: code=… signal=… fullOutputLen=…`
    inside the `close` handler (the `fullOutputLen` reveals when the
    child exits 0 with empty pipes — the symptom in issue #10948);
  - `[StandaloneTerminalProcess] child error: …` in the `error`
    handler;
  - `[StandaloneTerminalProcess] spawn threw synchronously: …` in
    the outer catch.

* StandaloneTerminalManager.runCommand() now logs entry
  (`[StandaloneTerminalManager] runCommand terminalId=…: <cmd>`) and
  attaches a `.catch` to the previously fire-and-forget
  `process.run(…)` Promise so an unhandled rejection surfaces as
  `[StandaloneTerminalManager] process.run rejected for terminal …`
  instead of disappearing.

* CommandExecutor.execute() extends the existing "Executing command
  in … terminal" line with `mode=<terminalExecutionMode>` and
  `managerCtor=<manager.constructor.name>`, so it's possible to
  confirm whether the `vscodeTerminal` path is in fact backed by a
  `StandaloneTerminalManager` on JetBrains (it is — see
  notes/issue-10948-…md).

* CommandOrchestrator.orchestrateCommandExecution() logs the
  `process.once("completed")` event with `exitCode`/`signal`/
  `terminalType`, the "resolved completed" return branch with the
  line/byte totals, and emits a `WARN` on the silent "still running"
  fall-through. The last one matters because the original repro
  reported "Command executed successfully (exit code 0)" with empty
  output — the WARN makes that branch loud the next time it fires.

These logs are what made the two distinct bugs in #10948 visible
(see the 2026-05-28 update in
notes/issue-10948-terminal-output-investigation-2026-05-27.md). They
stay in to keep the next regression debuggable.

Refs: cline/cline#10948

* fix(terminal): keep Windows child stdio attached to parent pipes

The non-cmd Windows branch in StandaloneTerminalProcess.run() spawned
the shell (powershell.exe in practice) with `detached: true` and no
`windowsHide`. When cline-core is launched by the JetBrains plugin it
has no console of its own, so Windows CreateProcess allocates a NEW
console for the detached child and the child's stdio routes to that
new console instead of the pipe handles the parent created. From the
parent's point of view the pipes immediately EOF, `close` fires with
`code=0`, and `fullOutput` is 0 bytes — exactly the symptom reported
in cline/cline#10948 ("Command executed successfully (exit code 0)"
with no output and no filesystem effect).

This bug applies to every command the agent runs through the
standalone terminal path on Windows, not just the
double-wrapped-PowerShell cases (verified by re-running a clean
`dir <file>` after the diagnostics from the previous commit landed:
`run() entered` and `spawned pid=<num>` both fired, then `close: code=0
fullOutputLen=0`).

Fix:

* `detached: process.platform !== "win32"` — keep the existing
  POSIX behavior (a separate process group helps `tree-kill`), but
  drop it on Windows where `tree-kill` walks the PID tree with
  `taskkill /T` and doesn't need a process group.
* `windowsHide: true` — matches every other `child_process.spawn`
  call site in cline-core (git, MCP, hooks, browser) and flips on
  `CREATE_NO_WINDOW`, keeping the child attached to our pipes
  without popping a console window.

Verified on Windows 11 + IntelliJ IDEA 2026.1 + Cline plugin
1.1.59-Internal: `dir <path>`-style probes now produce a non-zero
`fullOutputLen` in the close log, and the captured output bytes
match what would have been visible interactively. PowerShell
double-wrapping (the other half of #10948) is handled in a
follow-up commit.

Refs: cline/cline#10948

* fix(terminal): harden PowerShell command wrapping for standalone shell

`StandaloneTerminalProcess.getShellArgs()` blindly wrapped every
PowerShell command as `["-Command", command]`. That has two
end-user-visible failure modes on Windows, both observed in
cline/cline#10948:

1. The agent's `run_commands` tool call sometimes arrives already
   prefixed with `powershell -Command "…"`. We then spawned
   `powershell.exe -Command 'powershell -Command "…"'`, and the
   outer shell shredded the inner single/double-quote pairs while
   re-parsing its `-Command` argument. The inner pwsh saw
   quote-empty `Test-Path` calls, fell through to the `else` branch
   and reported "File not found" — to ITS stdout, which the outer
   inherited but the file deletion the LLM intended never ran.
2. The user's `$PROFILE` script ran on every spawn, leaking
   non-deterministic noise (e.g.
   `%windir%\System32\REG.exe : The module '%windir%' could not be
   loaded`) into the captured output and confusing the agent.
3. Bonus: the POSIX branch used `["-l", "-c", command]`. The `-l`
   re-sources login files on every command, which is slow and lets
   greeter scripts leak into output.
4. Bonus: the cmd branch used `["/c", command]`. `/d` skips
   AutoRun, `/s` makes the embedded-quote handling deterministic.

Fix:

* PowerShell branch returns
  `["-NoProfile", "-NonInteractive", "-Command", unwrap(command)]`.
  `-NoProfile` suppresses (1) the spurious profile noise that
  contaminated the captured output, and `-NonInteractive` ensures
  the child doesn't deadlock waiting on a prompt no one will answer.
* `unwrapPowerShell(command)` strips a leading
  `powershell|pwsh [.exe] -Command|-c "…"` (or single-quoted)
  wrapper that the LLM sometimes emits, fixing the double-pass
  argument-quoting destruction. If the command does not match the
  exact wrapper shape it is returned verbatim — worst case is "no
  change", preserving pre-fix behavior.
* cmd branch returns `["/d", "/s", "/c", command]`, matching the
  canonical helper in cline/sdk/packages/shared/src/parse/shell.ts.
* POSIX branch returns `["-c", command]`, dropping the unhelpful
  `-l`. Also matches the SDK helper.

Verified on Windows 11 + IntelliJ IDEA 2026.1 + Cline plugin
1.1.59-Internal in combination with the previous "keep Windows
child stdio attached" commit: `Remove-Item CHANGELOG.md` now
deletes the file, the agent's verification `Get-ChildItem CHANGELOG*`
returns nothing, and the profile-load REG.exe error no longer leaks
into captured output.

Refs: cline/cline#10948

* refactor(terminal): tone down standalone terminal diagnostics

The diagnostics added while chasing #10948 were intentionally loud so the
two bugs were visible. Now that the fixes are in, reduce them to a normal
operating posture:

* Demote fine-grained traces to `debug`: the per-spawn `spawning …` and
  `spawned pid=…` lines, `StandaloneTerminalManager.runCommand`, and the
  orchestrator's `resolved completed` summary.
* Drop the orchestrator's `completed event` line entirely — the
  `resolved completed` debug line already carries exit code, signal, and
  line/byte totals.
* Stop echoing the full command in the manager line and stop echoing the
  args vector in the spawn line. The command is still logged once at
  `info` by CommandExecutor (unchanged, pre-existing), so we go back from
  three command echoes to one. Commands routinely embed secrets
  (Authorization headers, tokens), so fewer copies on disk is better.

Kept loud on purpose:

* `info` on `close: code=… fullOutputLen=…` — the single line that proves
  the Windows stdio-capture fix and the most useful per-command signal.
* `warn` on `resolved without completion event` — the silent-success
  canary for the #10948 failure mode.
* `error` on child error / synchronous spawn failure / unhandled
  process.run rejection.

Refs: cline/cline#10948

* fix(terminal): tighten PowerShell unwrap regex and extract to a pure module

Two review follow-ups for the #10948 shell-arg handling:

1. The wrapper-strip regex used a greedy `([\s\S]*)` body, so a command
   like `powershell -Command "foo" "bar"` would match with the body
   captured as `foo" "bar`, silently rewriting a command into something
   different. Replace the body with a tempered match `((?:(?!\1).)*)`
   that cannot contain the captured delimiter, so anything other than
   exactly one quoted token is returned verbatim. Worst case is now
   "no change" rather than an incorrect rewrite. The legitimate
   double-wrapped case from #10948 (outer ", inner ') still unwraps.

2. `getShellArgs` and `unwrapPowerShell` were private methods on
   StandaloneTerminalProcess, untestable without spawning a process.
   Move them to a pure `shellArgs.ts` module. `getShellArgs` now takes
   an injectable `platform` (defaulting to `process.platform`) purely so
   the win32-vs-posix branch is testable; behavior is unchanged. This
   also gives us a single local seam to later consolidate onto the
   canonical `@cline/shared` helper (tracked as a follow-up).

No behavioral change beyond the regex correctness fix.

Refs: cline/cline#10948

* test(terminal): cover shell-arg construction and PowerShell unwrap

Add mocha unit tests (matching the repo's node:assert/strict + __tests__/
convention so the existing mocharc spec globs pick them up) for the newly
extracted shellArgs module:

* unwrapPowerShell: double-quote and single-quote wrappers, powershell.exe
  -c form, the #10948 nested-quote repro (inner quotes preserved),
  non-wrapped passthrough, and the two regressions the tightened regex
  must reject (`… "foo" "bar"` and a command that merely mentions
  powershell mid-string).
* getShellArgs: PowerShell -> -NoProfile -NonInteractive -Command (with
  unwrap), cmd -> /d /s /c, POSIX -> -c. The injectable platform arg lets
  these run on any CI host.

This closes the M1 review finding (the regex was the riskiest line in the
change and had zero coverage) and exercises the cmd/POSIX flag changes
called out in M2.

Refs: cline/cline#10948

* docs(terminal): drop issue references and clarify windowsHide comment

Remove inline issue-number references from source comments and a test
name; that context belongs in the commit history, not the code. Also add
a one-line note that windowsHide is a no-op on non-Windows platforms,
since it is set unconditionally while the surrounding comment is
Windows-specific.

No behavior change.

* refactor(terminal): drop warn on the non-completion return path

The orchestrator's final fall-through return is a normal, expected path:
the process resolved via `continue` without a `completed` event (e.g. a
terminal mode without shell integration, or proceed-while-running flows).
Logging it at `warn` cries wolf on healthy runs, so remove it. The
genuine failure mode this was meant to catch surfaces through the
`close`/error logs and the result string itself.

* fix(terminal): address review feedback on standalone spawn paths

Three follow-ups from code review:

* StandaloneTerminalManager.runCommand: the unawaited process.run()
  .catch only logged. run() emits "error" for failures it catches, but a
  rejection escaping without an "error" event would leave the outer
  promise (resolved via the "continue"/"error" events) pending forever,
  stalling the caller. Re-emit "error" from the catch so both paths stay
  consistent. Cannot trigger today (no await outside run()'s try/catch)
  but the guard exists precisely for future rejections.

* shellArgs POSIX branch: document that dropping the login flag (`-l`)
  is intentional and relies on the child inheriting the parent's PATH via
  process.env, with a note that a GUI-launched IDE without a login PATH
  is the edge case to watch.

* StandaloneTerminalProcess cmd.exe branch: add windowsHide:true. The
  console-allocation/window-pop problem is not exclusive to the non-cmd
  branch; a console-less parent could pop a window for cmd.exe too.
  No-op on non-Windows.
2026-06-24 14:11:28 +09:00
Ara 9c030a93b6 Remove Explain Changes feature (#11278)
* chore(vscode): remove explain changes entry points

* chore(vscode): remove explain changes feature
2026-06-24 14:11:28 +09:00
Max Paulus 🥪 f9fcf7f5ce make extension plan mode more similar to CLI
- basically, don't auto continue when agent switches to act mode
2026-06-24 14:11:28 +09:00