Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
754dd05
gate semantic search for search subagent
24anisha Sep 1, 2026
8c2aeb7
update test to resolve comment
24anisha Sep 2, 2026
7f3f8c9
Merge branch 'main' into anisha/semantic_search_flag
lramos15 Sep 2, 2026
b8d568e
fix: skip real AudioContext in Voice Mode onboarding preview under te…
vscodebot-pr Sep 2, 2026
ca90419
Update proxy agent to 0.45.0 (#333953)
chrmarti Sep 2, 2026
d2c4989
Revert mouse Back handling in the Chat view (#334052)
dmitrivMS Sep 2, 2026
2583f02
Agent Host: Expand E2E coverage for recent changes (#333922)
roblourens Sep 2, 2026
3be0354
Add extensible Markdown iframe editors
hediet Sep 2, 2026
69a4aa6
Update Markdown editor packages
hediet Sep 2, 2026
4601a54
Reuse existing webview bootstrap
hediet Sep 2, 2026
4e3ccb9
Configure the Markdown iframe bootstrap
hediet Sep 2, 2026
b728222
Update host-configurable Markdown editor
hediet Sep 2, 2026
06d46c4
Fix Markdown editor transport listener type
hediet Sep 2, 2026
6a6b6e6
Add web-editors MIT license override
hediet Sep 2, 2026
45f3f80
chore: bump @github/copilot to 1.0.83-2 (#333970)
vs-code-engineering[bot] Sep 2, 2026
7514a72
Show binary files in multi-diff editors
hediet Sep 2, 2026
ad6d84b
Represent multi-diff sides as sources
hediet Sep 2, 2026
1d36faf
agent host: support side chats from subagents (#334065)
connor4312 Sep 2, 2026
b576055
Browser: enforce network policy via session isolation (#333840)
kycutler Sep 2, 2026
6442769
Chat: Render pasted GitHub issue links (#334068)
meganrogge Sep 2, 2026
365175c
Fix list background color issue in customization modals (#334082)
hawkticehurst Sep 2, 2026
ec092ce
Fix agent session status alignment (#334083)
meganrogge Sep 2, 2026
2ad87ab
chat: Match request origin hover terminology (#334085)
meganrogge Sep 2, 2026
66eb4ca
chat: hide delegation from agent host sessions (#334089)
DonJayamanne Sep 2, 2026
be08585
automations: fix: preserve view during run archival (#334032)
ulugbekna Sep 2, 2026
f15eba2
Revert claude agent SDK lockfile bump from #333870 (#334094)
TylerLeonhardt Sep 2, 2026
35df44a
agentHost: do not declare a connection dead while the host waits for …
connor4312 Sep 2, 2026
e3660a9
sessions: avoid per-session resource label formatters (#334036)
sandy081 Sep 2, 2026
2931952
Ensure managed settings stability with regression tests (#334056)
joshspicer Sep 2, 2026
1152636
agentHost: Show Agent Merge changes in Agents Window (#334045)
benibenj Sep 2, 2026
a0a0a37
launch: Show agent session in editor window title (#334108)
meganrogge Sep 2, 2026
a3c780b
agentHost: add E2E coverage for resumed edit tracking (#334101)
roblourens Sep 2, 2026
5917dcc
Merge pull request #333869 from microsoft/anisha/semantic_search_flag
24anisha Sep 2, 2026
f4082bb
chat: deduplicate session widgets by target (#334123)
meganrogge Sep 2, 2026
d6207d7
chat: Move plugin update action to installed section (#334086)
houghj16 Sep 2, 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
12 changes: 8 additions & 4 deletions .agents/skills/launch/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ The clone is **slim**: workspace storage, browser caches, file history, cached V

> The launcher always sets `files.simpleDialog.enable: true` in the launched profile's `User/settings.json`. This is required for automation: VS Code's native OS file dialogs cannot be driven via `@playwright/cli` over CDP and are completely unreachable over SSH on headless macOS. The simple (quick-input) dialog can be navigated with `press` and clipboard paste. The override is per-launch and only affects throwaway profiles.

> When launching a regular editor window from an agent session, first call `get_current_session` and pass its `title` as `--session-title`. The launcher writes that title into the throwaway profile's `window.title` setting so each editor window can be mapped back to its originating session. This never modifies the source profile. Do not combine `--session-title` with `--agents`: `window.title` is read-only in the Agents window.

> For unattended automation, pass `--disable-workspace-trust` so a trust dialog cannot block the flow or extension-host startup. The override is process-scoped and does not modify the source profile. Only use it with content you trust.

## Launch
Expand All @@ -45,8 +47,9 @@ The launcher script lives next to this SKILL.md at `scripts/launch.sh` (macOS/Li

```bash
# LAUNCH=<dir-of-this-SKILL.md>/scripts/launch.sh
"$LAUNCH" # default: workbench
"$LAUNCH" --agents # Agents window
SESSION_TITLE=<title-from-get_current_session>
"$LAUNCH" --session-title "$SESSION_TITLE" # default: workbench
"$LAUNCH" --agents # Agents window (no custom title)
"$LAUNCH" -- <workspace-path> # forward extra args to code.sh
"$LAUNCH" --source-user-data-dir <path> # pick a specific authed profile
"$LAUNCH" --repo <vscode-repo-root> # if not run from the repo
Expand All @@ -61,8 +64,9 @@ On Windows, invoke the PowerShell launcher with the same flags:
```powershell
$skillDir = '<dir-of-this-SKILL.md>'
$launch = Join-Path $skillDir 'scripts\launch.ps1'
& $launch # default: workbench
& $launch --agents # Agents window
$sessionTitle = '<title-from-get_current_session>'
& $launch --session-title $sessionTitle # default: workbench
& $launch --agents # Agents window (no custom title)
& $launch -- --use-mock-keychain # forward extra args to code.bat
& $launch --source-user-data-dir C:\path\to\profile
& $launch --repo C:\path\to\vscode
Expand Down
119 changes: 21 additions & 98 deletions .agents/skills/launch/scripts/launch.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ $cloneExtensions = $false
$full = $false
$skipPreLaunch = $false
$disableWorkspaceTrust = $false
$sessionTitle = ''
if ($null -eq $cliArgs) {
$cliArgs = @()
}
Expand Down Expand Up @@ -279,103 +280,6 @@ function Test-SourceHasGitHubAuthenticationSecret([string]$node, [string]$source
return $false
}

function Get-JsoncCodeMask([string]$text) {
# Returns a same-length copy of $text with every comment span blanked out.
# Offsets are preserved so a match found in the mask can be applied to the
# original. String contents are respected, so a `//` inside a value (a URL,
# say) is not mistaken for a comment.
$chars = $text.ToCharArray()
$masked = [char[]]::new($chars.Length)
[Array]::Copy($chars, $masked, $chars.Length)

$inString = $false
$inLineComment = $false
$inBlockComment = $false
$escaped = $false

for ($i = 0; $i -lt $chars.Length; $i++) {
$current = $chars[$i]
$next = if ($i + 1 -lt $chars.Length) { $chars[$i + 1] } else { [char]0 }

if ($inLineComment) {
if ($current -eq "`n") { $inLineComment = $false } else { $masked[$i] = ' ' }
continue
}
if ($inBlockComment) {
if ($current -eq '*' -and $next -eq '/') {
$masked[$i] = ' '
$masked[$i + 1] = ' '
$i++
$inBlockComment = $false
} elseif ($current -ne "`n") {
$masked[$i] = ' '
}
continue
}
if ($inString) {
if ($escaped) { $escaped = $false }
elseif ($current -eq '\') { $escaped = $true }
elseif ($current -eq '"') { $inString = $false }
continue
}

if ($current -eq '"') { $inString = $true }
elseif ($current -eq '/' -and $next -eq '/') { $masked[$i] = ' '; $inLineComment = $true }
elseif ($current -eq '/' -and $next -eq '*') { $masked[$i] = ' '; $masked[$i + 1] = ' '; $i++; $inBlockComment = $true }
}

return (-join $masked)
}

function Ensure-SimpleDialogSetting([string]$settingsFile) {
$key = 'files.simpleDialog.enable'
$settingsDirectory = Split-Path -Parent $settingsFile
New-Item -ItemType Directory -Force -Path $settingsDirectory | Out-Null

if (Test-Path -LiteralPath $settingsFile -PathType Leaf) {
$text = [IO.File]::ReadAllText($settingsFile)
} else {
$text = ''
}

if ([string]::IsNullOrWhiteSpace($text)) {
[IO.File]::WriteAllText($settingsFile, "{`n `"$key`": true`n}`n", [Text.UTF8Encoding]::new($false))
return
}

# Match against a comment-masked copy so a commented-out occurrence such as
# `// "files.simpleDialog.enable": false` is not mistaken for the real
# setting. Offsets line up with the original, so the value is rewritten in
# place without disturbing comments.
$maskedText = Get-JsoncCodeMask $text
$keyPattern = [regex]::Escape($key)
$keyValueRegex = [regex]::new("(`"$keyPattern`"\s*:\s*)(true|false|null|`"[^`"`r`n]*`"|-?\d+(?:\.\d+)?)")
$keyMatch = $keyValueRegex.Match($maskedText)
if ($keyMatch.Success) {
$valueGroup = $keyMatch.Groups[2]
$updated = $text.Substring(0, $valueGroup.Index) + 'true' + $text.Substring($valueGroup.Index + $valueGroup.Length)
[IO.File]::WriteAllText($settingsFile, $updated, [Text.UTF8Encoding]::new($false))
return
}

$lastBrace = $maskedText.LastIndexOf('}')
if ($lastBrace -eq -1) {
throw "settings.json has no closing brace - refusing to clobber it: $settingsFile"
}
$firstBrace = $maskedText.IndexOf('{')
if ($firstBrace -eq -1 -or $firstBrace -ge $lastBrace) {
throw "settings.json has no opening brace - refusing to clobber it: $settingsFile"
}

# Whether a leading comma is needed depends only on real content, so decide
# it from the masked copy too.
$between = $maskedText.Substring($firstBrace + 1, $lastBrace - $firstBrace - 1).Trim()
$separator = if ($between.Length -eq 0 -or $between.EndsWith(',')) { '' } else { ',' }
$insertion = "$separator`n `"$key`": true`n"
$updated = $text.Substring(0, $lastBrace) + $insertion + $text.Substring($lastBrace)
[IO.File]::WriteAllText($settingsFile, $updated, [Text.UTF8Encoding]::new($false))
}

function Write-LogTail([string]$logFile) {
if (Test-Path -LiteralPath $logFile) {
Get-Content -LiteralPath $logFile -Tail 80 | ForEach-Object { [Console]::Error.WriteLine($_) }
Expand Down Expand Up @@ -442,6 +346,13 @@ for ($index = 0; $index -lt $cliArgs.Count; $index++) {
$agents = $true
continue
}
'--session-title' {
if ($index + 1 -ge $cliArgs.Count) {
Exit-Usage 'Missing value for --session-title.'
}
$sessionTitle = $cliArgs[++$index]
continue
}
'--source-user-data-dir' {
if ($index + 1 -ge $cliArgs.Count) {
Exit-Usage 'Missing value for --source-user-data-dir.'
Expand Down Expand Up @@ -489,6 +400,10 @@ for ($index = 0; $index -lt $cliArgs.Count; $index++) {
}
}

if ($agents -and -not [string]::IsNullOrWhiteSpace($sessionTitle)) {
Exit-Usage '--session-title is only supported for regular editor windows; window.title is read-only in the Agents window.'
}

try {
$launchStopwatch = [Diagnostics.Stopwatch]::StartNew()
if ([string]::IsNullOrWhiteSpace($repo)) {
Expand Down Expand Up @@ -574,8 +489,16 @@ try {
}

$settingsFile = Join-Path $destinationUdd 'User\settings.json'
Ensure-SimpleDialogSetting $settingsFile
$sourceSettingsFile = Join-Path $sourceUserDataDir 'User\settings.json'
$settingsScript = Join-Path $PSScriptRoot 'updateSettings.ts'
& $node $settingsScript $settingsFile $sessionTitle $sourceSettingsFile
if ($LASTEXITCODE -ne 0) {
throw "Failed to update launch settings in $settingsFile"
}
Write-LaunchError "[launch.ps1] ensured files.simpleDialog.enable=true in $settingsFile"
if (-not [string]::IsNullOrWhiteSpace($sessionTitle)) {
Write-LaunchError "[launch.ps1] set window.title for session: $sessionTitle"
}
$profileReadyMs = $launchStopwatch.ElapsedMilliseconds

$launchArgs = [System.Collections.Generic.List[string]]::new()
Expand Down
83 changes: 22 additions & 61 deletions .agents/skills/launch/scripts/launch.sh
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
# caller can pick them up programmatically. Logs go to stderr.
#
# Usage:
# launch.sh [--agents] [--source-user-data-dir <path>] [--repo <vscode-repo-root>]
# launch.sh [--agents] [--session-title <title>] [--source-user-data-dir <path>] [--repo <vscode-repo-root>]
# [--clone-extensions] [--full] [--skip-prelaunch]
# [--disable-workspace-trust] [-- <extra code.sh args>]
#
Expand Down Expand Up @@ -43,10 +43,19 @@ CLONE_EXTENSIONS=0
FULL=0
SKIP_PRELAUNCH=0
DISABLE_WORKSPACE_TRUST=0
SESSION_TITLE=""

while [[ $# -gt 0 ]]; do
case "$1" in
--agents) AGENTS=1; shift ;;
--session-title)
if [[ $# -lt 2 ]]; then
echo "Missing value for --session-title." >&2
exit 2
fi
SESSION_TITLE="$2"
shift 2
;;
--source-user-data-dir) SOURCE_UDD="$2"; shift 2 ;;
--repo) REPO="$2"; shift 2 ;;
--clone-extensions|--copy-extensions) CLONE_EXTENSIONS=1; shift ;;
Expand All @@ -58,6 +67,11 @@ while [[ $# -gt 0 ]]; do
esac
done

if [[ "$AGENTS" == "1" && -n "$SESSION_TITLE" ]]; then
echo "--session-title is only supported for regular editor windows; window.title is read-only in the Agents window." >&2
exit 2
fi

monotonic_ms() {
node -e 'process.stdout.write(String(process.hrtime.bigint() / 1_000_000n))'
}
Expand Down Expand Up @@ -166,70 +180,17 @@ fi
# always applied because every launched instance under this skill is
# a throwaway used for automation.
SETTINGS_FILE="$DEST_UDD/User/settings.json"
SOURCE_SETTINGS_FILE="$SOURCE_UDD/User/settings.json"
mkdir -p "$(dirname "$SETTINGS_FILE")"
# Data-preserving text-based merge: insert/update `files.simpleDialog.enable`
# without reparsing the whole file. Avoids dropping user comments and
# string values containing `//` (e.g. URLs). Fails loudly if the file
# exists but has no recognizable JSON object shape — never silently
# overwrites with `{}`.
if ! node - "$SETTINGS_FILE" <<'NODE'
const fs = require('fs');
const f = process.argv[2];
const KEY = 'files.simpleDialog.enable';

let text;
try { text = fs.readFileSync(f, 'utf8'); }
catch (e) {
if (e.code === 'ENOENT') text = '';
else { console.error('[launch.sh] cannot read ' + f + ': ' + e.message); process.exit(1); }
}

// Empty file → write a fresh object.
if (text.trim() === '') {
fs.writeFileSync(f, '{\n "' + KEY + '": true\n}\n');
process.exit(0);
}

// Key already present (with any value) → update its value to `true`
// via a targeted regex on the value slot only.
const keyValueRe = new RegExp('("' + KEY.replace(/\./g, '\\.') + '"\\s*:\\s*)(true|false|null|"[^"\\n]*"|-?\\d+(?:\\.\\d+)?)', 'g');
if (keyValueRe.test(text)) {
const updated = text.replace(keyValueRe, '$1true');
fs.writeFileSync(f, updated);
process.exit(0);
}

// Otherwise: find the LAST `}` and insert the new key before it.
// We deliberately don't parse JSONC — this preserves comments and
// any other content the source profile had.
const lastBrace = text.lastIndexOf('}');
if (lastBrace === -1) {
console.error('[launch.sh] settings.json has no closing brace — refusing to clobber it: ' + f);
process.exit(1);
}

// Decide whether to add a leading comma. If the only thing between the
// first `{` and the last `}` is whitespace and comments, the object is
// empty for our purposes and no comma is needed.
const firstBrace = text.indexOf('{');
if (firstBrace === -1 || firstBrace >= lastBrace) {
console.error('[launch.sh] settings.json has no opening brace — refusing to clobber it: ' + f);
process.exit(1);
}
const between = text.slice(firstBrace + 1, lastBrace)
.replace(/\/\*[\s\S]*?\*\//g, '')
.replace(/\/\/[^\n]*/g, '')
.trim();
const separator = between.length === 0 || between.endsWith(',') ? '' : ',';
const insertion = separator + '\n "' + KEY + '": true\n';

fs.writeFileSync(f, text.slice(0, lastBrace) + insertion + text.slice(lastBrace));
NODE
then
echo "[launch.sh] failed to ensure files.simpleDialog.enable=true in $SETTINGS_FILE — automation may need to fall back to per-key input" >&2
SETTINGS_SCRIPT="$(cd "$(dirname "$0")" && pwd)/updateSettings.ts"
if ! node "$SETTINGS_SCRIPT" "$SETTINGS_FILE" "$SESSION_TITLE" "$SOURCE_SETTINGS_FILE"; then
echo "[launch.sh] failed to update launch settings in $SETTINGS_FILE" >&2
exit 1
fi
echo "[launch.sh] ensured files.simpleDialog.enable=true in $SETTINGS_FILE" >&2
if [[ -n "$SESSION_TITLE" ]]; then
echo "[launch.sh] set window.title for session: $SESSION_TITLE" >&2
fi
PROFILE_READY_MS=$(monotonic_ms)

# Strip ELECTRON_RUN_AS_NODE, commonly inherited from VS Code's integrated
Expand Down
Loading
Loading