* policy: add managed-settings freshness contract
Groundwork for making `forceRemoteSettingsRefresh` a real fail-closed
startup gate (microsoft/vscode-internalbacklog#8825). Contract only — no
behavior change, and nothing gates on freshness yet.
Adds `managedSettingsFreshness.ts`, declaring the state machine shared by
the fetch path, the policy gate and Policy Diagnostics so those consumers
cannot drift: `NotRequired` / `Pending` / `Satisfied` / `Blocked`, the
failure categories every inability-to-refresh maps to, and scoping by
account + provider + endpoint so satisfaction is never transferable
across accounts or GHE hosts.
Replaces `shouldForceRemoteSettingsRefresh` with
`resolveForceRemoteSettingsRefresh`, which resolves through
`pickManagedSettings` instead of re-implementing precedence. Two fixes
fall out: the file channel now participates (the old helper read only
native MDM and server, silently ignoring managed-file delivery), and an
explicit managed `false` is now distinguishable from an absent value,
which a later change needs in order to know when the requirement may be
cleared.
The old helper had no production caller — it was left orphaned when
661f18fdeb reworked the managed-settings fetch — so this is inert.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* policy: enforce freshness invariants in the type
Address PR feedback: `IManagedSettingsFreshness` was a bag of optional
fields, so a consumer could construct `Blocked` with no failure,
`Satisfied` with no scope, or attach `httpStatus`/`retryAfter` to states
where they mean nothing — leaving the fetch, gate and diagnostics
consumers free to drift despite the type.
Models it as a discriminated union instead, so each active state requires
the fields its contract defines. `Blocked` is itself a union keyed on the
failure category, so a status code is required for an HTTP error, a
backoff deadline for rate limiting, and neither is accepted elsewhere.
`source` is now the shared `ManagedSettingsChannel` rather than `string`,
and is required on the effective states, which also encodes that it is
never `'none'` once a channel has supplied the control.
Adds `@ts-expect-error` coverage for the three rejected shapes: the
directives fail the build if any shape becomes constructible again.
`isSameManagedSettingsFreshnessScope` is now a private helper with
required arguments — the union guarantees a scope is present, so its
undefined-tolerance was unreachable, and nothing outside this module
used it.
Also trims two over-long comments flagged in review.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* chat: fail closed on forced managed settings refresh
Require a fresh managed-settings response before enabling AI features when forceRemoteSettingsRefresh is effective. Preserve recovery through sign-in and retry, expose diagnostics, and cover native, server, file, failure, scope, and sign-out behavior.
Related to microsoft/vscode-internalbacklog#8825.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* chat: tighten managed settings recovery UX
Re-render the Agents window when freshness failure details change and preserve startup notification deferral.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* chat: address managed settings review feedback
Scope cached server controls before precedence, avoid expired rate-limit poll loops, and align update-required recovery guidance across workbench and Agents window UI.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* test: await explicit managed settings recovery refresh
Classic web initialization intentionally skips the default-account fetch. Exercise the explicit refresh path before asserting the no-token fail-closed state so the browser suite observes the same lifecycle it is validating.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* test: provide product name in policy overlay fixture
Ensure managed-settings messages render Code - OSS instead of an undefined product label in component screenshots.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* test: add managed settings failure modes
Let the mock policy server return HTTP errors, malformed JSON, immediate disconnects, or no response until client timeout through presets, the GUI, and the control API.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* chat: tighten forced managed settings recovery
Improve forced-refresh progress and blocked-state UX, bound automatic retries after failures, preserve the ungoverned cache path, and simplify mock policy failure controls.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* chat: close managed settings dialog on retry
Start the explicit managed-settings refresh without making the dialog wait for the network request to complete.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* chat: separate managed settings requirement copy
Place the organization requirement and fetch failure remediation in separate paragraphs.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* chat: address managed settings review feedback
Preserve cached and blocked freshness state, report failed manual syncs, retain pending mock-server edits, and include attempted scope in diagnostics.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* chat: reduce managed settings freshness implementation
Trim redundant contract commentary and tests, simplify refresh resolution, deduplicate failure transitions, and keep no-flag tests independent from retry bypass behavior.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Mock Copilot policy server
Local Node server and web GUI for the four Copilot policy endpoints used by
DefaultAccountService. Mock selected endpoints while forwarding the rest to
the real API. It has no runtime dependencies and is not shipped with VS Code.
Start
npm run mock-policy-server
Open http://127.0.0.1:3000. Managed settings is mocked by default. Use the
switch beside each endpoint tab to choose mock or passthrough. Presets apply
immediately; response behavior, status, and JSON edits auto-save.
The GUI opens on the Policies workspace. Select Setup in the header to open a modal that guides you through either connection method:
- System proxy (recommended): works with Code OSS, Stable, Insiders, Copilot
CLI, and SDK/runtime clients. The page recommends Proxyman on macOS and
provides a Map Remote rule. VS Code normally uses the system proxy; the
http.proxysetting is available as an optional fallback when explicit client configuration is needed. - Code OSS overrides: the quicker option for Code OSS from this checkout. Select Apply Overrides, reload, and sign in. This option does not redirect SDK/runtime requests.
After connecting, open the VS Code Command Palette and run > Developer: Sync Account Policy. To refresh the policy used by Local Agent Host, also run > Developer: Restart Local Agent Host.
The Setup dialog checks Code OSS overrides directly. It tests the system proxy by sending a request without credentials to the managed settings URL and confirming that the response came from this local server. It does not inspect Proxyman or macOS proxy configuration. The test runs automatically, and the global header always shows a green or red connection indicator.
If no real request appears in Live Requests, use Clear Policy Cache. A fresh managed-settings cache entry can prevent the client from making a request for up to one hour. Then run the commands above again.
Other Copilot clients share that cache. For an isolated run, start both the server and Code OSS with the same temporary cache home:
COPILOT_CACHE_HOME="$PWD/.build/mock-policy-cache" npm run mock-policy-server
COPILOT_CACHE_HOME="$PWD/.build/mock-policy-cache" ./scripts/code.sh
HTTP API
The control API is JSON-only and supports complete configuration without the GUI. Start with its machine-readable index and current state:
BASE=http://127.0.0.1:3000
curl "$BASE/api"
curl "$BASE/api/state"
GET /api/state returns endpoint IDs, presets, current bodies, statuses, and
mock/passthrough state.
Apply a known preset:
curl -X POST "$BASE/api/state" \
-H 'Content-Type: application/json' \
-d '{"endpoint":"managedSettings","preset":"not-configured"}'
Set a custom response:
curl -X POST "$BASE/api/state" \
-H 'Content-Type: application/json' \
-d '{"endpoint":"managedSettings","active":true,"status":200,"body":{}}'
Configure multiple endpoints atomically:
curl -X POST "$BASE/api/state" \
-H 'Content-Type: application/json' \
-d '{"endpoints":[
{"endpoint":"managedSettings","preset":"empty"},
{"endpoint":"entitlements","active":false},
{"endpoint":"token","active":false},
{"endpoint":"mcpRegistry","active":false}
]}'
A preset sets the status and body and enables mocking. Response behavior is
configured independently with mode, including when a preset and mode are sent
in the same update. Explicit status, body, or active values override the
preset. Invalid requests are rejected before any endpoint changes. Supported
response modes are json, malformed-json, disconnect, and timeout.
Test fail-closed managed-settings refresh
First serve a successful policy that enables the forced-refresh requirement and sync it into VS Code. Then configure an HTTP error preset or a failing response behavior and sync again. Seeding the requirement first mirrors a real deployment where the cached control self-perpetuates through an outage.
curl -X POST "$BASE/api/state" \
-H 'Content-Type: application/json' \
-d '{"endpoint":"managedSettings","preset":"customization-lockdown"}'
# Run "Developer: Sync Account Policy" in VS Code, then choose one:
curl -X POST "$BASE/api/state" -H 'Content-Type: application/json' \
-d '{"endpoint":"managedSettings","preset":"server-error"}'
curl -X POST "$BASE/api/state" -H 'Content-Type: application/json' \
-d '{"endpoint":"managedSettings","mode":"malformed-json","status":200}'
curl -X POST "$BASE/api/state" -H 'Content-Type: application/json' \
-d '{"endpoint":"managedSettings","mode":"disconnect"}'
curl -X POST "$BASE/api/state" -H 'Content-Type: application/json' \
-d '{"endpoint":"managedSettings","mode":"timeout"}'
These configurations exercise HTTP error, malformed response, immediate network failure, and client-timeout paths respectively. Clear the policy cache if the request does not appear in Live Requests.
| Method | Route | Purpose |
|---|---|---|
GET |
/api |
Discover request shapes and routes |
GET |
/api/state |
Read definitions, presets, and current state |
POST |
/api/state |
Apply one update or an atomic endpoint array |
POST |
/api/reset |
Restore startup endpoint state |
GET |
/api/schema |
Read the managed-settings schema |
GET, DELETE |
/api/log |
Read or clear the request log |
DELETE |
/api/cache |
Clear the managed-settings disk cache |
POST |
/api/wire |
Apply product.overrides.json |
POST |
/api/unwire |
Restore product.overrides.json |
Schema and options
The server auto-detects
copilot-agent-runtime/schema/managed-settings-schema.json beside the primary
VS Code checkout, including from a Git worktree. Override it at startup with
--schema or MANAGED_SETTINGS_SCHEMA.
npm run mock-policy-server -- --upstream https://api.ghe.example.com
npm run mock-policy-server -- --schema /path/to/managed-settings-schema.json
npm run mock-policy-server -- --help
| Flag | Environment variable | Default |
|---|---|---|
--host |
— | 127.0.0.1 |
--upstream |
MOCK_POLICY_UPSTREAM |
https://api.github.com |
--schema |
MANAGED_SETTINGS_SCHEMA |
Auto-detected sibling checkout |