Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
d366fa3
fix: detect-child-process-23
Jan 30, 2026
1500569
Merge pull request #5 from jeevaratnamputla/fix/semgrep-detect-child-…
jeevaratnamputla Jan 30, 2026
6726c61
Fix code quality issues: error logging and JSDoc typo
kbhujbal Feb 26, 2026
8dea708
Merge branch 'main' into fix/code-quality-improvements
mjbvz Mar 16, 2026
a283594
Remove unused `IChatSessionDto.id` field
mjbvz Mar 20, 2026
5f3aba5
Make sure content part disposes of refs
mjbvz Mar 20, 2026
cafda08
Try to clean up inProgress handling for chat sessions
mjbvz Mar 20, 2026
4c055a0
feat: add context key expressions for slash command visibility (#303626)
TylerLeonhardt Mar 20, 2026
5f5fb85
Merge branch 'main' into fix/code-quality-improvements
mjbvz Mar 20, 2026
ba9458c
refactor: rename image carousel configuration and update related desc…
TylerLeonhardt Mar 20, 2026
5ce6509
Fix missing global flag in sanitizeId regex (#303603)
ShehabSherif0 Mar 20, 2026
36ec9db
Merge branch 'main' into child-process
mjbvz Mar 20, 2026
231b7e7
Use unknown instead of any
mjbvz Mar 20, 2026
1ec1913
Merge pull request #303620 from mjbvz/dev/mjbvz/tender-puffin
mjbvz Mar 20, 2026
5fc1bf6
Merge pull request #303625 from mjbvz/dev/mjbvz/possible-parakeet
mjbvz Mar 20, 2026
26d39c2
Merge pull request #303619 from mjbvz/dev/mjbvz/useful-narwhal
mjbvz Mar 20, 2026
5caa080
Merge pull request #297893 from kbhujbal/fix/code-quality-improvements
mjbvz Mar 20, 2026
f56eaf4
Merge pull request #291825 from jeevaratnamputla/child-process
mjbvz Mar 20, 2026
0697f7d
Mark many chat session related types as readonly
mjbvz Mar 20, 2026
4b98d21
Merge pull request #303640 from mjbvz/dev/mjbvz/boring-kingfisher
mjbvz Mar 20, 2026
4631c88
Update test
mjbvz Mar 20, 2026
c2b719f
Add one more readonly
mjbvz Mar 21, 2026
caa7e57
Also mark array itself as readonly
mjbvz Mar 21, 2026
2a09c1e
Use built-in progress instead of custom shimmer for agent debug panel…
pwang347 Mar 21, 2026
a7fffd1
Enable starting sessions on remote agent hosts in sessions app (#303631)
roblourens Mar 21, 2026
4b8a157
Replace gear menu with direct Customizations editor action (#303641)
joshspicer Mar 21, 2026
1519062
Add missing skills discovery info (#303646)
pwang347 Mar 21, 2026
28aaabc
Option to allow-list command when offering to run outside of sandbox …
dileepyavan Mar 21, 2026
8c3724e
Merge pull request #303644 from mjbvz/dev/mjbvz/fine-dingo
mjbvz Mar 21, 2026
7c45bc7
Fix browser focus stealing (#303647)
kycutler Mar 21, 2026
7503e59
Don't localize markdown icon syntax (#303655)
roblourens Mar 21, 2026
36ca95e
Show allow-list actions for unsandboxed terminal confirmations (#303660)
dileepyavan Mar 21, 2026
161ff42
carousel: improve image loading perf (#303662)
rebornix Mar 21, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
99 changes: 99 additions & 0 deletions .eslint-plugin-local/code-no-icons-in-localized-strings.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import * as eslint from 'eslint';
import { TSESTree } from '@typescript-eslint/utils';

/**
* Prevents theme icon syntax `$(iconName)` from appearing inside localized
* string arguments. Localizers may translate or corrupt the icon syntax,
* breaking rendering. Icon references should be kept outside the localized
* string - either prepended via concatenation or passed as a placeholder
* argument.
*
* Examples:
* ❌ localize('key', "$(gear) Settings")
* ✅ '$(gear) ' + localize('key', "Settings")
* ✅ localize('key', "Like {0}", '$(gear)')
*
* ❌ nls.localize('key', "$(loading~spin) Loading...")
* ✅ '$(loading~spin) ' + nls.localize('key', "Loading...")
*/
export default new class NoIconsInLocalizedStrings implements eslint.Rule.RuleModule {

readonly meta: eslint.Rule.RuleMetaData = {
messages: {
noIconInLocalizedString: 'Theme icon syntax $(…) should not appear inside localized strings. Move it outside the localize call or pass it as a placeholder argument.'
},
docs: {
description: 'Prevents $(icon) theme icon syntax inside localize() string arguments',
},
type: 'problem',
schema: false,
};

create(context: eslint.Rule.RuleContext): eslint.Rule.RuleListener {

// Matches $(iconName) or $(iconName~modifier) but not escaped \$(...)
const iconPattern = /(?<!\\)\$\([a-zA-Z][\w~-]*\)/;

function isLocalizeCall(callee: TSESTree.CallExpression['callee']): { isLocalize: boolean; messageArgIndex: number } {
// Direct localize('key', "message", ...) or localize2('key', "message", ...)
if (callee.type === 'Identifier' && (callee.name === 'localize' || callee.name === 'localize2')) {
return { isLocalize: true, messageArgIndex: 1 };
}

// nls.localize('key', "message", ...) or *.localize(...)
if (callee.type === 'MemberExpression' && callee.property.type === 'Identifier' && callee.property.name === 'localize') {
return { isLocalize: true, messageArgIndex: 1 };
}

return { isLocalize: false, messageArgIndex: -1 };
}

function getStringValue(node: TSESTree.Node): string | undefined {
if (node.type === 'Literal' && typeof node.value === 'string') {
return node.value;
}
if (node.type === 'TemplateLiteral' && node.expressions.length === 0 && node.quasis.length === 1) {
return node.quasis[0].value.cooked ?? undefined;
}
return undefined;
}

function checkCallExpression(node: TSESTree.CallExpression) {
const { isLocalize, messageArgIndex } = isLocalizeCall(node.callee);
if (!isLocalize) {
return;
}

// The first argument may be a string key or an object { key, comment }.
// Adjust the message argument index if the first arg is an object.
let actualMessageArgIndex = messageArgIndex;
const firstArg = node.arguments[0];
if (firstArg && firstArg.type === 'ObjectExpression') {
// localize({ key: '...', comment: [...] }, "message", ...)
actualMessageArgIndex = 1;
}

const messageArg = node.arguments[actualMessageArgIndex];
if (!messageArg) {
return;
}

const messageValue = getStringValue(messageArg);
if (messageValue !== undefined && iconPattern.test(messageValue)) {
context.report({
node: messageArg,
messageId: 'noIconInLocalizedString'
});
}
}

return {
CallExpression: (node: any) => checkCallExpression(node as TSESTree.CallExpression)
};
}
};
4 changes: 4 additions & 0 deletions build/lib/i18n.resources.json
Original file line number Diff line number Diff line change
Expand Up @@ -668,6 +668,10 @@
"name": "vs/sessions/contrib/logs",
"project": "vscode-sessions"
},
{
"name": "vs/sessions/contrib/remoteAgentHost",
"project": "vscode-sessions"
},
{
"name": "vs/sessions/contrib/sessions",
"project": "vscode-sessions"
Expand Down
1 change: 1 addition & 0 deletions eslint.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ export default tseslint.config(
'local/code-no-localized-model-description': 'warn',
'local/code-policy-localization-key-match': 'warn',
'local/code-no-localization-template-literals': 'error',
'local/code-no-icons-in-localized-strings': 'warn',
'local/code-no-http-import': ['warn', { target: 'src/vs/**' }],
'local/code-no-deep-import-of-internal': ['error', { '.*Internal': true, 'searchExtTypesInternal': false }],
'local/code-layering': [
Expand Down
8 changes: 4 additions & 4 deletions extensions/grunt/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@ function exists(file: string): Promise<boolean> {
});
}

function exec(command: string, options: cp.ExecOptions): Promise<{ stdout: string; stderr: string }> {
function exec(command: string, args: string[], options: cp.ExecFileOptions): Promise<{ stdout: string; stderr: string }> {
return new Promise<{ stdout: string; stderr: string }>((resolve, reject) => {
cp.exec(command, options, (error, stdout, stderr) => {
cp.execFile(command, args, options, (error, stdout, stderr) => {
if (error) {
reject({ error, stdout, stderr });
}
Expand Down Expand Up @@ -143,9 +143,9 @@ class FolderDetector {
return emptyTasks;
}

const commandLine = `${await this._gruntCommand} --help --no-color`;
const gruntCommand = await this._gruntCommand;
try {
const { stdout, stderr } = await exec(commandLine, { cwd: rootPath });
const { stdout, stderr } = await exec(gruntCommand, ['--help', '--no-color'], { cwd: rootPath });
if (stderr) {
getOutputChannel().appendLine(stderr);
showError();
Expand Down
2 changes: 1 addition & 1 deletion src/vs/editor/browser/view/domLineBreaksComputer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -307,7 +307,7 @@ function readLineBreaks(range: Range, lineDomNode: HTMLDivElement, lineContent:
try {
discoverBreaks(range, spans, charOffsets, 0, null, lineContent.length - 1, null, breakOffsets);
} catch (err) {
console.log(err);
console.error(err);
return null;
}

Expand Down
2 changes: 1 addition & 1 deletion src/vs/editor/common/core/ranges/offsetRange.ts
Original file line number Diff line number Diff line change
Expand Up @@ -257,7 +257,7 @@ export class OffsetRangeSet {
}

/**
* Returns of there is a value that is contained in this instance and the given range.
* Returns if there is a value that is contained in this instance and the given range.
*/
public intersectsStrict(other: OffsetRange): boolean {
// TODO use binary search
Expand Down
1 change: 1 addition & 0 deletions src/vs/platform/agentHost/common/agentService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ export interface IAgentSessionMetadata {
readonly startTime: number;
readonly modifiedTime: number;
readonly summary?: string;
readonly workingDirectory?: string;
}

export type AgentProvider = string;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

// allow-any-unicode-comment-file
// DO NOT EDIT -- auto-generated by scripts/sync-agent-host-protocol.ts
// Synced from agent-host-protocol @ 3116861
// Synced from agent-host-protocol @ 409b385

// Generated from types/actions.ts — do not edit
// Run `npm run generate` to regenerate.
Expand Down
2 changes: 1 addition & 1 deletion src/vs/platform/agentHost/common/state/protocol/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

// allow-any-unicode-comment-file
// DO NOT EDIT -- auto-generated by scripts/sync-agent-host-protocol.ts
// Synced from agent-host-protocol @ 3116861
// Synced from agent-host-protocol @ 409b385

import { ToolCallConfirmationReason, ToolCallCancellationReason, type URI, type StringOrMarkdown, type IAgentInfo, type IErrorInfo, type IUserMessage, type IResponsePart, type IToolCallResult, type IToolDefinition, type ISessionActiveClient, type IUsageInfo, type IPermissionRequest } from './state.js';

Expand Down
67 changes: 62 additions & 5 deletions src/vs/platform/agentHost/common/state/protocol/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

// allow-any-unicode-comment-file
// DO NOT EDIT -- auto-generated by scripts/sync-agent-host-protocol.ts
// Synced from agent-host-protocol @ 3116861
// Synced from agent-host-protocol @ 409b385

import type { URI, ISnapshot, ISessionSummary, ITurn } from './state.js';
import type { IActionEnvelope, IStateAction } from './actions.js';
Expand Down Expand Up @@ -172,7 +172,7 @@ export interface ICreateSessionParams {
/** Model ID to use */
model?: string;
/** Working directory for the session */
workingDirectory?: string;
workingDirectory?: URI;
}

// ─── disposeSession ──────────────────────────────────────────────────────────
Expand Down Expand Up @@ -255,25 +255,31 @@ export const enum ContentEncoding {
* { "jsonrpc": "2.0", "id": 10, "result": {
* "data": "iVBORw0KGgo...",
* "encoding": "base64",
* "mimeType": "image/png"
* "contentType": "image/png"
* }}
* ```
*/
export interface IFetchContentParams {
/** Content URI from a `ContentRef` */
uri: string;
/** Preferred encoding for the returned data (default: server-chosen) */
encoding?: ContentEncoding;
}

/**
* Result of the `fetchContent` command.
*
* The server SHOULD honor the `encoding` requested in the params. If the
* server cannot provide the requested encoding, it MUST fall back to either
* `base64` or `utf-8`.
*/
export interface IFetchContentResult {
/** Content encoded as a string */
data: string;
/** How `data` is encoded */
encoding: ContentEncoding;
/** MIME type of the content */
mimeType?: string;
/** Content type (e.g. `"image/png"`, `"text/plain"`) */
contentType?: string;
}

// ─── browseDirectory ────────────────────────────────────────────────────────
Expand Down Expand Up @@ -427,3 +433,54 @@ export interface IBrowseDirectoryEntry {
/** Whether this entry is a directory */
isDirectory: boolean;
}

// ─── authenticate ────────────────────────────────────────────────────────────

/**
* Pushes a Bearer token for a protected resource. The `resource` field MUST
* match an `IProtectedResourceMetadata.resource` value declared by an agent
* in `IAgentInfo.protectedResources`.
*
* Tokens are delivered using [RFC 6750](https://datatracker.ietf.org/doc/html/rfc6750)
* (Bearer Token Usage) semantics. The client obtains the token from the
* authorization server(s) listed in the resource's metadata and pushes it
* to the server via this command.
*
* @category Commands
* @method authenticate
* @direction Client → Server
* @messageType Request
* @version 1
* @see {@link /specification/authentication | Authentication}
* @example
* ```jsonc
* // Client → Server
* { "jsonrpc": "2.0", "id": 3, "method": "authenticate",
* "params": { "resource": "https://api.github.com", "token": "gho_xxxx" } }
*
* // Server → Client (success)
* { "jsonrpc": "2.0", "id": 3, "result": {} }
*
* // Server → Client (failure — invalid token)
* { "jsonrpc": "2.0", "id": 3, "error": { "code": -32007, "message": "Invalid token" } }
* ```
*/
export interface IAuthenticateParams {
/**
* The protected resource identifier. MUST match a `resource` value from
* `IProtectedResourceMetadata` declared in `IAgentInfo.protectedResources`.
*/
resource: string;
/** Bearer token obtained from the resource's authorization server */
token: string;
}

/**
* Result of the `authenticate` command.
*
* An empty object on success. If the token is invalid or the resource is
* unrecognized, the server MUST return a JSON-RPC error (e.g. `AuthRequired`
* `-32007` or `InvalidParams` `-32602`).
*/
export interface IAuthenticateResult {
}
11 changes: 10 additions & 1 deletion src/vs/platform/agentHost/common/state/protocol/errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

// allow-any-unicode-comment-file
// DO NOT EDIT -- auto-generated by scripts/sync-agent-host-protocol.ts
// Synced from agent-host-protocol @ 3116861
// Synced from agent-host-protocol @ 409b385

// ─── Standard JSON-RPC Codes ─────────────────────────────────────────────────

Expand Down Expand Up @@ -48,6 +48,15 @@ export const AhpErrorCodes = {
UnsupportedProtocolVersion: -32005,
/** The requested content URI does not exist */
ContentNotFound: -32006,
/**
* A command failed because the client has not authenticated for a required
* protected resource. The `data` field of the JSON-RPC error SHOULD contain
* an `IProtectedResourceMetadata[]` array describing the resources that
* require authentication.
*
* @see {@link /specification/authentication | Authentication}
*/
AuthRequired: -32007,
} as const;

/** Union type of all AHP application error codes. */
Expand Down
5 changes: 3 additions & 2 deletions src/vs/platform/agentHost/common/state/protocol/messages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@

// allow-any-unicode-comment-file
// DO NOT EDIT -- auto-generated by scripts/sync-agent-host-protocol.ts
// Synced from agent-host-protocol @ 3116861
// Synced from agent-host-protocol @ 409b385

import type { IInitializeParams, IInitializeResult, IReconnectParams, IReconnectResult, ISubscribeParams, ISubscribeResult, ICreateSessionParams, IDisposeSessionParams, IListSessionsParams, IListSessionsResult, IFetchContentParams, IFetchContentResult, IBrowseDirectoryParams, IBrowseDirectoryResult, IFetchTurnsParams, IFetchTurnsResult, IUnsubscribeParams, IDispatchActionParams } from './commands.js';
import type { IInitializeParams, IInitializeResult, IReconnectParams, IReconnectResult, ISubscribeParams, ISubscribeResult, ICreateSessionParams, IDisposeSessionParams, IListSessionsParams, IListSessionsResult, IFetchContentParams, IFetchContentResult, IBrowseDirectoryParams, IBrowseDirectoryResult, IFetchTurnsParams, IFetchTurnsResult, IUnsubscribeParams, IDispatchActionParams, IAuthenticateParams, IAuthenticateResult } from './commands.js';

import type { IActionEnvelope } from './actions.js';
import type { IProtocolNotification } from './notifications.js';
Expand Down Expand Up @@ -67,6 +67,7 @@ export interface ICommandMap {
'fetchContent': { params: IFetchContentParams; result: IFetchContentResult };
'browseDirectory': { params: IBrowseDirectoryParams; result: IBrowseDirectoryResult };
'fetchTurns': { params: IFetchTurnsParams; result: IFetchTurnsResult };
'authenticate': { params: IAuthenticateParams; result: IAuthenticateResult };
}

// ─── Notification Maps ───────────────────────────────────────────────────────
Expand Down
Loading
Loading