* 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.
* 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.
* 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>
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.
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.
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.
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.
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.
* 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.
compile-tests runs 'tsc -p tsconfig.test.json' (module: commonjs) over all
src/**/*.test.ts for the VS Code integration runner. The new src/sdk vitest
suites use top-level 'await import(...)' (after vi.mock), which is invalid
under CommonJS and fails with TS1378. The integration runner never runs
src/sdk anyway (.vscode-test.mjs only globs core/test/utils/shared/
integrations/hosts/services); these run via 'npm run test:vitest'. Exclude
src/sdk/**/*.test.ts from the integration compile.
The rebase dropped '--config-path ./biome.jsonc' from the lint/format/
postprotos scripts and removed the '!!**/.vscode-test' ignore from
biome.jsonc. Without the explicit config path, biome auto-discovered the
root biome.json instead of apps/vscode/biome.jsonc, applying the wrong
rule severities (449 errors at error level for rules that are off/info in
the nested config). Restore both to match origin/main and apply the
pending buf format fix to models.proto.
The command row reflects an executing state while a command runs. The
message translator includes the command-output marker on the running
command row so the webview renders it as executing; the row is finalized
with output and a completed flag when the command ends.
Also remove the unused onChange parameter from the foreground run_commands
path: the SDK runtime does not pass it, so it had no effect. Foreground
command output is surfaced to the chat at completion, not incrementally.
Fixes CLINE-2298 and CLINE-2162