src/vs/workbench/services/agentHost/common/agentHostResourceService.ts

519 LOC · 354 covered · 165 uncovered · 94 ranges · 58 concepts · 39 introducers · 33 tests

File neighbourhood

The centred file is linked to every concept that introduces one of its ranges, every test that runs code from the file, and the gray connector concepts standing between those tests and the file's own introducer concepts. Undirected links join concepts to every file where they introduce source and concepts to the tests they introduce; arrows show specialization between the displayed concepts and bridge only concepts omitted from this view. Concept colors match the source ranges below; connector concepts have no source color and are shown in gray.

Focused file, its introducer and connector concepts, their introduced files, and tests that run code from the file

In the embedded map, ordinary wheel input scrolls the page; use the visible controls to zoom and drag to pan. Open the full-screen map for canvas navigation: wheel pans, Ctrl/Command plus wheel zooms, and arrow keys pan when this region is focused. On touch screens, open the full-screen map to pan or pinch. If JavaScript or WebGL is unavailable, use the related-file, concept, and source links on this page.

Focused file, its introducer and connector concepts, their introduced files, and tests that run code from the filesrc/vs/editor/common/services/resolverService.ts · 90 LOCservices/resolverService…src/vs/platform/agentHost/common/agentHostResourceService.ts · 168 LOCcommon/agentHostResource…agentHostResourceService.ts ×1 · 2 introduced LOCagentHostResourceService…agentHostResourceService.test|title=AgentHostResourceService allowAlways covers immediate retry without waiting for the settings write|occurrence=1 · 0 introduced LOCagentHostResourceService…agentHostResourceService.ts ×2 · 5 introduced LOCagentHostResourceService…agentHostResourceService.ts ×1 · 3 introduced LOCagentHostResourceService…agentHostResourceService.test|title=AgentHostResourceService allow for write also covers read on the same URI|occurrence=1 · 0 introduced LOCagentHostResourceService…agentHostResourceService.test|title=AgentHostResourceService allow grants in-memory until connection closes|occurrence=1 · 0 introduced LOCagentHostResourceService…agentHostResourceService.test|title=AgentHostResourceService allowAlways persists the grant|occurrence=1, agentHostResourceService.test|title=AgentHostResourceService allowAlways defaults to APPLICATION scope when no value is configured anywhere|occurrence=1 · 0 introduced LOCagentHostResourceService…agentHostResourceService.ts ×1 · 1 introduced LOCagentHostResourceService…agentHostResourceService.test|title=AgentHostResourceService allow for read does not grant write|occurrence=1 · 0 introduced LOCagentHostResourceService…agentHostResourceService.ts ×1 · 2 introduced LOCagentHostResourceService…agentHostResourceService.ts ×1 · 2 introduced LOCagentHostResourceService…agentHostResourceService.ts ×3 · 3 introduced LOCagentHostResourceService…agentHostResourceService.ts ×1 · 1 introduced LOCagentHostResourceService…agentHostResourceService.ts ×1 · 2 introduced LOCagentHostResourceService…agentHostResourceService.ts ×1 · 1 introduced LOCagentHostResourceService…agentHostResourceService.test|title=AgentHostResourceService request with both read and write prompts sequentially|occurrence=1 · 0 introduced LOCagentHostResourceService…agentHostResourceService.ts ×8 · 22 introduced LOCagentHostResourceService…agentHostResourceService.test|title=AgentHostResourceService write request that already has read grant still prompts for write|occurrence=1 · 0 introduced LOCagentHostResourceService…agentHostResourceService.ts ×1 · 2 introduced LOCagentHostResourceService…agentHostResourceService.ts ×3 · 6 introduced LOCagentHostResourceService…agentHostResourceService.ts ×2 · 2 introduced LOCagentHostResourceService…agentHostResourceService.test|title=AgentHostResourceService request resolves immediately when already granted|occurrence=1 · 0 introduced LOCagentHostResourceService…agentHostResourceService.ts ×1 · 3 introduced LOCagentHostResourceService…agentHostResourceService.ts ×2 · 3 introduced LOCagentHostResourceService…agentHostResourceService.ts ×1 · 2 introduced LOCagentHostResourceService…agentHostResourceService.ts ×3 · 13 introduced LOCagentHostResourceService…agentHostResourceService.ts ×1 · 1 introduced LOCagentHostResourceService…agentHostResourceService.test|title=AgentHostResourceService grants for one host do not leak into another host|occurrence=1 · 0 introduced LOCagentHostResourceService…agentHostResourceService.test|title=AgentHostResourceService persisted "r" allows read, denies write|occurrence=1 · 0 introduced LOCagentHostResourceService…agentHostResourceService.test|title=AgentHostResourceService allowAlways skips persistence when covered by parent grant|occurrence=1 · 0 introduced LOCagentHostResourceService…agentHostResourceService.test|title=AgentHostResourceService persisted "rw" allows read and write|occurrence=1 · 0 introduced LOCagentHostResourceService…agentHostResourceService.ts ×1 · 5 introduced LOCagentHostResourceService…agentHostResourceService.ts ×3 · 18 introduced LOCagentHostResourceService…agentHostResourceService.ts ×1 · 2 introduced LOCagentHostResourceService… prefix|occurrence=1 · 0 introduced LOC prefix|occurrence=1agentHostResourceService.ts ×1 · 3 introduced LOCagentHostResourceService…agentHostResourceService.test|title=AgentHostResourceService check rejects path traversal via .. segments|occurrence=1 · 0 introduced LOCagentHostResourceService…agentHostResourceService.test|title=AgentHostResourceService connectionClosed drops implicit grants|occurrence=1 · 0 introduced LOCagentHostResourceService…agentHostResourceService.ts ×1 · 2 introduced LOCagentHostResourceService…agentHostResourceService.ts ×1 · 1 introduced LOCagentHostResourceService…agentHostResourceService.test|title=AgentHostResourceService implicit read grant covers descendants but not parent or sibling|occurrence=1 · 0 introduced LOCagentHostResourceService…agentHostResourceService.ts ×5 · 7 introduced LOCagentHostResourceService…agentHostResourceService.test|title=AgentHostResourceService grantImplicitRead asynchronously upgrades to realpath|occurrence=1, agentHostResourceService.test|title=AgentHostResourceService check canonicalizes via realpath so symlink to outside the grant is denied|occurrence=1, +1 · 0 introduced LOCagentHostResourceService…agentHostResourceService.ts ×1 · 1 introduced LOCagentHostResourceService…agentHostResourceService.ts ×2 · 7 introduced LOCagentHostResourceService…agentHostResourceService.ts ×1 · 4 introduced LOCagentHostResourceService…agentHostResourceService.test|title=AgentHostResourceService implicit grant does not allow write|occurrence=1 · 0 introduced LOCagentHostResourceService…agentHostResourceService.ts ×1 · 2 introduced LOCagentHostResourceService…agentHostResourceService.ts ×4 · 9 introduced LOCagentHostResourceService…agentHostResourceService.ts ×1 · 13 introduced LOCagentHostResourceService…agentHostResourceService.ts ×1 · 2 introduced LOCagentHostResourceService…agentHostResourceService.test|title=AgentHostResourceService check denies when no grant exists|occurrence=1 · 0 introduced LOCagentHostResourceService…agentHostResourceService.ts ×2 · 2 introduced LOCagentHostResourceService…agentHostResourceService.ts ×1 · 2 introduced LOCagentHostResourceService…agentHostResourceService.ts ×2 · 5 introduced LOCagentHostResourceService…agentHostResourceService.ts ×1 · 2 introduced LOCagentHostResourceService…agentHostResourceService.ts ×1 · 4 introduced LOCagentHostResourceService…agentHostResourceService.ts ×28 · 436 introduced LOCagentHostResourceService… prefix|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/services/agentHost/test/common/agentHostResourceService.test|title=AgentHostResourceService address normalization strips ws:// prefix|occurrence=1 prefix|occurrence=1agentHostResourceService.test|title=AgentHostResourceService allow for read does not grant write|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/services/agentHost/test/common/agentHostResourceService.test|title=AgentHostResourceService allow for read does not grant write|occurrence=1agentHostResourceService…agentHostResourceService.test|title=AgentHostResourceService allow for write also covers read on the same URI|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/services/agentHost/test/common/agentHostResourceService.test|title=AgentHostResourceService allow for write also covers read on the same URI|occurrence=1agentHostResourceService…agentHostResourceService.test|title=AgentHostResourceService allow grants in-memory until connection closes|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/services/agentHost/test/common/agentHostResourceService.test|title=AgentHostResourceService allow grants in-memory until connection closes|occurrence=1agentHostResourceService…agentHostResourceService.test|title=AgentHostResourceService allowAlways covers immediate retry without waiting for the settings write|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/services/agentHost/test/common/agentHostResourceService.test|title=AgentHostResourceService allowAlways covers immediate retry without waiting for the settings write|occurrence=1agentHostResourceService…agentHostResourceService.test|title=AgentHostResourceService allowAlways defaults to APPLICATION scope when no value is configured anywhere|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/services/agentHost/test/common/agentHostResourceService.test|title=AgentHostResourceService allowAlways defaults to APPLICATION scope when no value is configured anywhere|occurrence=1agentHostResourceService…agentHostResourceService.test|title=AgentHostResourceService allowAlways for write persists rw|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/services/agentHost/test/common/agentHostResourceService.test|title=AgentHostResourceService allowAlways for write persists rw|occurrence=1agentHostResourceService…agentHostResourceService.test|title=AgentHostResourceService allowAlways merges with existing APPLICATION-scoped grants instead of overwriting them|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/services/agentHost/test/common/agentHostResourceService.test|title=AgentHostResourceService allowAlways merges with existing APPLICATION-scoped grants instead of overwriting them|occurrence=1agentHostResourceService…agentHostResourceService.test|title=AgentHostResourceService allowAlways persists into USER_LOCAL when a pre-existing value is in USER_LOCAL|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/services/agentHost/test/common/agentHostResourceService.test|title=AgentHostResourceService allowAlways persists into USER_LOCAL when a pre-existing value is in USER_LOCAL|occurrence=1agentHostResourceService…agentHostResourceService.test|title=AgentHostResourceService allowAlways persists the grant|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/services/agentHost/test/common/agentHostResourceService.test|title=AgentHostResourceService allowAlways persists the grant|occurrence=1agentHostResourceService…agentHostResourceService.test|title=AgentHostResourceService allowAlways skips persistence when covered by parent grant|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/services/agentHost/test/common/agentHostResourceService.test|title=AgentHostResourceService allowAlways skips persistence when covered by parent grant|occurrence=1agentHostResourceService…agentHostResourceService.test|title=AgentHostResourceService check canonicalizes nonexistent paths via the parent realpath|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/services/agentHost/test/common/agentHostResourceService.test|title=AgentHostResourceService check canonicalizes nonexistent paths via the parent realpath|occurrence=1agentHostResourceService…agentHostResourceService.test|title=AgentHostResourceService check canonicalizes via realpath so symlink to outside the grant is denied|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/services/agentHost/test/common/agentHostResourceService.test|title=AgentHostResourceService check canonicalizes via realpath so symlink to outside the grant is denied|occurrence=1agentHostResourceService…agentHostResourceService.test|title=AgentHostResourceService check denies when no grant exists|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/services/agentHost/test/common/agentHostResourceService.test|title=AgentHostResourceService check denies when no grant exists|occurrence=1agentHostResourceService…agentHostResourceService.test|title=AgentHostResourceService check rejects path traversal via .. segments|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/services/agentHost/test/common/agentHostResourceService.test|title=AgentHostResourceService check rejects path traversal via .. segments|occurrence=1agentHostResourceService…agentHostResourceService.test|title=AgentHostResourceService concurrent identical requests share one pending entry|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/services/agentHost/test/common/agentHostResourceService.test|title=AgentHostResourceService concurrent identical requests share one pending entry|occurrence=1agentHostResourceService…agentHostResourceService.test|title=AgentHostResourceService connectionClosed drops implicit grants|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/services/agentHost/test/common/agentHostResourceService.test|title=AgentHostResourceService connectionClosed drops implicit grants|occurrence=1agentHostResourceService…agentHostResourceService.test|title=AgentHostResourceService connectionClosed only affects the named address|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/services/agentHost/test/common/agentHostResourceService.test|title=AgentHostResourceService connectionClosed only affects the named address|occurrence=1agentHostResourceService…agentHostResourceService.test|title=AgentHostResourceService connectionClosed rejects pending and clears the queue|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/services/agentHost/test/common/agentHostResourceService.test|title=AgentHostResourceService connectionClosed rejects pending and clears the queue|occurrence=1agentHostResourceService…agentHostResourceService.test|title=AgentHostResourceService findPending returns the pending request by id|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/services/agentHost/test/common/agentHostResourceService.test|title=AgentHostResourceService findPending returns the pending request by id|occurrence=1agentHostResourceService…agentHostResourceService.test|title=AgentHostResourceService grantImplicitRead asynchronously upgrades to realpath|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/services/agentHost/test/common/agentHostResourceService.test|title=AgentHostResourceService grantImplicitRead asynchronously upgrades to realpath|occurrence=1agentHostResourceService…agentHostResourceService.test|title=AgentHostResourceService grants for one host do not leak into another host|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/services/agentHost/test/common/agentHostResourceService.test|title=AgentHostResourceService grants for one host do not leak into another host|occurrence=1agentHostResourceService…agentHostResourceService.test|title=AgentHostResourceService implicit grant does not allow write|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/services/agentHost/test/common/agentHostResourceService.test|title=AgentHostResourceService implicit grant does not allow write|occurrence=1agentHostResourceService…agentHostResourceService.test|title=AgentHostResourceService implicit grant for a symlinked directory still covers descendants resolved through the symlink|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/services/agentHost/test/common/agentHostResourceService.test|title=AgentHostResourceService implicit grant for a symlinked directory still covers descendants resolved through the symlink|occurrence=1agentHostResourceService…agentHostResourceService.test|title=AgentHostResourceService implicit read grant covers descendants but not parent or sibling|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/services/agentHost/test/common/agentHostResourceService.test|title=AgentHostResourceService implicit read grant covers descendants but not parent or sibling|occurrence=1agentHostResourceService…agentHostResourceService.test|title=AgentHostResourceService pendingFor returns only this host's requests, with normalized address|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/services/agentHost/test/common/agentHostResourceService.test|title=AgentHostResourceService pendingFor returns only this host's requests, with normalized address|occurrence=1agentHostResourceService…agentHostResourceService.test|title=AgentHostResourceService persisted "r" allows read, denies write|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/services/agentHost/test/common/agentHostResourceService.test|title=AgentHostResourceService persisted "r" allows read, denies write|occurrence=1agentHostResourceService…agentHostResourceService.test|title=AgentHostResourceService persisted "rw" allows read and write|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/services/agentHost/test/common/agentHostResourceService.test|title=AgentHostResourceService persisted "rw" allows read and write|occurrence=1agentHostResourceService…agentHostResourceService.test|title=AgentHostResourceService persisted entries with malformed URI keys or unknown modes are ignored|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/services/agentHost/test/common/agentHostResourceService.test|title=AgentHostResourceService persisted entries with malformed URI keys or unknown modes are ignored|occurrence=1agentHostResourceService…agentHostResourceService.test|title=AgentHostResourceService request rejects with CancellationError on deny|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/services/agentHost/test/common/agentHostResourceService.test|title=AgentHostResourceService request rejects with CancellationError on deny|occurrence=1agentHostResourceService…agentHostResourceService.test|title=AgentHostResourceService request resolves immediately when already granted|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/services/agentHost/test/common/agentHostResourceService.test|title=AgentHostResourceService request resolves immediately when already granted|occurrence=1agentHostResourceService…agentHostResourceService.test|title=AgentHostResourceService request with both read and write prompts sequentially|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/services/agentHost/test/common/agentHostResourceService.test|title=AgentHostResourceService request with both read and write prompts sequentially|occurrence=1agentHostResourceService…agentHostResourceService.test|title=AgentHostResourceService write request that already has read grant still prompts for write|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/workbench/services/agentHost/test/common/agentHostResourceService.test|title=AgentHostResourceService write request that already has read grant still prompts for write|occurrence=1agentHostResourceService…Focused file · src/vs/workbench/services/agentHost/common/agentHostResourceService.ts · 519 LOCcommon/agentHostResource…

Graph controls are ready.

Interactive rendering requires JavaScript and WebGL. Use the related-file, concept, and source links on this page while the interactive map is unavailable.

1 > /*--------------------------------------------------------------------------------------------- agentHostResourceService.ts ×28
2 > * Copyright (c) Microsoft Corporation. All rights reserved.
3 > * Licensed under the MIT License. See License.txt in the project root for license information.
4 > *--------------------------------------------------------------------------------------------*/
5 >
6 > import { DeferredPromise } from '../../../../base/common/async.js';
7 > import { VSBuffer, decodeBase64 } from '../../../../base/common/buffer.js';
8 > import { CancellationError } from '../../../../base/common/errors.js';
9 > import { Disposable, IDisposable, toDisposable } from '../../../../base/common/lifecycle.js';
10 > import { IObservable, derived, observableValue } from '../../../../base/common/observable.js';
11 > import { extUri } from '../../../../base/common/resources.js';
12 > import { URI } from '../../../../base/common/uri.js';
13 > import { generateUuid } from '../../../../base/common/uuid.js';
14 > import { ITextModelService } from '../../../../editor/common/services/resolverService.js';
15 > import {
16 > AgentHostAccessMode,
17 > AgentHostLocalFilePermissionsSettingId,
18 > AgentHostPermissionMode,
19 > AgentHostPermissionsSetting,
20 > AgentHostResourcePermissionError,
21 > IAgentHostResourceService,
22 > IPendingResourceRequest,
23 > IResourceListResult,
24 > IResourceReadResult,
25 > LOCAL_AGENT_HOST_ADDRESS,
26 > } from '../../../../platform/agentHost/common/agentHostResourceService.js';
27 > import { normalizeRemoteAgentHostAddress } from '../../../../platform/agentHost/common/agentHostUri.js';
28 > import {
29 > ContentEncoding,
30 > ResourceCopyParams, ResourceDeleteParams, ResourceMkdirParams, ResourceMoveParams,
31 > ResourceRequestParams, ResourceResolveParams, ResourceResolveResult, ResourceType, ResourceWriteParams,
32 > } from '../../../../platform/agentHost/common/state/protocol/commands.js';
33 > import { ROOT_STATE_URI } from '../../../../platform/agentHost/common/state/sessionState.js';
34 > import { ConfigurationTarget, IConfigurationService } from '../../../../platform/configuration/common/configuration.js';
35 > import { IFileService } from '../../../../platform/files/common/files.js';
36 > import { InstantiationType, registerSingleton } from '../../../../platform/instantiation/common/extensions.js';
37 > import { ILogService } from '../../../../platform/log/common/log.js';
38 >
39 > interface IInternalPendingRequest extends IPendingResourceRequest {
40 > readonly deferred: DeferredPromise<void>;
41 > }
42 >
43 > interface IInMemoryGrant {
44 > readonly address: string;
45 > /**
46 > * Resolves to the realpath'd URI for the grant. Stored as a promise so
47 > * `grantImplicitRead` can return synchronously while the realpath lookup
48 > * is in flight; consumers in `_isCovered` await the resolved URI before
49 > * comparing, so a check that happens before the lookup completes still
50 > * compares against the canonical path. Always resolves (never rejects).
51 > */
52 > readonly realpath: Promise<URI>;
53 > readonly mode: AgentHostAccessMode;
54 > }
55 >
56 > /**
57 > * Default implementation of {@link IAgentHostResourceService} — the unified
58 > * owner of agent-host-facing filesystem operations and the permission
59 > * policy that gates them. Reads transparently fall back to
60 > * {@link ITextModelService} so virtual resources (untitled documents,
61 > * notebook cells, ...) work without the host having to know about them.
62 > *
63 > * Permission storage shape (in user settings):
64 > *
65 > * ```jsonc
66 > * "chat.agentHost.localFilePermissions": {
67 > * "localhost:3000": {
68 > * "file:///Users/me/.gitconfig": "r",
69 > * "file:///Users/me/.agentConfig": "rw"
70 > * },
71 > * "local": { ... }
72 > * }
73 > * ```
74 > *
75 > * - Keys are addresses normalized via {@link normalizeRemoteAgentHostAddress},
76 > * with the in-process local agent host keyed under `'local'`.
77 > * - Values are URI strings → `r` | `rw`. Descendant URIs are covered by a
78 > * parent grant.
79 > */
80 > export class AgentHostResourceService extends Disposable implements IAgentHostResourceService {
81 > declare readonly _serviceBrand: undefined;
82 >
83 > private readonly _inMemoryGrants = new Map<string, IInMemoryGrant>();
84 > private readonly _pending = observableValue<readonly IInternalPendingRequest[]>('agentHostResources.pending', []);
85 >
86 > readonly allPending: IObservable<readonly IPendingResourceRequest[]> = this._pending;
87 >
88 > constructor(
89 > @IConfigurationService private readonly _configurationService: IConfigurationService,
90 > @IFileService private readonly _fileService: IFileService,
91 > @ITextModelService private readonly _textModelService: ITextModelService,
92 > @ILogService private readonly _logService: ILogService,
93 > ) {
94 > super();
95 > }
96 >
97 > // ---- Gated FS operations ------------------------------------------------
98 >
99 > async list(address: string, uri: URI): Promise<IResourceListResult> {
100 await this._gate(address, uri, AgentHostPermissionMode.Read, { channel: ROOT_STATE_URI, uri: uri.toString(), read: true });
101 const stat = await this._fileService.resolve(uri);
102 if (!stat.isDirectory) {
103 throw new Error(`Resource is not a directory: ${uri.toString()}`);
104 }
105 return {
106 entries: (stat.children ?? []).map(c => ({
107 name: c.name,
108 type: c.isDirectory ? 'directory' : 'file',
109 })),
110 };
111 }
113 > async read(address: string, uri: URI): Promise<IResourceReadResult> {
114 await this._gate(address, uri, AgentHostPermissionMode.Read, { channel: ROOT_STATE_URI, uri: uri.toString(), read: true });
115 try {
116 const content = await this._fileService.readFile(uri);
117 return { bytes: content.value };
118 } catch (err) {
119 const virtual = await this._readVirtual(uri);
120 if (virtual) {
121 return { bytes: virtual };
122 }
123 throw err;
124 }
125 }
127 > async write(address: string, params: ResourceWriteParams): Promise<void> {
128 const uri = URI.parse(params.uri);
129 await this._gate(address, uri, AgentHostPermissionMode.Write, { channel: ROOT_STATE_URI, uri: uri.toString(), write: true });
130 const buf = params.encoding === ContentEncoding.Base64
131 ? decodeBase64(params.data)
132 : VSBuffer.fromString(params.data);
133 try {
134 if (params.createOnly) {
135 await this._fileService.createFile(uri, buf, { overwrite: false });
136 } else {
137 await this._fileService.writeFile(uri, buf);
138 }
139 } catch (err) {
140 if (await this._writeVirtual(uri, buf)) {
141 return;
142 }
143 throw err;
144 }
145 }
147 > async del(address: string, params: ResourceDeleteParams): Promise<void> {
148 const uri = URI.parse(params.uri);
149 await this._gate(address, uri, AgentHostPermissionMode.Write, { channel: ROOT_STATE_URI, uri: uri.toString(), write: true });
150 await this._fileService.del(uri, { recursive: !!params.recursive });
151 }
153 > async move(address: string, params: ResourceMoveParams): Promise<void> {
154 const source = URI.parse(params.source);
155 const destination = URI.parse(params.destination);
156 await this._gate(address, source, AgentHostPermissionMode.Write, { channel: ROOT_STATE_URI, uri: source.toString(), write: true });
157 await this._gate(address, destination, AgentHostPermissionMode.Write, { channel: ROOT_STATE_URI, uri: destination.toString(), write: true });
158 await this._fileService.move(source, destination, !params.failIfExists);
159 }
161 > async copy(address: string, params: ResourceCopyParams): Promise<void> {
162 const source = URI.parse(params.source);
163 const destination = URI.parse(params.destination);
164 await this._gate(address, source, AgentHostPermissionMode.Read, { channel: ROOT_STATE_URI, uri: source.toString(), read: true });
165 await this._gate(address, destination, AgentHostPermissionMode.Write, { channel: ROOT_STATE_URI, uri: destination.toString(), write: true });
166 await this._fileService.copy(source, destination, !params.failIfExists);
167 }
169 > async resolve(address: string, params: ResourceResolveParams): Promise<ResourceResolveResult> {
170 const uri = URI.parse(params.uri);
171 await this._gate(address, uri, AgentHostPermissionMode.Read, { channel: ROOT_STATE_URI, uri: uri.toString(), read: true });
172 let stat;
173 try {
174 stat = await this._fileService.stat(uri);
175 } catch (err) {
176 const virtual = await this._statVirtual(uri);
177 if (virtual) {
178 return virtual;
179 }
180 throw err;
181 }
182 let type: ResourceType;
183 if (stat.isSymbolicLink && params.followSymlinks === false) {
184 type = ResourceType.Symlink;
185 } else if (stat.isDirectory) {
186 type = ResourceType.Directory;
187 } else {
188 type = ResourceType.File;
189 }
190 return {
191 uri: uri.toString(),
192 type,
193 ...(stat.size !== undefined ? { size: stat.size } : {}),
194 ...(stat.mtime !== undefined ? { mtime: new Date(stat.mtime).toISOString() } : {}),
195 ...(stat.ctime !== undefined ? { ctime: new Date(stat.ctime).toISOString() } : {}),
196 ...(stat.etag ? { etag: stat.etag } : {}),
197 };
198 }
200 > async mkdir(address: string, params: ResourceMkdirParams): Promise<void> {
201 const uri = URI.parse(params.uri);
202 await this._gate(address, uri, AgentHostPermissionMode.Write, { channel: ROOT_STATE_URI, uri: uri.toString(), write: true });
203 const existing = await this._fileService.stat(uri).catch(() => undefined);
204 if (existing && !existing.isDirectory) {
205 throw new Error(`Path exists and is not a directory: ${uri.toString()}`);
206 }
207 await this._fileService.createFolder(uri);
208 }
210 > // ---- Permission requests / observables ---------------------------------
211 >
212 > async check(address: string, uri: URI, mode: AgentHostPermissionMode): Promise<boolean> {
213 > const normalized = normalizeRemoteAgentHostAddress(address); agentHostResourceService.ts ×1
214 > const canonical = await this._canonicalize(uri);
215 > return this._isCovered(normalized, canonical, mode);
216 > }
218 > async request(address: string, params: ResourceRequestParams): Promise<void> {
219 > const normalized = normalizeRemoteAgentHostAddress(address); agentHostResourceService.ts ×2
220 > const canonical = await this._canonicalize(URI.parse(params.uri));
221 > const wantsWrite = params.write === true;
222 > const wantsRead = params.read === true || !wantsWrite;
223 >
224 > if (wantsRead && !await this._isCovered(normalized, canonical, AgentHostPermissionMode.Read)) {
225 > await this._enqueue(normalized, canonical, AgentHostPermissionMode.Read); agentHostResourceService.ts ×1
227 > if (wantsWrite && !await this._isCovered(normalized, canonical, AgentHostPermissionMode.Write)) { agentHostResourceService.ts ×1
228 > await this._enqueue(normalized, canonical, AgentHostPermissionMode.Write); agentHostResourceService.ts ×2
229 > }
232 > pendingFor(address: string): IObservable<readonly IPendingResourceRequest[]> {
233 > const normalized = normalizeRemoteAgentHostAddress(address); agentHostResourceService.ts ×1
234 > return derived(reader => this._pending.read(reader).filter(r => r.address === normalized));
235 > }
237 > findPending(id: string): IPendingResourceRequest | undefined {
238 > return this._pending.get().find(r => r.id === id); agentHostResourceService.ts ×1
239 > }
241 > grantImplicitRead(address: string, uri: URI): IDisposable {
242 > const handle = generateUuid(); agentHostResourceService.ts ×1
243 > const lexical = extUri.normalizePath(uri);
244 > const realpath = this._fileService.realpath(lexical).then(
245 > real => real ?? lexical,
246 > () => lexical,
247 > );
248 > this._inMemoryGrants.set(handle, {
249 > address: normalizeRemoteAgentHostAddress(address),
250 > realpath,
251 > mode: AgentHostAccessMode.Read,
252 > });
253 > return toDisposable(() => this._inMemoryGrants.delete(handle));
254 > }
256 > connectionClosed(address: string): void {
257 > const normalized = normalizeRemoteAgentHostAddress(address); agentHostResourceService.ts ×4
258 >
259 > for (const [handle, grant] of this._inMemoryGrants) {
260 > if (grant.address === normalized) { agentHostResourceService.ts ×1
261 > this._inMemoryGrants.delete(handle);
262 > }
263 > }
265 > const cancel = new CancellationError();
266 > const remaining: IInternalPendingRequest[] = [];
267 > for (const request of this._pending.get()) {
268 > if (request.address === normalized) { agentHostResourceService.ts ×3
269 > request.deferred.error(cancel);
270 > } else {
271 > remaining.push(request); agentHostResourceService.ts ×1
272 > }
274 > if (remaining.length !== this._pending.get().length) { agentHostResourceService.ts ×4
275 > this._pending.set(remaining, undefined); agentHostResourceService.ts ×3
276 > }
279 > // ---- internals ---------------------------------------------------------
280 >
281 > private async _gate(
282 address: string,
283 uri: URI,
284 mode: AgentHostPermissionMode,
285 deniedRequest: ResourceRequestParams,
286 ): Promise<void> {
287 if (!await this.check(address, uri, mode)) {
288 throw new AgentHostResourcePermissionError(deniedRequest);
289 }
290 }
292 > private async _readVirtual(uri: URI): Promise<VSBuffer | undefined> {
293 try {
294 const ref = await this._textModelService.createModelReference(uri);
295 try {
296 return VSBuffer.fromString(ref.object.textEditorModel.getValue());
297 } finally {
298 ref.dispose();
299 }
300 } catch {
301 return undefined;
302 }
303 }
305 > /**
306 > * Write {@link bytes} as text into the resolved text model for {@link uri},
307 > * if one can be resolved and is writable. Returns `true` when the model was
308 > * updated, `false` otherwise (no provider, readonly, decode failure).
309 > */
310 > private async _writeVirtual(uri: URI, bytes: VSBuffer): Promise<boolean> {
311 try {
312 const ref = await this._textModelService.createModelReference(uri);
313 try {
314 if (ref.object.isReadonly()) {
315 return false;
316 }
317 ref.object.textEditorModel.setValue(bytes.toString());
318 return true;
319 } finally {
320 ref.dispose();
321 }
322 } catch {
323 return false;
324 }
325 }
327 > /**
328 > * Resolve {@link uri} via {@link ITextModelService} and synthesize a
329 > * {@link ResourceResolveResult} so virtual resources stat as `File` with
330 > * a size matching their text content. Returns `undefined` if no model
331 > * can be resolved.
332 > */
333 > private async _statVirtual(uri: URI): Promise<ResourceResolveResult | undefined> {
334 try {
335 const ref = await this._textModelService.createModelReference(uri);
336 try {
337 const size = VSBuffer.fromString(ref.object.textEditorModel.getValue()).byteLength;
338 return {
339 uri: uri.toString(),
340 type: ResourceType.File,
341 size,
342 };
343 } finally {
344 ref.dispose();
345 }
346 } catch {
347 return undefined;
348 }
349 }
351 > /**
352 > * Resolve {@link uri} against the local filesystem, collapsing `..`
353 > * segments and following symlinks so the policy check sees the same
354 > * path the OS will actually open. For URIs that don't exist (e.g. a
355 > * `resourceWrite` for a new file), realpath the deepest existing
356 > * ancestor and re-append the leaf.
357 > */
358 > private async _canonicalize(uri: URI): Promise<URI> {
359 > const normalized = extUri.normalizePath(uri);
360 > const real = await this._fileService.realpath(normalized).catch(() => undefined);
361 > if (real) {
362 > return real; agentHostResourceService.ts ×1
363 > }
364 > const parent = extUri.dirname(normalized); agentHostResourceService.ts ×2
365 > if (extUri.isEqual(parent, normalized)) {
366 return normalized;
367 }
368 > const realParent = await this._fileService.realpath(parent).catch(() => undefined); agentHostResourceService.ts ×2
369 > return realParent
370 > ? extUri.joinPath(realParent, extUri.basename(normalized))
371 : normalized;
373 >
374 > private async _isCovered(address: string, canonicalUri: URI, mode: AgentHostPermissionMode): Promise<boolean> {
375 > if (address === LOCAL_AGENT_HOST_ADDRESS) {
376 return true;
377 }
378 > const requireWrite = mode === AgentHostPermissionMode.Write; agentHostResourceService.ts ×28
379 >
380 > for (const grant of this._readPersistedGrants(address)) {
381 > if (requireWrite && grant.mode !== AgentHostAccessMode.ReadWrite) { agentHostResourceService.ts ×5
383 > }
384 > if (extUri.isEqualOrParent(canonicalUri, grant.uri)) { agentHostResourceService.ts ×1
385 > return true;
386 > }
389 > const candidates: Promise<URI>[] = [];
390 > for (const grant of this._inMemoryGrants.values()) {
391 > if (grant.address !== address) { agentHostResourceService.ts ×2
393 > }
394 > if (requireWrite && grant.mode !== AgentHostAccessMode.ReadWrite) { agentHostResourceService.ts ×2
396 > }
397 > candidates.push(grant.realpath); agentHostResourceService.ts ×1
398 > }
399 > const realpaths = await Promise.all(candidates); agentHostResourceService.ts ×2
400 > return realpaths.some(uri => extUri.isEqualOrParent(canonicalUri, uri));
402 >
403 > private _enqueue(address: string, canonicalUri: URI, mode: AgentHostPermissionMode): Promise<void> {
404 > const existing = this._pending.get().find(r => agentHostResourceService.ts ×3
405 > r.address === address && r.mode === mode && extUri.isEqual(r.uri, canonicalUri));
406 > if (existing) {
407 > return existing.deferred.p; agentHostResourceService.ts ×1
408 > }
410 > const deferred = new DeferredPromise<void>();
411 > const request: IInternalPendingRequest = {
412 > id: generateUuid(),
413 > address,
414 > uri: canonicalUri,
415 > mode,
416 > deferred,
417 > allow: () => this._resolve(request, 'memory'),
418 > allowAlways: () => this._resolve(request, 'persist'),
419 > deny: () => {
420 > this._dropPending(request); agentHostResourceService.ts ×1
421 > deferred.error(new CancellationError());
422 > },
424 > this._pending.set([...this._pending.get(), request], undefined);
425 > return deferred.p;
426 > }
428 > private _resolve(request: IInternalPendingRequest, scope: 'memory' | 'persist'): void {
429 > const accessMode = request.mode === AgentHostPermissionMode.Write agentHostResourceService.ts ×3
430 > ? AgentHostAccessMode.ReadWrite agentHostResourceService.ts ×2
431 > : AgentHostAccessMode.Read; agentHostResourceService.ts ×2
433 > this._inMemoryGrants.set(generateUuid(), {
434 > address: request.address,
435 > realpath: Promise.resolve(request.uri),
436 > mode: accessMode,
437 > });
438 >
439 > if (scope === 'persist') {
440 > void this._persistGrant(request.address, request.uri, request.mode).catch(err => { agentHostResourceService.ts ×8
441 this._logService.warn('[AgentHostResourceService] Failed to persist grant', err);
443 > }
445 > this._dropPending(request);
446 > request.deferred.complete();
447 > }
449 > private _dropPending(request: IInternalPendingRequest): void {
450 > const next = this._pending.get().filter(r => r !== request); agentHostResourceService.ts ×1
451 > if (next.length !== this._pending.get().length) {
452 > this._pending.set(next, undefined);
453 > }
454 > }
456 > private *_readPersistedGrants(address: string): Iterable<{ uri: URI; mode: AgentHostAccessMode }> {
457 > const forAddress = this._configurationService
458 > .getValue<AgentHostPermissionsSetting>(AgentHostLocalFilePermissionsSettingId)?.[address];
459 > if (!forAddress) {
461 > }
462 > for (const [uriStr, mode] of Object.entries(forAddress)) { agentHostResourceService.ts ×5
463 > if (mode !== AgentHostAccessMode.Read && mode !== AgentHostAccessMode.ReadWrite) {
465 > }
467 > yield { uri: URI.parse(uriStr), mode };
469 // Ignore malformed URI keys.
470 }
473 >
474 > private async _persistGrant(address: string, uri: URI, mode: AgentHostPermissionMode): Promise<void> {
475 > const requested: AgentHostAccessMode = mode === AgentHostPermissionMode.Write agentHostResourceService.ts ×8
476 > ? AgentHostAccessMode.ReadWrite agentHostResourceService.ts ×1
477 > : AgentHostAccessMode.Read; agentHostResourceService.ts ×1
479 > for (const grant of this._readPersistedGrants(address)) {
480 const covers = grant.mode === AgentHostAccessMode.ReadWrite || requested === AgentHostAccessMode.Read;
481 if (covers && extUri.isEqualOrParent(uri, grant.uri)) {
482 return;
483 }
484 }
486 > const { target, value } = this._inspectScopedSetting();
487 > const forAddress: Record<string, AgentHostAccessMode> = { ...(value[address] ?? {}) };
488 > const uriKey = uri.toString();
489 > if (forAddress[uriKey] === AgentHostAccessMode.ReadWrite) {
490 return;
491 }
492 > forAddress[uriKey] = requested; agentHostResourceService.ts ×8
493 >
494 > await this._configurationService.updateValue(
495 > AgentHostLocalFilePermissionsSettingId,
496 > { ...value, [address]: forAddress },
497 > target,
498 > );
499 > }
501 > private _inspectScopedSetting(): { target: ConfigurationTarget; value: AgentHostPermissionsSetting } {
502 > const inspected = this._configurationService.inspect<AgentHostPermissionsSetting>(AgentHostLocalFilePermissionsSettingId); agentHostResourceService.ts ×8
503 > if (inspected.applicationValue !== undefined) {
504 > return { target: ConfigurationTarget.APPLICATION, value: inspected.applicationValue }; agentHostResourceService.ts ×1
505 > }
506 > if (inspected.userLocalValue !== undefined) { agentHostResourceService.ts ×1
507 > return { target: ConfigurationTarget.USER_LOCAL, value: inspected.userLocalValue }; agentHostResourceService.ts ×1
508 > }
509 > if (inspected.userRemoteValue !== undefined) { agentHostResourceService.ts ×3
510 return { target: ConfigurationTarget.USER_REMOTE, value: inspected.userRemoteValue };
511 }
512 > if (inspected.userValue !== undefined) { agentHostResourceService.ts ×3
513 return { target: ConfigurationTarget.USER, value: inspected.userValue };
514 }
515 > return { target: ConfigurationTarget.APPLICATION, value: {} }; agentHostResourceService.ts ×3
518 >
519 > registerSingleton(IAgentHostResourceService, AgentHostResourceService, InstantiationType.Delayed);