Skip to content

feat: only load chat/notebook/mcp when their service overrides are used - #830

Merged
CGNonofr merged 6 commits into
CodinGame:mainfrom
brianjenkins94:feat/gate-chat-notebook-mcp
Sep 29, 2026
Merged

CGNonofr merged 6 commits into
CodinGame:mainfrom
brianjenkins94:feat/gate-chat-notebook-mcp

Conversation

@brianjenkins94

@brianjenkins94 brianjenkins94 commented Sep 28, 2026 •

Copy link
Copy Markdown

Follows up on #827 and #828.

Problem

A workbench that doesn't use the chat, notebook or mcp service overrides still loads most of their implementation. #828 split the extension host participants, but that turned out to be only a small part of it: most of it was loaded by core code (search, terminal, SCM, tasks, getting started, the extension host...) that only needed a type, a command id or a small helper, but imported it from a module that loads the whole chat implementation or notebook editor.

Measured with the build's own module graph (all entry points except the chat, notebook, mcp, interactive, ai, speech and welcome service overrides):

main this PR
chat/notebook/mcp modules loaded 1183 378
their size (transformed code) 8751KB 911KB

Changes

0109-refactor-avoid-runtime-dependencies-from-core-on-cha.patch: small changes at the call sites, each one keeping the behavior:

  • searchResult: type-only cast instead of instanceof NotebookEditorWidget (only NotebookEditorWidget registers itself in the notebook editor service)
  • searchView: compare the editor id with NOTEBOOK_EDITOR_ID instead of instanceof NotebookEditor (nothing extends it)
  • terminal decorationAddon: lazy import() of TerminalContext, only used when attaching a command to chat
  • SCM and tasks: import the chat action ids from the existing chatConstants instead of chatActions
  • getting started: the agent sessions welcome command id moved to agentSessionsWelcomeInput
  • extension host: coerceImageBuffer and 3 builtin tool ids moved to dependency-free modules (re-exported from their previous location)

0110-refactor-split-extension-host-contribution-per-featu.patch: as suggested in #828, extensionHost.base.contribution + extensionHost.{chat,notebook,mcp}.contribution, with extensionHost.contribution still importing all of them. The feature ones are imported by their service override.

Missing main thread participants (service-override/tools/missingMainThreadCustomers, imported by the extensions service override; the Unsupported decorator moved from missing-services to tools so both use it, and check-unsupported-decorator covers both): the extension host requires every MainContext proxy to be registered, and some extHost implementations call the main thread when they are constructed (e.g. ExtHostLanguageModelTools calls $getTools()). So, like the missing services, there are typed implementations of the 16 chat/language model/notebook/mcp MainThread*Shapes: notifications and registrations do nothing, queries return nothing ([]/false), and actions (invoking a tool, opening a notebook, mcp tokens...) are @Unsupported. They are registered only for those ids and only when nothing else registered them, without depending on the import order (unnamed customers are instantiated after the named ones, and a real implementation set later replaces it).

missing-services: CustomizationHarnessService no longer calls createVSCodeHarnessDescriptor, which alone loaded ~480KB of chat.

Build check (feature-isolation-check rollup plugin): fails when more than 1000KB of chat/notebook/mcp is loaded without their service override, and always prints the 20 heaviest imports responsible, so a regression after a VSCode update points to the exact import.

A participant lost while rebasing 0110 makes the extension host fail to start (there is no missing implementation for it), and one left in extensionHost.contribution is detected by check-workbench-contributions from #831.

Behavior changes

Without the chat/lm service overrides, queries of the corresponding extension API return nothing instead of throwing: e.g. vscode.lm.selectChatModels() resolves with [] instead of rejecting with "Unsupported". Actions (e.g. vscode.lm.invokeTool, vscode.workspace.openNotebookDocument) still reject with "Unsupported".

Known remaining (~911KB)

Not addressed because they are actual dependencies rather than misplaced imports:

  • editor/terminal dictation (dictationSession, chatSpeechToTextService), also used by missing-services for ChatSpeechToTextState
  • go to symbol in a chat widget (ChatOutline, chatViewModel)
  • extHostTypeConverters / extHostLanguageModels -> languageModels
  • mainThreadEditorTabs -> chatEditorInput (tab input kinds)
  • chat context keys used in menus (debug, terminal, SCM...)

Testing

  • npm run build steps (compile, rollup plugins, default extensions, language packs, monaco languages), lint, check-unsupported-decorator, commitlint
  • demo production build as-is (with chat/notebook/mcp): no console error, extension language model provider visible in the chat model picker (the real participants are used)
  • demo production build without the chat, notebook, ai, interactive, speech and mcp service overrides, with an extension calling the chat/lm/notebook API: the extension host starts, the demo extension works, selectChatModels/tools are empty, registrations work, invokeTool and openNotebookDocument reject with "Unsupported", no console error (note: the demo dev server pre-bundles every local package, including the unused service overrides, so this has to be checked on a production build)
  • in a consumer on 36.2.7 (Vite 8), simulating the same changes removed ~1.26MB of rendered chat/notebook/mcp code (-811KB minified, -191KB gzip) with the same results on a runtime smoke test

The 0109 changes could also be proposed to microsoft/vscode, which would remove most of the patch.

🤖 Generated with Claude Code

brianjenkins94 and others added 3 commits September 28, 2026 01:13
Search, terminal decorations, SCM, tasks, getting started and the extension
host only needed a type, a lazily-used class, a command/tool id or a small
helper from chat/notebook, but imported them from modules that load most of
the chat implementation or the notebook editor.

The same goes for the missing CustomizationHarnessService, which only needed
a descriptor.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…eir service override

The extension service override was loading every main thread participant,
including the chat, notebook and mcp ones along with their implementation.
They are now loaded by their own service override.

As the extension host requires every main thread participant to be
registered, an inert implementation is registered for the missing ones.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…override

Fail the build when too much of chat/notebook/mcp is loaded without their
service override, or when an extension host main thread participant is not
imported anymore (it would silently be replaced by an inert implementation).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@CGNonofr CGNonofr 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.

Thanks! it looks quite promising!

Comment thread src/service-override/tools/inertMainThreadCustomer.ts Outdated
Comment thread rollup/plugins/extension-host-bridges-check-plugin.ts Outdated
brianjenkins94 and others added 2 commits September 28, 2026 11:43
…participants

Replace the generic proxy with implementations of the chat, language model,
notebook and mcp MainThread shapes, like the missing services: calls that are
only notifications do nothing, queries return nothing, and actions are
unsupported. They are only registered for those ids, when nothing else did.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A participant that isn't imported anymore is no longer silently replaced by
an inert implementation: without a missing implementation for it, the
extension host fails to start. The contribution check from #831 also detects a
participant left in extensionHost.contribution.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@CGNonofr CGNonofr 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.

Thanks, really cool!

Comment thread src/missing-services.ts Outdated
They are only needed with the extension host, so they are now imported by the
extensions service override instead of living in missing-services. The
Unsupported decorator moves to tools so both can use it, and the decorator
check covers the new module.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@CGNonofr
CGNonofr self-requested a review September 29, 2026 08:05
@CGNonofr
CGNonofr merged commit 662045e into CodinGame:main Sep 29, 2026
2 checks passed
@github-actions

Copy link
Copy Markdown

🎉 This PR is included in version 37.2.0 🎉

The release is available on GitHub release

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants