From 6ca01668a6cf469a71493364e1de54c15c7f35ea Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Tue, 8 Sep 2026 16:48:50 +0200 Subject: [PATCH 1/3] sessions: add explicit worktree option to create_session Honor user-requested worktree overrides while preserving isolation inheritance when omitted. Reject overrides for current-session chats and resolve known worktree folders to their project root when worktree is false. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/vs/platform/agentHost/AGENTS.md | 15 +- .../node/shared/sessionServerTools.ts | 27 ++- .../test/node/sessionServerTools.test.ts | 160 +++++++++++++++++- 3 files changed, 188 insertions(+), 14 deletions(-) diff --git a/src/vs/platform/agentHost/AGENTS.md b/src/vs/platform/agentHost/AGENTS.md index b7c44861ec506c..d9673ba232099a 100644 --- a/src/vs/platform/agentHost/AGENTS.md +++ b/src/vs/platform/agentHost/AGENTS.md @@ -244,7 +244,8 @@ Treat a session as the user-visible unit of work. `create_session` requires a relationship: `currentSession` creates a peer chat for tasks in the current plan or deliverable, sharing its workspace, lifecycle, and aggregate diff; `independent` creates a top-level session for a separate deliverable that needs -its own workspace, provider, or lifecycle. A title is required for both +its own workspace, provider, or lifecycle, or for an explicitly requested +worktree. A title is required for both relationships and is applied before the initial prompt starts. Sessions created by the `create_session` server tool record only the creating @@ -261,9 +262,15 @@ configured project root over a transient worktree. Ambiguous names require an explicit project URI. An independent session inherits the creating session's host-owned isolation -selection independently of provider-owned configuration. The target workspace -still constrains the effective selection, so a folder that cannot support Git -worktrees resolves to folder isolation. +selection independently of provider-owned configuration; +otherwise it uses worktree isolation. The optional `worktree` argument overrides +that selection. Agents must set it only when the user explicitly asks to create a +worktree (`true`) or work without one (`false`); otherwise they must omit it. +With `false`, a supplied worktree folder resolves to its known project root +before session creation; folders without a known project are used directly. +The option is invalid with `currentSession`, whose chats share the existing +workspace. The target workspace still constrains the effective selection, so a +folder that cannot support Git worktrees resolves to folder isolation. --- diff --git a/src/vs/platform/agentHost/node/shared/sessionServerTools.ts b/src/vs/platform/agentHost/node/shared/sessionServerTools.ts index dad63d53cdf6d7..922078123f4ec1 100644 --- a/src/vs/platform/agentHost/node/shared/sessionServerTools.ts +++ b/src/vs/platform/agentHost/node/shared/sessionServerTools.ts @@ -73,10 +73,11 @@ const createSessionInputSchema: ToolDefinition['inputSchema'] = { relationship: { type: 'string', enum: [...createSessionRelationshipValues], - description: 'Whether this work belongs to the current session or is independently managed. Use `currentSession` for tasks from the current plan or deliverable, including parallel or delegated tasks. Use `independent` only for a separate deliverable that needs its own workspace and top-level lifecycle.', + description: 'Whether this work belongs to the current session or is independently managed. Use `currentSession` for tasks from the current plan or deliverable, including parallel or delegated tasks, unless the user explicitly requests a worktree. Use `independent` for a separate deliverable that needs its own workspace, provider, or top-level lifecycle, or for an explicitly requested worktree.', }, prompt: { type: 'string', description: 'Initial prompt to send to the new session.' }, workspace: { type: 'string', description: 'For `independent` work: unique project name, project/workspace URI, absolute folder path, or working directory from an existing session. Required for `independent` and invalid for `currentSession`.' }, + worktree: { type: 'boolean', description: 'Override isolation for the new independent session. Set true only when the user explicitly asks to create a worktree, or false only when the user explicitly asks to work without one. Omit to preserve the existing isolation behavior: inherit the creating session\'s isolation for the same project, otherwise use worktree isolation. Only valid with relationship `independent`; omit for `currentSession`.' }, title: { type: 'string', maxLength: 200, description: 'Short title for the new chat or independent session.' }, model: { type: 'string', description: 'Optional model ID or display name. Defaults to the current chat\'s model. For `currentSession`, the model must belong to the current session\'s provider; for `independent`, the model selects the new session\'s provider.' }, }, @@ -173,7 +174,7 @@ export const sessionServerToolDefinitions: IAgentServerToolDefinition[] = [ { name: SessionServerToolName.CreateSession, title: 'Create Session', - description: 'Create delegated work and start it with an initial prompt. Set `relationship` to `currentSession` when the task belongs to the current plan or deliverable; this creates a new chat that shares the current session\'s workspace, lifecycle, and aggregate diff. Set it to `independent` only for a separate deliverable that needs its own workspace, provider, or top-level lifecycle.', + description: 'Create delegated work and start it with an initial prompt, either in a new chat sharing the current session\'s workspace, lifecycle, and aggregate diff, or in an independent session. Only supply `worktree` when the user explicitly requests working with or without a new worktree; never combine it with `currentSession`.', inputSchema: createSessionInputSchema, annotations: { readOnlyHint: false }, }, @@ -216,6 +217,7 @@ export function currentSessionUri(toolCallChannel: ProtocolURI): URI { interface ICreateSessionArgs { readonly relationship?: unknown; readonly workspace?: unknown; + readonly worktree?: unknown; readonly prompt?: unknown; readonly title?: unknown; readonly model?: unknown; @@ -229,6 +231,7 @@ export type IResolvedCreateSessionArgs = { } | { readonly relationship: 'independent'; readonly workspace: URI; + readonly worktree?: boolean; readonly prompt: string; readonly title: string; readonly model?: IAgentModelInfo; @@ -429,12 +432,12 @@ export function getSetWorkspaceArgs(rawArgs: unknown): { readonly workspaceFolde }; } -function resolveWorkspace(workspace: string, sessions: readonly IAgentSessionMetadata[]): URI { +function resolveWorkspace(workspace: string, sessions: readonly IAgentSessionMetadata[], preferProject = false): URI { const parsed = parseWorkspaceUri(workspace); for (const session of sessions) { for (const candidate of [session.project?.uri, ...(session.workingDirectories ?? [])]) { if (candidate && parsed && isEqual(candidate, parsed)) { - return candidate; + return preferProject ? session.project?.uri ?? candidate : candidate; } } } @@ -515,11 +518,15 @@ export function getCreateSessionArgs(rawArgs: unknown, sessions: readonly IAgent validateRenameTitle(title, SessionServerToolName.CreateSession); const workspace = getOptionalString(args.workspace, 'workspace', SessionServerToolName.CreateSession); const modelName = getOptionalString(args.model, 'model', SessionServerToolName.CreateSession); + const worktree = getOptionalBoolean(args.worktree, 'worktree', SessionServerToolName.CreateSession); const model = resolveModel(modelName, models, relationship === 'currentSession' ? currentProvider : undefined); if (relationship === 'currentSession') { if (workspace !== undefined) { throw new Error(`Invalid ${SessionServerToolName.CreateSession} input: workspace is only valid when relationship is "independent".`); } + if (worktree !== undefined) { + throw new Error(`Invalid ${SessionServerToolName.CreateSession} input: worktree is only valid when relationship is "independent"; chats in the current session share its workspace.`); + } return { relationship, prompt, @@ -529,7 +536,8 @@ export function getCreateSessionArgs(rawArgs: unknown, sessions: readonly IAgent } return { relationship, - workspace: resolveWorkspace(getRequiredString(workspace, 'workspace', SessionServerToolName.CreateSession), sessions), + workspace: resolveWorkspace(getRequiredString(workspace, 'workspace', SessionServerToolName.CreateSession), sessions, worktree === false), + ...(worktree !== undefined ? { worktree } : {}), prompt, title, ...(model !== undefined ? { model } : {}), @@ -821,9 +829,12 @@ export async function applyCreateSessionTool(accessor: ISessionServerToolAccesso const provider = args.model?.provider ?? defaults?.provider; const inheritsSourceProvider = provider !== undefined && provider === defaults?.provider; const inheritedProviderConfig = inheritsSourceProvider ? defaults?.config : undefined; - const isolation = defaults?.project !== undefined && isEqual(defaults.project, args.workspace) - ? defaults.isolation - : 'worktree'; + let isolation: 'folder' | 'worktree' | undefined = 'worktree'; + if (args.worktree !== undefined) { + isolation = args.worktree ? 'worktree' : 'folder'; + } else if (defaults?.project !== undefined && isEqual(defaults.project, args.workspace)) { + isolation = defaults.isolation; + } const configValues = inheritedProviderConfig === undefined && isolation === undefined ? undefined : { diff --git a/src/vs/platform/agentHost/test/node/sessionServerTools.test.ts b/src/vs/platform/agentHost/test/node/sessionServerTools.test.ts index 327df814b41267..6ef778e0ef635a 100644 --- a/src/vs/platform/agentHost/test/node/sessionServerTools.test.ts +++ b/src/vs/platform/agentHost/test/node/sessionServerTools.test.ts @@ -115,10 +115,11 @@ suite('SessionServerTools', () => { relationship: { type: 'string', enum: ['currentSession', 'independent'], - description: 'Whether this work belongs to the current session or is independently managed. Use `currentSession` for tasks from the current plan or deliverable, including parallel or delegated tasks. Use `independent` only for a separate deliverable that needs its own workspace and top-level lifecycle.', + description: 'Whether this work belongs to the current session or is independently managed. Use `currentSession` for tasks from the current plan or deliverable, including parallel or delegated tasks, unless the user explicitly requests a worktree. Use `independent` for a separate deliverable that needs its own workspace, provider, or top-level lifecycle, or for an explicitly requested worktree.', }, prompt: { type: 'string', description: 'Initial prompt to send to the new session.' }, workspace: { type: 'string', description: 'For `independent` work: unique project name, project/workspace URI, absolute folder path, or working directory from an existing session. Required for `independent` and invalid for `currentSession`.' }, + worktree: { type: 'boolean', description: 'Override isolation for the new independent session. Set true only when the user explicitly asks to create a worktree, or false only when the user explicitly asks to work without one. Omit to preserve the existing isolation behavior: inherit the creating session\'s isolation for the same project, otherwise use worktree isolation. Only valid with relationship `independent`; omit for `currentSession`.' }, title: { type: 'string', maxLength: 200, description: 'Short title for the new chat or independent session.' }, model: { type: 'string', description: 'Optional model ID or display name. Defaults to the current chat\'s model. For `currentSession`, the model must belong to the current session\'s provider; for `independent`, the model selects the new session\'s provider.' }, }, @@ -644,6 +645,12 @@ suite('SessionServerTools', () => { }); }); + test('create_session guidance requires an explicit isolation choice and excludes currentSession', () => { + const description = sessionServerToolDefinitions.find(definition => definition.name === SessionServerToolName.CreateSession)?.description ?? ''; + assert.match(description, /Only supply `worktree` when the user explicitly requests working with or without a new worktree/); + assert.match(description, /never combine it with `currentSession`/); + }); + test('getCreateSessionArgs resolves workspace by working directory and model by id/name', () => { const sessions = [sessionMeta('s1', SessionStatus.Idle, workspace)]; const byId = getCreateSessionArgs({ relationship: 'independent', workspace: workspace.toString(), prompt: 'hi', title: 'Task', model: 'gpt-4o' }, sessions, [model]); @@ -698,6 +705,63 @@ suite('SessionServerTools', () => { }); }); + for (const scheme of ['file', 'vscode-remote']) { + for (const worktree of [false, true, undefined]) { + test(`create_session resolves ${scheme} worktree folders to the project only with worktree=false (value=${worktree})`, async () => { + const project = URI.from({ scheme, authority: scheme === 'file' ? '' : 'ssh-remote+example', path: '/workspace/repo' }); + const existingWorktree = project.with({ path: '/worktrees/existing' }); + let created: IAgentCreateSessionConfig | undefined; + const accessor = createAccessor({ + listSessions: async () => [{ + ...sessionMeta('source', SessionStatus.Idle, existingWorktree), + project: { uri: project, displayName: 'Repo' }, + }], + getCreationDefaults: () => ({ provider: 'copilot', project, isolation: 'worktree' }), + onCreate: config => { created = config; }, + }); + + await applyCreateSessionTool(accessor, { + relationship: 'independent', + workspace: scheme === 'file' ? existingWorktree.fsPath : existingWorktree.toString(), + ...(worktree !== undefined ? { worktree } : {}), + prompt: 'do it', + title: 'Task', + }, URI.parse('copilot:/source')); + + assert.deepStrictEqual({ + workingDirectories: created?.workingDirectories?.map(directory => directory.toString()), + isolation: created?.config?.[SessionConfigKey.Isolation], + }, { + workingDirectories: [(worktree === false ? project : existingWorktree).toString()], + isolation: worktree === false ? 'folder' : 'worktree', + }); + }); + } + } + + test('getCreateSessionArgs preserves folders without a known project when worktree=false', () => { + for (const sessions of [[], [sessionMeta('folder', SessionStatus.Idle, workspace)]]) { + const args = getCreateSessionArgs({ + relationship: 'independent', + workspace: workspace.toString(), + worktree: false, + prompt: 'do it', + title: 'Task', + }, sessions, []); + + assert.deepStrictEqual({ + ...args, + workspace: args.relationship === 'independent' ? args.workspace.toString() : undefined, + }, { + relationship: 'independent', + workspace: workspace.toString(), + worktree: false, + prompt: 'do it', + title: 'Task', + }); + } + }); + test('getCreateSessionArgs reports ambiguous project names', () => { const sessions = [ { ...sessionMeta('one', SessionStatus.Idle, URI.parse('file:///worktrees/one')), project: { uri: URI.parse('file:///projects/one'), displayName: 'App' } }, @@ -981,7 +1045,7 @@ suite('SessionServerTools', () => { const gitWorkspace = URI.file('/workspace/git-repository'); let created: IAgentCreateSessionConfig | undefined; const accessor = createAccessor({ - getCreationDefaults: () => ({ provider: 'copilot', isolation: 'worktree', project: gitWorkspace }), + getCreationDefaults: () => ({ provider: 'copilot', config: { [SessionConfigKey.Isolation]: 'worktree' }, isolation: 'worktree', project: gitWorkspace }), onCreate: config => { created = config; }, }); @@ -1003,6 +1067,98 @@ suite('SessionServerTools', () => { }); }); + for (const worktree of [false, true]) { + test(`create_session honors explicit worktree=${worktree} over inherited isolation`, async () => { + let created: IAgentCreateSessionConfig | undefined; + const accessor = createAccessor({ + getCreationDefaults: () => ({ + provider: 'copilot', + project: workspace, + isolation: worktree ? 'folder' : 'worktree', + config: { [SessionConfigKey.Isolation]: worktree ? 'folder' : 'worktree', autoApprove: 'autoApprove' }, + }), + onCreate: config => { created = config; }, + }); + + await applyCreateSessionTool(accessor, { + relationship: 'independent', + workspace: workspace.toString(), + worktree, + prompt: 'do it', + title: 'Task', + }, URI.parse('copilot:/source')); + + assert.deepStrictEqual(created?.config, { + [SessionConfigKey.Isolation]: worktree ? 'worktree' : 'folder', + autoApprove: 'autoApprove', + }); + }); + + test(`create_session rejects currentSession with worktree=${worktree} before creating work`, async () => { + const operations: string[] = []; + const accessor = createAccessor({ + onCreate: () => operations.push('session'), + onCreateChat: () => operations.push('chat'), + onPrompt: () => operations.push('prompt'), + }); + + await assert.rejects(applyCreateSessionTool(accessor, { + relationship: 'currentSession', + worktree, + prompt: 'do it', + title: 'Task', + }, URI.parse('copilot:/source')), /worktree is only valid when relationship is "independent"/); + assert.deepStrictEqual(operations, []); + }); + } + + test('create_session preserves unspecified isolation for the same project', async () => { + let created: IAgentCreateSessionConfig | undefined; + const accessor = createAccessor({ + getCreationDefaults: () => ({ provider: 'copilot', project: workspace }), + onCreate: config => { created = config; }, + }); + + await applyCreateSessionTool(accessor, { + relationship: 'independent', + workspace: workspace.toString(), + prompt: 'do it', + title: 'Task', + }, URI.parse('copilot:/source')); + + assert.deepStrictEqual(created?.config, undefined); + }); + + test('create_session honors worktree=false for a different project', async () => { + let created: IAgentCreateSessionConfig | undefined; + const accessor = createAccessor({ + getCreationDefaults: () => ({ provider: 'copilot', project: URI.file('/workspace/source'), isolation: 'worktree' }), + onCreate: config => { created = config; }, + }); + + await applyCreateSessionTool(accessor, { + relationship: 'independent', + workspace: workspace.toString(), + worktree: false, + prompt: 'do it', + title: 'Task', + }, URI.parse('copilot:/source')); + + assert.deepStrictEqual(created?.config, { [SessionConfigKey.Isolation]: 'folder' }); + }); + + test('getCreateSessionArgs rejects non-boolean worktree values', () => { + for (const worktree of ['true', 'false', 1, null, {}]) { + assert.throws(() => getCreateSessionArgs({ + relationship: 'independent', + workspace: workspace.toString(), + worktree, + prompt: 'do it', + title: 'Task', + }, [], []), /worktree must be a boolean/); + } + }); + test('create_session uses a remote project root with a model from another provider', async () => { const remoteProject = URI.parse('vscode-remote://ssh-remote+example/home/me/app'); const remoteWorktree = URI.parse('vscode-remote://ssh-remote+example/home/me/app-worktree'); From 4725615df1c253de959c04dca3586846dbdc7457 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Tue, 8 Sep 2026 17:11:04 +0200 Subject: [PATCH 2/3] sessions: preserve folders when disabling worktree isolation Resolve only exact linked-worktree roots reported by Git to the primary checkout. Preserve nested and additional workspace folders instead of inferring worktree identity from session project metadata. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/vs/platform/agentHost/AGENTS.md | 5 +- .../platform/agentHost/node/agentService.ts | 1 + .../agentHost/node/agentServiceFoundation.ts | 1 + .../node/shared/sessionServerTools.ts | 18 ++-- .../test/node/sessionServerTools.test.ts | 86 ++++++++++++++++++- 5 files changed, 103 insertions(+), 8 deletions(-) diff --git a/src/vs/platform/agentHost/AGENTS.md b/src/vs/platform/agentHost/AGENTS.md index d9673ba232099a..2f4bc50e06b8bc 100644 --- a/src/vs/platform/agentHost/AGENTS.md +++ b/src/vs/platform/agentHost/AGENTS.md @@ -266,8 +266,9 @@ selection independently of provider-owned configuration; otherwise it uses worktree isolation. The optional `worktree` argument overrides that selection. Agents must set it only when the user explicitly asks to create a worktree (`true`) or work without one (`false`); otherwise they must omit it. -With `false`, a supplied worktree folder resolves to its known project root -before session creation; folders without a known project are used directly. +With `false`, an exact linked-worktree root reported by Git resolves to its +primary checkout before session creation. Nested and ordinary additional +workspace folders are preserved. The option is invalid with `currentSession`, whose chats share the existing workspace. The target workspace still constrains the effective selection, so a folder that cannot support Git worktrees resolves to folder isolation. diff --git a/src/vs/platform/agentHost/node/agentService.ts b/src/vs/platform/agentHost/node/agentService.ts index 36c53366234f12..853ce5afbff526 100644 --- a/src/vs/platform/agentHost/node/agentService.ts +++ b/src/vs/platform/agentHost/node/agentService.ts @@ -1178,6 +1178,7 @@ export class AgentService extends Disposable implements IAgentService { && readSessionWorkspaceless(this._stateManager.getSessionState(session.toString())?._meta), listSessions: () => this.listSessions(), getSession: session => this._getSessionMetadata(session), + getWorktreeRoots: workspace => this._gitService.getWorktreeRoots(workspace), createSession: config => this.createSession(config), getModels: () => { const models: IAgentModelInfo[] = []; diff --git a/src/vs/platform/agentHost/node/agentServiceFoundation.ts b/src/vs/platform/agentHost/node/agentServiceFoundation.ts index 8506160d570ad6..a4461a63d48839 100644 --- a/src/vs/platform/agentHost/node/agentServiceFoundation.ts +++ b/src/vs/platform/agentHost/node/agentServiceFoundation.ts @@ -39,6 +39,7 @@ export class AgentServiceCallbackAdapter implements IAgentServiceCallbackBinder canConvertWorkspace: session => this.value.sessionServerToolAccessor.canConvertWorkspace(session), listSessions: () => this.value.sessionServerToolAccessor.listSessions(), getSession: session => this.value.sessionServerToolAccessor.getSession(session), + getWorktreeRoots: workspace => this.value.sessionServerToolAccessor.getWorktreeRoots(workspace), createSession: config => this.value.sessionServerToolAccessor.createSession(config), getModels: () => this.value.sessionServerToolAccessor.getModels(), getCreationDefaults: source => this.value.sessionServerToolAccessor.getCreationDefaults(source), diff --git a/src/vs/platform/agentHost/node/shared/sessionServerTools.ts b/src/vs/platform/agentHost/node/shared/sessionServerTools.ts index 922078123f4ec1..ce925bb291cf0e 100644 --- a/src/vs/platform/agentHost/node/shared/sessionServerTools.ts +++ b/src/vs/platform/agentHost/node/shared/sessionServerTools.ts @@ -243,6 +243,7 @@ export interface IAgentServiceSessionServerToolAccessor { readonly canConvertWorkspace: (session: URI) => boolean; readonly listSessions: () => Promise; readonly getSession: (session: URI) => Promise; + readonly getWorktreeRoots: (workspace: URI) => Promise; readonly createSession: (config: IAgentCreateSessionConfig) => Promise; readonly getModels: () => readonly IAgentModelInfo[]; readonly getCreationDefaults: (source: URI) => ISessionCreationDefaults | undefined; @@ -432,12 +433,12 @@ export function getSetWorkspaceArgs(rawArgs: unknown): { readonly workspaceFolde }; } -function resolveWorkspace(workspace: string, sessions: readonly IAgentSessionMetadata[], preferProject = false): URI { +function resolveWorkspace(workspace: string, sessions: readonly IAgentSessionMetadata[]): URI { const parsed = parseWorkspaceUri(workspace); for (const session of sessions) { for (const candidate of [session.project?.uri, ...(session.workingDirectories ?? [])]) { if (candidate && parsed && isEqual(candidate, parsed)) { - return preferProject ? session.project?.uri ?? candidate : candidate; + return candidate; } } } @@ -536,7 +537,7 @@ export function getCreateSessionArgs(rawArgs: unknown, sessions: readonly IAgent } return { relationship, - workspace: resolveWorkspace(getRequiredString(workspace, 'workspace', SessionServerToolName.CreateSession), sessions, worktree === false), + workspace: resolveWorkspace(getRequiredString(workspace, 'workspace', SessionServerToolName.CreateSession), sessions), ...(worktree !== undefined ? { worktree } : {}), prompt, title, @@ -825,6 +826,13 @@ export async function applyCreateSessionTool(accessor: ISessionServerToolAccesso if (parentDepth >= maxSessionSpawnDepth) { throw new Error(`Refusing to create a session: recursion limit reached (max spawn depth ${maxSessionSpawnDepth}). This session was itself created ${parentDepth} level(s) deep.`); } + let workspace = args.workspace; + if (args.worktree === false) { + const [primaryRoot, ...linkedRoots] = await accessor.getWorktreeRoots(workspace); + if (primaryRoot && linkedRoots.some(root => isEqual(root, workspace))) { + workspace = primaryRoot; + } + } const defaults = source ? accessor.getCreationDefaults(source) : undefined; const provider = args.model?.provider ?? defaults?.provider; const inheritsSourceProvider = provider !== undefined && provider === defaults?.provider; @@ -832,7 +840,7 @@ export async function applyCreateSessionTool(accessor: ISessionServerToolAccesso let isolation: 'folder' | 'worktree' | undefined = 'worktree'; if (args.worktree !== undefined) { isolation = args.worktree ? 'worktree' : 'folder'; - } else if (defaults?.project !== undefined && isEqual(defaults.project, args.workspace)) { + } else if (defaults?.project !== undefined && isEqual(defaults.project, workspace)) { isolation = defaults.isolation; } const configValues = inheritedProviderConfig === undefined && isolation === undefined @@ -842,7 +850,7 @@ export async function applyCreateSessionTool(accessor: ISessionServerToolAccesso ...(isolation !== undefined ? { [SessionConfigKey.Isolation]: isolation } : {}), }; const config: IAgentCreateSessionConfig = { - workingDirectories: args.workspace ? [args.workspace] : undefined, + workingDirectories: [workspace], ...(provider !== undefined ? { provider } : {}), ...(args.model !== undefined ? { model: { id: args.model.id } } : defaults?.model !== undefined ? { model: defaults.model } : {}), ...(configValues !== undefined ? { config: configValues } : {}), diff --git a/src/vs/platform/agentHost/test/node/sessionServerTools.test.ts b/src/vs/platform/agentHost/test/node/sessionServerTools.test.ts index 6ef778e0ef635a..8e295cdd5b476b 100644 --- a/src/vs/platform/agentHost/test/node/sessionServerTools.test.ts +++ b/src/vs/platform/agentHost/test/node/sessionServerTools.test.ts @@ -68,6 +68,7 @@ suite('SessionServerTools', () => { canConvertWorkspace: overrides?.canConvertWorkspace ?? (() => true), listSessions: overrides?.listSessions ?? (async () => [sessionMeta('s1', SessionStatus.InProgress, workspace)]), getSession: overrides?.getSession ?? (async session => session.toString() === 'copilot:/s1' ? sessionMeta('s1', SessionStatus.InProgress, workspace) : undefined), + getWorktreeRoots: overrides?.getWorktreeRoots ?? (async () => []), createSession: overrides?.createSession ?? (async config => { overrides?.onCreate?.(config); return URI.parse('copilot:/new'); }), getModels: overrides?.getModels ?? (() => [model]), getCreationDefaults: overrides?.getCreationDefaults ?? (() => undefined), @@ -707,7 +708,7 @@ suite('SessionServerTools', () => { for (const scheme of ['file', 'vscode-remote']) { for (const worktree of [false, true, undefined]) { - test(`create_session resolves ${scheme} worktree folders to the project only with worktree=false (value=${worktree})`, async () => { + test(`create_session resolves ${scheme} linked worktree roots only with worktree=false (value=${worktree})`, async () => { const project = URI.from({ scheme, authority: scheme === 'file' ? '' : 'ssh-remote+example', path: '/workspace/repo' }); const existingWorktree = project.with({ path: '/worktrees/existing' }); let created: IAgentCreateSessionConfig | undefined; @@ -717,6 +718,11 @@ suite('SessionServerTools', () => { project: { uri: project, displayName: 'Repo' }, }], getCreationDefaults: () => ({ provider: 'copilot', project, isolation: 'worktree' }), + getWorktreeRoots: async directory => { + assert.strictEqual(directory.toString(), existingWorktree.toString()); + assert.strictEqual(worktree, false); + return [project, existingWorktree]; + }, onCreate: config => { created = config; }, }); @@ -739,6 +745,84 @@ suite('SessionServerTools', () => { } } + test('create_session with worktree=false preserves nested and additional workspace folders', async () => { + const project = URI.file('/repo'); + const linkedRoot = URI.file('/worktrees/linked'); + const nestedFolder = URI.file('/repo/packages/foo'); + const linkedNestedFolder = URI.file('/worktrees/linked/packages/foo'); + const additionalRoot = URI.file('/other'); + const plainFolder = URI.file('/plain'); + const created: (string[] | undefined)[] = []; + const accessor = createAccessor({ + listSessions: async () => [{ + ...sessionMeta('source', SessionStatus.Idle, nestedFolder), + workingDirectories: [nestedFolder, additionalRoot, linkedNestedFolder, plainFolder], + project: { uri: project, displayName: 'Repo' }, + }], + getWorktreeRoots: async directory => { + if (directory.toString() === additionalRoot.toString()) { + return [additionalRoot]; + } + if (directory.toString() === plainFolder.toString()) { + return []; + } + return [project, linkedRoot]; + }, + onCreate: config => created.push(config.workingDirectories?.map(directory => directory.toString())), + }); + + for (const directory of [project, nestedFolder, linkedNestedFolder, additionalRoot, plainFolder]) { + await applyCreateSessionTool(accessor, { + relationship: 'independent', + workspace: directory.toString(), + worktree: false, + prompt: 'do it', + title: 'Task', + }); + } + + assert.deepStrictEqual(created, [project, nestedFolder, linkedNestedFolder, additionalRoot, plainFolder].map(directory => [directory.toString()])); + }); + + test('create_session with worktree=false resolves linked roots without session metadata', async () => { + const project = URI.file('/repo'); + const linkedRoot = URI.file('/worktrees/linked'); + let created: IAgentCreateSessionConfig | undefined; + const accessor = createAccessor({ + listSessions: async () => [], + getWorktreeRoots: async () => [project, linkedRoot], + onCreate: config => { created = config; }, + }); + + await applyCreateSessionTool(accessor, { + relationship: 'independent', + workspace: linkedRoot.toString(), + worktree: false, + prompt: 'do it', + title: 'Task', + }); + + assert.deepStrictEqual(created?.workingDirectories?.map(directory => directory.toString()), [project.toString()]); + }); + + test('create_session propagates worktree lookup failures before creating a session', async () => { + const operations: string[] = []; + const accessor = createAccessor({ + getWorktreeRoots: async () => { throw new Error('Worktree lookup failed'); }, + onCreate: () => operations.push('create'), + onPrompt: () => operations.push('prompt'), + }); + + await assert.rejects(applyCreateSessionTool(accessor, { + relationship: 'independent', + workspace: workspace.toString(), + worktree: false, + prompt: 'do it', + title: 'Task', + }), /Worktree lookup failed/); + assert.deepStrictEqual(operations, []); + }); + test('getCreateSessionArgs preserves folders without a known project when worktree=false', () => { for (const sessions of [[], [sessionMeta('folder', SessionStatus.Idle, workspace)]]) { const args = getCreateSessionArgs({ From 9a8d2b79c53df0bbe367ef9ab82523a4465de736 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Tue, 8 Sep 2026 18:32:39 +0200 Subject: [PATCH 3/3] sessions: fix worktree tests and refresh tool prompt snapshots Use drive-qualified paths for Windows filesystem test inputs and regenerate Copilot prompt baselines for the explicit worktree tool option. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- ..._Host_E2E___Copilot_prompts_claude-haiku-4_5.prompt.md | 8 ++++++-- ...t_Host_E2E___Copilot_prompts_claude-opus-4_5.prompt.md | 8 ++++++-- ...t_Host_E2E___Copilot_prompts_claude-opus-4_6.prompt.md | 8 ++++++-- ...t_Host_E2E___Copilot_prompts_claude-opus-4_7.prompt.md | 8 ++++++-- ...t_Host_E2E___Copilot_prompts_claude-opus-4_8.prompt.md | 8 ++++++-- ...ent_Host_E2E___Copilot_prompts_claude-opus-5.prompt.md | 8 ++++++-- ...Host_E2E___Copilot_prompts_claude-sonnet-4_5.prompt.md | 8 ++++++-- ...Host_E2E___Copilot_prompts_claude-sonnet-4_6.prompt.md | 8 ++++++-- ...t_Host_E2E___Copilot_prompts_claude-sonnet-5.prompt.md | 8 ++++++-- ..._Host_E2E___Copilot_prompts_gemini-2_0-flash.prompt.md | 8 ++++++-- ...Agent_Host_E2E___Copilot_prompts_gpt-5-codex.prompt.md | 8 ++++++-- .../Agent_Host_E2E___Copilot_prompts_gpt-5-mini.prompt.md | 8 ++++++-- .../Agent_Host_E2E___Copilot_prompts_gpt-5.prompt.md | 8 ++++++-- ...ost_E2E___Copilot_prompts_gpt-5_1-codex-mini.prompt.md | 8 ++++++-- ...ent_Host_E2E___Copilot_prompts_gpt-5_1-codex.prompt.md | 8 ++++++-- .../Agent_Host_E2E___Copilot_prompts_gpt-5_1.prompt.md | 8 ++++++-- ...gent_Host_E2E___Copilot_prompts_gpt-5_6-luna.prompt.md | 8 ++++++-- ...Agent_Host_E2E___Copilot_prompts_gpt-5_6-sol.prompt.md | 8 ++++++-- ...ent_Host_E2E___Copilot_prompts_gpt-5_6-terra.prompt.md | 8 ++++++-- .../agentHost/test/node/sessionServerTools.test.ts | 6 ++++-- 20 files changed, 118 insertions(+), 40 deletions(-) diff --git a/src/vs/platform/agentHost/test/node/e2e/providers/__snapshots__/Agent_Host_E2E___Copilot_prompts_claude-haiku-4_5.prompt.md b/src/vs/platform/agentHost/test/node/e2e/providers/__snapshots__/Agent_Host_E2E___Copilot_prompts_claude-haiku-4_5.prompt.md index 224bd3aa3ca002..e42c352951bf9f 100644 --- a/src/vs/platform/agentHost/test/node/e2e/providers/__snapshots__/Agent_Host_E2E___Copilot_prompts_claude-haiku-4_5.prompt.md +++ b/src/vs/platform/agentHost/test/node/e2e/providers/__snapshots__/Agent_Host_E2E___Copilot_prompts_claude-haiku-4_5.prompt.md @@ -733,7 +733,7 @@ }, { "name": "create_session", - "description": "Create delegated work and start it with an initial prompt. Set `relationship` to `currentSession` when the task belongs to the current plan or deliverable; this creates a new chat that shares the current session's workspace, lifecycle, and aggregate diff. Set it to `independent` only for a separate deliverable that needs its own workspace, provider, or top-level lifecycle.", + "description": "Create delegated work and start it with an initial prompt, either in a new chat sharing the current session's workspace, lifecycle, and aggregate diff, or in an independent session. Only supply `worktree` when the user explicitly requests working with or without a new worktree; never combine it with `currentSession`.", "input_schema": { "type": "object", "properties": { @@ -743,7 +743,7 @@ "currentSession", "independent" ], - "description": "Whether this work belongs to the current session or is independently managed. Use `currentSession` for tasks from the current plan or deliverable, including parallel or delegated tasks. Use `independent` only for a separate deliverable that needs its own workspace and top-level lifecycle." + "description": "Whether this work belongs to the current session or is independently managed. Use `currentSession` for tasks from the current plan or deliverable, including parallel or delegated tasks, unless the user explicitly requests a worktree. Use `independent` for a separate deliverable that needs its own workspace, provider, or top-level lifecycle, or for an explicitly requested worktree." }, "prompt": { "type": "string", @@ -753,6 +753,10 @@ "type": "string", "description": "For `independent` work: unique project name, project/workspace URI, absolute folder path, or working directory from an existing session. Required for `independent` and invalid for `currentSession`." }, + "worktree": { + "type": "boolean", + "description": "Override isolation for the new independent session. Set true only when the user explicitly asks to create a worktree, or false only when the user explicitly asks to work without one. Omit to preserve the existing isolation behavior: inherit the creating session's isolation for the same project, otherwise use worktree isolation. Only valid with relationship `independent`; omit for `currentSession`." + }, "title": { "type": "string", "description": "Short title for the new chat or independent session.\n\n{maxLength: 200}" diff --git a/src/vs/platform/agentHost/test/node/e2e/providers/__snapshots__/Agent_Host_E2E___Copilot_prompts_claude-opus-4_5.prompt.md b/src/vs/platform/agentHost/test/node/e2e/providers/__snapshots__/Agent_Host_E2E___Copilot_prompts_claude-opus-4_5.prompt.md index 88d3faa90ed745..b69997595dfc27 100644 --- a/src/vs/platform/agentHost/test/node/e2e/providers/__snapshots__/Agent_Host_E2E___Copilot_prompts_claude-opus-4_5.prompt.md +++ b/src/vs/platform/agentHost/test/node/e2e/providers/__snapshots__/Agent_Host_E2E___Copilot_prompts_claude-opus-4_5.prompt.md @@ -733,7 +733,7 @@ }, { "name": "create_session", - "description": "Create delegated work and start it with an initial prompt. Set `relationship` to `currentSession` when the task belongs to the current plan or deliverable; this creates a new chat that shares the current session's workspace, lifecycle, and aggregate diff. Set it to `independent` only for a separate deliverable that needs its own workspace, provider, or top-level lifecycle.", + "description": "Create delegated work and start it with an initial prompt, either in a new chat sharing the current session's workspace, lifecycle, and aggregate diff, or in an independent session. Only supply `worktree` when the user explicitly requests working with or without a new worktree; never combine it with `currentSession`.", "input_schema": { "type": "object", "properties": { @@ -743,7 +743,7 @@ "currentSession", "independent" ], - "description": "Whether this work belongs to the current session or is independently managed. Use `currentSession` for tasks from the current plan or deliverable, including parallel or delegated tasks. Use `independent` only for a separate deliverable that needs its own workspace and top-level lifecycle." + "description": "Whether this work belongs to the current session or is independently managed. Use `currentSession` for tasks from the current plan or deliverable, including parallel or delegated tasks, unless the user explicitly requests a worktree. Use `independent` for a separate deliverable that needs its own workspace, provider, or top-level lifecycle, or for an explicitly requested worktree." }, "prompt": { "type": "string", @@ -753,6 +753,10 @@ "type": "string", "description": "For `independent` work: unique project name, project/workspace URI, absolute folder path, or working directory from an existing session. Required for `independent` and invalid for `currentSession`." }, + "worktree": { + "type": "boolean", + "description": "Override isolation for the new independent session. Set true only when the user explicitly asks to create a worktree, or false only when the user explicitly asks to work without one. Omit to preserve the existing isolation behavior: inherit the creating session's isolation for the same project, otherwise use worktree isolation. Only valid with relationship `independent`; omit for `currentSession`." + }, "title": { "type": "string", "description": "Short title for the new chat or independent session.\n\n{maxLength: 200}" diff --git a/src/vs/platform/agentHost/test/node/e2e/providers/__snapshots__/Agent_Host_E2E___Copilot_prompts_claude-opus-4_6.prompt.md b/src/vs/platform/agentHost/test/node/e2e/providers/__snapshots__/Agent_Host_E2E___Copilot_prompts_claude-opus-4_6.prompt.md index fc7650db5b9191..6ddab93b585741 100644 --- a/src/vs/platform/agentHost/test/node/e2e/providers/__snapshots__/Agent_Host_E2E___Copilot_prompts_claude-opus-4_6.prompt.md +++ b/src/vs/platform/agentHost/test/node/e2e/providers/__snapshots__/Agent_Host_E2E___Copilot_prompts_claude-opus-4_6.prompt.md @@ -733,7 +733,7 @@ }, { "name": "create_session", - "description": "Create delegated work and start it with an initial prompt. Set `relationship` to `currentSession` when the task belongs to the current plan or deliverable; this creates a new chat that shares the current session's workspace, lifecycle, and aggregate diff. Set it to `independent` only for a separate deliverable that needs its own workspace, provider, or top-level lifecycle.", + "description": "Create delegated work and start it with an initial prompt, either in a new chat sharing the current session's workspace, lifecycle, and aggregate diff, or in an independent session. Only supply `worktree` when the user explicitly requests working with or without a new worktree; never combine it with `currentSession`.", "input_schema": { "type": "object", "properties": { @@ -743,7 +743,7 @@ "currentSession", "independent" ], - "description": "Whether this work belongs to the current session or is independently managed. Use `currentSession` for tasks from the current plan or deliverable, including parallel or delegated tasks. Use `independent` only for a separate deliverable that needs its own workspace and top-level lifecycle." + "description": "Whether this work belongs to the current session or is independently managed. Use `currentSession` for tasks from the current plan or deliverable, including parallel or delegated tasks, unless the user explicitly requests a worktree. Use `independent` for a separate deliverable that needs its own workspace, provider, or top-level lifecycle, or for an explicitly requested worktree." }, "prompt": { "type": "string", @@ -753,6 +753,10 @@ "type": "string", "description": "For `independent` work: unique project name, project/workspace URI, absolute folder path, or working directory from an existing session. Required for `independent` and invalid for `currentSession`." }, + "worktree": { + "type": "boolean", + "description": "Override isolation for the new independent session. Set true only when the user explicitly asks to create a worktree, or false only when the user explicitly asks to work without one. Omit to preserve the existing isolation behavior: inherit the creating session's isolation for the same project, otherwise use worktree isolation. Only valid with relationship `independent`; omit for `currentSession`." + }, "title": { "type": "string", "description": "Short title for the new chat or independent session.\n\n{maxLength: 200}" diff --git a/src/vs/platform/agentHost/test/node/e2e/providers/__snapshots__/Agent_Host_E2E___Copilot_prompts_claude-opus-4_7.prompt.md b/src/vs/platform/agentHost/test/node/e2e/providers/__snapshots__/Agent_Host_E2E___Copilot_prompts_claude-opus-4_7.prompt.md index a8f698a3b51f4c..f377b1c2fadc0d 100644 --- a/src/vs/platform/agentHost/test/node/e2e/providers/__snapshots__/Agent_Host_E2E___Copilot_prompts_claude-opus-4_7.prompt.md +++ b/src/vs/platform/agentHost/test/node/e2e/providers/__snapshots__/Agent_Host_E2E___Copilot_prompts_claude-opus-4_7.prompt.md @@ -733,7 +733,7 @@ }, { "name": "create_session", - "description": "Create delegated work and start it with an initial prompt. Set `relationship` to `currentSession` when the task belongs to the current plan or deliverable; this creates a new chat that shares the current session's workspace, lifecycle, and aggregate diff. Set it to `independent` only for a separate deliverable that needs its own workspace, provider, or top-level lifecycle.", + "description": "Create delegated work and start it with an initial prompt, either in a new chat sharing the current session's workspace, lifecycle, and aggregate diff, or in an independent session. Only supply `worktree` when the user explicitly requests working with or without a new worktree; never combine it with `currentSession`.", "input_schema": { "type": "object", "properties": { @@ -743,7 +743,7 @@ "currentSession", "independent" ], - "description": "Whether this work belongs to the current session or is independently managed. Use `currentSession` for tasks from the current plan or deliverable, including parallel or delegated tasks. Use `independent` only for a separate deliverable that needs its own workspace and top-level lifecycle." + "description": "Whether this work belongs to the current session or is independently managed. Use `currentSession` for tasks from the current plan or deliverable, including parallel or delegated tasks, unless the user explicitly requests a worktree. Use `independent` for a separate deliverable that needs its own workspace, provider, or top-level lifecycle, or for an explicitly requested worktree." }, "prompt": { "type": "string", @@ -753,6 +753,10 @@ "type": "string", "description": "For `independent` work: unique project name, project/workspace URI, absolute folder path, or working directory from an existing session. Required for `independent` and invalid for `currentSession`." }, + "worktree": { + "type": "boolean", + "description": "Override isolation for the new independent session. Set true only when the user explicitly asks to create a worktree, or false only when the user explicitly asks to work without one. Omit to preserve the existing isolation behavior: inherit the creating session's isolation for the same project, otherwise use worktree isolation. Only valid with relationship `independent`; omit for `currentSession`." + }, "title": { "type": "string", "description": "Short title for the new chat or independent session.\n\n{maxLength: 200}" diff --git a/src/vs/platform/agentHost/test/node/e2e/providers/__snapshots__/Agent_Host_E2E___Copilot_prompts_claude-opus-4_8.prompt.md b/src/vs/platform/agentHost/test/node/e2e/providers/__snapshots__/Agent_Host_E2E___Copilot_prompts_claude-opus-4_8.prompt.md index 4f6e9c308c01ea..b8631a4a9992bd 100644 --- a/src/vs/platform/agentHost/test/node/e2e/providers/__snapshots__/Agent_Host_E2E___Copilot_prompts_claude-opus-4_8.prompt.md +++ b/src/vs/platform/agentHost/test/node/e2e/providers/__snapshots__/Agent_Host_E2E___Copilot_prompts_claude-opus-4_8.prompt.md @@ -733,7 +733,7 @@ }, { "name": "create_session", - "description": "Create delegated work and start it with an initial prompt. Set `relationship` to `currentSession` when the task belongs to the current plan or deliverable; this creates a new chat that shares the current session's workspace, lifecycle, and aggregate diff. Set it to `independent` only for a separate deliverable that needs its own workspace, provider, or top-level lifecycle.", + "description": "Create delegated work and start it with an initial prompt, either in a new chat sharing the current session's workspace, lifecycle, and aggregate diff, or in an independent session. Only supply `worktree` when the user explicitly requests working with or without a new worktree; never combine it with `currentSession`.", "input_schema": { "type": "object", "properties": { @@ -743,7 +743,7 @@ "currentSession", "independent" ], - "description": "Whether this work belongs to the current session or is independently managed. Use `currentSession` for tasks from the current plan or deliverable, including parallel or delegated tasks. Use `independent` only for a separate deliverable that needs its own workspace and top-level lifecycle." + "description": "Whether this work belongs to the current session or is independently managed. Use `currentSession` for tasks from the current plan or deliverable, including parallel or delegated tasks, unless the user explicitly requests a worktree. Use `independent` for a separate deliverable that needs its own workspace, provider, or top-level lifecycle, or for an explicitly requested worktree." }, "prompt": { "type": "string", @@ -753,6 +753,10 @@ "type": "string", "description": "For `independent` work: unique project name, project/workspace URI, absolute folder path, or working directory from an existing session. Required for `independent` and invalid for `currentSession`." }, + "worktree": { + "type": "boolean", + "description": "Override isolation for the new independent session. Set true only when the user explicitly asks to create a worktree, or false only when the user explicitly asks to work without one. Omit to preserve the existing isolation behavior: inherit the creating session's isolation for the same project, otherwise use worktree isolation. Only valid with relationship `independent`; omit for `currentSession`." + }, "title": { "type": "string", "description": "Short title for the new chat or independent session.\n\n{maxLength: 200}" diff --git a/src/vs/platform/agentHost/test/node/e2e/providers/__snapshots__/Agent_Host_E2E___Copilot_prompts_claude-opus-5.prompt.md b/src/vs/platform/agentHost/test/node/e2e/providers/__snapshots__/Agent_Host_E2E___Copilot_prompts_claude-opus-5.prompt.md index 38fc3b56132e62..f822d904c6581f 100644 --- a/src/vs/platform/agentHost/test/node/e2e/providers/__snapshots__/Agent_Host_E2E___Copilot_prompts_claude-opus-5.prompt.md +++ b/src/vs/platform/agentHost/test/node/e2e/providers/__snapshots__/Agent_Host_E2E___Copilot_prompts_claude-opus-5.prompt.md @@ -733,7 +733,7 @@ }, { "name": "create_session", - "description": "Create delegated work and start it with an initial prompt. Set `relationship` to `currentSession` when the task belongs to the current plan or deliverable; this creates a new chat that shares the current session's workspace, lifecycle, and aggregate diff. Set it to `independent` only for a separate deliverable that needs its own workspace, provider, or top-level lifecycle.", + "description": "Create delegated work and start it with an initial prompt, either in a new chat sharing the current session's workspace, lifecycle, and aggregate diff, or in an independent session. Only supply `worktree` when the user explicitly requests working with or without a new worktree; never combine it with `currentSession`.", "input_schema": { "type": "object", "properties": { @@ -743,7 +743,7 @@ "currentSession", "independent" ], - "description": "Whether this work belongs to the current session or is independently managed. Use `currentSession` for tasks from the current plan or deliverable, including parallel or delegated tasks. Use `independent` only for a separate deliverable that needs its own workspace and top-level lifecycle." + "description": "Whether this work belongs to the current session or is independently managed. Use `currentSession` for tasks from the current plan or deliverable, including parallel or delegated tasks, unless the user explicitly requests a worktree. Use `independent` for a separate deliverable that needs its own workspace, provider, or top-level lifecycle, or for an explicitly requested worktree." }, "prompt": { "type": "string", @@ -753,6 +753,10 @@ "type": "string", "description": "For `independent` work: unique project name, project/workspace URI, absolute folder path, or working directory from an existing session. Required for `independent` and invalid for `currentSession`." }, + "worktree": { + "type": "boolean", + "description": "Override isolation for the new independent session. Set true only when the user explicitly asks to create a worktree, or false only when the user explicitly asks to work without one. Omit to preserve the existing isolation behavior: inherit the creating session's isolation for the same project, otherwise use worktree isolation. Only valid with relationship `independent`; omit for `currentSession`." + }, "title": { "type": "string", "description": "Short title for the new chat or independent session.\n\n{maxLength: 200}" diff --git a/src/vs/platform/agentHost/test/node/e2e/providers/__snapshots__/Agent_Host_E2E___Copilot_prompts_claude-sonnet-4_5.prompt.md b/src/vs/platform/agentHost/test/node/e2e/providers/__snapshots__/Agent_Host_E2E___Copilot_prompts_claude-sonnet-4_5.prompt.md index 3bde585137ef70..424a08bd1098dc 100644 --- a/src/vs/platform/agentHost/test/node/e2e/providers/__snapshots__/Agent_Host_E2E___Copilot_prompts_claude-sonnet-4_5.prompt.md +++ b/src/vs/platform/agentHost/test/node/e2e/providers/__snapshots__/Agent_Host_E2E___Copilot_prompts_claude-sonnet-4_5.prompt.md @@ -733,7 +733,7 @@ }, { "name": "create_session", - "description": "Create delegated work and start it with an initial prompt. Set `relationship` to `currentSession` when the task belongs to the current plan or deliverable; this creates a new chat that shares the current session's workspace, lifecycle, and aggregate diff. Set it to `independent` only for a separate deliverable that needs its own workspace, provider, or top-level lifecycle.", + "description": "Create delegated work and start it with an initial prompt, either in a new chat sharing the current session's workspace, lifecycle, and aggregate diff, or in an independent session. Only supply `worktree` when the user explicitly requests working with or without a new worktree; never combine it with `currentSession`.", "input_schema": { "type": "object", "properties": { @@ -743,7 +743,7 @@ "currentSession", "independent" ], - "description": "Whether this work belongs to the current session or is independently managed. Use `currentSession` for tasks from the current plan or deliverable, including parallel or delegated tasks. Use `independent` only for a separate deliverable that needs its own workspace and top-level lifecycle." + "description": "Whether this work belongs to the current session or is independently managed. Use `currentSession` for tasks from the current plan or deliverable, including parallel or delegated tasks, unless the user explicitly requests a worktree. Use `independent` for a separate deliverable that needs its own workspace, provider, or top-level lifecycle, or for an explicitly requested worktree." }, "prompt": { "type": "string", @@ -753,6 +753,10 @@ "type": "string", "description": "For `independent` work: unique project name, project/workspace URI, absolute folder path, or working directory from an existing session. Required for `independent` and invalid for `currentSession`." }, + "worktree": { + "type": "boolean", + "description": "Override isolation for the new independent session. Set true only when the user explicitly asks to create a worktree, or false only when the user explicitly asks to work without one. Omit to preserve the existing isolation behavior: inherit the creating session's isolation for the same project, otherwise use worktree isolation. Only valid with relationship `independent`; omit for `currentSession`." + }, "title": { "type": "string", "description": "Short title for the new chat or independent session.\n\n{maxLength: 200}" diff --git a/src/vs/platform/agentHost/test/node/e2e/providers/__snapshots__/Agent_Host_E2E___Copilot_prompts_claude-sonnet-4_6.prompt.md b/src/vs/platform/agentHost/test/node/e2e/providers/__snapshots__/Agent_Host_E2E___Copilot_prompts_claude-sonnet-4_6.prompt.md index 2d306031176b46..a4354931e81987 100644 --- a/src/vs/platform/agentHost/test/node/e2e/providers/__snapshots__/Agent_Host_E2E___Copilot_prompts_claude-sonnet-4_6.prompt.md +++ b/src/vs/platform/agentHost/test/node/e2e/providers/__snapshots__/Agent_Host_E2E___Copilot_prompts_claude-sonnet-4_6.prompt.md @@ -733,7 +733,7 @@ }, { "name": "create_session", - "description": "Create delegated work and start it with an initial prompt. Set `relationship` to `currentSession` when the task belongs to the current plan or deliverable; this creates a new chat that shares the current session's workspace, lifecycle, and aggregate diff. Set it to `independent` only for a separate deliverable that needs its own workspace, provider, or top-level lifecycle.", + "description": "Create delegated work and start it with an initial prompt, either in a new chat sharing the current session's workspace, lifecycle, and aggregate diff, or in an independent session. Only supply `worktree` when the user explicitly requests working with or without a new worktree; never combine it with `currentSession`.", "input_schema": { "type": "object", "properties": { @@ -743,7 +743,7 @@ "currentSession", "independent" ], - "description": "Whether this work belongs to the current session or is independently managed. Use `currentSession` for tasks from the current plan or deliverable, including parallel or delegated tasks. Use `independent` only for a separate deliverable that needs its own workspace and top-level lifecycle." + "description": "Whether this work belongs to the current session or is independently managed. Use `currentSession` for tasks from the current plan or deliverable, including parallel or delegated tasks, unless the user explicitly requests a worktree. Use `independent` for a separate deliverable that needs its own workspace, provider, or top-level lifecycle, or for an explicitly requested worktree." }, "prompt": { "type": "string", @@ -753,6 +753,10 @@ "type": "string", "description": "For `independent` work: unique project name, project/workspace URI, absolute folder path, or working directory from an existing session. Required for `independent` and invalid for `currentSession`." }, + "worktree": { + "type": "boolean", + "description": "Override isolation for the new independent session. Set true only when the user explicitly asks to create a worktree, or false only when the user explicitly asks to work without one. Omit to preserve the existing isolation behavior: inherit the creating session's isolation for the same project, otherwise use worktree isolation. Only valid with relationship `independent`; omit for `currentSession`." + }, "title": { "type": "string", "description": "Short title for the new chat or independent session.\n\n{maxLength: 200}" diff --git a/src/vs/platform/agentHost/test/node/e2e/providers/__snapshots__/Agent_Host_E2E___Copilot_prompts_claude-sonnet-5.prompt.md b/src/vs/platform/agentHost/test/node/e2e/providers/__snapshots__/Agent_Host_E2E___Copilot_prompts_claude-sonnet-5.prompt.md index 969abbe8fb4f5a..8d997ba4df01ee 100644 --- a/src/vs/platform/agentHost/test/node/e2e/providers/__snapshots__/Agent_Host_E2E___Copilot_prompts_claude-sonnet-5.prompt.md +++ b/src/vs/platform/agentHost/test/node/e2e/providers/__snapshots__/Agent_Host_E2E___Copilot_prompts_claude-sonnet-5.prompt.md @@ -733,7 +733,7 @@ }, { "name": "create_session", - "description": "Create delegated work and start it with an initial prompt. Set `relationship` to `currentSession` when the task belongs to the current plan or deliverable; this creates a new chat that shares the current session's workspace, lifecycle, and aggregate diff. Set it to `independent` only for a separate deliverable that needs its own workspace, provider, or top-level lifecycle.", + "description": "Create delegated work and start it with an initial prompt, either in a new chat sharing the current session's workspace, lifecycle, and aggregate diff, or in an independent session. Only supply `worktree` when the user explicitly requests working with or without a new worktree; never combine it with `currentSession`.", "input_schema": { "type": "object", "properties": { @@ -743,7 +743,7 @@ "currentSession", "independent" ], - "description": "Whether this work belongs to the current session or is independently managed. Use `currentSession` for tasks from the current plan or deliverable, including parallel or delegated tasks. Use `independent` only for a separate deliverable that needs its own workspace and top-level lifecycle." + "description": "Whether this work belongs to the current session or is independently managed. Use `currentSession` for tasks from the current plan or deliverable, including parallel or delegated tasks, unless the user explicitly requests a worktree. Use `independent` for a separate deliverable that needs its own workspace, provider, or top-level lifecycle, or for an explicitly requested worktree." }, "prompt": { "type": "string", @@ -753,6 +753,10 @@ "type": "string", "description": "For `independent` work: unique project name, project/workspace URI, absolute folder path, or working directory from an existing session. Required for `independent` and invalid for `currentSession`." }, + "worktree": { + "type": "boolean", + "description": "Override isolation for the new independent session. Set true only when the user explicitly asks to create a worktree, or false only when the user explicitly asks to work without one. Omit to preserve the existing isolation behavior: inherit the creating session's isolation for the same project, otherwise use worktree isolation. Only valid with relationship `independent`; omit for `currentSession`." + }, "title": { "type": "string", "description": "Short title for the new chat or independent session.\n\n{maxLength: 200}" diff --git a/src/vs/platform/agentHost/test/node/e2e/providers/__snapshots__/Agent_Host_E2E___Copilot_prompts_gemini-2_0-flash.prompt.md b/src/vs/platform/agentHost/test/node/e2e/providers/__snapshots__/Agent_Host_E2E___Copilot_prompts_gemini-2_0-flash.prompt.md index 7bec1bc62d0236..40fa1b9b2d1674 100644 --- a/src/vs/platform/agentHost/test/node/e2e/providers/__snapshots__/Agent_Host_E2E___Copilot_prompts_gemini-2_0-flash.prompt.md +++ b/src/vs/platform/agentHost/test/node/e2e/providers/__snapshots__/Agent_Host_E2E___Copilot_prompts_gemini-2_0-flash.prompt.md @@ -765,7 +765,7 @@ }, { "name": "create_session", - "description": "Create delegated work and start it with an initial prompt. Set `relationship` to `currentSession` when the task belongs to the current plan or deliverable; this creates a new chat that shares the current session's workspace, lifecycle, and aggregate diff. Set it to `independent` only for a separate deliverable that needs its own workspace, provider, or top-level lifecycle.", + "description": "Create delegated work and start it with an initial prompt, either in a new chat sharing the current session's workspace, lifecycle, and aggregate diff, or in an independent session. Only supply `worktree` when the user explicitly requests working with or without a new worktree; never combine it with `currentSession`.", "parameters": { "type": "object", "properties": { @@ -775,7 +775,7 @@ "currentSession", "independent" ], - "description": "Whether this work belongs to the current session or is independently managed. Use `currentSession` for tasks from the current plan or deliverable, including parallel or delegated tasks. Use `independent` only for a separate deliverable that needs its own workspace and top-level lifecycle." + "description": "Whether this work belongs to the current session or is independently managed. Use `currentSession` for tasks from the current plan or deliverable, including parallel or delegated tasks, unless the user explicitly requests a worktree. Use `independent` for a separate deliverable that needs its own workspace, provider, or top-level lifecycle, or for an explicitly requested worktree." }, "prompt": { "type": "string", @@ -785,6 +785,10 @@ "type": "string", "description": "For `independent` work: unique project name, project/workspace URI, absolute folder path, or working directory from an existing session. Required for `independent` and invalid for `currentSession`." }, + "worktree": { + "type": "boolean", + "description": "Override isolation for the new independent session. Set true only when the user explicitly asks to create a worktree, or false only when the user explicitly asks to work without one. Omit to preserve the existing isolation behavior: inherit the creating session's isolation for the same project, otherwise use worktree isolation. Only valid with relationship `independent`; omit for `currentSession`." + }, "title": { "type": "string", "description": "Short title for the new chat or independent session.\n\n{maxLength: 200}" diff --git a/src/vs/platform/agentHost/test/node/e2e/providers/__snapshots__/Agent_Host_E2E___Copilot_prompts_gpt-5-codex.prompt.md b/src/vs/platform/agentHost/test/node/e2e/providers/__snapshots__/Agent_Host_E2E___Copilot_prompts_gpt-5-codex.prompt.md index 4589a2396b3f13..ae4606be3e29d6 100644 --- a/src/vs/platform/agentHost/test/node/e2e/providers/__snapshots__/Agent_Host_E2E___Copilot_prompts_gpt-5-codex.prompt.md +++ b/src/vs/platform/agentHost/test/node/e2e/providers/__snapshots__/Agent_Host_E2E___Copilot_prompts_gpt-5-codex.prompt.md @@ -726,7 +726,7 @@ }, { "name": "create_session", - "description": "Create delegated work and start it with an initial prompt. Set `relationship` to `currentSession` when the task belongs to the current plan or deliverable; this creates a new chat that shares the current session's workspace, lifecycle, and aggregate diff. Set it to `independent` only for a separate deliverable that needs its own workspace, provider, or top-level lifecycle.", + "description": "Create delegated work and start it with an initial prompt, either in a new chat sharing the current session's workspace, lifecycle, and aggregate diff, or in an independent session. Only supply `worktree` when the user explicitly requests working with or without a new worktree; never combine it with `currentSession`.", "parameters": { "type": "object", "properties": { @@ -736,7 +736,7 @@ "currentSession", "independent" ], - "description": "Whether this work belongs to the current session or is independently managed. Use `currentSession` for tasks from the current plan or deliverable, including parallel or delegated tasks. Use `independent` only for a separate deliverable that needs its own workspace and top-level lifecycle." + "description": "Whether this work belongs to the current session or is independently managed. Use `currentSession` for tasks from the current plan or deliverable, including parallel or delegated tasks, unless the user explicitly requests a worktree. Use `independent` for a separate deliverable that needs its own workspace, provider, or top-level lifecycle, or for an explicitly requested worktree." }, "prompt": { "type": "string", @@ -746,6 +746,10 @@ "type": "string", "description": "For `independent` work: unique project name, project/workspace URI, absolute folder path, or working directory from an existing session. Required for `independent` and invalid for `currentSession`." }, + "worktree": { + "type": "boolean", + "description": "Override isolation for the new independent session. Set true only when the user explicitly asks to create a worktree, or false only when the user explicitly asks to work without one. Omit to preserve the existing isolation behavior: inherit the creating session's isolation for the same project, otherwise use worktree isolation. Only valid with relationship `independent`; omit for `currentSession`." + }, "title": { "type": "string", "description": "Short title for the new chat or independent session.\n\n{maxLength: 200}" diff --git a/src/vs/platform/agentHost/test/node/e2e/providers/__snapshots__/Agent_Host_E2E___Copilot_prompts_gpt-5-mini.prompt.md b/src/vs/platform/agentHost/test/node/e2e/providers/__snapshots__/Agent_Host_E2E___Copilot_prompts_gpt-5-mini.prompt.md index 063a87373067e8..96ec9caa3908d5 100644 --- a/src/vs/platform/agentHost/test/node/e2e/providers/__snapshots__/Agent_Host_E2E___Copilot_prompts_gpt-5-mini.prompt.md +++ b/src/vs/platform/agentHost/test/node/e2e/providers/__snapshots__/Agent_Host_E2E___Copilot_prompts_gpt-5-mini.prompt.md @@ -765,7 +765,7 @@ }, { "name": "create_session", - "description": "Create delegated work and start it with an initial prompt. Set `relationship` to `currentSession` when the task belongs to the current plan or deliverable; this creates a new chat that shares the current session's workspace, lifecycle, and aggregate diff. Set it to `independent` only for a separate deliverable that needs its own workspace, provider, or top-level lifecycle.", + "description": "Create delegated work and start it with an initial prompt, either in a new chat sharing the current session's workspace, lifecycle, and aggregate diff, or in an independent session. Only supply `worktree` when the user explicitly requests working with or without a new worktree; never combine it with `currentSession`.", "parameters": { "type": "object", "properties": { @@ -775,7 +775,7 @@ "currentSession", "independent" ], - "description": "Whether this work belongs to the current session or is independently managed. Use `currentSession` for tasks from the current plan or deliverable, including parallel or delegated tasks. Use `independent` only for a separate deliverable that needs its own workspace and top-level lifecycle." + "description": "Whether this work belongs to the current session or is independently managed. Use `currentSession` for tasks from the current plan or deliverable, including parallel or delegated tasks, unless the user explicitly requests a worktree. Use `independent` for a separate deliverable that needs its own workspace, provider, or top-level lifecycle, or for an explicitly requested worktree." }, "prompt": { "type": "string", @@ -785,6 +785,10 @@ "type": "string", "description": "For `independent` work: unique project name, project/workspace URI, absolute folder path, or working directory from an existing session. Required for `independent` and invalid for `currentSession`." }, + "worktree": { + "type": "boolean", + "description": "Override isolation for the new independent session. Set true only when the user explicitly asks to create a worktree, or false only when the user explicitly asks to work without one. Omit to preserve the existing isolation behavior: inherit the creating session's isolation for the same project, otherwise use worktree isolation. Only valid with relationship `independent`; omit for `currentSession`." + }, "title": { "type": "string", "description": "Short title for the new chat or independent session.\n\n{maxLength: 200}" diff --git a/src/vs/platform/agentHost/test/node/e2e/providers/__snapshots__/Agent_Host_E2E___Copilot_prompts_gpt-5.prompt.md b/src/vs/platform/agentHost/test/node/e2e/providers/__snapshots__/Agent_Host_E2E___Copilot_prompts_gpt-5.prompt.md index 180e0a9031009c..044dcfda399627 100644 --- a/src/vs/platform/agentHost/test/node/e2e/providers/__snapshots__/Agent_Host_E2E___Copilot_prompts_gpt-5.prompt.md +++ b/src/vs/platform/agentHost/test/node/e2e/providers/__snapshots__/Agent_Host_E2E___Copilot_prompts_gpt-5.prompt.md @@ -765,7 +765,7 @@ }, { "name": "create_session", - "description": "Create delegated work and start it with an initial prompt. Set `relationship` to `currentSession` when the task belongs to the current plan or deliverable; this creates a new chat that shares the current session's workspace, lifecycle, and aggregate diff. Set it to `independent` only for a separate deliverable that needs its own workspace, provider, or top-level lifecycle.", + "description": "Create delegated work and start it with an initial prompt, either in a new chat sharing the current session's workspace, lifecycle, and aggregate diff, or in an independent session. Only supply `worktree` when the user explicitly requests working with or without a new worktree; never combine it with `currentSession`.", "parameters": { "type": "object", "properties": { @@ -775,7 +775,7 @@ "currentSession", "independent" ], - "description": "Whether this work belongs to the current session or is independently managed. Use `currentSession` for tasks from the current plan or deliverable, including parallel or delegated tasks. Use `independent` only for a separate deliverable that needs its own workspace and top-level lifecycle." + "description": "Whether this work belongs to the current session or is independently managed. Use `currentSession` for tasks from the current plan or deliverable, including parallel or delegated tasks, unless the user explicitly requests a worktree. Use `independent` for a separate deliverable that needs its own workspace, provider, or top-level lifecycle, or for an explicitly requested worktree." }, "prompt": { "type": "string", @@ -785,6 +785,10 @@ "type": "string", "description": "For `independent` work: unique project name, project/workspace URI, absolute folder path, or working directory from an existing session. Required for `independent` and invalid for `currentSession`." }, + "worktree": { + "type": "boolean", + "description": "Override isolation for the new independent session. Set true only when the user explicitly asks to create a worktree, or false only when the user explicitly asks to work without one. Omit to preserve the existing isolation behavior: inherit the creating session's isolation for the same project, otherwise use worktree isolation. Only valid with relationship `independent`; omit for `currentSession`." + }, "title": { "type": "string", "description": "Short title for the new chat or independent session.\n\n{maxLength: 200}" diff --git a/src/vs/platform/agentHost/test/node/e2e/providers/__snapshots__/Agent_Host_E2E___Copilot_prompts_gpt-5_1-codex-mini.prompt.md b/src/vs/platform/agentHost/test/node/e2e/providers/__snapshots__/Agent_Host_E2E___Copilot_prompts_gpt-5_1-codex-mini.prompt.md index 3cf34126e2f449..a5d40e15165603 100644 --- a/src/vs/platform/agentHost/test/node/e2e/providers/__snapshots__/Agent_Host_E2E___Copilot_prompts_gpt-5_1-codex-mini.prompt.md +++ b/src/vs/platform/agentHost/test/node/e2e/providers/__snapshots__/Agent_Host_E2E___Copilot_prompts_gpt-5_1-codex-mini.prompt.md @@ -726,7 +726,7 @@ }, { "name": "create_session", - "description": "Create delegated work and start it with an initial prompt. Set `relationship` to `currentSession` when the task belongs to the current plan or deliverable; this creates a new chat that shares the current session's workspace, lifecycle, and aggregate diff. Set it to `independent` only for a separate deliverable that needs its own workspace, provider, or top-level lifecycle.", + "description": "Create delegated work and start it with an initial prompt, either in a new chat sharing the current session's workspace, lifecycle, and aggregate diff, or in an independent session. Only supply `worktree` when the user explicitly requests working with or without a new worktree; never combine it with `currentSession`.", "parameters": { "type": "object", "properties": { @@ -736,7 +736,7 @@ "currentSession", "independent" ], - "description": "Whether this work belongs to the current session or is independently managed. Use `currentSession` for tasks from the current plan or deliverable, including parallel or delegated tasks. Use `independent` only for a separate deliverable that needs its own workspace and top-level lifecycle." + "description": "Whether this work belongs to the current session or is independently managed. Use `currentSession` for tasks from the current plan or deliverable, including parallel or delegated tasks, unless the user explicitly requests a worktree. Use `independent` for a separate deliverable that needs its own workspace, provider, or top-level lifecycle, or for an explicitly requested worktree." }, "prompt": { "type": "string", @@ -746,6 +746,10 @@ "type": "string", "description": "For `independent` work: unique project name, project/workspace URI, absolute folder path, or working directory from an existing session. Required for `independent` and invalid for `currentSession`." }, + "worktree": { + "type": "boolean", + "description": "Override isolation for the new independent session. Set true only when the user explicitly asks to create a worktree, or false only when the user explicitly asks to work without one. Omit to preserve the existing isolation behavior: inherit the creating session's isolation for the same project, otherwise use worktree isolation. Only valid with relationship `independent`; omit for `currentSession`." + }, "title": { "type": "string", "description": "Short title for the new chat or independent session.\n\n{maxLength: 200}" diff --git a/src/vs/platform/agentHost/test/node/e2e/providers/__snapshots__/Agent_Host_E2E___Copilot_prompts_gpt-5_1-codex.prompt.md b/src/vs/platform/agentHost/test/node/e2e/providers/__snapshots__/Agent_Host_E2E___Copilot_prompts_gpt-5_1-codex.prompt.md index b5fb7809f3f48a..e4737cac501e1f 100644 --- a/src/vs/platform/agentHost/test/node/e2e/providers/__snapshots__/Agent_Host_E2E___Copilot_prompts_gpt-5_1-codex.prompt.md +++ b/src/vs/platform/agentHost/test/node/e2e/providers/__snapshots__/Agent_Host_E2E___Copilot_prompts_gpt-5_1-codex.prompt.md @@ -726,7 +726,7 @@ }, { "name": "create_session", - "description": "Create delegated work and start it with an initial prompt. Set `relationship` to `currentSession` when the task belongs to the current plan or deliverable; this creates a new chat that shares the current session's workspace, lifecycle, and aggregate diff. Set it to `independent` only for a separate deliverable that needs its own workspace, provider, or top-level lifecycle.", + "description": "Create delegated work and start it with an initial prompt, either in a new chat sharing the current session's workspace, lifecycle, and aggregate diff, or in an independent session. Only supply `worktree` when the user explicitly requests working with or without a new worktree; never combine it with `currentSession`.", "parameters": { "type": "object", "properties": { @@ -736,7 +736,7 @@ "currentSession", "independent" ], - "description": "Whether this work belongs to the current session or is independently managed. Use `currentSession` for tasks from the current plan or deliverable, including parallel or delegated tasks. Use `independent` only for a separate deliverable that needs its own workspace and top-level lifecycle." + "description": "Whether this work belongs to the current session or is independently managed. Use `currentSession` for tasks from the current plan or deliverable, including parallel or delegated tasks, unless the user explicitly requests a worktree. Use `independent` for a separate deliverable that needs its own workspace, provider, or top-level lifecycle, or for an explicitly requested worktree." }, "prompt": { "type": "string", @@ -746,6 +746,10 @@ "type": "string", "description": "For `independent` work: unique project name, project/workspace URI, absolute folder path, or working directory from an existing session. Required for `independent` and invalid for `currentSession`." }, + "worktree": { + "type": "boolean", + "description": "Override isolation for the new independent session. Set true only when the user explicitly asks to create a worktree, or false only when the user explicitly asks to work without one. Omit to preserve the existing isolation behavior: inherit the creating session's isolation for the same project, otherwise use worktree isolation. Only valid with relationship `independent`; omit for `currentSession`." + }, "title": { "type": "string", "description": "Short title for the new chat or independent session.\n\n{maxLength: 200}" diff --git a/src/vs/platform/agentHost/test/node/e2e/providers/__snapshots__/Agent_Host_E2E___Copilot_prompts_gpt-5_1.prompt.md b/src/vs/platform/agentHost/test/node/e2e/providers/__snapshots__/Agent_Host_E2E___Copilot_prompts_gpt-5_1.prompt.md index 2c3011c9f5e57e..6c2bf215f1499f 100644 --- a/src/vs/platform/agentHost/test/node/e2e/providers/__snapshots__/Agent_Host_E2E___Copilot_prompts_gpt-5_1.prompt.md +++ b/src/vs/platform/agentHost/test/node/e2e/providers/__snapshots__/Agent_Host_E2E___Copilot_prompts_gpt-5_1.prompt.md @@ -765,7 +765,7 @@ }, { "name": "create_session", - "description": "Create delegated work and start it with an initial prompt. Set `relationship` to `currentSession` when the task belongs to the current plan or deliverable; this creates a new chat that shares the current session's workspace, lifecycle, and aggregate diff. Set it to `independent` only for a separate deliverable that needs its own workspace, provider, or top-level lifecycle.", + "description": "Create delegated work and start it with an initial prompt, either in a new chat sharing the current session's workspace, lifecycle, and aggregate diff, or in an independent session. Only supply `worktree` when the user explicitly requests working with or without a new worktree; never combine it with `currentSession`.", "parameters": { "type": "object", "properties": { @@ -775,7 +775,7 @@ "currentSession", "independent" ], - "description": "Whether this work belongs to the current session or is independently managed. Use `currentSession` for tasks from the current plan or deliverable, including parallel or delegated tasks. Use `independent` only for a separate deliverable that needs its own workspace and top-level lifecycle." + "description": "Whether this work belongs to the current session or is independently managed. Use `currentSession` for tasks from the current plan or deliverable, including parallel or delegated tasks, unless the user explicitly requests a worktree. Use `independent` for a separate deliverable that needs its own workspace, provider, or top-level lifecycle, or for an explicitly requested worktree." }, "prompt": { "type": "string", @@ -785,6 +785,10 @@ "type": "string", "description": "For `independent` work: unique project name, project/workspace URI, absolute folder path, or working directory from an existing session. Required for `independent` and invalid for `currentSession`." }, + "worktree": { + "type": "boolean", + "description": "Override isolation for the new independent session. Set true only when the user explicitly asks to create a worktree, or false only when the user explicitly asks to work without one. Omit to preserve the existing isolation behavior: inherit the creating session's isolation for the same project, otherwise use worktree isolation. Only valid with relationship `independent`; omit for `currentSession`." + }, "title": { "type": "string", "description": "Short title for the new chat or independent session.\n\n{maxLength: 200}" diff --git a/src/vs/platform/agentHost/test/node/e2e/providers/__snapshots__/Agent_Host_E2E___Copilot_prompts_gpt-5_6-luna.prompt.md b/src/vs/platform/agentHost/test/node/e2e/providers/__snapshots__/Agent_Host_E2E___Copilot_prompts_gpt-5_6-luna.prompt.md index 75162a227fa56c..dc1cf943c2f064 100644 --- a/src/vs/platform/agentHost/test/node/e2e/providers/__snapshots__/Agent_Host_E2E___Copilot_prompts_gpt-5_6-luna.prompt.md +++ b/src/vs/platform/agentHost/test/node/e2e/providers/__snapshots__/Agent_Host_E2E___Copilot_prompts_gpt-5_6-luna.prompt.md @@ -726,7 +726,7 @@ }, { "name": "create_session", - "description": "Create delegated work and start it with an initial prompt. Set `relationship` to `currentSession` when the task belongs to the current plan or deliverable; this creates a new chat that shares the current session's workspace, lifecycle, and aggregate diff. Set it to `independent` only for a separate deliverable that needs its own workspace, provider, or top-level lifecycle.", + "description": "Create delegated work and start it with an initial prompt, either in a new chat sharing the current session's workspace, lifecycle, and aggregate diff, or in an independent session. Only supply `worktree` when the user explicitly requests working with or without a new worktree; never combine it with `currentSession`.", "parameters": { "type": "object", "properties": { @@ -736,7 +736,7 @@ "currentSession", "independent" ], - "description": "Whether this work belongs to the current session or is independently managed. Use `currentSession` for tasks from the current plan or deliverable, including parallel or delegated tasks. Use `independent` only for a separate deliverable that needs its own workspace and top-level lifecycle." + "description": "Whether this work belongs to the current session or is independently managed. Use `currentSession` for tasks from the current plan or deliverable, including parallel or delegated tasks, unless the user explicitly requests a worktree. Use `independent` for a separate deliverable that needs its own workspace, provider, or top-level lifecycle, or for an explicitly requested worktree." }, "prompt": { "type": "string", @@ -746,6 +746,10 @@ "type": "string", "description": "For `independent` work: unique project name, project/workspace URI, absolute folder path, or working directory from an existing session. Required for `independent` and invalid for `currentSession`." }, + "worktree": { + "type": "boolean", + "description": "Override isolation for the new independent session. Set true only when the user explicitly asks to create a worktree, or false only when the user explicitly asks to work without one. Omit to preserve the existing isolation behavior: inherit the creating session's isolation for the same project, otherwise use worktree isolation. Only valid with relationship `independent`; omit for `currentSession`." + }, "title": { "type": "string", "description": "Short title for the new chat or independent session.\n\n{maxLength: 200}" diff --git a/src/vs/platform/agentHost/test/node/e2e/providers/__snapshots__/Agent_Host_E2E___Copilot_prompts_gpt-5_6-sol.prompt.md b/src/vs/platform/agentHost/test/node/e2e/providers/__snapshots__/Agent_Host_E2E___Copilot_prompts_gpt-5_6-sol.prompt.md index d09c2dd4d3f13c..6b6d836fb329f6 100644 --- a/src/vs/platform/agentHost/test/node/e2e/providers/__snapshots__/Agent_Host_E2E___Copilot_prompts_gpt-5_6-sol.prompt.md +++ b/src/vs/platform/agentHost/test/node/e2e/providers/__snapshots__/Agent_Host_E2E___Copilot_prompts_gpt-5_6-sol.prompt.md @@ -726,7 +726,7 @@ }, { "name": "create_session", - "description": "Create delegated work and start it with an initial prompt. Set `relationship` to `currentSession` when the task belongs to the current plan or deliverable; this creates a new chat that shares the current session's workspace, lifecycle, and aggregate diff. Set it to `independent` only for a separate deliverable that needs its own workspace, provider, or top-level lifecycle.", + "description": "Create delegated work and start it with an initial prompt, either in a new chat sharing the current session's workspace, lifecycle, and aggregate diff, or in an independent session. Only supply `worktree` when the user explicitly requests working with or without a new worktree; never combine it with `currentSession`.", "parameters": { "type": "object", "properties": { @@ -736,7 +736,7 @@ "currentSession", "independent" ], - "description": "Whether this work belongs to the current session or is independently managed. Use `currentSession` for tasks from the current plan or deliverable, including parallel or delegated tasks. Use `independent` only for a separate deliverable that needs its own workspace and top-level lifecycle." + "description": "Whether this work belongs to the current session or is independently managed. Use `currentSession` for tasks from the current plan or deliverable, including parallel or delegated tasks, unless the user explicitly requests a worktree. Use `independent` for a separate deliverable that needs its own workspace, provider, or top-level lifecycle, or for an explicitly requested worktree." }, "prompt": { "type": "string", @@ -746,6 +746,10 @@ "type": "string", "description": "For `independent` work: unique project name, project/workspace URI, absolute folder path, or working directory from an existing session. Required for `independent` and invalid for `currentSession`." }, + "worktree": { + "type": "boolean", + "description": "Override isolation for the new independent session. Set true only when the user explicitly asks to create a worktree, or false only when the user explicitly asks to work without one. Omit to preserve the existing isolation behavior: inherit the creating session's isolation for the same project, otherwise use worktree isolation. Only valid with relationship `independent`; omit for `currentSession`." + }, "title": { "type": "string", "description": "Short title for the new chat or independent session.\n\n{maxLength: 200}" diff --git a/src/vs/platform/agentHost/test/node/e2e/providers/__snapshots__/Agent_Host_E2E___Copilot_prompts_gpt-5_6-terra.prompt.md b/src/vs/platform/agentHost/test/node/e2e/providers/__snapshots__/Agent_Host_E2E___Copilot_prompts_gpt-5_6-terra.prompt.md index 30f1d3b7ad4919..bc2f1cf41f39a1 100644 --- a/src/vs/platform/agentHost/test/node/e2e/providers/__snapshots__/Agent_Host_E2E___Copilot_prompts_gpt-5_6-terra.prompt.md +++ b/src/vs/platform/agentHost/test/node/e2e/providers/__snapshots__/Agent_Host_E2E___Copilot_prompts_gpt-5_6-terra.prompt.md @@ -726,7 +726,7 @@ }, { "name": "create_session", - "description": "Create delegated work and start it with an initial prompt. Set `relationship` to `currentSession` when the task belongs to the current plan or deliverable; this creates a new chat that shares the current session's workspace, lifecycle, and aggregate diff. Set it to `independent` only for a separate deliverable that needs its own workspace, provider, or top-level lifecycle.", + "description": "Create delegated work and start it with an initial prompt, either in a new chat sharing the current session's workspace, lifecycle, and aggregate diff, or in an independent session. Only supply `worktree` when the user explicitly requests working with or without a new worktree; never combine it with `currentSession`.", "parameters": { "type": "object", "properties": { @@ -736,7 +736,7 @@ "currentSession", "independent" ], - "description": "Whether this work belongs to the current session or is independently managed. Use `currentSession` for tasks from the current plan or deliverable, including parallel or delegated tasks. Use `independent` only for a separate deliverable that needs its own workspace and top-level lifecycle." + "description": "Whether this work belongs to the current session or is independently managed. Use `currentSession` for tasks from the current plan or deliverable, including parallel or delegated tasks, unless the user explicitly requests a worktree. Use `independent` for a separate deliverable that needs its own workspace, provider, or top-level lifecycle, or for an explicitly requested worktree." }, "prompt": { "type": "string", @@ -746,6 +746,10 @@ "type": "string", "description": "For `independent` work: unique project name, project/workspace URI, absolute folder path, or working directory from an existing session. Required for `independent` and invalid for `currentSession`." }, + "worktree": { + "type": "boolean", + "description": "Override isolation for the new independent session. Set true only when the user explicitly asks to create a worktree, or false only when the user explicitly asks to work without one. Omit to preserve the existing isolation behavior: inherit the creating session's isolation for the same project, otherwise use worktree isolation. Only valid with relationship `independent`; omit for `currentSession`." + }, "title": { "type": "string", "description": "Short title for the new chat or independent session.\n\n{maxLength: 200}" diff --git a/src/vs/platform/agentHost/test/node/sessionServerTools.test.ts b/src/vs/platform/agentHost/test/node/sessionServerTools.test.ts index 8e295cdd5b476b..20b8d5c1fd0ab9 100644 --- a/src/vs/platform/agentHost/test/node/sessionServerTools.test.ts +++ b/src/vs/platform/agentHost/test/node/sessionServerTools.test.ts @@ -6,6 +6,7 @@ import assert from 'assert'; import { DeferredPromise } from '../../../../base/common/async.js'; import { DisposableStore } from '../../../../base/common/lifecycle.js'; +import { isWindows } from '../../../../base/common/platform.js'; import { URI } from '../../../../base/common/uri.js'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../base/test/common/utils.js'; import { NullLogService } from '../../../log/common/log.js'; @@ -709,8 +710,9 @@ suite('SessionServerTools', () => { for (const scheme of ['file', 'vscode-remote']) { for (const worktree of [false, true, undefined]) { test(`create_session resolves ${scheme} linked worktree roots only with worktree=false (value=${worktree})`, async () => { - const project = URI.from({ scheme, authority: scheme === 'file' ? '' : 'ssh-remote+example', path: '/workspace/repo' }); - const existingWorktree = project.with({ path: '/worktrees/existing' }); + const drive = scheme === 'file' && isWindows ? '/c:' : ''; + const project = URI.from({ scheme, authority: scheme === 'file' ? '' : 'ssh-remote+example', path: `${drive}/workspace/repo` }); + const existingWorktree = project.with({ path: `${drive}/worktrees/existing` }); let created: IAgentCreateSessionConfig | undefined; const accessor = createAccessor({ listSessions: async () => [{