Skip to content

canvases: refactor: share definition refresh logic across windows - #341826

Merged
Ulugbek Abdullaev (ulugbekna) merged 1 commit into
mainfrom
ulugbekna/agents/investigate-vscode-issue-341696
Oct 15, 2026
Merged

Ulugbek Abdullaev (ulugbekna) merged 1 commit into
mainfrom
ulugbekna/agents/investigate-vscode-issue-341696

Conversation

@ulugbekna

Copy link
Copy Markdown
Contributor

Fixes #341696

The Editor window New Chat › Canvas menu (#341597) and the Agents window Add Tab › Canvas menu (#340575) each had their own loop for refreshing the canvases registered in the active chat. The two had drifted apart:

Behavior Agents window (before) Editor window (before)
Chat identity Owner key ICanvasContext object identity; a new object for the same chat removed the menu's commands
Several refresh triggers in one tick 2 list calls 1
Switching chats during a list call New chat listed immediately New chat waited for the old call
Queued refresh for the previous chat Still sent to the agent host Skipped
Older result for the same chat while a newer call is pending Applied in order Dropped, so the menu could stay empty while triggers kept arriving
Failed refresh List cleared (submenu disappeared) Last list kept

Changes

  • Add CanvasDefinitionStore in vs/workbench/contrib/canvases/common. Each window passes a callback that returns its active chat (ICanvasOwner) and a way to list that chat's canvases; anything the callback reads is a refresh trigger. The store exposes the openable definitions in menu order and:
    • keeps the current list while the same chat refreshes, so an open menu keeps working commands (Agents window: picking a canvas from "+ › Canvas" fails with "command … not found" #341495);
    • coalesces triggers in the same tick and runs at most one list call per chat, plus one follow-up that uses the newest source;
    • lists a newly active chat immediately, and drops queued calls and results for chats that are no longer active or after disposal;
    • applies results for the active chat in order and does not notify for content-identical results;
    • clears the list only when the chat changes or canvases become unavailable, and derives it from the active chat so a menu never pairs one chat with another chat's canvases;
    • keeps the last list when a refresh fails.
  • SessionCanvasRegistryService (Agents window) and registerEditorCanvasActions (Editor window) now only supply their chat routing to the store. Menu contributions, command registration, ordering and opening canvases are unchanged.

User-visible behavior is unchanged, except that a failed refresh in the Agents window now keeps the Canvas submenu instead of emptying it, matching the Editor window.

Follow-up, not in this PR: the Agents window still refreshes on every chat.canvases emission. Switching it to the dedicated canvas-registry signal the Editor window uses would cut list calls to the agent host, but it needs "chat is ready" gating or a retry, as in #341701.

Validation

  • node build/next/index.ts transpile — passed. (npm run build-fast -- --client-only stops before compiling in this worktree because its shared node_modules has an older @github/copilot-sdk than package.json declares.)
  • ./scripts/test.sh --reporter dot --runGlob "**/canvases/test/**/*.test.js" — passed, 113 tests: every canvas test in vs/workbench and vs/sessions, including the existing Editor and Agents window menu tests, unchanged.
  • npm run eslint -- src/vs/workbench/contrib/canvases/common/canvasDefinitionStore.ts src/vs/workbench/contrib/canvases/test/common/canvasDefinitionStore.test.ts src/vs/workbench/contrib/canvases/browser/editorCanvasActions.ts src/vs/sessions/contrib/canvases/electron-browser/sessionCanvasService.ts — passed.
  • npm run typecheck-client — failed (exit 1) with 20 errors, all in src/vs/platform/agentHost/** from the older @github/copilot-sdk in this worktree's node_modules; none in the changed files.
  • npm run valid-layers-check — failed (exit 1) on the same 20 src/vs/platform/agentHost/** type errors after the layer checker itself passed.
  • Mutation check of the compiled store: each of 8 deliberate breakages (clearing on failure, one shared throttle, no stale-chat pre-check, list not tied to the active chat, no coalescing, strict equality, logging cancelled refreshes, reusing an old source) fails at least one new test.

Not run: manual validation in a Dev build, and the full unit and integration suites.

The Editor window Canvas menu and the Agents window Add Tab > Canvas menu each kept their own canvas-definition refresh loop, and the two had drifted apart in owner identity, refresh coalescing, owner-switch latency, stale-request handling, and failure behavior.

Move the shared lifecycle into a Workbench-owned CanvasDefinitionStore that both windows consume, while each window keeps its own chat routing, menus, and open actions. Refreshes keep the current list so open menus keep working commands, results apply in order per chat, a new chat is listed immediately, stale results are dropped, and a failed refresh keeps the last list.

Fixes #341696

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI balanced review requested due to automatic review settings October 15, 2026 17:10

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Changes recommended

Refresh races can restore obsolete definitions or delay the latest refresh behind an outdated source.

2 open findings
What changed in this PR

Shares canvas-definition refresh logic between the Editor and Agents windows while keeping menu placement and canvas-opening behavior separate.

Changes:

  • Adds a shared observable store for refresh scheduling, filtering, ordering, and failure handling.
  • Replaces both window-specific refresh implementations with adapters.
  • Adds focused lifecycle and refresh regression tests.
File Description
src/​vs/​workbench/​contrib/​canvases/​test/​common/​canvasDefinitionStore.test.ts Tests shared refresh behavior.
src/​vs/​workbench/​contrib/​canvases/​common/​canvasDefinitionStore.ts Implements the shared definition store.
src/​vs/​workbench/​contrib/​canvases/​browser/​editorCanvasActions.ts Adapts Editor menus to the store.
src/​vs/​sessions/​contrib/​canvases/​electron-browser/​sessionCanvasService.ts Adapts Agents registry to the store.

🧠 Review effort: Balanced


💡 Configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

this.loaded.set(undefined, undefined);
}
if (next) {
this.refreshDelayer.trigger(() => this.refreshes.queue(next.ownerKey, () => this.refresh(next))).catch(error => {
}

private isCurrent(request: ICanvasDefinitionRequest): boolean {
return !this._store.isDisposed && request.ownerKey === this.ownerKey;
@ulugbekna
Ulugbek Abdullaev (ulugbekna) merged commit 710a276 into main Oct 15, 2026
36 checks passed
@ulugbekna
Ulugbek Abdullaev (ulugbekna) deleted the ulugbekna/agents/investigate-vscode-issue-341696 branch October 15, 2026 17:31
@vs-code-engineering vs-code-engineering Bot added this to the 1.143.0 milestone Oct 15, 2026
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.

Canvases: share definition refresh logic across Editor and Agents windows

3 participants