feat: add scratchpad chat sessions

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
David Kaya
2026-03-22 10:11:42 +01:00
co-authored by Copilot
parent 2f90a19736
commit 1b41cbd1e0
15 changed files with 300 additions and 73 deletions
@@ -71,6 +71,7 @@ public sealed class RunTurnCommandDto : SidecarCommandEnvelope
{
public string SessionId { get; init; } = string.Empty;
public string ProjectPath { get; init; } = string.Empty;
public string WorkspaceKind { get; init; } = "project";
public PatternDefinitionDto Pattern { get; init; } = new();
public IReadOnlyList<ChatMessageDto> Messages { get; init; } = [];
}
@@ -7,12 +7,22 @@ internal static class AgentInstructionComposer
public static string Compose(
PatternDefinitionDto pattern,
PatternAgentDefinitionDto agent,
int agentIndex)
int agentIndex,
string workspaceKind = "project")
{
string baseInstructions = agent.Instructions.Trim();
string workspaceGuidance = string.Equals(workspaceKind, "scratchpad", StringComparison.OrdinalIgnoreCase)
? """
You are operating in scratchpad mode.
Treat this session as pure ad-hoc Q&A rather than repository automation.
Do not inspect, modify, create, or delete files, and do not behave as though you are working inside a user project.
Answer conversationally and focus on the user's question directly.
"""
: string.Empty;
if (!string.Equals(pattern.Mode, "handoff", StringComparison.OrdinalIgnoreCase))
{
return baseInstructions;
return JoinInstructionBlocks(baseInstructions, workspaceGuidance);
}
string runtimeGuidance = agentIndex == 0
@@ -28,8 +38,13 @@ internal static class AgentInstructionComposer
Do not push the actual work back to triage unless you are blocked or the request is clearly outside your specialty.
""";
return string.IsNullOrWhiteSpace(baseInstructions)
? runtimeGuidance
: $"{baseInstructions}\n\n{runtimeGuidance}";
return JoinInstructionBlocks(baseInstructions, workspaceGuidance, runtimeGuidance);
}
private static string JoinInstructionBlocks(params string[] blocks)
{
return string.Join(
"\n\n",
blocks.Where(block => !string.IsNullOrWhiteSpace(block)).Select(block => block.Trim()));
}
}
@@ -40,7 +40,7 @@ public sealed class CopilotWorkflowRunner : ITurnWorkflowRunner
throw new InvalidOperationException(validationError.Message);
}
await using AgentBundle bundle = await AgentBundle.CreateAsync(command.Pattern, command.ProjectPath, cancellationToken);
await using AgentBundle bundle = await AgentBundle.CreateAsync(command, cancellationToken);
Workflow workflow = bundle.BuildWorkflow(command.Pattern);
List<ChatMessage> inputMessages = command.Messages.Select(ToChatMessage).ToList();
@@ -436,15 +436,15 @@ public sealed class CopilotWorkflowRunner : ITurnWorkflowRunner
public IReadOnlyList<AIAgent> Agents { get; }
public static async Task<AgentBundle> CreateAsync(
PatternDefinitionDto pattern,
string projectPath,
RunTurnCommandDto command,
CancellationToken cancellationToken)
{
List<IAsyncDisposable> disposables = [];
List<AIAgent> agents = [];
CopilotClientOptions clientOptions = CopilotCliPathResolver.CreateClientOptions();
bool isScratchpad = string.Equals(command.WorkspaceKind, "scratchpad", StringComparison.OrdinalIgnoreCase);
foreach ((PatternAgentDefinitionDto definition, int agentIndex) in pattern.Agents.Select((definition, index) => (definition, index)))
foreach ((PatternAgentDefinitionDto definition, int agentIndex) in command.Pattern.Agents.Select((definition, index) => (definition, index)))
{
CopilotClient client = new(clientOptions);
await client.StartAsync(cancellationToken).ConfigureAwait(false);
@@ -455,13 +455,18 @@ public sealed class CopilotWorkflowRunner : ITurnWorkflowRunner
ReasoningEffort = definition.ReasoningEffort,
SystemMessage = new SystemMessageConfig
{
Content = AgentInstructionComposer.Compose(pattern, definition, agentIndex),
Content = AgentInstructionComposer.Compose(command.Pattern, definition, agentIndex, command.WorkspaceKind),
},
WorkingDirectory = projectPath,
WorkingDirectory = command.ProjectPath,
OnPermissionRequest = ApprovePermissionAsync,
Streaming = true,
};
if (isScratchpad)
{
sessionConfig.AvailableTools = [];
}
GitHubCopilotAgent agent = new(
client,
sessionConfig,