Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,34 @@
from published versions since it shows up in the VS Code extension changelog
tab and is confusing to users. Add it back between releases if needed. -->

## Unreleased

### Added

- The experimental Workspaces panel can start, stop, restart, update, retry
and delete workspaces, open their sharing settings in the dashboard, and run
ping, speed test and support bundle on an agent. Each action checks the
workspace's current status first and explains when it no longer applies.
A spinner marks a workspace while its action runs, and its other actions
are disabled until it finishes.

### Fixed

- Ping and speed test from an agent in the sidebar now target that agent
instead of the workspace's first agent.
- Pending Workspaces panel actions clear on session changes, and completed
actions no longer resume fetching after a session change or disposal.
- The Workspaces panel's updated time can be focused with the keyboard to
show its full date and time.
- Workspace lifecycle actions halt after a session change instead of continuing
against a replacement deployment or account. Canceled OAuth refreshes no
longer overwrite replacement credentials or reopen sign-in prompts.
- Restart preserves the workspace's template version and refuses to start over
an intervening build. Transient stop-polling failures are retried without
repeating stop or start requests.
- Update halts when its confirmed operation changes during preparation.
Updates on Coder 2.36+ no longer require a locally available CLI.

## [v1.17.2](https://github.com/coder/vscode-coder/releases/tag/v1.17.2) 2026-10-28

### Changed
Expand Down
1 change: 1 addition & 0 deletions packages/shared/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,5 @@ export type {

// Workspaces types and API
export * from "./workspaces/types";
export * from "./workspaces/actions";
export { WorkspacesApi } from "./workspaces/api";
78 changes: 78 additions & 0 deletions packages/shared/src/workspaces/actions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import type {
Workspace,
WorkspaceAgent,
WorkspaceStatus,
} from "coder/site/src/api/typesGenerated";

export type WorkspaceAction =
| "start"
| "stop"
| "restart"
| "update"
| "delete";

export type AgentDiagnostic = "ping" | "speedTest" | "supportBundle";

const BUSY_STATUSES: ReadonlySet<WorkspaceStatus> = new Set([
"pending",
"starting",
"stopping",
"canceling",
"deleting",
]);

export function isWorkspaceBusy(workspace: Workspace): boolean {
return BUSY_STATUSES.has(workspace.latest_build.status);
}

/** Actions allowed now, in display order, as in the dashboard's `abilitiesByWorkspaceStatus`. */
export function workspaceActions(
workspace: Workspace,
): readonly WorkspaceAction[] {
if (
isWorkspaceBusy(workspace) ||
workspace.latest_build.status === "deleted"
) {
return [];
}
// `coder start` reactivates a dormant workspace before starting it.
if (workspace.dormant_at) {
return ["start", "delete"];
}
if (workspace.latest_build.has_external_agent) {
return ["delete"];
}
const { status, transition } = workspace.latest_build;
// A policy that forces the active version leaves only an update.
const updateOnly =
workspace.outdated &&
(workspace.template_require_active_version ||
workspace.automatic_updates === "always");
const actions: WorkspaceAction[] = workspace.outdated ? ["update"] : [];
if (status === "running") {
actions.push("stop");
if (!updateOnly) {
actions.push("restart");
}
} else if (status === "stopped" && !updateOnly) {
actions.push("start");
} else if (status === "canceled") {
actions.push("start", "stop");
} else if (
status === "failed" &&
(transition === "start" || transition === "stop")
) {
actions.push(transition);
}
actions.push("delete");
return actions;
}

/** A support bundle is still useful without a connection. */
export function agentDiagnostics(
agent: WorkspaceAgent,
): readonly AgentDiagnostic[] {
return agent.status === "connected"
? ["ping", "speedTest", "supportBundle"]
: ["supportBundle"];
}
6 changes: 6 additions & 0 deletions packages/shared/src/workspaces/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,12 @@
import { defineCommand, defineNotification } from "../ipc/protocol";

import type {
AgentDiagnosticParams,
OpenWorkspaceParams,
SetFilterParams,
ViewInDashboardParams,
WatchAgentsParams,
WorkspaceActionParams,
WorkspacesState,
} from "./types";

Expand All @@ -25,6 +27,10 @@ export const WorkspacesApi = {
login: defineCommand<void>("login"),
openWorkspace: defineCommand<OpenWorkspaceParams>("openWorkspace"),
viewInDashboard: defineCommand<ViewInDashboardParams>("viewInDashboard"),
runWorkspaceAction:
defineCommand<WorkspaceActionParams>("runWorkspaceAction"),
runAgentDiagnostic:
defineCommand<AgentDiagnosticParams>("runAgentDiagnostic"),
refresh: defineCommand<void>("refresh"),
setFilter: defineCommand<SetFilterParams>("setFilter"),
/** Watch metadata for these agents only, so idle rows cost nothing */
Expand Down
17 changes: 16 additions & 1 deletion packages/shared/src/workspaces/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import type {
WorkspaceAppStatus,
} from "coder/site/src/api/typesGenerated";

import type { AgentDiagnostic, WorkspaceAction } from "./actions";

// Re-export SDK types for convenience
export type {
Workspace,
Expand All @@ -16,7 +18,7 @@ export type {
export type WorkspaceFilter = "mine" | "shared" | "all";

/** A workspace page in the dashboard, opened in the browser. */
export type DashboardPage = "workspace" | "settings";
export type DashboardPage = "workspace" | "settings" | "sharing";

/** What the panel may offer for the current session. */
export interface WorkspacesCapabilities {
Expand Down Expand Up @@ -60,6 +62,8 @@ export interface WorkspacesState {
readonly listedAt?: number;
/** Listed workspace IDs associated with Agent chats. */
readonly agentWorkspaceIds?: readonly string[];
/** Keyed by workspace ID: the action running there, prompts included. */
readonly pendingActions?: Readonly<Record<string, WorkspaceAction>>;
}

export interface OpenWorkspaceParams {
Expand All @@ -73,6 +77,17 @@ export interface ViewInDashboardParams {
readonly page: DashboardPage;
}

export interface WorkspaceActionParams {
readonly workspaceId: string;
readonly action: WorkspaceAction;
}

export interface AgentDiagnosticParams {
readonly workspaceId: string;
readonly agentId: string;
readonly diagnostic: AgentDiagnostic;
}

export interface SetFilterParams {
readonly filter: WorkspaceFilter;
}
Expand Down
Loading
Loading