Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
10 changes: 10 additions & 0 deletions .agents/skills/launch/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,16 @@ npx @playwright/cli snapshot
- The `--agents` flag launches the Agents workbench instead of the standard VS Code workbench.
- Set `VSCODE_SKIP_PRELAUNCH=1` to skip the compile step if you've already built.

> **🚨 Driving the Agents window UI is not the same as driving regular Code OSS.**
> The session sidebar, agent picker, and chat input have their own quirks that
> trip up the obvious `click + type` patterns. Before you spend time fighting
> the UI, read **[references/agents-window-guide.md](./references/agents-window-guide.md)**
> — it covers the agent picker (Copilot CLI vs Claude), `[Local]` vs non-`[Local]`
> sessions, the reliable keyboard-navigation trick for the agent dropdown, how
> to send a follow-up turn in the same session, how to restore an existing
> chat, and the kill / relaunch / verify-out-is-fresh dance you need after a
> source code change.

## Launching VS Code Extensions for Debugging

To debug a VS Code extension via npx @playwright/cli, launch VS Code Insiders with `--extensionDevelopmentPath` and `--remote-debugging-port`. Use `--user-data-dir` to avoid conflicting with an already-running instance.
Expand Down
5 changes: 4 additions & 1 deletion eslint.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -1503,6 +1503,7 @@ export default defineConfig(
'when': 'hasNode',
'allow': [
'@github/copilot-sdk',
'zod',
'@microsoft/dev-tunnels-contracts',
'@microsoft/dev-tunnels-management',
'@parcel/watcher',
Expand Down Expand Up @@ -1646,7 +1647,9 @@ export default defineConfig(
'@vscode/tree-sitter-wasm', // used by agentHost for command auto-approval
'@vscode/copilot-api', // used by agentHost for Copilot API requests
'@anthropic-ai/sdk', // used by agentHost for Anthropic API requests
'@anthropic-ai/claude-agent-sdk' // used by agentHost for Claude Agent SDK session enumeration / queries
'@anthropic-ai/claude-agent-sdk', // used by agentHost for Claude Agent SDK session enumeration / queries
'@modelcontextprotocol/sdk/**/*', // used by agentHost for Claude client-tool MCP result types (Phase 10)
'zod' // used by agentHost for Claude client-tool MCP input schemas
]
},
{
Expand Down
3 changes: 2 additions & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,8 @@
"vscode-textmate": "^9.3.2",
"ws": "^8.19.0",
"yauzl": "^3.0.0",
"yazl": "^2.4.3"
"yazl": "^2.4.3",
"zod": "^3.25.76"
},
"devDependencies": {
"@anthropic-ai/claude-agent-sdk": "0.2.128",
Expand Down
3 changes: 2 additions & 1 deletion remote/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion remote/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,8 @@
"vscode-textmate": "^9.3.2",
"ws": "^8.19.0",
"yauzl": "^3.0.0",
"yazl": "^2.4.3"
"yazl": "^2.4.3",
"zod": "^3.25.76"
},
"overrides": {
"node-gyp-build": "4.8.1",
Expand Down
50 changes: 50 additions & 0 deletions src/vs/platform/agentHost/common/pendingRequestRegistry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
*--------------------------------------------------------------------------------------------*/

import { DeferredPromise } from '../../../base/common/async.js';
import { CancellationError } from '../../../base/common/errors.js';

/**
* Registry of parked deferred promises keyed by string id. Used to
Expand All @@ -27,6 +28,28 @@ export class PendingRequestRegistry<T> {
return deferred.p;
}

/**
* Park a deferred under `key` and return its promise. Use when there
* is no synchronous responder to guard against — the request that
* eventually feeds {@link respond} originates from a different code
* path (e.g. an MCP handler invoked by the SDK whose completion
* arrives via a workbench round-trip).
*
* If `key` is already registered (duplicate `tool_use_id` from the
* SDK, retry, or logic bug), the previous deferred is rejected with
* a {@link CancellationError} so its awaiter unwinds instead of
* leaking forever.
*/
register(key: string): Promise<T> {
const existing = this._entries.get(key);
if (existing && !existing.isSettled) {
existing.error(new CancellationError());
}
const deferred = new DeferredPromise<T>();
this._entries.set(key, deferred);
return deferred.p;
}
Comment thread
TylerLeonhardt marked this conversation as resolved.

respond(key: string, value: T): boolean {
const deferred = this._entries.get(key);
if (!deferred) {
Expand All @@ -37,6 +60,14 @@ export class PendingRequestRegistry<T> {
return true;
}

/**
* Resolve every parked deferred with `denyValue` and clear the registry.
*
* Designed for the permission-deny path: a "deny" answer is itself a
* successful round-trip result, so awaiting consumers receive `denyValue`
* rather than an error. Use {@link rejectAll} when callers must observe
* a thrown error instead (cancellation, dispose).
*/
denyAll(denyValue: T): void {
for (const [, deferred] of this._entries) {
if (!deferred.isSettled) {
Expand All @@ -45,4 +76,23 @@ export class PendingRequestRegistry<T> {
}
this._entries.clear();
}

/**
* Reject every parked deferred with `error` and clear the registry.
*
* Use this when in-flight requests must be cancelled rather than
* answered (e.g. session dispose, `Query` rebind on tool-set change).
* Compare with {@link denyAll}, which *resolves* every deferred with a
* supplied value — that is right for the permission-deny path where a
* "deny" is itself a successful answer, but wrong for cancellation
* where the awaited consumer must observe an error to unwind.
*/
rejectAll(error: Error): void {
for (const [, deferred] of this._entries) {
if (!deferred.isSettled) {
deferred.error(error);
}
}
this._entries.clear();
}
}
Loading
Loading