Skip to content

fix: keep legacy Coder authorities instead of reopening onto per-editor hosts - #1092

Merged
EhabY merged 3 commits into
mainfrom
fix/keep-legacy-coder-authority
Aug 25, 2026
Merged

fix: keep legacy Coder authorities instead of reopening onto per-editor hosts#1092
EhabY merged 3 commits into
mainfrom
fix/keep-legacy-coder-authority

Conversation

@EhabY

@EhabY EhabY commented Aug 25, 2026

Copy link
Copy Markdown
Collaborator

Fixes #1094: agent chats disappearing in Cursor after v1.16.1.

Problem

The SSH host prefix became per-editor in #1061 (coder-vscode. to coder-cursor.). That host name is part of the remote authority, and editors key workspaceStorage off a hash of the folder URI, so a changed prefix reads as a workspace the editor has never seen. Cursor's chats are workspace-scoped, so they went with it, along with layout and every extension's workspaceState.

setup() reopened the window on the new authority automatically (remote.ts:165), so it happened silently to every workspace opened before the upgrade, and Open Recent bounced straight back.

Why the prefix stays

It is load-bearing for the shared glob include: every editor's file is visible to every ssh invocation, and OpenSSH takes the first value it finds, so without namespaced patterns the alphabetically first filename decides whose ProxyCommand runs. Making the block editor-neutral is not safe either, since --global-config carries the session token and coder.globalFlags, coder.sshFlags, coder.binaryDestination, and coder.sshConfig are per-editor.

So the namespacing stays. What goes is everything that renames an existing workspace.

Commit 1: keep legacy authorities

  • Drop migrateLegacyAuthority. updateSSHConfig already takes hostPrefix from the authority, so a coder-vscode window still regenerates a matching block and connects. Users already moved onto coder-cursor. recover through Open Recent.
  • Name a generated config file after the editor in the host prefix, not the one writing it, so the legacy prefix is served by one file.
  • Derive that editor with hostEditorId() instead of storing what hostPrefix already encodes.
  • Tests pick an editor through the vscode mock (useEditor) instead of overwriting env.uriScheme.

This also fixes a live bug: v1.16.1 took the prefix from the authority but always wrote to <thisEditor>--<host>.conf, so a legacy window in Cursor and one in VS Code declared the same Host coder-vscode.dev--* in two files. cursor-- sorts first, so VS Code used Cursor's block and credentials on every connect.

Commit 2: reopen on the host already in use

openWorkspace matched recent folders with isRemoteAuthorityCompatible, which accepts a legacy authority, then kept only the path and opened it under a freshly minted per-editor authority. It now reuses the matched folder's authority, so only workspaces with no history get this editor's prefix. Commit 3 replaces that path match with a host comparison.

Commit 3: decide the host per workspace

Keying reuse on an exact folder path left holes. A multi-root workspace is recorded by its .code-workspace file, so it has no folderUri to match. A devcontainer authority carries a container payload, so only the identical container matched. A folder the agent supplies through expanded_directory has no entry at all until it is opened once. Each fell through to a per-editor authority while the rest of the workspace sat on the legacy host, and since an editor keys window state by the whole URI, the workspace looked like two.

A compatible entry can only be named with this editor's host or the legacy one, so matching paths was never choosing between more than those two. Comparing SSH hosts instead closes the holes and removes code.

  • recentlyOpened() reads folder entries and .code-workspace entries, and tolerates an editor without the private command instead of failing the open.
  • reusableAuthority() returns whichever of the two hosts a recent entry last connected over. Only the host carries over, never the entry's own authority, which can name another devcontainer of the same workspace.
  • openWorkspace, runOpenDevContainer, and toWorkspaceAuthority all use it, so the panel, a dashboard link, a devcontainer link, and Open Recent agree, and a support bundle for a workspace you are not connected to looks for remote logs under the host that workspace opens on.

Per @code-asher's review, the tie goes to the most recently used host rather than to legacy. A workspace already moved onto a per-editor host comes back after one trip through Open Recent, and stays there.

Also in this commit:

  • Coder: Open Generated SSH Configuration File lists the legacy file. It walked only this editor's prefix, so a local window could never open it.
  • Open Recent marks the shared host (legacy), since a workspace listed on both hosts was two identical lines, and the folder picker lists each folder once rather than once per host.
  • retargetRemoteAuthority becomes toLegacyAuthority, a single direction, since nothing migrates any more.

Known limitation

Two editors on the same deployment over the legacy host share one file, last writer wins, and setup() runs on the onResolveRemoteAuthority activation event, so a reconnect does not rewrite it. If Cursor connects, VS Code connects after it, and Cursor drops, Cursor's reconnect uses VS Code's block.

That is the pre-1.16.1 behavior and better than today, where alphabetical order always wins and never self-corrects. Closing it fully needs the per-editor prefix, which is what costs users their workspace identity.

Testing

pnpm test (2465 passed, 6 skipped), pnpm typecheck, pnpm lint, pnpm format:check, pnpm build.

Migration tests are replaced with coverage that a folder, a saved multi-root workspace, an untitled multi-root workspace, and an empty window all connect over the legacy host without reopening. New coverage for host reuse from the panel and from a link, a multi-root workspace, a devcontainer, the generated config picker, the Open Recent suffix, and toLegacyAuthority.

🤖 Generated with Claude Code

@EhabY
EhabY force-pushed the fix/keep-legacy-coder-authority branch from 5f412e0 to d8bf088 Compare August 25, 2026 10:53
EhabY added 2 commits August 25, 2026 14:11
…or hosts

Reopening a legacy window on this editor's own SSH host changed the remote
authority, and editors key per-workspace state to it, so Cursor's agent chats
and other window state looked lost after v1.16.1.

- Drop the automatic migration; a coder-vscode authority stays supported and
  keeps the workspace identity it already had.
- Name a generated config file after the editor in the host prefix rather than
  the one writing it, so the legacy prefix is served by a single file. Two
  files declaring the same host pattern left glob order, not the connecting
  editor, deciding which CLI and credentials ran.
- Let tests pick an editor through the vscode mock instead of overwriting
  env.uriScheme, restoring the previous one when the test ends.
Opening from the Coder panel matched a recent folder with
isRemoteAuthorityCompatible, which accepts the legacy coder-vscode authority,
but then kept only its path and opened it under a freshly minted per-editor
authority. In a fork that moved the folder to a URI the editor had never seen,
orphaning the window state stored against the old one.

Reuse the matched folder's own authority, so only workspaces with no history
get this editor's prefix.
@EhabY

EhabY commented Aug 25, 2026

Copy link
Copy Markdown
Collaborator Author

Tracking issue: #1094.

@code-asher code-asher left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Confirmed that it keeps using the legacy host when I launch the IDE again, and also when I disconnect then reconnect via the sidebar or URL when I have a legacy recent entry. 👍

One potential quirk though. When I first connect to the workspace, and then click "open folder" in the sidebar (or use a URL with folder set), apparently no recent entry is created?? I only get a recent if I specifically use the "open folder" link on the welcome screen, and even that only works if I open a new folder. Does that happen to you as well?

So in that case connecting from the sidebar or URL gave me the new host even though I previously connected to the old one, since there is no recent entry to correlate.

Assuming it is not just a me issue, is there some other way we might have to check what workspaces have previously been open?

Comment thread src/commands.ts Outdated
@code-asher

code-asher commented Aug 25, 2026

Copy link
Copy Markdown
Member

Huh, tested again and the recent is created so maybe just a weird issue with my setup? I was wiping my editor state.vscdb file to get a fresh start so this time I tried deleting the entire User dir and it works. So maybe deleting state.vscdb only partially deletes and leaves my IDE in a weird state or something. In any case, it looks good to me 👍

@code-asher

Copy link
Copy Markdown
Member

OK idk something seems weird with my VS Code. I connect, I have a recent, if I close the remote and exit then relaunch, the entry is gone.

@EhabY

EhabY commented Aug 25, 2026

Copy link
Copy Markdown
Collaborator Author

apparently no recent entry is created?

I'm not entirely sure what the conditions are for creating a recents entry, it is not really consistent and could be that it only happens after connecting a few times? Or maybe connecting for a bit? In any case, this PR should maintain that legacy entry if it exists 🤔

@EhabY
EhabY force-pushed the fix/keep-legacy-coder-authority branch 7 times, most recently from dd6ae43 to b11a50f Compare August 25, 2026 21:57
Reuse was keyed on an exact folder path, which left holes. A multi-root
workspace is recorded by its .code-workspace file and so has no folder URI to
match; a devcontainer authority carries a container payload that only matches
the identical container; and a folder the agent supplies has no entry at all
until it is opened once. Each fell through to a host named after this editor
while the rest of the workspace sat on the legacy one, and since an editor keys
window state by the whole URI, the workspace looked like two.

Decide per workspace instead: whichever of the two hosts a recent entry last
connected over is the one we open on. A compatible entry can only be on this
editor's host or the legacy one, so matching paths was never choosing between
more than those two -- dropping it removes code and closes the holes.

- Read the .code-workspace entries the private recents command returns, and
  tolerate an editor without that command instead of failing the open.
- Look up the same host when guessing an authority for a support bundle, and
  try both hosts against remote.SSH.serverInstallPath, so a bundle for a
  workspace on the legacy host still finds its remote logs.
- Swap an authority's host prefix in one place, so retargetRemoteAuthority and
  its new inverse toLegacyAuthority cannot drift apart.
- List the legacy file in Coder: Open Generated SSH Configuration File, which
  walked only this editor's prefix, so a local window could never open it.
@EhabY
EhabY force-pushed the fix/keep-legacy-coder-authority branch from b11a50f to 9ef304c Compare August 25, 2026 22:12
@EhabY EhabY mentioned this pull request Aug 25, 2026
@EhabY
EhabY merged commit b28f33a into main Aug 25, 2026
13 checks passed
@EhabY
EhabY deleted the fix/keep-legacy-coder-authority branch August 25, 2026 22:38
EhabY added a commit that referenced this pull request Aug 25, 2026
Release v1.16.2, stacked on #1092: bumps `package.json` to 1.16.2 and
cuts the changelog's `Unreleased` section as the v1.16.2 release.

## Changes

- `package.json`: version 1.16.1 → 1.16.2
- `CHANGELOG.md`: `## Unreleased` → `##
[v1.16.2](https://github.com/coder/vscode-coder/releases/tag/v1.16.2)
2026-08-25`

## Notes

- Based on `fix/keep-legacy-coder-authority` (#1092), so its fixes are
what v1.16.2 ships. **Retarget this PR to `main` once #1092 merges.**
- The release tag must point at a commit where `package.json` reads
1.16.2 — `release.yaml` validates tag against `package.json` and
requires the tag to be an ancestor of `origin/main`.

<sub>This pull request was created by Coder Agents on behalf of
@EhabY.</sub>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Workspace state lost after v1.16.1 moved workspaces onto per-editor SSH hosts

2 participants