feat(workspaces): IPC API and extension-side data layer for the panel - #1099
Open
EhabY wants to merge 1 commit into
Open
feat(workspaces): IPC API and extension-side data layer for the panel#1099EhabY wants to merge 1 commit into
EhabY wants to merge 1 commit into
Conversation
EhabY
force-pushed
the
ehab/devex-622-workspaces-ipc-api-extension-side-provider-with-data-layer
branch
12 times, most recently
from
August 26, 2026 20:36
a17482e to
5ae7bef
Compare
EhabY
marked this pull request as ready for review
August 27, 2026 18:01
EhabY
force-pushed
the
ehab/devex-622-workspaces-ipc-api-extension-side-provider-with-data-layer
branch
10 times, most recently
from
September 9, 2026 14:11
869bb9d to
ba2ea39
Compare
Adds the typed IPC contract for the experimental Workspaces panel and the extension-side store that owns its data, porting the tree views' behaviors to push through IPC. No UI yet: the webview prints the pushed state so the data flow can be verified. `packages/shared/src/workspaces` defines the contract. One notification, `stateChanged`, carries the whole `WorkspacesState` whenever any of it changes; `ready`, `openWorkspace`, `viewInDashboard`, `refresh`, `setFilter` and `watchAgents` come back. The state is flat: capabilities, filter, workspaces, a `status` union (loading, ready, or failed with the error) and per-agent metadata as a union (pending, reported, or failed). Payloads carry decisions rather than facts to derive, so the webview holds no data and applies no policy: it asks for the state with `ready` and renders what arrives. `ready` also resets the watched agents, since a webview that just loaded shows nothing expanded. `WorkspaceStore` owns the panel's state: the active filter and its list, the filters the session may select (minus those the deployment rejected with HTTP 400, until a visible refresh re-validates them), and the agents the webview is showing. It pushes the whole state on every change and skips the push when nothing changed, so quiet polls stay off the wire. A hidden store keeps its list and its sockets; a revealed one lists again without a loading flicker. Callers raise `loading` (a filter switch, a refresh, a session change) and the list lowers it, so a poll never touches it. Split by concern: - `src/util/poller.ts`: runs one task at a time with a cancellation token per run, so a superseded fetch drops its result. The task answers with a delay in ms, `retry` (backing off to a cap) or `idle`. - `src/workspace/agentMetadataTracker.ts`: the watched set and its sockets, which linger briefly after release so toggling a row reuses them. - `src/workspace/filters.ts`: each filter's query, role requirement, poll interval and presentation, shared with the tree views, which now take a `WorkspaceFilter` instead of the `WorkspaceQuery` enum. `isQueryRejected` names the HTTP 400 both clients handle. - `src/webviews/dispatch.ts`: `dispatchWebviewMessage`, the request and command routing the Tasks, Workspaces and result panels use, with one `DispatchOptions` shape. `WorkspacesPanelProvider` takes the store and `Commands`, follows the view's visibility through one `syncVisibility`, and leaves theme changes to the webview. `Commands` gains `openWorkspaceFromSidebar` and `openWorkspaceInDashboard`, which the tree's sidebar commands share. `isOwner(user)` moved to `api-helper.ts` for `deploymentManager` and the filters. The mock `WebviewView` lives in `testHelpers.createMockWebviewView`, shared with the Tasks panel test.
EhabY
force-pushed
the
ehab/devex-622-workspaces-ipc-api-extension-side-provider-with-data-layer
branch
from
September 9, 2026 14:21
ba2ea39 to
2182874
Compare
jeremyruppel
reviewed
Sep 9, 2026
| return; | ||
| } | ||
| const state = this.state; | ||
| if (!isDeepStrictEqual(state, this.lastEmitted)) { |
There was a problem hiding this comment.
this deep comparison could be kind of expensive because the state is a pretty complex object (it can contain a bunch of workspaces, for example). wonder if we could get away with some sort of brand/version/uuid? also if the comparison isn't a concern, feel free to ignore!
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes DEVEX-622.
Extension side of the new Workspaces view, behind
coder.experimental.workspacesPanel. No UI yet: the panel prints the pushed state so the data flow can be verified.Contract
packages/shared/src/workspaces/holds the types and messages both sides import.stateChanged(the wholeWorkspacesState)ready,openWorkspace,viewInDashboard,refresh,setFilter,watchAgentsThe extension pushes the whole state whenever any of it changes and skips the push when nothing did, so the webview never merges deltas and quiet polls stay off the wire. Payloads carry decisions, not facts to derive:
capabilitieslists the filters the user may selectstatusisloading(only for a list the user waits on: the first for a filter, or a refresh),ready, orfailedwith the errorpending,reported, orfailedreadyresets the watched agents, since a fresh webview shows nothing expanded.Data layer
WorkspaceStoreowns the state: the active filter and its list, the filters the session may select (minus those the deployment rejected with HTTP 400, until a visible refresh re-validates them), and the agents the webview is showing. Callers raiseloadingand the list lowers it. A hidden store keeps its list and sockets, and lists again on reveal. A rejected query falls back to the default filter within the same run.Pollerruns the store'slist(token)task one run at a time, with a cancellation token per run so a superseded fetch drops its result. The task answers with the filter's poll interval,retry(backing off to a cap), oridle.AgentMetadataTrackerowns the watched set and its sockets. A released socket lingers 15 s, so collapsing and re-expanding a row reuses it.clearcloses everything on a session change.filters.tsowns each filter's query, role requirement, poll interval, and presentation, shared with the tree views, which now take aWorkspaceFilterinstead of theWorkspaceQueryenum.dispatch.tsgainsdispatchWebviewMessage, used by the Tasks, Workspaces, and result panels.CommandsgainsopenWorkspaceFromSidebarandopenWorkspaceInDashboard, shared with the tree's sidebar commands.isOwnermoved toapi-helper.ts.WorkspacesPanelProvidertakes(extensionUri, store, commands, logger), follows the view's visibility through onesyncVisibility, and pushes every store change to the retained view.Notes for review
mine; if it ever matters, give metadata its own notification rather than diffing fields.watchAgents, not visibility, so they stay open while the panel is hidden, as the tree's do. The UI must send the rows it shows and an empty set when it unmounts.sharedquery drops the filter from the picker instead of showing the tree's "requires Coder 2.27.0" notice. That copy belongs to the UI ticket.packages/uire-reads theme tokens in the webview.Pollerand the tracker can replace it later.setup().createMockWebviewViewintestHelpers.tsis shared with the Tasks panel test.Review log
The first review pass fixed a visible view flapping hidden on re-resolve,
requestedAgentssurviving a webview rebuild, the first retry waiting two intervals, and a hiddenrefreshre-offering filters it could not validate.A simplification pass then merged
stateReplacedandstateUpdatedintostateChanged, replaced theloadinganderrorfields with status unions, dropped the poller'sagain,generation,running, andpause, foldedTREE_PRESENTATIONand theWorkspaceQueryenum intoWORKSPACE_FILTERS, moved the panel ontoCommands, removed the reveal and theme replays, and cut the tests from 1482 to 1075 lines with asetup()per file. A final architecture review found no races or ordering hazards.