Commit Graph
246 Commits
Author SHA1 Message Date
Saoud RizwanandSaoud Rizwan faf05ef067 fix(desktop): stop UPX-packing the Linux SSH remote helpers (#14928)
* fix(desktop): stop UPX-packing the Linux SSH remote helpers

Since the Bun 1.4.2 upgrade (#14631) the compiled helper lazily reads its
embedded module payload back from its own executable at runtime. On a
UPX-packed binary those reads return NUL padding, so the helper starts but
every --remote-hub-ensure fails with "SyntaxError: Invalid character:
'\0'" and SSH remotes cannot start (desktop 0.0.38 through 0.0.44).

Ship the helpers uncompressed again and drop the upx installs from the
publish workflow. Bundles grow by roughly 175 MB until the CLI-managed Hub
replaces the helper binary.

Fixes #14815
Fixes #14781

Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>

* ci(desktop): smoke-test the bundled SSH remote helper before publishing

Run each Linux job's native remote helper through --remote-hub-ensure and
--remote-hub-stop against an isolated CLINE_DIR and discovery record. The
UPX regression started the helper fine but failed on exactly this path,
and nothing in the publish workflow exercised it.

Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>

---------

Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
2026-10-07 15:54:57 -07:00
Saoud RizwanandSaoud Rizwan af7e4ce5e9 ci(desktop): publish Linux arm64 deb and rpm packages (#14672)
* ci(desktop): publish Linux arm64 deb and rpm packages

Turn the build-linux job into an x64/arm64 matrix. The arm64 leg runs
natively on ubuntu-22.04-arm so the sidecar smoke test exercises the real
binary; the collect step names packages per arch (amd64/arm64 deb,
x86_64/aarch64 rpm), which generate-update-manifest.ts already maps to the
linux-aarch64-deb and linux-aarch64-rpm updater keys.

Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>

* test(desktop): cover arm64 Linux packages in updater manifest test

Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>

---------

Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
2026-09-29 15:53:22 -07:00
Dominic Cooney 159951f82d ci(vscode): make the e2e job spend its time on the tests (#14607)
* ci(vscode): make the e2e job spend its time on the tests

Windows e2e runs hit the 20-minute limit after their tests had passed:
the job was saving a multi-gigabyte bun cache that only that pull request
could read. The job also repeated the type check and lint that Quality
Checks already runs, and downloaded Playwright browsers the tests never
start.

- Restore the bun cache on every run and save it only from main, right
  after the install, so a later test failure keeps it.
- Build the extension with build:production and load it as the
  extension under development. The tests already ran that copy; the VSIX
  they also installed was never used, and packaging it ran
  vscode:prepublish's checks.
- Install only Playwright's ffmpeg, which it records test videos with.
  The tests launch VS Code through Electron, not a Playwright browser.
- Let runs on main finish instead of cancelling them on the next merge.
- Raise the timeout to 30 minutes for the run on main that installs
  without a cache and saves a new one.

* ci(vscode): make the e2e workflow comments match its behavior

A run on main that is still waiting to start is replaced by the next merge; only a run that has started always finishes. The comments now say so, and name the directory VS Code loads the extension from.

* ci(vscode): run Quality Checks when the Playwright config changes

The e2e build no longer runs the type check and lint, and
playwright.config.ts was the one lintable e2e input outside Quality
Checks' path filter. A change to only that file could pass CI unlinted.

* ci(vscode): stop caching Bun's install cache in e2e

Restoring the cache and installing took about as long as an uncached
`bun install` in ext-vscode-test's jobs on the same commits: 3:48-4:31
against 2:39-5:06 on Windows, and 25s against 23s on Ubuntu. With no
cache to save, a run on main can be cancelled by the next merge again
and the job fits main's 20-minute timeout, so concurrency and the
timeout go back to main's settings.

The e2e path filter also drops apps/vscode/.vscodeignore: test:e2e no
longer packages a VSIX, so nothing in the job reads that file.
2026-09-29 21:18:37 +02:00
Saoud Rizwan 3ba8dd8fa7 chore: upgrade Bun to 1.4.2 (#14631)
* chore: upgrade Bun toolchain and embedded runtime to 1.4.2

Bun 1.4.1+ writes a valid ad-hoc Mach-O signature for bun build --compile
output, so the CLI is no longer SIGKILLed on macOS 27, and 1.4 ships
x64 runtimes as baseline-only, so compiled binaries run on pre-AVX2 CPUs.

Pins engines.bun, packageManager, .tool-versions, every setup-bun step,
and @types/bun. The lockfile only moves @types/bun/bun-types so the AI SDK
hoisting is left untouched.

* fix(core): swallow reader.cancel() rejection in web_fetch size guard

Bun 1.4 rejects cancel() on an errored stream, which surfaced as an
unhandled rejection right before the size-limit error was thrown.

* build(cli): boot the hub from the compiled binary as a smoke test

--version only proves the executable launches. Bun 1.4.1/1.4.2 have a
compile-time chunk ordering regression with splitting: true that only
shows up once real modules load, so start the hub daemon from the freshly
compiled host binary in an isolated data dir and require a healthy status
before the build is considered good.

* test(core): stop asserting the forced-exit path in hub shutdown e2e

Under Bun 1.3 http.Server.close() never settled while a WebSocket was
open, so the daemon always exited through the 2s deadline and the e2e
asserted the forced-exit log line. Bun 1.4 completes the graceful close
(~50ms instead of ~2s in a side-by-side probe), so that line no longer
appears. The exit code, timing, discovery cleanup, and endpoint checks
still cover the behavior; the forced path stays covered by
shutdown-coordinator.test.ts.

* build(cli): isolate the smoke test hub from CLINE_HUB_DISCOVERY_PATH

The env var overrides the data-dir-derived discovery record, so an
inherited value would have pointed hub start/stop at a real hub.
2026-09-28 21:33:32 -07:00
Saoud Rizwan d16bd8c7d5 desktop: add Linux support (#14464)
* desktop: use GTK folder picker on Linux and open it off the main thread

rfd's default xdg-portal backend needs a D-Bus session and a portal
backend; without them pick_folder() returns None and the app treats it as
a cancel, so Open folder... silently does nothing. Switch to the gtk3
backend (the app is a GTK window already) and, like tauri-plugin-dialog,
create the dialog on the main thread and await it elsewhere, because the
GTK dialog is driven by the GLib main loop Tauri owns and a blocking
pick_folder() on the main thread deadlocks. macOS and Windows keep the
blocking dialog on the main thread.

* desktop: stage Linux updates and install them on restart

On Linux deb/rpm installs, Update::install runs pkexec dpkg -i / rpm -U,
which asks for the user's password. The background cycle was calling
download_and_install on every non-Windows platform, so an available
update would pop a root password prompt out of nowhere every two hours.
Take the Windows path instead: download and stage in the background,
install when the user chooses Restart now, then relaunch into the new
version.

* desktop: add Linux deb/rpm entries to the updater manifest

Map <Product>_<version>_amd64.deb and <Product>_<version>_x86_64.rpm
(plus their arm64 counterparts) to the installer-specific
linux-<arch>-deb / linux-<arch>-rpm keys. No bare linux-<arch> key is
emitted because the updater falls back to it for any package format
without an entry of its own.

* desktop: Linux packages ship deb + rpm only, with package metadata

The AppImage target is dropped from local Linux packaging: linuxdeploy
cannot process the Bun-compiled sidecar (ldd fails on it and patchelf
corrupts it), which aborts the whole bundle step. Add category,
descriptions, and homepage so the .desktop entry gets Categories and
Comment and the deb/rpm stop reporting Description: (none).

* ci(desktop-publish): build and publish Linux x64 deb and rpm

New build-linux job on ubuntu-22.04 mirroring the Windows job: Tauri
Linux build deps plus upx, updater-signed deb and rpm via
--bundles deb,rpm, the same feed-endpoint, telemetry, and packaged
sidecar guardrails, and a desktop-linux-x64 artifact the release and
nightly jobs consume.

* docs(desktop): document Linux deb/rpm releases and install steps
2026-09-23 21:56:59 -07:00
Saoud Rizwan c10b8cb4be ci(ext): add ext-vscode-publish workflow for standalone stable releases (#14277)
* ci(ext): add ext-vscode-publish workflow for standalone stable releases

Copy of ext-vscode-ab-package.yml with the A/B machinery removed: no
legacy-extension checkout or npm test suite, no loader build, no
stitch/set-version stamping, no union manifest. What remains is the
job layout that has been shipping since 4.1.0 — ungated preflight
(version regex, main-only publish, Marketplace monotonicity) and test
gate, an environment-less build job that packages the .vsix as a run
artifact, and a publish job on the `publish` environment that uploads
that artifact with `vsce publish --packagePath` before tagging.

The prebuilt-artifact upload is the point. ext-vscode-publish-stable.yml
lets a bare `vsce publish` re-run vscode:prepublish and repack the
source tree, and that upload died with `Request timeout: /_apis/gallery`
on all five v4.1.18 attempts (Sep 15-17) while the same code line went
out as 4.1.19 through the packagePath path in three seconds.

Differences from the A/B workflow beyond the removals: the version
input is asserted equal to apps/vscode/package.json instead of being
stamped in, publish=true is refused off main, README.marketplace.md is
swapped in for packaging (as the standalone workflows did), and the
build asserts the packaged manifest identity plus the absence of a
process.env.TELEMETRY_SERVICE_API_KEY literal in dist/extension.js.

* docs(skills): point publish-extension at ext-vscode-publish

Stable releases go through the new workflow; the stable-repack workflow
is marked do-not-use with the 4.1.18 timeout evidence, the dispatch and
post-publish sections describe the new job order (tag after publish,
continue-on-error bookkeeping), and the retirement list now covers
deleting ext-vscode-publish-stable.yml and publish-marketplace.mjs.

* ci(ext): retry Marketplace publish and correct the failure analysis

The workflow header and runbook previously attributed the v4.1.18 stable
failures to the repack/upload path. Reading the error against vsce's
source shows the hung request was OPTIONS /_apis/gallery, the client's
route-discovery call with no body, so the VSIX was never uploaded and
nothing about the artifact can be the cause. The stable job was also
running the workspace-vendored vsce 3.9.2 (bun run puts node_modules/.bin
first on PATH), not the global 4.0.0 it installed. Both are now stated
as facts, with the mechanism explicitly marked as unknown.

Add a 3-attempt retry around vsce publish. It cannot double-publish:
vsce queries the listing before uploading and exits 0 with 'already
published' if an earlier attempt landed but its response was lost.

* ci(ext): make publish retry duplicate-safe and check every inlined env var

Greptile findings on #14277, both verified against source:

- vsce only treats an already-published version as success when
  --skip-duplicate is passed; without it _publish throws 'already
  exists' (both on the pre-upload listing check and on a 409 from the
  upload). The retry loop therefore needed the flag, otherwise an
  attempt whose upload landed but whose response was lost would fail
  all three attempts and skip the tag/release/Slack bookkeeping.

- apps/vscode/esbuild.mjs inlines eleven build-time variables, each only
  when non-empty, but the packaged-bytes check looked for just one. Now
  every variable the build step supplies is asserted absent from
  dist/extension.js. Self-tested against the shipped 4.1.19 bundle: all
  eleven absent; OTEL_METRIC_EXPORT_INTERVAL (not supplied) present as
  the negative control.

* ci(ext): pass github.ref to the shell via env, not inline interpolation

Ref names may contain shell metacharacters; keep the run block free of
inline expression interpolation for anything that is not the
regex-validated version input.
2026-09-23 18:43:22 -07:00
Mikołaj Kondratek 4a56a43f39 test(desktop): run the x86 NSIS installer hooks against x64 processes in CI (#14176)
* test(desktop): run the x86 NSIS installer hooks against x64 processes in CI

The sidecar-lock installer hook shipped broken for four releases because
it was only ever exercised under 64-bit pwsh, never inside the 32-bit
installer NSIS actually produces. Add a bun test that compiles the real
hooks file into an x86 NSIS installer with the same NSIS 3.11 build the
Tauri bundler pins, then runs it against 64-bit fixture processes:

- a fresh install and a repeated install with nothing running succeed
- running cline-app.exe and code-sidecar.exe holders in the target
  directory are stopped, the locked exe is replaced, and a same-named
  process from a side-by-side install (Beta) survives untouched
- the uninstaller releases and deletes the files
- a silent install fails closed (exit 2, nothing written) when Restart
  Manager reports an error

The test needs Windows x64 and makensis (MAKENSIS_PATH). A new
windows-installer job in desktop-test.yml downloads the pinned NSIS zip,
verifies its SHA1, and runs the test whenever the hooks or the test
change. Passive-mode failures show a Retry/Cancel dialog by design and
are not automated.

* ci(desktop): disable setup-bun cache in the windows-installer job

The job installs nothing, so there is no cache to save, and setup-bun's
cache-save post step hangs on windows-latest until the 10-minute timeout
cancels the run after the test has already passed.

* test(desktop): fail the installer suite when the NSIS or Tauri CLI pin drifts

desktop-test.yml downloads nsis-3.11 by hand to match what the locked
@tauri-apps/cli bundles installers with. Nothing tied the two together, so
a Tauri CLI bump (new NSIS pin in tauri-bundler) or an edit to the download
would leave the suite exercising a different NSIS than the shipped
installer. Pin both ends in the test: bun.lock must still resolve the CLI
version whose pin was checked, and makensis must report that NSIS version.

* ci(desktop): run the installer drift guard when bun.lock changes

The suite asserts that bun.lock still resolves the pinned @tauri-apps/cli,
but both workspaces declare it as ^2.0.0, so a lockfile refresh can move
the resolved version without touching any path the workflow watched. Add
bun.lock to both trigger lists so the guard runs on its own input.
2026-09-21 11:24:12 +02:00
Saoud Rizwan 9a2512bb98 ci(desktop): tag nightlies and announce the changes to test in Slack (#14310)
* ci(desktop): tag nightlies and announce the changes to test in Slack

Each successful nightly now ends with a job that tags the built commit
desktop-nightly-<utc stamp> and posts to the release channel the
commits since the nearest desktop tag (previous nightly or last
release), scoped to the desktop app, the SDK packages the sidecar
bundles, and the publish workflow, with PR and compare links. The tag
is the only record a nightly leaves behind and is what the next one
compares against.

* ci(desktop): make the nightly announce job rerun-safe and length-capped

Cap the Slack commit list by total characters as well as count, since
15 long subjects could still exceed a section block's 3000-character
limit and fail the job. Skip tag creation when this build's tag already
exists at HEAD, and exclude it from the baseline lookup, so a rerun
after a failed Slack post retries the notification instead of dying on
'tag already exists'.
2026-09-19 14:55:41 -07:00
Saoud Rizwan 93ceab9782 fix(ci): prime the Trusted Signing token before the Windows desktop build (#14309)
The Windows desktop job signs with jsign + Azure Trusted Signing, and
tauri-sign-windows.ps1 fetches its token with az account get-access-token
on every file. The first such call comes after the Rust build, and az
must present the GitHub OIDC assertion from azure/login to get a token
for a new resource. That assertion is only accepted for ~10 minutes
(5-minute lifetime plus 5 minutes of clock skew). Every Windows job
signed its first file within 8.5 minutes of login until UPX-packing the
remote helpers (#14303) added two minutes to the build, after which both
attempts of the Sep 19 nightly failed at 10:28 and 10:08 with only
Tauri's 'failed to run pwsh' in the log.

Fetch the codesigning token once right after login so the per-file calls
are served from az's cache, and let that step fail with az's real error
if the token can't be acquired.
2026-09-19 14:07:54 -07:00
Saoud RizwanandSaoud Rizwan 18b8a63109 build(desktop): UPX-compress the bundled Linux SSH remote helpers (#14303)
Each helper is a bun --compile executable: ~100 MB of Bun runtime around
~14 MB of our code, and both Linux helpers ship inside every desktop
bundle. Since #14117 that added ~220 MB to the macOS .app (175 MB -> 435
MB installed, 74 MB -> 154 MB DMG) and ~52 MB to the Windows installer.

UPX packs each ELF in place to ~24-27 MB and it self-extracts in memory
on launch, so the installer, the SSH upload, and the remote run are
unchanged. Verified the packed x64 helper boots a Hub via
--remote-hub-ensure and shuts it down via --remote-hub-stop; cost is
~1.6 s extra startup per invocation and ~55 MB extra RSS on the daemon.
strip is not usable here: it drops Bun's appended module payload.

The publish workflow installs upx on the macOS and Windows runners and
the build fails under CI if it is missing; local dev builds warn and
skip so bun run dev keeps working without it.

Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
2026-09-19 13:07:07 -07:00
Bee 8f0978111f fix(desktop): build Linux x64 helpers for pre-AVX2 CPUs (#14262) 2026-09-18 13:47:38 -07:00
Saoud Rizwan 8872d81661 ci(desktop): boot the packaged sidecar against an isolated Hub before publishing (#14249) 2026-09-17 22:07:27 -07:00
Bee 706ceac669 feat(ci): add nightly desktop publish workflow (#14244)
* feat(ci): add nightly desktop publish workflow

Support manually dispatched nightly desktop builds from main, with auto-generated version tags and conflict-safe concurrency.

* fix(desktop): keep nightly builds as Actions artifacts
2026-09-17 17:39:14 -07:00
Saoud Rizwan 999a2b8e7b fix(desktop): claim the Hub daemon sentinel in the sidecar entrypoint (#14243)
* fix(desktop): claim the Hub daemon sentinel in the sidecar entrypoint

Since #14117 the sidecar left the daemon sentinel claim to the shared
remote helper in @cline/core, whose daemon import resolves to the dist
build of core, while the sidecar bundle itself resolves core from source
through tsconfig paths. The daemon therefore published a different build
id than the sidecar expected, the sidecar retired its own Hub on every
launch, timed out with "No compatible hub runtime is available", and
Tauri reported "desktop backend exited before publishing its endpoint"
(desktop 0.0.31). Claim the sentinel in the sidecar again so the daemon
loads from the same copy of core.

* test(desktop): start the compiled sidecar against an isolated Hub in CI

Source-level tests cannot catch a build identity split between the
desktop client and its embedded Hub daemon, so compile the real
entrypoint, bootstrap an isolated Hub with --remote-hub-ensure on an
OS-assigned port and a dedicated discovery path, pin the sidecar to that
port, and require the ready line plus a healthy /health response. Fails
with "No compatible hub runtime is available" without the entrypoint fix.
2026-09-17 17:06:36 -07:00
Saoud Rizwan d6ede1a89c fix(desktop): seed Bun's cache so the Windows publish job can cross-compile the Linux SSH helpers
build-sidecar-bin.ts cross-compiles the Linux remote helpers on every
platform, and the Windows installer ships them as Tauri resources. Bun
cross-compiles by downloading the target runtime's release zip and
extracting the executable, and on a Windows host that extraction fails
deterministically ("Failed to extract executable for
'bun-linux-x64-v1.3.13'. The download may be incomplete.") even though
the download and archive are intact. This broke the 0.0.31 publish run
twice on fresh runners; the macOS job builds the same helpers fine.

Bun only downloads when $BUN_INSTALL_CACHE_DIR lacks
bun-<target>-v<version>, so the Windows job now seeds both Linux
runtimes from the @oven/bun-<target> npm packages (byte-identical to
the zip payload) before building, and the broken extractor never runs.
2026-09-17 14:14:19 -07:00
John Choi 9e12953bed fix(ci): build LLM dependency before UI release chat tests (#14202) 2026-09-17 09:35:34 -07:00
063ce7c06d feat(telemetry): enable release traces and preserve Langfuse user/session tracking (#13982)
* feat(telemetry): enable OTLP traces exporter in publish builds

Bakes OTEL_TRACES_EXPORTER=otlp into stable/nightly extension and CLI
publish builds. With the client trace pipeline (#13974) this turns on
cline-provider AI SDK trace emission at 100% task sampling, metadata-only
(no prompt/completion content).

Kill switches without a release: remote config openTelemetryTracesExporter
(extension) and the prod otel-collector's probabilistic sampler.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* feat(telemetry): record prompt/completion content on traced requests

Sets CLINE_TRACE_RECORD_CONTENT=true alongside the traces exporter, so
cline-provider traces carry full message content (recordInputs/
recordOutputs) instead of metadata only.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* ci: assert content-capture env is inlined into the packaged extension

Stable gates pre-publish (right after vsce package); nightly packages
inside publish-nightly.mjs so its check is post-publish detection.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(ci): gate nightly publication on content flag inlining

* fix(telemetry): cover combined release packaging and verify trace artifacts

* fix(ci): type trace artifact checker and assert missing bundle fixture

* fix(test): use the .exe outfile Bun compile produces on Windows

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(telemetry): propagate Langfuse identity attributes through OTLP

* refactor(telemetry): move Langfuse integration ownership to llms

* refactor(ci): remove brittle trace artifact checks

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: abeatrix <beatrix@cline.bot>
2026-09-16 17:50:22 -07:00
Haley Park e7d224dec6 feat(ui): share native Switch across desktop settings (#14073)
* feat(ui): add shared native Switch

* refactor(desktop): adopt shared Switch

* ci(ui): run desktop settings integration tests
2026-09-11 16:18:15 -07:00
John ChoiandSaoud Rizwan e15bb0a7e5 feat(desktop): add cloud session foundations (#13558)
* feat(sdk): support authenticated remote Hub connections

* fix(sdk): pin compatible SAP connectivity

* chore(sdk): defer SAP smoke fix to main

* fix(sdk): preserve hub connection failures

* feat(hub): recover pending session approvals

* fix(hub): scope approval recovery to attached clients

* feat(desktop): add cloud session foundations

* fix(desktop): require boolean cloud rollout flag

* test(desktop): run sidecar suite in pull requests

* ci: test desktop changes in stacked pull requests

* fix(desktop): scope cloud model catalog rollout

* fix(sdk): clear feature flags on identity change

* test(hub): reconnect approval session owner

* docs(sdk): clarify remote Hub connection headers

* docs(hub): describe approval recovery

* docs(llms): describe cloud catalog opt-in

* test(sdk): tighten hub header coverage

* test(hub): remove redundant approval setup

* chore(desktop): trim cloud foundation scaffolding

* fix(hub): preserve approval recovery for existing clients

* test(core): batch root history fixture inserts

* test(sdk): await daemon health after discovery publication

* feat(hub): recover pending session approvals

* fix(hub): scope approval recovery to attached clients

* test(hub): reconnect approval session owner

* docs(hub): describe approval recovery

* test(hub): remove redundant approval setup

* fix(hub): preserve approval recovery for existing clients

* refactor(hub): drop cloud-only pending approval API

* chore(desktop): trim redundant cloud session comments

* fix(desktop): preserve cached flags and share account auth

* fix(llms): retain cloud model names after catalog merge

---------

Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
2026-09-11 13:49:57 -07:00
Saoud RizwanandSaoud Rizwan 194214af76 ci(desktop): stop Tauri from skipping the DMG layout AppleScript on Actions (#13998)
Tauri's DMG bundler passes --skip-jenkins to bundle_dmg.sh whenever CI=true,
which GitHub Actions always sets. That skips the Finder AppleScript that
applies the background, window size, and icon positions from tauri.conf.json,
so every published DMG since #13563 shipped with the stock Finder window even
though the artwork was generated and validated. Set TAURI_BUNDLER_DMG_IGNORE_CI
so the script runs on the macOS runner's GUI session.

Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
2026-09-09 12:18:30 -07:00
Saoud RizwanandSaoud Rizwan 8981079a43 Build and Authenticode-sign a Windows x64 desktop installer in desktop releases (#13607)
* feat(desktop): build and Authenticode-sign a Windows x64 NSIS installer in desktop releases

Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>

* fix(desktop): pin OIDC-adjacent actions to commit SHAs in the Windows signing job

Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>

* fix(desktop): pin checkout and upload-artifact to commit SHAs in the Windows signing job

Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>

---------

Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
2026-08-26 23:07:50 -07:00
89970ea794 Sign Windows CLI binaries with Azure Trusted Signing; surface app-control launch errors (#13021)
* feat(cli): sign Windows binaries with Azure Trusted Signing and surface app-control launch errors

Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>

* fix(cli): use _CLI-suffixed signing profile secret, normalize endpoint, fail loud on partial config

Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>

---------

Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
2026-08-26 16:13:23 -07:00
3497391c5a feat(desktop): customize macOS DMG install window (#13563)
* feat(desktop): add Retina DMG background tooling

* feat(desktop): customize the macOS DMG layout

* ci(desktop): validate DMG background assets

* fix(desktop): adjust DMG Applications icon position

* ci(desktop): drop redundant DMG artwork validation from publish workflow

Tauri's beforeBuildCommand already runs dmg:background (with its own
validation) at the start of the build/sign/notarize step, and the
release/beta config overlays do not override the build section, so this
step duplicated work the publish job performs anyway. PR-time coverage
lives in desktop-test.yml.

Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>

---------

Co-authored-by: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com>
Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
2026-08-25 14:30:59 -07:00
Saoud Rizwan e7ed29109b ci(vscode): make combined nightly manual-dispatch only
The PublishNightly environment gained required reviewers, so each cron
run parked on approval, held the workflow's concurrency group, and
silently cancelled every scheduled run queued behind it. 20 consecutive
scheduled nightlies died this way between 2026-07-31 and 2026-08-21;
the only nightlies that shipped in that window were manual dispatches.

Drop the cron rather than leave a trigger that cannot succeed unattended.
2026-08-21 17:01:56 -07:00
Mikołaj Kondratek e70b3ffc4b ci(vscode): upload E2E failure recordings from the right path (#13427)
The job sets working-directory: apps/vscode, but that default applies to run
steps only, not to `uses:` steps. Since #10961 moved the extension under apps/
and added that default, the artifact path has resolved against the repo root,
matched nothing, and every failing run logged "No files were found with the
provided path: test-results/playwright/" instead of uploading recordings.

Widen to test-results/ so Playwright's error-context snapshots ship alongside
the videos.
2026-08-20 20:10:12 +02:00
Saoud RizwanandSaoud Rizwan 878faf0a95 Rename desktop app from "Cline Code" to "Cline" (#13401)
* Rename desktop app from Cline Code to Cline

Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>

* Format touched Rust test assertions

Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>

---------

Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
2026-08-19 15:29:00 -07:00
Saoud Rizwan 36397f47eb ci: tidy workflow cache config and job permissions (#13403)
Publish workflows now always do clean npm installs (no dependency
cache in their test gates), the e2e workflow's cache keys are
exact-match only, and the e2e job drops an id-token permission it
never used.
2026-08-19 14:31:41 -07:00
Saoud Rizwan 3f0c5cdc92 ci: stop over-long changelogs from silently dropping release Slack posts (#12955)
Slack section blocks reject text longer than 3000 characters. The Slack
action logs that rejection as ##[error] but does not fail the step, so an
over-long changelog drops the release announcement while the run stays
green — cline@3.0.50 (3272 chars) published to npm, tagged, and cut a
GitHub release with no Slack post and nothing red to notice.

Every publish workflow pasted the changelog section verbatim into one
section block, so all six were exposed; the SDK, desktop, and extension
sections were only 150-350 chars under the ceiling.

Add a slack_content output alongside content: unchanged when the section
fits, otherwise trimmed on a line boundary with a link to the full
release notes. Only the Slack payload uses it — GitHub release bodies and
the desktop updater manifest still get the whole section.
2026-08-19 13:34:40 -07:00
Saoud Rizwan 76ac1c7f55 ci(ui-publish): build @cline/shared before ui typecheck (#13354)
@cline/ui's generated-media imports @cline/shared/browser, which resolves to
shared's dist output. The build-shared step sat after typecheck/test/build,
so the first ui-publish dispatch since #13025 failed at Typecheck UI with
TS2307. Move the step to right after install.
2026-08-18 18:01:10 -07:00
Saoud Rizwan eeaed357ef fix(ci): lock the legacy publish workflow to the legacy-extension branch (#13350)
The branch dispatch input was a free-form string with no validation. Both
jobs checked it out and ran full npm lifecycle scripts from it: the publish
job next to VSCE_PAT/OVSX_PAT (and npm run publish:marketplace executes a
script from that same ref with the PATs in env), and the test job with NO
environment approval at all while inheriting the workflow-level
contents/packages/checks/pull-requests write grants. A dispatch pointing at
e.g. refs/pull/N/head would run outside-contributor code with the
marketplace keys behind one approval, or with a repo-write token behind
none.

Remove the input and hardcode the protected legacy-extension branch, drop
the workflow-level permissions to contents: read, and elevate only the
publish job to contents: write (tag push + GitHub release). The branch
input's default was legacy-extension, so normal publishes are unchanged.
publish-extension skill dispatch command updated to match.
2026-08-18 16:12:04 -07:00
Saoud Rizwan bca9b64206 fix(ci): always build the legacy bundle from the legacy-extension branch (#13349)
The combined-VSIX workflow took legacy-ref as a free-form dispatch input
with no publish-time validation (next-ref has one: publish requires main).
Any typed ref — a PR merge ref, an unprotected branch — would be built
into the published VSIX by the environment-less build job, and the publish
environment approver only ever sees an opaque prebuilt artifact, so the
approval protected the marketplace PAT but not the shipped bytes.

Remove the input entirely and hardcode the protected legacy-extension
branch, which makes that branch's protection rules load-bearing for
releases. The tested-sha pinning between test-legacy and build is
unchanged. publish-extension skill dispatch command updated to match.
2026-08-18 16:00:35 -07:00
Saoud Rizwan 5ad2dd5fc8 feat(desktop): beta release channel from desktop-experimental branch (#13321)
* feat(desktop): add beta release channel from desktop-experimental branch

Adds a 'channel' input (stable|beta) to desktop-publish.yml. Beta releases
are tagged desktop-vX.Y.Z-beta.N on the desktop-experimental branch, built
with the tauri.beta.conf.json overlay (Cline Code Beta / bot.cline.app.beta,
side-by-side install with stable), published as prerelease GitHub releases,
and served by a separate rolling desktop-beta update feed. Both channels
dispatch from main so the PublishDesktop signing gates are unchanged.

Guards: stable channel now rejects prerelease tags (previously a beta tag
could clobber desktop-latest and auto-update every stable install onto it),
feed selection is fail-closed and cross-checked in the release job, and the
build asserts the compiled binary embeds exactly its own channel's feed URL.
Changelog extraction is exact-version now that stable and beta sections
interleave across branch merges.

Process doc in apps/examples/desktop-app/EXPERIMENTAL.md; publish-desktop
skill now asks stable-or-beta.

* docs(desktop): warn against renaming the desktop-latest feed

* docs(desktop): document the code-trust model for publish approvals

The beta dispatch-from-main invariant protects the workflow definition, not
the checked-out tag's build scripts, which run with signing secrets in scope
for stable and beta alike. Make explicit that the PublishDesktop reviewer
approval is the trust gate for that code, and that desktop-experimental
therefore needs main-grade merge controls.
2026-08-17 17:24:01 -07:00
Saoud RizwanandSaoud Rizwan ddfb67515b fix(desktop): inline telemetry config into the packaged sidecar binary (#12925)
* fix(desktop): inline telemetry config into the packaged sidecar binary

Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>

* fix(desktop): reject non-http OTLP endpoints in the telemetry selfcheck

---------

Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
2026-08-04 20:43:01 -07:00
Saoud Rizwan 62c57a0ccd ci(ui): publish @cline/ui without a manual approval gate (#12938)
The publish job ran under the shared `Publish` GitHub environment, whose
required reviewers turned every @cline/ui release into a two-person
ceremony. Nothing in the job reads secrets from that environment — it
authenticates to npm purely over OIDC trusted publishing — so the
environment bought us an approval prompt and nothing else. sdk-publish
and cli-publish already publish unattended the same way.

The npm trusted publisher for @cline/ui was registered with
`environment: Publish`, which pins the OIDC token's environment claim, so
it has been re-registered without it (same repo, workflow file, and
permissions). That change is already live; landing this without it would
have broken publishing.

Access is still gated by workflow_dispatch (write access required), the
`refs/heads/main` ref check, and the typed `publish` confirmation.
2026-08-04 16:23:46 -07:00
Saoud RizwanandSaoud Rizwan 058c8c90a2 feat(desktop): ship a single universal macOS DMG instead of per-arch downloads (#12923)
Tauri's universal-apple-darwin target lipos the Rust binary but expects
sidecars to already be fat binaries, so build-sidecar-bin.ts now compiles
both Bun slices and merges them when the target triple is universal.
The publish workflow builds one universal bundle instead of a two-leg
matrix, verifies every Mach-O in the bundle carries both slices, and the
updater manifest points both darwin-aarch64 and darwin-x86_64 at the same
universal artifact so existing per-arch installs migrate automatically.

Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
2026-08-04 12:42:55 -07:00
Saoud RizwanandSaoud Rizwan 46fcde0a96 ci(desktop): drop the Rust build cache from the code-signing job (#12898)
* ci(desktop): drop the Rust build cache from the code-signing job

The `build` job is the only one that can read the Apple Developer ID
certificate and the Tauri updater signing key, and it restored a
swatinem/rust-cache archive before running them. A restored cache archive
is attacker-controlled the moment the Actions cache is poisoned, which is
the pivot used against this repo's nightly workflow in Feb 2026 and the
reason actions/cache was stripped from the credential-bearing publish
jobs at the time. This workflow was added months later and reintroduced
the pattern. The updater key is the worst thing here to leak: it signs
every auto-update the installed desktop app accepts.

The cache was also not buying anything. Across the eight runs of this
workflow, seven logged "No cache found" on both matrix legs; only the run
32 minutes after another one hit, saving 1-3 minutes. A release cadence
measured in days does not outlive the entry under the repo's 10 GB LRU
eviction, so the steady state was a cold build regardless. Cold builds
took 5-7 minutes against a 90-minute timeout.

No behaviour change otherwise: the step had no id and no outputs, so
nothing referenced it.

Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>

* ci(desktop): trim the cache-removal comment to the constraint

Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>

---------

Co-authored-by: Saoud Rizwan <saoudrizwan@users.noreply.github.com>
2026-08-03 19:16:23 -07:00
Saoud Rizwan 1654517614 ci(desktop): gate desktop publish secrets behind PublishDesktop environment (#12854)
* ci(desktop): gate desktop publish secrets behind PublishDesktop environment

The Apple signing/notarization and Tauri updater secrets were repository
secrets, readable by any workflow in the repo and by anyone with push
access via a branch carrying a modified workflow. Move them behind the
PublishDesktop environment, which requires reviewer approval and
restricts deployments to main.

The build job now declares the environment, so those secrets are readable
only there and only after an approval. Add a preflight check because a
missing secret fails dangerously rather than loudly: Tauri silently skips
code signing when APPLE_CERTIFICATE is empty and skips notarization when
APPLE_API_KEY is empty, so a misconfigured environment would still
publish an unsigned, un-notarized bundle. Only a missing updater key was
already caught, by the .sig check in Collect artifacts.

validate stays ungated so a bad tag fails in seconds rather than after an
approval, matching the ungated-build/gated-publish split in
ext-vscode-ab-package. The shared Slack and telemetry secrets stay where
they are; scoping them to this environment would silently empty them in
the CLI, SDK, and extension publish workflows.

* ci(desktop): verify signing secrets are not repository-scoped

The preflight added in the previous commit checks that the signing
secrets are non-empty, which proves presence but not scope, and then
reported that they had resolved from PublishDesktop. An environment-gated
job resolves repository and organization secrets too — environment values
merely take precedence — so a credential left at repository level would
pass that check while the message claimed the migration had worked. This
workflow already demonstrates it: the gated build job reads the shared
Slack and telemetry secrets, none of which are on the environment.

Add the complementary check to validate, which declares no environment: a
signing secret that resolves there can only be repository- or
organization-scoped, so it fails the run and names the offenders. Neither
check establishes provenance alone; together they do. validate is
ungated, so a misplaced secret now fails before the approval rather than
after it.

Also drop the provenance claim from the build message and correct the
skill doc, which stated that a repository-level secret would be invisible
to the gated job.

Reported by greptile on #12854.
2026-08-02 01:55:24 -07:00
Saoud Rizwan b0ee2cc80a ci(vscode): publish combined stable VSIX to Open VSX and harden ab-package gates (#12805)
* ci(vscode): publish combined stable VSIX to Open VSX and harden ab-package gates

* ci(vscode): address greptile review — env-routed version input, bookkeeping survives Open VSX failure
2026-07-31 20:09:09 -07:00
Saoud Rizwan 69e3149f5f ci(vscode): add tag, GitHub Release, and Slack bookkeeping to combined publish workflow 2026-07-30 20:59:08 -07:00
Saoud Rizwan 311959e757 ci(vscode): add test gate to combined A/B publish + publish-extension skill (#12764)
* ci(vscode): gate the combined A/B package workflow on both bundles' test suites

* docs(skills): add publish-extension skill for VS Code extension releases

* ci(vscode): pin tested revision for next bundle and refuse publishing untested next-refs

* ci(vscode): pin legacy bundle to the revision its test gate ran against
2026-07-30 17:54:29 -07:00
Saoud Rizwan 078abcd055 fix(ci): build shared package before the ui-publish desktop chat test
The desktop chat integration test renders components from @cline/ui, but
it also pulls @cline/shared/browser through the desktop app's own
message-content module. That subpath resolves to dist output no step in
this job produced, so the suite failed to collect.

Build @cline/shared before the test, and install the full workspace: the
two-package filter did not provide enough of the tree for that build.
2026-07-30 12:02:42 -07:00
Saoud Rizwan 283c3ba937 ci: grant pull-requests write so promo-comment deletion works on PRs (#12606) 2026-07-27 16:51:00 -07:00
Saoud Rizwan 69c9a9ac28 ci: delete coding-agent promo comments on PRs (#12604) 2026-07-27 16:44:48 -07:00
Saoud Rizwan 1bd200e906 ci: strip cloud-agent promo wrappers from PR bodies (#12588)
* ci: strip cloud-agent promo wrappers from PR bodies

* ci: make PR body strip vendor-agnostic, pin github-script to SHA
2026-07-27 15:10:05 -07:00
Saoud Rizwan f5224abdf5 feat(desktop): auto-updates + automated signed releases from GitHub Actions (#12420)
* feat(desktop): auto-update via Tauri updater with restart prompt

The Rust shell now checks the desktop-latest GitHub release feed on launch
and every 2 hours, downloads and stages updates in the background, and
exposes get_update_status/restart_to_apply_update commands. The webview
polls the status and shows a persistent toast with a one-click restart once
an update is staged; ignored updates apply on next launch. Updater
artifacts are only produced with the CI config overlay
(tauri.release.conf.json) so local packaging keeps working without the
updater signing key. Also mounts the previously-unmounted Toaster so
existing toast() calls render.

* ci(desktop): add desktop-publish release workflow and publish-desktop skill

desktop-publish.yml mirrors cli-publish: dispatch with a desktop-vX.Y.Z
tag + confirm gate, validates the tag against package.json and
tauri.conf.json, builds signed+notarized DMGs for aarch64 (native) and
x86_64 (cross-compiled sidecar via bun --target), generates the updater
manifest, publishes the versioned GitHub release, refreshes the rolling
desktop-latest auto-update feed, and posts to Slack. Adds the release
skill, changelog, and README docs for the required GitHub secrets.

* fix(desktop): address review — outlast sidecar shutdown window, dedupe update toast across remounts

stop() now polls for 7s before escalating to kill, past the sidecar's own
5s SHUTDOWN_TIMEOUT_MS graceful-shutdown budget, so clicking Restart now
(or quitting) during session persistence can't SIGKILL the sidecar
mid-write. notifiedVersion moves to module scope so a page remount doesn't
re-toast an update the user already dismissed.

* docs(desktop): move publish-desktop skill to .cline/skills, slim README release section

Match the publish-cli convention: the skill lives in .cline/skills/ and is
symlinked from both .agents/skills/ and .claude/skills/ so all agents pick
it up. The README's release section shrinks to a pointer + the two
never-lose invariants (desktop-latest feed, updater private key); the repo
secrets table moves into the skill, which also fixes its dangling reference
to a 'Release automation' README section and escapes the pipe that broke
the GFM table cell.
2026-07-21 16:37:18 -07:00
Saoud Rizwan 7274d8badc feat(ui): add agent chat components, Storybook, and npm releases (#12374)
* feat(ui): add shared agent chat components and Storybook

* ci(ui): add standalone npm publishing

* docs(ui): keep release commands environment-neutral

* refactor(ui): simplify package validation

* refactor(ui): tighten package and release contracts

* docs(ui): remove duplicate install guidance

* ci(ui): make publishing workflow manual-only
2026-07-17 18:22:57 -07:00
Saoud Rizwan d618f8073a fix(vscode-rollout): align bundle versions and harden combined publish workflows (#12321)
* fix(vscode-rollout): align bundle versions in the stable AB workflow

Found by Max in local testing: the union manifest's version (what the
Marketplace and auto-update see) is the stitch input, but each bundle's
About tab and telemetry extension_version read that bundle's OWN
package.json — so the stable combined VSIX reported three different
versions (dispatch input / main's 4.0.0 / legacy's 4.0.8) depending on
where you looked. The nightly channel doesn't have this problem
(nightlify.mjs stamps one version into everything); this gives the stable
channel the identity-preserving equivalent: scripts/set-version.mjs stamps
the dispatch version into each checkout after install, before its build.

Also fixes a latent ab-package bug while restructuring the steps: the
next-bundle build never ran build:sdk, so the @cline/* workspace deps had
no dist and esbuild would fail on a fresh CI checkout (the workflow has
never run end-to-end — the publish environment gate blocked pre-merge
dispatches). Split install/build:sdk/align/build into separate steps,
mirroring the nightly workflow.

* fix(vscode-rollout): assert bundle sub-manifest versions in identity guardrails

Greptile round on #12321: the stable guardrail didn't assert version at
all. Went one further than the suggestion — both workflows' guardrails now
also assert each bundle sub-manifest's version (and name, for nightly)
matches the expected version, which is the check that actually regression-
guards the set-version.mjs/nightlify.mjs stamping (About tab + telemetry
extension_version read the sub-manifests, not the union). Expected version
routed through env rather than interpolated into the script body. Adds the
conventional paired test for set-version.mjs.

* fix(vscode-rollout): don't fail the nightly run when the tag push is rejected

First real combined publish (run 29454994164) published to both registries
successfully but the run went red at the last step: the default
GITHUB_TOKEN cannot create a ref whose commit modifies workflow files, and
HEAD was the #12253 squash merge which rewrote this very workflow. There
is no workflows permission grantable to the token, so this recurs any
night HEAD touched .github/workflows. The tag is bookkeeping — mark the
step continue-on-error so a successful publish isn't reported as a
failure. (Today's missing tag was pushed manually.)
2026-07-15 17:39:44 -07:00
Saoud Rizwan eb21ba583c fix(ci): restrict nightly publishing to main (#12322) 2026-07-15 15:35:09 -07:00
f29c25395c feat(vscode-rollout): A/B loader and packaging for staged SDK extension rollout (#12253)
* feat(vscode-rollout): A/B loader and packaging for staged SDK extension rollout

Ship one marketplace VSIX containing a tiny loader plus two complete
extension bundles: next/ (SDK-based apps/vscode from main) and legacy/
(the legacy-extension branch). The loader picks one bundle per window
from a PostHog-flag-driven, sticky, one-way cohort assignment, activates
it with a Proxy-scoped ExtensionContext so each bundle resolves its
resources from its own subdirectory, and falls back to legacy (with
partial-registration cleanup and version pinning) if the next bundle
crashes during activation.

Includes the union-manifest generator with per-cohort when-clause
gating, the VSIX stitcher, a node-level loader smoke test, and the
ext-vscode-ab-package workflow that builds both refs and packages
(optionally publishes) the combined VSIX.

* fix(vscode-rollout): address rollout review feedback

* feat(vscode-rollout): versioned kill-switch, user-setting override, launch-cadence telemetry

Review follow-ups from #12253:

- Kill-switch is now scoped by version instead of boolean: the PostHog flag's
  payload carries {"maxKilledVersion": "x.y.z"} and the loader demotes only
  combined VSIXes <= that version, so killing a broken release never blocks
  the release that fixes it. Arming with no payload still demotes everything,
  and the old boolean memento format is normalized on read.

- cline.rollout.bundleOverride user setting (auto | next | legacy) as a
  manual escape hatch editable straight from settings.json: beats flags and
  the kill-switch in both directions, applies on window reload, reported as
  'override' on the activation event. Injected into the union manifest by
  gen-manifest so neither bundle has to know about it.

- parseRolloutFlags hardens flag typing: only a literal boolean true promotes
  (multivariate variants, numbers, junk fail safe), kill payloads are parsed
  defensively from /decide's JSON-string encoding.

- Activation events now carry ms_since_last_activation so the real window-
  reload cadence bounds how fast the rollout percentage gets dialed up.

- Walkthrough manifest invariant relaxed from byte-equality to structural
  equality (ids/media/completionEvents): the branches already diverge on one
  MCP step description, and since walkthrough markdown at the VSIX root comes
  from next regardless, hard-failing on copy tweaks bricked the release
  pipeline while protecting nothing. Copy divergence now warns and ships
  next's text.

* feat(vscode-rollout): identity-aware namespace, authoritative activation telemetry, nightly indicator

- Derive the setting section and sdkBundle context key from the packaged
  manifest name (cline.* for stable claude-dev, cline-nightly.* for the
  nightly identity, whose packaging rewrites the whole ID namespace);
  gen-manifest derives the same prefix for gates and the injected
  bundleOverride setting.
- Call the activated bundle's reportRolloutActivation export (merged on
  both branches) with attempted/actual/fallback — the authoritative
  extension.rollout.bundle_activated event, attributed via the bundle's
  variant-built telemetry. On crash fallback the LEGACY bundle reports it.
- Rename the loader's direct PostHog event to
  extension.rollout.loader_decision: it collided byte-for-byte with the
  bundles' event name under a different schema. It keeps the loader-side
  metadata (override, launch cadence, loader_version, extension_name) and
  gains double_failure for the both-bundles-dead case.
- Fix duplicate activation events on crash fallback: the recursive legacy
  activation no longer emits a second, contradictory fallback:false event.
- Nightly-only status bar indicator (Cline: Next / Cline: Legacy) so
  dogfooders can see which bundle a window is running.
- Union diverged engines to the newer requirement instead of hard-failing:
  main's VS Code engine (^1.101.0) has legitimately moved ahead of
  legacy-extension's (^1.84.0), which bricked every combined build.
- Smoke scenarios for all of the above.

* feat(vscode-rollout): publish the nightly as the combined A/B VSIX

Convert ext-vscode-publish-nightly.yml (cron + dispatch) from the
standalone SDK build to the combined loader + next + legacy package,
published as saoudrizwan.cline-nightly at <major>.<minor>.<unix-seconds>:

- scripts/nightlify.mjs reproduces publish-nightly.mjs's identity mutation
  (claude-dev -> cline-nightly, "cline. -> "cline-nightly., displayName,
  activity bar title) with the version as an explicit argument so ONE
  version reaches both bundle manifests and the union manifest. Runs after
  dependency install and before each bundle build.
- Both bundle builds get CLINE_ROLLOUT_VARIANT (next/legacy) in the nightly
  AND stable workflows — without it the merged rollout telemetry
  (extension_variant common prop + the authoritative bundle_activated
  capture) silently no-ops.
- dry-run dispatch input builds and uploads the installable .vsix without
  publishing or tagging; publish/tag steps are additionally gated to main,
  so the PR branch can be dispatched for pre-merge verification.
- Identity guardrails before packaging: nightly workflow asserts
  cline-nightly, the stable ab-package workflow asserts claude-dev.
- The nightly tag now records the legacy bundle sha in its message.
- README: nightly channel section (identity mapping, the two telemetry
  events and their owners, dry-run verification), and a note that the
  PostHog flags govern nightly only until the stable combined VSIX ships.

The single-bundle publish-nightly.mjs path remains for manual
feature-branch pre-release publishes; CI no longer invokes it.

* chore(vscode-rollout): harden nightly workflow gating

- Restore a job-level branch allowlist on the publish job (main + the
  rehearsal branch). Advisory defense-in-depth: the enforced gate is the
  PublishNightly environment's deployment-branch policy in repo settings,
  which must list the same branches; a dispatched branch runs its own copy
  of this file.
- Route the legacy-ref dispatch input through env instead of interpolating
  it into the run script body (script-injection hygiene; dispatch already
  requires write access).

* add otel vars to rollout build (#12316)

- Extension will not emit otel metrics to otel without these vars, so
adding those into the slow-rollout build workflow

Co-authored-by: Max Paulus 🥪 <max@cline.bot>

* fix(vscode-rollout): pass OTel env to the nightly legacy bundle build

Legacy's esbuild inlines OTEL_* at build time and its standalone publish
workflow passes them, so the combined nightly's legacy bundle was being
built with the OTel logs/metrics pipeline dead. Companion to #12316,
which fixes the same gap in ext-vscode-ab-package.yml (both bundles
there).

* feat(vscode-rollout): make the rollout two-way, remove the kill-switch

The one-way cohort + versioned kill-switch existed to avoid demoting users
whose SDK-bundle tasks aren't listed by legacy and whose rotated creds may
need a re-login. Decision: those are acceptable, temporary UX costs on an
emergency-only path — not worth a second flag and permanent mechanism
complexity (payload parsing, version scoping, killed-up-to cache format).

Now there is ONE knob: each background refresh caches exactly what
ext-sdk-bundle-rollout says for the next window. Dialing the percentage
down demotes; 0% pulls everyone back to legacy on their next reload.
Fail-safe direction preserved: only a literal boolean true promotes —
variant strings / numbers / a deleted flag all resolve to legacy; malformed
/decide responses leave the cache untouched. Local crash pinning (next
threw -> pin this version to legacy on this machine) is unchanged and
independent of the flag.

Removes KILLSWITCH_FLAG/KILLSWITCH_STATE_KEY/isVersionKilled/
normalizeKilledUpTo/compareVersions/nextCachedBundle; parseRolloutFlags
becomes parseRolloutAssignment returning the bundle to cache. Smoke
scenarios replaced with two-way promote/demote coverage.

---------

Co-authored-by: Max <maxpaulus43@gmail.com>
Co-authored-by: Max Paulus 🥪 <max@cline.bot>
2026-07-15 15:07:45 -07:00
Dominic Cooney 12703bf407 Improve VS Code terminal reliability: OSC 633 parser, exit codes, timeout handling (#11972) 2026-07-14 16:34:36 -07:00