diff --git a/src/vs/platform/agentHost/AGENTS.md b/src/vs/platform/agentHost/AGENTS.md index cd9cca2028289..261d76664041b 100644 --- a/src/vs/platform/agentHost/AGENTS.md +++ b/src/vs/platform/agentHost/AGENTS.md @@ -180,13 +180,13 @@ For every provider, migration and discovery partition the same native catalog: m ### Server-tool creation provenance -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 relationships and is applied before the initial prompt starts. +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, 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 session, chat, and turn as immutable, provider-neutral creation provenance in the initial session summary `_meta` bag, before the session is published or its first prompt starts. The reference supports related-session placement, source identification and session-list presentation; it does not define a hierarchy, grant communication privileges, or trigger lifecycle notifications. `list_sessions` exposes a session's configured project URI separately from its primary and additional working directories. `create_session` accepts those URIs directly and can resolve a unique project display name, preferring the 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. +An independent session inherits the creating session's host-owned 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`, 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 ad36193a7c7a5..5e61f20f3d653 100644 --- a/src/vs/platform/agentHost/node/agentService.ts +++ b/src/vs/platform/agentHost/node/agentService.ts @@ -1169,6 +1169,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 da4619964b8cc..a67869ff19972 100644 --- a/src/vs/platform/agentHost/node/agentServiceFoundation.ts +++ b/src/vs/platform/agentHost/node/agentServiceFoundation.ts @@ -31,6 +31,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 dad63d53cdf6d..ce925bb291cf0 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; @@ -240,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; @@ -515,11 +519,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, @@ -530,6 +538,7 @@ export function getCreateSessionArgs(rawArgs: unknown, sessions: readonly IAgent return { relationship, workspace: resolveWorkspace(getRequiredString(workspace, 'workspace', SessionServerToolName.CreateSession), sessions), + ...(worktree !== undefined ? { worktree } : {}), prompt, title, ...(model !== undefined ? { model } : {}), @@ -817,13 +826,23 @@ 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; 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, workspace)) { + isolation = defaults.isolation; + } const configValues = inheritedProviderConfig === undefined && isolation === undefined ? undefined : { @@ -831,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/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 bba101369a01a..d9d0315ba172a 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 @@ -757,7 +757,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": { @@ -767,7 +767,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", @@ -777,6 +777,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 c1f18bfc804e2..2a91a9f84e4ee 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 @@ -757,7 +757,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": { @@ -767,7 +767,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", @@ -777,6 +777,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 4d651dcdc9449..ccf8b3bb4a492 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 @@ -757,7 +757,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": { @@ -767,7 +767,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", @@ -777,6 +777,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 491aece238ad4..f6652638b1ba5 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 @@ -757,7 +757,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": { @@ -767,7 +767,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", @@ -777,6 +777,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 534cf826ca35b..2fffb3a58547c 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 @@ -757,7 +757,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": { @@ -767,7 +767,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", @@ -777,6 +777,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 61c757b022d7d..3e919758fc350 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 @@ -757,7 +757,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": { @@ -767,7 +767,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", @@ -777,6 +777,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 debdcf4e5a7a4..ac71e6069014b 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 @@ -757,7 +757,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": { @@ -767,7 +767,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", @@ -777,6 +777,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 d44d7be9728c5..cc22f427d8135 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 @@ -757,7 +757,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": { @@ -767,7 +767,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", @@ -777,6 +777,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 d6cf6f1ec9614..9c8891fba4965 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 @@ -757,7 +757,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": { @@ -767,7 +767,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", @@ -777,6 +777,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 04cf3c83df3f3..87f758c44c12d 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 @@ -789,7 +789,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": { @@ -799,7 +799,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", @@ -809,6 +809,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 f6e3e6da07743..0396a155041e1 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 @@ -750,7 +750,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": { @@ -760,7 +760,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", @@ -770,6 +770,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 0e0ada56e4798..bf611713fecef 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 @@ -789,7 +789,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": { @@ -799,7 +799,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", @@ -809,6 +809,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 f27bf98becf99..b795fbc0472de 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 @@ -789,7 +789,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": { @@ -799,7 +799,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", @@ -809,6 +809,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 7a4918e265836..667d1926decbf 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 @@ -750,7 +750,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": { @@ -760,7 +760,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", @@ -770,6 +770,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 6224ae64aa092..0503649539148 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 @@ -750,7 +750,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": { @@ -760,7 +760,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", @@ -770,6 +770,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 70280204084fd..2fde01ab897ab 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 @@ -789,7 +789,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": { @@ -799,7 +799,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", @@ -809,6 +809,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 2fc885130da71..a22ae640a205a 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 @@ -750,7 +750,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": { @@ -760,7 +760,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", @@ -770,6 +770,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 419398dc5fe75..6739faa4fb176 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 @@ -750,7 +750,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": { @@ -760,7 +760,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", @@ -770,6 +770,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 afe5f8189294b..5e3aec59abe77 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 @@ -750,7 +750,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": { @@ -760,7 +760,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", @@ -770,6 +770,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 327df814b4126..20b8d5c1fd0ab 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'; @@ -68,6 +69,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), @@ -115,10 +117,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 +647,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 +707,147 @@ 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 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 () => [{ + ...sessionMeta('source', SessionStatus.Idle, existingWorktree), + 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; }, + }); + + 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('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({ + 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 +1131,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 +1153,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');