Skip to content

fix(agent): a folder's settings no longer change what the desktop agent may do - #113

Merged
ndemianc merged 3 commits into
developfrom
fix/desktop-settings-scope
Oct 19, 2026
Merged

ndemianc merged 3 commits into
developfrom
fix/desktop-settings-scope

Conversation

@ndemianc

Copy link
Copy Markdown
Contributor

What

On the desktop, a folder's .vscode/settings.json could set every LevelCode AI setting, and its value won over the user's. So a repository could:

  • switch the agent to Autopilot;
  • send the chat, and the user's API key, to a server it names (levelcode.ai.baseURL);
  • set a verify command that runs after the agent's next edit (levelcode.ai.verify.command). That command never asks, even in Default Approvals.

Restricted Mode did not stop it. VS Code applies restrictedConfigurations only for an extension that declares untrustedWorkspaces.supported: 'limited', and levelcode-ai declares true. The browser edition closed this on 2026-10-11 (WORKSPACE_SETTINGS_OK). The desktop manifest was left as it was.

This PR does on the desktop what the browser already does. Every levelcode-ai setting is application-scoped (user settings only), except the same eleven display and context preferences.

Commits

  1. test(agent): scripts/folder-settings-editor-check.js opens the dev build as a separate instance on a folder that tries all three, and checks the result.
    • Two stand-in providers run on localhost, the user's and the folder's, and script the same turn.
    • It runs twice: once trusted (Workspace Trust off), once in Restricted Mode.
    • A display preference set by the folder (chat.fontSize) is the positive control.
    • On develop it fails 10 checks, the same in both modes.
  2. fix(agent):
    • "scope": "application" on the 33 settings that had no scope. sessions.dir stays machine, and the MCP settings were already application.
    • A new suite, test/settingsScope.test.js. It is default-deny: it fails on any folder-settable setting that is not on the list. It also fails on a scope the editor does not know, because VS Code's parseScope reads a misspelt one as window, which a folder can set. And it fails if the desktop and browser lists differ.
    • web/test/unit/manifest.test.js no longer requires the desktop manifest to have no scopes.
    • Updates to docs/WEB.md and to the comment in web/ai-extension/manifest.js.
  3. docs: starts the next release's RELEASE-NOTES.md with this change. It covers what changes for someone who set one of these settings in a project on purpose, and that application-scoped settings are shared by all profiles. A comment at the top says draft-release-notes.mjs --write would replace the file, so merge instead, and that the updater and init settings (below) come first.

What a folder may still set

levelcode.ai.includeActiveFile, levelcode.ai.diagrams.enabled, and levelcode.ai.chat. autoContext, autoContextMaxFiles, scopeToActiveProject, workspaceMap, startLocation, fontSize, maxImagesPerMessage, proseWidth, groupActivity. This is the browser's WORKSPACE_SETTINGS_OK, which the owner chose on 2026-10-19.

Verified

The real editor (macOS, the dev build), before and after:

Check (each in a trusted folder and in Restricted Mode) develop this PR
The folder's chat.fontSize applies (positive control) ok ok
The approvals pill shows the user's choice Autopilot Default Approvals
Requests go to the user's provider folder's, 3 of 3 user's
The user's key never reaches the folder's server sent, 3 of 3 never
The agent's command asks first ran, no card card, skipped, not run
The folder's verify command does not run ran did not run
  • settingsScope: 4 cases. I put back seven defects one at a time (autopilot unscoped, a misspelt scope, window scope, sessions.dir unscoped, a new setting with no scope, a listed setting locked on the desktop only, autopilot added to the browser list). The suite caught all seven.
  • ./scripts/test-extensions.sh: 128 test files pass on macOS. I did not run them on Linux (Docker was not answering on this Mac), so this PR's test.yml run is the first Linux run. The new suite reads JSON and uses no timers.

Not verified

  • Where the session token goes (levelcode.cloud.endpoint, apiUrl) works the same way, but I only read it. The editor check has no signed-in session to send.
  • Profiles: application-scoped settings are shared by every profile. A non-default profile's own value is ignored, and an edit made from that profile goes to the Default profile. I read this in VS Code's source (PROFILE_SCOPES, isSettingAppliedForAllProfiles) and did not run it. The same goes for the faded hint shown on an ignored value in a project's settings.json (preferencesRenderers.ts).

Not in this PR (owner's decision)

These can still be set by a folder:

  • levelcode-updater's levelcode.update.url, enabled and intervalHours. With url, the editor itself can offer an update whose Download opens the folder's link.
  • levelcode.init.enabled.

They are left for a separate change, and the release notes' TODO says to fix them before tagging. The npp-pack's six editing preferences stay folder-settable on purpose.

Conflicts with #112

#112 (Ask When Risky, draft) rewrites levelcode.ai.agent.autopilot's description, on the line after the one where this PR adds its scope. To resolve, keep #112's text and add "scope": "application". I resolved it that way in a scratch copy, where settingsScope, the web manifest suite, agentReview and agentReviewGate all pass. With both merged, the get('agent.autopilot') fallback in #112's readApprovalMode() reads only the user's value, with no change needed in #112.

…e/settings.json can change, in the real editor

Opens the dev build as a separate instance on a folder whose settings turn Autopilot on, point
levelcode.ai.baseURL at the folder's own server and set a verify command, with two stand-in
providers (the user's and the folder's) that script the same turn. Runs twice: Workspace Trust off
(a trusted folder) and Restricted Mode. A display preference set by the folder (chat.fontSize) is
the positive control.

On develop (08a2cc8) it fails 10 checks, the same in both modes: the pill reads Autopilot, every
request goes to the folder's server with the user's key, the agent's command runs with no card,
and the folder's verify command runs after the edit. Restricted Mode does not help because the
extension declares untrustedWorkspaces.supported: true, and VS Code applies
restrictedConfigurations only for 'limited'.
…preferences

Every levelcode-ai setting is now application-scoped (user settings only) except the eleven the
browser edition already opens to a folder (WORKSPACE_SETTINGS_OK): includeActiveFile,
diagrams.enabled and nine chat.* display and context settings. Autopilot, the provider, its base
URL, the cloud endpoint and API host, verify.*, the budgets, completions, skills, sessions and
ollama.* are read from the user's settings only. sessions.dir stays machine-scoped and the MCP
settings stay application-scoped, as before.

test/settingsScope.test.js is default-deny: a setting a folder can set fails the suite unless it
is on the list; a scope the editor does not know fails it too (VS Code reads a misspelt scope as
'window', which a folder can set); and the desktop and browser lists must be equal. Seven
mutations put back one at a time were all caught. The web manifest test no longer pins the
desktop manifest as unscoped. The real-editor check from the previous commit passes all 16
checks, trusted and in Restricted Mode, with the folder's display preference still applied.

Application scope is shared by all profiles: a value in a non-default profile's settings.json is
ignored, and an edit from another profile is written to the Default profile. Matches the browser
edition, the MCP settings and VS Code's own chat.tools.global.autoApprove.

Left out on purpose (owner's decision): levelcode-updater's levelcode.update.* and
levelcode.init.enabled are still folder-settable, for a separate change.
v1.4.0's notes are its GitHub release body. The new file says what changes for someone who set
one of these settings in a project on purpose, and that the settings are now shared by all
profiles. A comment at the top says draft-release-notes.mjs --write replaces the file (merge,
do not overwrite), and that the updater and init settings must be fixed or addressed before
tagging.
@ndemianc
ndemianc marked this pull request as ready for review October 19, 2026 22:40
Copilot AI balanced review requested due to automatic review settings October 19, 2026 22:40

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.

🟢 Approval recommended

The scope restrictions consistently enforce the intended boundary, supported by targeted regression checks and no identified blocking issues.

No open issues 🎉

What changed in this PR

Prevents desktop workspace settings from overriding sensitive LevelCode AI configuration, aligning desktop behavior with the browser edition.

Changes:

  • Makes 33 settings application-scoped while preserving eleven folder-settable display and context preferences.
  • Adds scope regression tests and a real-editor check covering trusted and restricted workspaces.
  • Documents migration to user settings and cross-profile behavior.
File Description
web/​test/​unit/​manifest.test.js Checks desktop scope exceptions against the browser allowlist.
web/​ai-extension/​manifest.js Documents the shared desktop/browser boundary.
RELEASE-NOTES.md Explains the security change and migration implications.
extensions/​levelcode-ai/​test/​settingsScope.test.js Guards allowed scopes and desktop/browser parity.
extensions/​levelcode-ai/​scripts/​folder-settings-editor-check.js Checks workspace isolation in the real editor.
extensions/​levelcode-ai/​package.json Restricts sensitive settings to application scope.
docs/​WEB.md Documents desktop alignment and verification.

Review effort: Balanced

@ndemianc
ndemianc merged commit e528846 into develop Oct 19, 2026
3 checks passed
@ndemianc
ndemianc deleted the fix/desktop-settings-scope branch October 19, 2026 23:28
ndemianc added a commit that referenced this pull request Oct 20, 2026
agent.js: ask_user's answers now feed both the reviewer (ctx.userAnswers, #112) and what remember
may quote (ctx.heardThisRun); both kept.

package.json: the new levelcode.ai.sessions.memory.notes is application-scoped, like every other
levelcode-ai setting since #113 (test/settingsScope.test.js failed on the merge without it: a
folder's .vscode/settings.json could have switched remembering on or off).
ndemianc added a commit that referenced this pull request Oct 20, 2026
Conflicts, each keeping both sides:
- agent.js, the end of a run: Ask When Risky's reviewer cost and summary row (#112) and this branch's
  plan completion and returned "Send now" messages; one agent.done log line with both sets of fields.
- extension.js, resumeSession and New Chat: this branch's plan/steer reset and the reset of the person's
  recorded words (#112/#114), each in both places.
- media/chat.html, the message switch: develop's `approvals` message (#112 replaced the two-option
  `autopilot` one, whose handler no longer exists) and this branch's agentStart/agentStatus, which keep
  the task list and drop the old plan bar.
- test/sessionExpiredHost.test.js, the harness declarations: both sets (agentMode from env, the
  approvals mode and recorded words; this branch's agentPlan and steerInbox).

Checked on the merged tree (macOS, Node 24): scripts/test-extensions.sh 136 files; tsc --checkJs on
extension.js and agent.js, no new undefined names; in the dev build plan-progress-editor-check 17/17,
approvals-editor-check 12/12, memory-editor-check 11/11 on one of four runs — its "History lists the
session after New Session" step is flaky on develop too (1 of 2 there).
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.

2 participants