From 66b0bfb9cc2d26fd8b431cc9ec9cf7fdcbfda299 Mon Sep 17 00:00:00 2001 From: Aaron Powell Date: Thu, 22 Jan 2026 15:51:40 +1100 Subject: [PATCH] Adding copilot-sdk stuff --- README.md | 1 + collections/copilot-sdk.collection.yml | 17 + collections/copilot-sdk.md | 17 + docs/README.collections.md | 1 + docs/README.instructions.md | 4 + .../copilot-sdk-csharp.instructions.md | 550 ++++++++++++ instructions/copilot-sdk-go.instructions.md | 626 ++++++++++++++ .../copilot-sdk-nodejs.instructions.md | 717 ++++++++++++++++ .../copilot-sdk-python.instructions.md | 806 ++++++++++++++++++ 9 files changed, 2739 insertions(+) create mode 100644 collections/copilot-sdk.collection.yml create mode 100644 collections/copilot-sdk.md create mode 100644 instructions/copilot-sdk-csharp.instructions.md create mode 100644 instructions/copilot-sdk-go.instructions.md create mode 100644 instructions/copilot-sdk-nodejs.instructions.md create mode 100644 instructions/copilot-sdk-python.instructions.md diff --git a/README.md b/README.md index 461734f5..935f833a 100644 --- a/README.md +++ b/README.md @@ -21,6 +21,7 @@ Discover our curated collections of prompts, instructions, and agents organized | Name | Description | Items | Tags | | ---- | ----------- | ----- | ---- | | [Awesome Copilot](collections/awesome-copilot.md) | Meta prompts that help you discover and generate curated GitHub Copilot chat modes, collections, instructions, prompts, and agents. | 6 items | github-copilot, discovery, meta, prompt-engineering, agents | +| [Copilot SDK](collections/copilot-sdk.md) | Build applications with the GitHub Copilot SDK across multiple programming languages. Includes comprehensive instructions for C#, Go, Node.js/TypeScript, and Python to help you create AI-powered applications. | 4 items | copilot-sdk, sdk, csharp, go, nodejs, typescript, python, ai, github-copilot | | [Partners](collections/partners.md) | Custom agents that have been created by GitHub partners | 20 items | devops, security, database, cloud, infrastructure, observability, feature-flags, cicd, migration, performance | diff --git a/collections/copilot-sdk.collection.yml b/collections/copilot-sdk.collection.yml new file mode 100644 index 00000000..1f1b534a --- /dev/null +++ b/collections/copilot-sdk.collection.yml @@ -0,0 +1,17 @@ +id: copilot-sdk +name: Copilot SDK +description: Build applications with the GitHub Copilot SDK across multiple programming languages. Includes comprehensive instructions for C#, Go, Node.js/TypeScript, and Python to help you create AI-powered applications. +tags: [copilot-sdk, sdk, csharp, go, nodejs, typescript, python, ai, github-copilot] +items: + - path: instructions/copilot-sdk-csharp.instructions.md + kind: instruction + - path: instructions/copilot-sdk-go.instructions.md + kind: instruction + - path: instructions/copilot-sdk-nodejs.instructions.md + kind: instruction + - path: instructions/copilot-sdk-python.instructions.md + kind: instruction +display: + ordering: manual + show_badge: true + featured: true diff --git a/collections/copilot-sdk.md b/collections/copilot-sdk.md new file mode 100644 index 00000000..c5fa1fac --- /dev/null +++ b/collections/copilot-sdk.md @@ -0,0 +1,17 @@ +# Copilot SDK + +Build applications with the GitHub Copilot SDK across multiple programming languages. Includes comprehensive instructions for C#, Go, Node.js/TypeScript, and Python to help you create AI-powered applications. + +**Tags:** copilot-sdk, sdk, csharp, go, nodejs, typescript, python, ai, github-copilot + +## Items in this Collection + +| Title | Type | Description | +| ----- | ---- | ----------- | +| [GitHub Copilot SDK C# Instructions](../instructions/copilot-sdk-csharp.instructions.md)
[![Install in VS Code](https://img.shields.io/badge/VS_Code-Install-0098FF?style=flat-square&logo=visualstudiocode&logoColor=white)](https://aka.ms/awesome-copilot/install/instructions?url=vscode%3Achat-instructions%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Finstructions%2Fcopilot-sdk-csharp.instructions.md)
[![Install in VS Code Insiders](https://img.shields.io/badge/VS_Code_Insiders-Install-24bfa5?style=flat-square&logo=visualstudiocode&logoColor=white)](https://aka.ms/awesome-copilot/install/instructions?url=vscode-insiders%3Achat-instructions%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Finstructions%2Fcopilot-sdk-csharp.instructions.md) | Instruction | This file provides guidance on building C# applications using GitHub Copilot SDK. | +| [GitHub Copilot SDK Go Instructions](../instructions/copilot-sdk-go.instructions.md)
[![Install in VS Code](https://img.shields.io/badge/VS_Code-Install-0098FF?style=flat-square&logo=visualstudiocode&logoColor=white)](https://aka.ms/awesome-copilot/install/instructions?url=vscode%3Achat-instructions%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Finstructions%2Fcopilot-sdk-go.instructions.md)
[![Install in VS Code Insiders](https://img.shields.io/badge/VS_Code_Insiders-Install-24bfa5?style=flat-square&logo=visualstudiocode&logoColor=white)](https://aka.ms/awesome-copilot/install/instructions?url=vscode-insiders%3Achat-instructions%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Finstructions%2Fcopilot-sdk-go.instructions.md) | Instruction | This file provides guidance on building Go applications using GitHub Copilot SDK. | +| [GitHub Copilot SDK Node.js Instructions](../instructions/copilot-sdk-nodejs.instructions.md)
[![Install in VS Code](https://img.shields.io/badge/VS_Code-Install-0098FF?style=flat-square&logo=visualstudiocode&logoColor=white)](https://aka.ms/awesome-copilot/install/instructions?url=vscode%3Achat-instructions%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Finstructions%2Fcopilot-sdk-nodejs.instructions.md)
[![Install in VS Code Insiders](https://img.shields.io/badge/VS_Code_Insiders-Install-24bfa5?style=flat-square&logo=visualstudiocode&logoColor=white)](https://aka.ms/awesome-copilot/install/instructions?url=vscode-insiders%3Achat-instructions%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Finstructions%2Fcopilot-sdk-nodejs.instructions.md) | Instruction | This file provides guidance on building Node.js/TypeScript applications using GitHub Copilot SDK. | +| [GitHub Copilot SDK Python Instructions](../instructions/copilot-sdk-python.instructions.md)
[![Install in VS Code](https://img.shields.io/badge/VS_Code-Install-0098FF?style=flat-square&logo=visualstudiocode&logoColor=white)](https://aka.ms/awesome-copilot/install/instructions?url=vscode%3Achat-instructions%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Finstructions%2Fcopilot-sdk-python.instructions.md)
[![Install in VS Code Insiders](https://img.shields.io/badge/VS_Code_Insiders-Install-24bfa5?style=flat-square&logo=visualstudiocode&logoColor=white)](https://aka.ms/awesome-copilot/install/instructions?url=vscode-insiders%3Achat-instructions%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Finstructions%2Fcopilot-sdk-python.instructions.md) | Instruction | This file provides guidance on building Python applications using GitHub Copilot SDK. | + +--- +*This collection includes 4 curated items for **Copilot SDK**.* \ No newline at end of file diff --git a/docs/README.collections.md b/docs/README.collections.md index 02f29460..9785b4ab 100644 --- a/docs/README.collections.md +++ b/docs/README.collections.md @@ -17,6 +17,7 @@ Curated collections of related prompts, instructions, and agents organized aroun | Name | Description | Items | Tags | | ---- | ----------- | ----- | ---- | | [⭐ Awesome Copilot](../collections/awesome-copilot.md) | Meta prompts that help you discover and generate curated GitHub Copilot chat modes, collections, instructions, prompts, and agents. | 6 items | github-copilot, discovery, meta, prompt-engineering, agents | +| [⭐ Copilot SDK](../collections/copilot-sdk.md) | Build applications with the GitHub Copilot SDK across multiple programming languages. Includes comprehensive instructions for C#, Go, Node.js/TypeScript, and Python to help you create AI-powered applications. | 4 items | copilot-sdk, sdk, csharp, go, nodejs, typescript, python, ai, github-copilot | | [⭐ Partners](../collections/partners.md) | Custom agents that have been created by GitHub partners | 20 items | devops, security, database, cloud, infrastructure, observability, feature-flags, cicd, migration, performance | | [Azure & Cloud Development](../collections/azure-cloud-development.md) | Comprehensive Azure cloud development tools including Infrastructure as Code, serverless functions, architecture patterns, and cost optimization for building scalable cloud applications. | 18 items | azure, cloud, infrastructure, bicep, terraform, serverless, architecture, devops | | [C# .NET Development](../collections/csharp-dotnet-development.md) | Essential prompts, instructions, and chat modes for C# and .NET development including testing, documentation, and best practices. | 8 items | csharp, dotnet, aspnet, testing | diff --git a/docs/README.instructions.md b/docs/README.instructions.md index 4574b3d5..ab17703c 100644 --- a/docs/README.instructions.md +++ b/docs/README.instructions.md @@ -82,6 +82,10 @@ Team and project-specific instructions to enhance GitHub Copilot's behavior for | [Get Tooling for Power Apps Component Framework](../instructions/pcf-tooling.instructions.md)
[![Install in VS Code](https://img.shields.io/badge/VS_Code-Install-0098FF?style=flat-square&logo=visualstudiocode&logoColor=white)](https://aka.ms/awesome-copilot/install/instructions?url=vscode%3Achat-instructions%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Finstructions%2Fpcf-tooling.instructions.md)
[![Install in VS Code Insiders](https://img.shields.io/badge/VS_Code_Insiders-Install-24bfa5?style=flat-square&logo=visualstudiocode&logoColor=white)](https://aka.ms/awesome-copilot/install/instructions?url=vscode-insiders%3Achat-instructions%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Finstructions%2Fpcf-tooling.instructions.md) | Get Microsoft Power Platform CLI tooling for Power Apps Component Framework | | [Gilfoyle Code Review Instructions](../instructions/gilfoyle-code-review.instructions.md)
[![Install in VS Code](https://img.shields.io/badge/VS_Code-Install-0098FF?style=flat-square&logo=visualstudiocode&logoColor=white)](https://aka.ms/awesome-copilot/install/instructions?url=vscode%3Achat-instructions%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Finstructions%2Fgilfoyle-code-review.instructions.md)
[![Install in VS Code Insiders](https://img.shields.io/badge/VS_Code_Insiders-Install-24bfa5?style=flat-square&logo=visualstudiocode&logoColor=white)](https://aka.ms/awesome-copilot/install/instructions?url=vscode-insiders%3Achat-instructions%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Finstructions%2Fgilfoyle-code-review.instructions.md) | Gilfoyle-style code review instructions that channel the sardonic technical supremacy of Silicon Valley's most arrogant systems architect. | | [GitHub Actions CI/CD Best Practices](../instructions/github-actions-ci-cd-best-practices.instructions.md)
[![Install in VS Code](https://img.shields.io/badge/VS_Code-Install-0098FF?style=flat-square&logo=visualstudiocode&logoColor=white)](https://aka.ms/awesome-copilot/install/instructions?url=vscode%3Achat-instructions%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Finstructions%2Fgithub-actions-ci-cd-best-practices.instructions.md)
[![Install in VS Code Insiders](https://img.shields.io/badge/VS_Code_Insiders-Install-24bfa5?style=flat-square&logo=visualstudiocode&logoColor=white)](https://aka.ms/awesome-copilot/install/instructions?url=vscode-insiders%3Achat-instructions%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Finstructions%2Fgithub-actions-ci-cd-best-practices.instructions.md) | Comprehensive guide for building robust, secure, and efficient CI/CD pipelines using GitHub Actions. Covers workflow structure, jobs, steps, environment variables, secret management, caching, matrix strategies, testing, and deployment strategies. | +| [GitHub Copilot SDK C# Instructions](../instructions/copilot-sdk-csharp.instructions.md)
[![Install in VS Code](https://img.shields.io/badge/VS_Code-Install-0098FF?style=flat-square&logo=visualstudiocode&logoColor=white)](https://aka.ms/awesome-copilot/install/instructions?url=vscode%3Achat-instructions%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Finstructions%2Fcopilot-sdk-csharp.instructions.md)
[![Install in VS Code Insiders](https://img.shields.io/badge/VS_Code_Insiders-Install-24bfa5?style=flat-square&logo=visualstudiocode&logoColor=white)](https://aka.ms/awesome-copilot/install/instructions?url=vscode-insiders%3Achat-instructions%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Finstructions%2Fcopilot-sdk-csharp.instructions.md) | This file provides guidance on building C# applications using GitHub Copilot SDK. | +| [GitHub Copilot SDK Go Instructions](../instructions/copilot-sdk-go.instructions.md)
[![Install in VS Code](https://img.shields.io/badge/VS_Code-Install-0098FF?style=flat-square&logo=visualstudiocode&logoColor=white)](https://aka.ms/awesome-copilot/install/instructions?url=vscode%3Achat-instructions%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Finstructions%2Fcopilot-sdk-go.instructions.md)
[![Install in VS Code Insiders](https://img.shields.io/badge/VS_Code_Insiders-Install-24bfa5?style=flat-square&logo=visualstudiocode&logoColor=white)](https://aka.ms/awesome-copilot/install/instructions?url=vscode-insiders%3Achat-instructions%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Finstructions%2Fcopilot-sdk-go.instructions.md) | This file provides guidance on building Go applications using GitHub Copilot SDK. | +| [GitHub Copilot SDK Node.js Instructions](../instructions/copilot-sdk-nodejs.instructions.md)
[![Install in VS Code](https://img.shields.io/badge/VS_Code-Install-0098FF?style=flat-square&logo=visualstudiocode&logoColor=white)](https://aka.ms/awesome-copilot/install/instructions?url=vscode%3Achat-instructions%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Finstructions%2Fcopilot-sdk-nodejs.instructions.md)
[![Install in VS Code Insiders](https://img.shields.io/badge/VS_Code_Insiders-Install-24bfa5?style=flat-square&logo=visualstudiocode&logoColor=white)](https://aka.ms/awesome-copilot/install/instructions?url=vscode-insiders%3Achat-instructions%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Finstructions%2Fcopilot-sdk-nodejs.instructions.md) | This file provides guidance on building Node.js/TypeScript applications using GitHub Copilot SDK. | +| [GitHub Copilot SDK Python Instructions](../instructions/copilot-sdk-python.instructions.md)
[![Install in VS Code](https://img.shields.io/badge/VS_Code-Install-0098FF?style=flat-square&logo=visualstudiocode&logoColor=white)](https://aka.ms/awesome-copilot/install/instructions?url=vscode%3Achat-instructions%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Finstructions%2Fcopilot-sdk-python.instructions.md)
[![Install in VS Code Insiders](https://img.shields.io/badge/VS_Code_Insiders-Install-24bfa5?style=flat-square&logo=visualstudiocode&logoColor=white)](https://aka.ms/awesome-copilot/install/instructions?url=vscode-insiders%3Achat-instructions%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Finstructions%2Fcopilot-sdk-python.instructions.md) | This file provides guidance on building Python applications using GitHub Copilot SDK. | | [Go Development Instructions](../instructions/go.instructions.md)
[![Install in VS Code](https://img.shields.io/badge/VS_Code-Install-0098FF?style=flat-square&logo=visualstudiocode&logoColor=white)](https://aka.ms/awesome-copilot/install/instructions?url=vscode%3Achat-instructions%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Finstructions%2Fgo.instructions.md)
[![Install in VS Code Insiders](https://img.shields.io/badge/VS_Code_Insiders-Install-24bfa5?style=flat-square&logo=visualstudiocode&logoColor=white)](https://aka.ms/awesome-copilot/install/instructions?url=vscode-insiders%3Achat-instructions%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Finstructions%2Fgo.instructions.md) | Instructions for writing Go code following idiomatic Go practices and community standards | | [Go MCP Server Development Guidelines](../instructions/go-mcp-server.instructions.md)
[![Install in VS Code](https://img.shields.io/badge/VS_Code-Install-0098FF?style=flat-square&logo=visualstudiocode&logoColor=white)](https://aka.ms/awesome-copilot/install/instructions?url=vscode%3Achat-instructions%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Finstructions%2Fgo-mcp-server.instructions.md)
[![Install in VS Code Insiders](https://img.shields.io/badge/VS_Code_Insiders-Install-24bfa5?style=flat-square&logo=visualstudiocode&logoColor=white)](https://aka.ms/awesome-copilot/install/instructions?url=vscode-insiders%3Achat-instructions%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Finstructions%2Fgo-mcp-server.instructions.md) | Best practices and patterns for building Model Context Protocol (MCP) servers in Go using the official github.com/modelcontextprotocol/go-sdk package. | | [Guidance for Localization](../instructions/localization.instructions.md)
[![Install in VS Code](https://img.shields.io/badge/VS_Code-Install-0098FF?style=flat-square&logo=visualstudiocode&logoColor=white)](https://aka.ms/awesome-copilot/install/instructions?url=vscode%3Achat-instructions%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Finstructions%2Flocalization.instructions.md)
[![Install in VS Code Insiders](https://img.shields.io/badge/VS_Code_Insiders-Install-24bfa5?style=flat-square&logo=visualstudiocode&logoColor=white)](https://aka.ms/awesome-copilot/install/instructions?url=vscode-insiders%3Achat-instructions%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Finstructions%2Flocalization.instructions.md) | Guidelines for localizing markdown documents | diff --git a/instructions/copilot-sdk-csharp.instructions.md b/instructions/copilot-sdk-csharp.instructions.md new file mode 100644 index 00000000..87d04b82 --- /dev/null +++ b/instructions/copilot-sdk-csharp.instructions.md @@ -0,0 +1,550 @@ +--- +applyTo: '**.cs, **.csproj' +description: 'This file provides guidance on building C# applications using GitHub Copilot SDK.' +name: 'GitHub Copilot SDK C# Instructions' +--- + +## Core Principles + +- The SDK is in technical preview and may have breaking changes +- Requires .NET 10.0 or later +- Requires GitHub Copilot CLI installed and in PATH +- Uses async/await patterns throughout +- Implements IAsyncDisposable for resource cleanup + +## Installation + +Always install via NuGet: +```bash +dotnet add package GitHub.Copilot.SDK +``` + +## Client Initialization + +### Basic Client Setup + +```csharp +await using var client = new CopilotClient(); +await client.StartAsync(); +``` + +### Client Configuration Options + +When creating a CopilotClient, use `CopilotClientOptions`: + +- `CliPath` - Path to CLI executable (default: "copilot" from PATH) +- `CliArgs` - Extra arguments prepended before SDK-managed flags +- `CliUrl` - URL of existing CLI server (e.g., "localhost:8080"). When provided, client won't spawn a process +- `Port` - Server port (default: 0 for random) +- `UseStdio` - Use stdio transport instead of TCP (default: true) +- `LogLevel` - Log level (default: "info") +- `AutoStart` - Auto-start server (default: true) +- `AutoRestart` - Auto-restart on crash (default: true) +- `Cwd` - Working directory for the CLI process +- `Environment` - Environment variables for the CLI process +- `Logger` - ILogger instance for SDK logging + +### Manual Server Control + +For explicit control: +```csharp +var client = new CopilotClient(new CopilotClientOptions { AutoStart = false }); +await client.StartAsync(); +// Use client... +await client.StopAsync(); +``` + +Use `ForceStopAsync()` when `StopAsync()` takes too long. + +## Session Management + +### Creating Sessions + +Use `SessionConfig` for configuration: + +```csharp +await using var session = await client.CreateSessionAsync(new SessionConfig +{ + Model = "gpt-5", + Streaming = true, + Tools = [...], + SystemMessage = new SystemMessageConfig { ... }, + AvailableTools = ["tool1", "tool2"], + ExcludedTools = ["tool3"], + Provider = new ProviderConfig { ... } +}); +``` + +### Session Config Options + +- `SessionId` - Custom session ID +- `Model` - Model name ("gpt-5", "claude-sonnet-4.5", etc.) +- `Tools` - Custom tools exposed to the CLI +- `SystemMessage` - System message customization +- `AvailableTools` - Allowlist of tool names +- `ExcludedTools` - Blocklist of tool names +- `Provider` - Custom API provider configuration (BYOK) +- `Streaming` - Enable streaming response chunks (default: false) + +### Resuming Sessions + +```csharp +var session = await client.ResumeSessionAsync(sessionId, new ResumeSessionConfig { ... }); +``` + +### Session Operations + +- `session.SessionId` - Get session identifier +- `session.SendAsync(new MessageOptions { Prompt = "...", Attachments = [...] })` - Send message +- `session.AbortAsync()` - Abort current processing +- `session.GetMessagesAsync()` - Get all events/messages +- `await session.DisposeAsync()` - Clean up resources + +## Event Handling + +### Event Subscription Pattern + +ALWAYS use TaskCompletionSource for waiting on session events: + +```csharp +var done = new TaskCompletionSource(); + +session.On(evt => +{ + if (evt is AssistantMessageEvent msg) + { + Console.WriteLine(msg.Data.Content); + } + else if (evt is SessionIdleEvent) + { + done.SetResult(); + } +}); + +await session.SendAsync(new MessageOptions { Prompt = "..." }); +await done.Task; +``` + +### Unsubscribing from Events + +The `On()` method returns an IDisposable: + +```csharp +var subscription = session.On(evt => { /* handler */ }); +// Later... +subscription.Dispose(); +``` + +### Event Types + +Use pattern matching or switch expressions for event handling: + +```csharp +session.On(evt => +{ + switch (evt) + { + case UserMessageEvent userMsg: + // Handle user message + break; + case AssistantMessageEvent assistantMsg: + Console.WriteLine(assistantMsg.Data.Content); + break; + case ToolExecutionStartEvent toolStart: + // Tool execution started + break; + case ToolExecutionCompleteEvent toolComplete: + // Tool execution completed + break; + case SessionStartEvent start: + // Session started + break; + case SessionIdleEvent idle: + // Session is idle (processing complete) + break; + case SessionErrorEvent error: + Console.WriteLine($"Error: {error.Data.Message}"); + break; + } +}); +``` + +## Streaming Responses + +### Enabling Streaming + +Set `Streaming = true` in SessionConfig: + +```csharp +var session = await client.CreateSessionAsync(new SessionConfig +{ + Model = "gpt-5", + Streaming = true +}); +``` + +### Handling Streaming Events + +Handle both delta events (incremental) and final events: + +```csharp +var done = new TaskCompletionSource(); + +session.On(evt => +{ + switch (evt) + { + case AssistantMessageDeltaEvent delta: + // Incremental text chunk + Console.Write(delta.Data.DeltaContent); + break; + case AssistantReasoningDeltaEvent reasoningDelta: + // Incremental reasoning chunk (model-dependent) + Console.Write(reasoningDelta.Data.DeltaContent); + break; + case AssistantMessageEvent msg: + // Final complete message + Console.WriteLine("\n--- Final ---"); + Console.WriteLine(msg.Data.Content); + break; + case AssistantReasoningEvent reasoning: + // Final reasoning content + Console.WriteLine("--- Reasoning ---"); + Console.WriteLine(reasoning.Data.Content); + break; + case SessionIdleEvent: + done.SetResult(); + break; + } +}); + +await session.SendAsync(new MessageOptions { Prompt = "Tell me a story" }); +await done.Task; +``` + +Note: Final events (`AssistantMessageEvent`, `AssistantReasoningEvent`) are ALWAYS sent regardless of streaming setting. + +## Custom Tools + +### Defining Tools with AIFunctionFactory + +Use `Microsoft.Extensions.AI.AIFunctionFactory.Create` for type-safe tools: + +```csharp +using Microsoft.Extensions.AI; +using System.ComponentModel; + +var session = await client.CreateSessionAsync(new SessionConfig +{ + Model = "gpt-5", + Tools = [ + AIFunctionFactory.Create( + async ([Description("Issue ID")] string id) => { + var issue = await FetchIssueAsync(id); + return issue; + }, + "lookup_issue", + "Fetch issue details from tracker"), + ] +}); +``` + +### Tool Return Types + +- Return any JSON-serializable value (automatically wrapped) +- Or return `ToolResultAIContent` wrapping `ToolResultObject` for full control over metadata + +### Tool Execution Flow + +When Copilot invokes a tool, the client automatically: +1. Runs your handler function +2. Serializes the return value +3. Responds to the CLI + +## System Message Customization + +### Append Mode (Default - Preserves Guardrails) + +```csharp +var session = await client.CreateSessionAsync(new SessionConfig +{ + Model = "gpt-5", + SystemMessage = new SystemMessageConfig + { + Mode = SystemMessageMode.Append, + Content = @" + +- Always check for security vulnerabilities +- Suggest performance improvements when applicable + +" + } +}); +``` + +### Replace Mode (Full Control - Removes Guardrails) + +```csharp +var session = await client.CreateSessionAsync(new SessionConfig +{ + Model = "gpt-5", + SystemMessage = new SystemMessageConfig + { + Mode = SystemMessageMode.Replace, + Content = "You are a helpful assistant." + } +}); +``` + +## File Attachments + +Attach files to messages using `UserMessageDataAttachmentsItem`: + +```csharp +await session.SendAsync(new MessageOptions +{ + Prompt = "Analyze this file", + Attachments = new List + { + new UserMessageDataAttachmentsItem + { + Type = UserMessageDataAttachmentsItemType.File, + Path = "/path/to/file.cs", + DisplayName = "My File" + } + } +}); +``` + +## Message Delivery Modes + +Use the `Mode` property in `MessageOptions`: + +- `"enqueue"` - Queue message for processing +- `"immediate"` - Process message immediately + +```csharp +await session.SendAsync(new MessageOptions +{ + Prompt = "...", + Mode = "enqueue" +}); +``` + +## Multiple Sessions + +Sessions are independent and can run concurrently: + +```csharp +var session1 = await client.CreateSessionAsync(new SessionConfig { Model = "gpt-5" }); +var session2 = await client.CreateSessionAsync(new SessionConfig { Model = "claude-sonnet-4.5" }); + +await session1.SendAsync(new MessageOptions { Prompt = "Hello from session 1" }); +await session2.SendAsync(new MessageOptions { Prompt = "Hello from session 2" }); +``` + +## Bring Your Own Key (BYOK) + +Use custom API providers via `ProviderConfig`: + +```csharp +var session = await client.CreateSessionAsync(new SessionConfig +{ + Provider = new ProviderConfig + { + Type = "openai", + BaseUrl = "https://api.openai.com/v1", + ApiKey = "your-api-key" + } +}); +``` + +## Session Lifecycle Management + +### Listing Sessions + +```csharp +var sessions = await client.ListSessionsAsync(); +foreach (var metadata in sessions) +{ + Console.WriteLine($"Session: {metadata.SessionId}"); +} +``` + +### Deleting Sessions + +```csharp +await client.DeleteSessionAsync(sessionId); +``` + +### Checking Connection State + +```csharp +var state = client.State; +``` + +## Error Handling + +### Standard Exception Handling + +```csharp +try +{ + var session = await client.CreateSessionAsync(); + await session.SendAsync(new MessageOptions { Prompt = "Hello" }); +} +catch (StreamJsonRpc.RemoteInvocationException ex) +{ + Console.Error.WriteLine($"JSON-RPC Error: {ex.Message}"); +} +catch (Exception ex) +{ + Console.Error.WriteLine($"Error: {ex.Message}"); +} +``` + +### Session Error Events + +Monitor `SessionErrorEvent` for runtime errors: + +```csharp +session.On(evt => +{ + if (evt is SessionErrorEvent error) + { + Console.Error.WriteLine($"Session Error: {error.Data.Message}"); + } +}); +``` + +## Connectivity Testing + +Use PingAsync to verify server connectivity: + +```csharp +var response = await client.PingAsync("test message"); +``` + +## Resource Cleanup + +### Automatic Cleanup with Using + +ALWAYS use `await using` for automatic disposal: + +```csharp +await using var client = new CopilotClient(); +await using var session = await client.CreateSessionAsync(); +// Resources automatically cleaned up +``` + +### Manual Cleanup + +If not using `await using`: + +```csharp +var client = new CopilotClient(); +try +{ + await client.StartAsync(); + // Use client... +} +finally +{ + await client.StopAsync(); +} +``` + +## Best Practices + +1. **Always use `await using`** for CopilotClient and CopilotSession +2. **Use TaskCompletionSource** to wait for SessionIdleEvent +3. **Handle SessionErrorEvent** for robust error handling +4. **Use pattern matching** (switch expressions) for event handling +5. **Enable streaming** for better UX in interactive scenarios +6. **Use AIFunctionFactory** for type-safe tool definitions +7. **Dispose event subscriptions** when no longer needed +8. **Use SystemMessageMode.Append** to preserve safety guardrails +9. **Provide descriptive tool names and descriptions** for better model understanding +10. **Handle both delta and final events** when streaming is enabled + +## Common Patterns + +### Simple Query-Response + +```csharp +await using var client = new CopilotClient(); +await client.StartAsync(); + +await using var session = await client.CreateSessionAsync(new SessionConfig +{ + Model = "gpt-5" +}); + +var done = new TaskCompletionSource(); + +session.On(evt => +{ + if (evt is AssistantMessageEvent msg) + { + Console.WriteLine(msg.Data.Content); + } + else if (evt is SessionIdleEvent) + { + done.SetResult(); + } +}); + +await session.SendAsync(new MessageOptions { Prompt = "What is 2+2?" }); +await done.Task; +``` + +### Multi-Turn Conversation + +```csharp +await using var session = await client.CreateSessionAsync(); + +async Task SendAndWait(string prompt) +{ + var done = new TaskCompletionSource(); + var subscription = session.On(evt => + { + if (evt is AssistantMessageEvent msg) + { + Console.WriteLine(msg.Data.Content); + } + else if (evt is SessionIdleEvent) + { + done.SetResult(); + } + }); + + await session.SendAsync(new MessageOptions { Prompt = prompt }); + await done.Task; + subscription.Dispose(); +} + +await SendAndWait("What is the capital of France?"); +await SendAndWait("What is its population?"); +``` + +### Tool with Complex Return Type + +```csharp +var session = await client.CreateSessionAsync(new SessionConfig +{ + Tools = [ + AIFunctionFactory.Create( + ([Description("User ID")] string userId) => { + return new { + Id = userId, + Name = "John Doe", + Email = "john@example.com", + Role = "Developer" + }; + }, + "get_user", + "Retrieve user information") + ] +}); +``` + diff --git a/instructions/copilot-sdk-go.instructions.md b/instructions/copilot-sdk-go.instructions.md new file mode 100644 index 00000000..b8741721 --- /dev/null +++ b/instructions/copilot-sdk-go.instructions.md @@ -0,0 +1,626 @@ +--- +applyTo: "**.go, go.mod" +description: "This file provides guidance on building Go applications using GitHub Copilot SDK." +name: "GitHub Copilot SDK Go Instructions" +--- + +## Core Principles + +- The SDK is in technical preview and may have breaking changes +- Requires Go 1.21 or later +- Requires GitHub Copilot CLI installed and in PATH +- Uses goroutines and channels for concurrent operations +- No external dependencies beyond the standard library + +## Installation + +Always install via Go modules: + +```bash +go get github.com/github/copilot-sdk/go +``` + +## Client Initialization + +### Basic Client Setup + +```go +import "github.com/github/copilot-sdk/go" + +client := copilot.NewClient(nil) +if err := client.Start(); err != nil { + log.Fatal(err) +} +defer client.Stop() +``` + +### Client Configuration Options + +When creating a CopilotClient, use `ClientOptions`: + +- `CLIPath` - Path to CLI executable (default: "copilot" from PATH) +- `CLIUrl` - URL of existing CLI server (e.g., "localhost:8080"). When provided, client won't spawn a process +- `Port` - Server port (default: 0 for random) +- `UseStdio` - Use stdio transport instead of TCP (default: true) +- `LogLevel` - Log level (default: "info") +- `AutoStart` - Auto-start server (default: true, use pointer: `boolPtr(true)`) +- `AutoRestart` - Auto-restart on crash (default: true, use pointer: `boolPtr(true)`) +- `Cwd` - Working directory for the CLI process +- `Env` - Environment variables for the CLI process ([]string) + +### Manual Server Control + +For explicit control: + +```go +autoStart := false +client := copilot.NewClient(&copilot.ClientOptions{AutoStart: &autoStart}) +if err := client.Start(); err != nil { + log.Fatal(err) +} +// Use client... +client.Stop() +``` + +Use `ForceStop()` when `Stop()` takes too long. + +## Session Management + +### Creating Sessions + +Use `SessionConfig` for configuration: + +```go +session, err := client.CreateSession(&copilot.SessionConfig{ + Model: "gpt-5", + Streaming: true, + Tools: []copilot.Tool{...}, + SystemMessage: &copilot.SystemMessageConfig{ ... }, + AvailableTools: []string{"tool1", "tool2"}, + ExcludedTools: []string{"tool3"}, + Provider: &copilot.ProviderConfig{ ... }, +}) +if err != nil { + log.Fatal(err) +} +``` + +### Session Config Options + +- `SessionID` - Custom session ID +- `Model` - Model name ("gpt-5", "claude-sonnet-4.5", etc.) +- `Tools` - Custom tools exposed to the CLI ([]Tool) +- `SystemMessage` - System message customization (\*SystemMessageConfig) +- `AvailableTools` - Allowlist of tool names ([]string) +- `ExcludedTools` - Blocklist of tool names ([]string) +- `Provider` - Custom API provider configuration (BYOK) (\*ProviderConfig) +- `Streaming` - Enable streaming response chunks (bool) +- `MCPServers` - MCP server configurations +- `CustomAgents` - Custom agent configurations +- `ConfigDir` - Config directory override +- `SkillDirectories` - Skill directories ([]string) +- `DisabledSkills` - Disabled skills ([]string) + +### Resuming Sessions + +```go +session, err := client.ResumeSession("session-id") +// Or with options: +session, err := client.ResumeSessionWithOptions("session-id", &copilot.ResumeSessionConfig{ ... }) +``` + +### Session Operations + +- `session.SessionID` - Get session identifier (string) +- `session.Send(copilot.MessageOptions{Prompt: "...", Attachments: []copilot.Attachment{...}})` - Send message, returns (messageID string, error) +- `session.SendAndWait(options, timeout)` - Send and wait for idle, returns (\*SessionEvent, error) +- `session.Abort()` - Abort current processing, returns error +- `session.GetMessages()` - Get all events/messages, returns ([]SessionEvent, error) +- `session.Destroy()` - Clean up session, returns error + +## Event Handling + +### Event Subscription Pattern + +ALWAYS use channels or done signals for waiting on session events: + +```go +done := make(chan struct{}) + +unsubscribe := session.On(func(evt copilot.SessionEvent) { + switch evt.Type { + case copilot.AssistantMessage: + fmt.Println(*evt.Data.Content) + case copilot.SessionIdle: + close(done) + } +}) +defer unsubscribe() + +session.Send(copilot.MessageOptions{Prompt: "..."}) +<-done +``` + +### Unsubscribing from Events + +The `On()` method returns a function that unsubscribes: + +```go +unsubscribe := session.On(func(evt copilot.SessionEvent) { + // handler +}) +// Later... +unsubscribe() +``` + +### Event Types + +Use type switches for event handling: + +```go +session.On(func(evt copilot.SessionEvent) { + switch evt.Type { + case copilot.UserMessage: + // Handle user message + case copilot.AssistantMessage: + if evt.Data.Content != nil { + fmt.Println(*evt.Data.Content) + } + case copilot.ToolExecutionStart: + // Tool execution started + case copilot.ToolExecutionComplete: + // Tool execution completed + case copilot.SessionStart: + // Session started + case copilot.SessionIdle: + // Session is idle (processing complete) + case copilot.SessionError: + if evt.Data.Message != nil { + fmt.Println("Error:", *evt.Data.Message) + } + } +}) +``` + +## Streaming Responses + +### Enabling Streaming + +Set `Streaming: true` in SessionConfig: + +```go +session, err := client.CreateSession(&copilot.SessionConfig{ + Model: "gpt-5", + Streaming: true, +}) +``` + +### Handling Streaming Events + +Handle both delta events (incremental) and final events: + +```go +done := make(chan struct{}) + +session.On(func(evt copilot.SessionEvent) { + switch evt.Type { + case copilot.AssistantMessageDelta: + // Incremental text chunk + if evt.Data.DeltaContent != nil { + fmt.Print(*evt.Data.DeltaContent) + } + case copilot.AssistantReasoningDelta: + // Incremental reasoning chunk (model-dependent) + if evt.Data.DeltaContent != nil { + fmt.Print(*evt.Data.DeltaContent) + } + case copilot.AssistantMessage: + // Final complete message + fmt.Println("\n--- Final ---") + if evt.Data.Content != nil { + fmt.Println(*evt.Data.Content) + } + case copilot.AssistantReasoning: + // Final reasoning content + fmt.Println("--- Reasoning ---") + if evt.Data.Content != nil { + fmt.Println(*evt.Data.Content) + } + case copilot.SessionIdle: + close(done) + } +}) + +session.Send(copilot.MessageOptions{Prompt: "Tell me a story"}) +<-done +``` + +Note: Final events (`AssistantMessage`, `AssistantReasoning`) are ALWAYS sent regardless of streaming setting. + +## Custom Tools + +### Defining Tools + +```go +session, err := client.CreateSession(&copilot.SessionConfig{ + Model: "gpt-5", + Tools: []copilot.Tool{ + { + Name: "lookup_issue", + Description: "Fetch issue details from tracker", + Parameters: map[string]interface{}{ + "type": "object", + "properties": map[string]interface{}{ + "id": map[string]interface{}{ + "type": "string", + "description": "Issue ID", + }, + }, + "required": []string{"id"}, + }, + Handler: func(inv copilot.ToolInvocation) (copilot.ToolResult, error) { + args := inv.Arguments.(map[string]interface{}) + issueID := args["id"].(string) + + issue, err := fetchIssue(issueID) + if err != nil { + return copilot.ToolResult{}, err + } + + return copilot.ToolResult{ + TextResultForLLM: fmt.Sprintf("Issue: %v", issue), + ResultType: "success", + ToolTelemetry: map[string]interface{}{}, + }, nil + }, + }, + }, +}) +``` + +### Tool Return Types + +- Return `ToolResult` struct with fields: + - `TextResultForLLM` (string) - Result text for the LLM + - `ResultType` (string) - "success" or "failure" + - `Error` (string, optional) - Internal error message (not shown to LLM) + - `ToolTelemetry` (map[string]interface{}) - Telemetry data + +### Tool Execution Flow + +When Copilot invokes a tool, the client automatically: + +1. Runs your handler function +2. Returns the ToolResult +3. Responds to the CLI + +## System Message Customization + +### Append Mode (Default - Preserves Guardrails) + +```go +session, err := client.CreateSession(&copilot.SessionConfig{ + Model: "gpt-5", + SystemMessage: &copilot.SystemMessageConfig{ + Mode: "append", + Content: ` + +- Always check for security vulnerabilities +- Suggest performance improvements when applicable + +`, + }, +}) +``` + +### Replace Mode (Full Control - Removes Guardrails) + +```go +session, err := client.CreateSession(&copilot.SessionConfig{ + Model: "gpt-5", + SystemMessage: &copilot.SystemMessageConfig{ + Mode: "replace", + Content: "You are a helpful assistant.", + }, +}) +``` + +## File Attachments + +Attach files to messages using `Attachment`: + +```go +messageID, err := session.Send(copilot.MessageOptions{ + Prompt: "Analyze this file", + Attachments: []copilot.Attachment{ + { + Type: "file", + Path: "/path/to/file.go", + DisplayName: "My File", + }, + }, +}) +``` + +## Message Delivery Modes + +Use the `Mode` field in `MessageOptions`: + +- `"enqueue"` - Queue message for processing +- `"immediate"` - Process message immediately + +```go +session.Send(copilot.MessageOptions{ + Prompt: "...", + Mode: "enqueue", +}) +``` + +## Multiple Sessions + +Sessions are independent and can run concurrently: + +```go +session1, _ := client.CreateSession(&copilot.SessionConfig{Model: "gpt-5"}) +session2, _ := client.CreateSession(&copilot.SessionConfig{Model: "claude-sonnet-4.5"}) + +session1.Send(copilot.MessageOptions{Prompt: "Hello from session 1"}) +session2.Send(copilot.MessageOptions{Prompt: "Hello from session 2"}) +``` + +## Bring Your Own Key (BYOK) + +Use custom API providers via `ProviderConfig`: + +```go +session, err := client.CreateSession(&copilot.SessionConfig{ + Provider: &copilot.ProviderConfig{ + Type: "openai", + BaseURL: "https://api.openai.com/v1", + APIKey: "your-api-key", + }, +}) +``` + +## Session Lifecycle Management + +### Checking Connection State + +```go +state := client.GetState() +// Returns: "disconnected", "connecting", "connected", or "error" +``` + +## Error Handling + +### Standard Exception Handling + +```go +session, err := client.CreateSession(&copilot.SessionConfig{}) +if err != nil { + log.Fatalf("Failed to create session: %v", err) +} + +_, err = session.Send(copilot.MessageOptions{Prompt: "Hello"}) +if err != nil { + log.Printf("Failed to send: %v", err) +} +``` + +### Session Error Events + +Monitor `SessionError` type for runtime errors: + +```go +session.On(func(evt copilot.SessionEvent) { + if evt.Type == copilot.SessionError { + if evt.Data.Message != nil { + fmt.Fprintf(os.Stderr, "Session Error: %s\n", *evt.Data.Message) + } + } +}) +``` + +## Connectivity Testing + +Use Ping to verify server connectivity: + +```go +resp, err := client.Ping("test message") +if err != nil { + log.Printf("Server unreachable: %v", err) +} else { + log.Printf("Server responded at %d", resp.Timestamp) +} +``` + +## Resource Cleanup + +### Cleanup with Defer + +ALWAYS use `defer` for cleanup: + +```go +client := copilot.NewClient(nil) +if err := client.Start(); err != nil { + log.Fatal(err) +} +defer client.Stop() + +session, err := client.CreateSession(nil) +if err != nil { + log.Fatal(err) +} +defer session.Destroy() +``` + +### Manual Cleanup + +If not using defer: + +```go +client := copilot.NewClient(nil) +err := client.Start() +if err != nil { + log.Fatal(err) +} + +session, err := client.CreateSession(nil) +if err != nil { + client.Stop() + log.Fatal(err) +} + +// Use session... + +session.Destroy() +errors := client.Stop() +for _, err := range errors { + log.Printf("Cleanup error: %v", err) +} +``` + +## Best Practices + +1. **Always use `defer`** for cleanup of clients and sessions +2. **Use channels** to wait for SessionIdle event +3. **Handle SessionError** events for robust error handling +4. **Use type switches** for event handling +5. **Enable streaming** for better UX in interactive scenarios +6. **Provide descriptive tool names and descriptions** for better model understanding +7. **Call unsubscribe functions** when no longer needed +8. **Use SystemMessageConfig with Mode: "append"** to preserve safety guardrails +9. **Handle both delta and final events** when streaming is enabled +10. **Check nil pointers** in event data (Content, Message, etc. are pointers) + +## Common Patterns + +### Simple Query-Response + +```go +client := copilot.NewClient(nil) +if err := client.Start(); err != nil { + log.Fatal(err) +} +defer client.Stop() + +session, err := client.CreateSession(&copilot.SessionConfig{Model: "gpt-5"}) +if err != nil { + log.Fatal(err) +} +defer session.Destroy() + +done := make(chan struct{}) + +session.On(func(evt copilot.SessionEvent) { + if evt.Type == copilot.AssistantMessage && evt.Data.Content != nil { + fmt.Println(*evt.Data.Content) + } else if evt.Type == copilot.SessionIdle { + close(done) + } +}) + +session.Send(copilot.MessageOptions{Prompt: "What is 2+2?"}) +<-done +``` + +### Multi-Turn Conversation + +```go +session, _ := client.CreateSession(nil) +defer session.Destroy() + +sendAndWait := func(prompt string) error { + done := make(chan struct{}) + var eventErr error + + unsubscribe := session.On(func(evt copilot.SessionEvent) { + switch evt.Type { + case copilot.AssistantMessage: + if evt.Data.Content != nil { + fmt.Println(*evt.Data.Content) + } + case copilot.SessionIdle: + close(done) + case copilot.SessionError: + if evt.Data.Message != nil { + eventErr = fmt.Errorf(*evt.Data.Message) + } + } + }) + defer unsubscribe() + + if _, err := session.Send(copilot.MessageOptions{Prompt: prompt}); err != nil { + return err + } + <-done + return eventErr +} + +sendAndWait("What is the capital of France?") +sendAndWait("What is its population?") +``` + +### SendAndWait Helper + +```go +// Use built-in SendAndWait for simpler synchronous interaction +response, err := session.SendAndWait(copilot.MessageOptions{ + Prompt: "What is 2+2?", +}, 0) // 0 uses default 60s timeout + +if err != nil { + log.Printf("Error: %v", err) +} +if response != nil && response.Data.Content != nil { + fmt.Println(*response.Data.Content) +} +``` + +### Tool with Struct Return Type + +```go +type UserInfo struct { + ID string `json:"id"` + Name string `json:"name"` + Email string `json:"email"` + Role string `json:"role"` +} + +session, _ := client.CreateSession(&copilot.SessionConfig{ + Tools: []copilot.Tool{ + { + Name: "get_user", + Description: "Retrieve user information", + Parameters: map[string]interface{}{ + "type": "object", + "properties": map[string]interface{}{ + "user_id": map[string]interface{}{ + "type": "string", + "description": "User ID", + }, + }, + "required": []string{"user_id"}, + }, + Handler: func(inv copilot.ToolInvocation) (copilot.ToolResult, error) { + args := inv.Arguments.(map[string]interface{}) + userID := args["user_id"].(string) + + user := UserInfo{ + ID: userID, + Name: "John Doe", + Email: "john@example.com", + Role: "Developer", + } + + jsonBytes, _ := json.Marshal(user) + return copilot.ToolResult{ + TextResultForLLM: string(jsonBytes), + ResultType: "success", + ToolTelemetry: map[string]interface{}{}, + }, nil + }, + }, + }, +}) +``` diff --git a/instructions/copilot-sdk-nodejs.instructions.md b/instructions/copilot-sdk-nodejs.instructions.md new file mode 100644 index 00000000..78c6f2b2 --- /dev/null +++ b/instructions/copilot-sdk-nodejs.instructions.md @@ -0,0 +1,717 @@ +--- +applyTo: "**.ts, **.js, package.json" +description: "This file provides guidance on building Node.js/TypeScript applications using GitHub Copilot SDK." +name: "GitHub Copilot SDK Node.js Instructions" +--- + +## Core Principles + +- The SDK is in technical preview and may have breaking changes +- Requires Node.js 18.0 or later +- Requires GitHub Copilot CLI installed and in PATH +- Built with TypeScript for type safety +- Uses async/await patterns throughout +- Provides full TypeScript type definitions + +## Installation + +Always install via npm/pnpm/yarn: + +```bash +npm install @github/copilot-sdk +# or +pnpm add @github/copilot-sdk +# or +yarn add @github/copilot-sdk +``` + +## Client Initialization + +### Basic Client Setup + +```typescript +import { CopilotClient } from "@github/copilot-sdk"; + +const client = new CopilotClient(); +await client.start(); +// Use client... +await client.stop(); +``` + +### Client Configuration Options + +When creating a CopilotClient, use `CopilotClientOptions`: + +- `cliPath` - Path to CLI executable (default: "copilot" from PATH) +- `cliArgs` - Extra arguments prepended before SDK-managed flags (string[]) +- `cliUrl` - URL of existing CLI server (e.g., "localhost:8080"). When provided, client won't spawn a process +- `port` - Server port (default: 0 for random) +- `useStdio` - Use stdio transport instead of TCP (default: true) +- `logLevel` - Log level (default: "debug") +- `autoStart` - Auto-start server (default: true) +- `autoRestart` - Auto-restart on crash (default: true) +- `cwd` - Working directory for the CLI process (default: process.cwd()) +- `env` - Environment variables for the CLI process (default: process.env) + +### Manual Server Control + +For explicit control: + +```typescript +const client = new CopilotClient({ autoStart: false }); +await client.start(); +// Use client... +await client.stop(); +``` + +Use `forceStop()` when `stop()` takes too long. + +## Session Management + +### Creating Sessions + +Use `SessionConfig` for configuration: + +```typescript +const session = await client.createSession({ + model: "gpt-5", + streaming: true, + tools: [...], + systemMessage: { ... }, + availableTools: ["tool1", "tool2"], + excludedTools: ["tool3"], + provider: { ... } +}); +``` + +### Session Config Options + +- `sessionId` - Custom session ID (string) +- `model` - Model name ("gpt-5", "claude-sonnet-4.5", etc.) +- `tools` - Custom tools exposed to the CLI (Tool[]) +- `systemMessage` - System message customization (SystemMessageConfig) +- `availableTools` - Allowlist of tool names (string[]) +- `excludedTools` - Blocklist of tool names (string[]) +- `provider` - Custom API provider configuration (BYOK) (ProviderConfig) +- `streaming` - Enable streaming response chunks (boolean) +- `mcpServers` - MCP server configurations (MCPServerConfig[]) +- `customAgents` - Custom agent configurations (CustomAgentConfig[]) +- `configDir` - Config directory override (string) +- `skillDirectories` - Skill directories (string[]) +- `disabledSkills` - Disabled skills (string[]) +- `onPermissionRequest` - Permission request handler (PermissionHandler) + +### Resuming Sessions + +```typescript +const session = await client.resumeSession("session-id", { + tools: [myNewTool], +}); +``` + +### Session Operations + +- `session.sessionId` - Get session identifier (string) +- `await session.send({ prompt: "...", attachments: [...] })` - Send message, returns Promise +- `await session.sendAndWait({ prompt: "..." }, timeout)` - Send and wait for idle, returns Promise +- `await session.abort()` - Abort current processing +- `await session.getMessages()` - Get all events/messages, returns Promise +- `await session.destroy()` - Clean up session + +## Event Handling + +### Event Subscription Pattern + +ALWAYS use async/await or Promises for waiting on session events: + +```typescript +await new Promise((resolve) => { + session.on((event) => { + if (event.type === "assistant.message") { + console.log(event.data.content); + } else if (event.type === "session.idle") { + resolve(); + } + }); + + session.send({ prompt: "..." }); +}); +``` + +### Unsubscribing from Events + +The `on()` method returns a function that unsubscribes: + +```typescript +const unsubscribe = session.on((event) => { + // handler +}); +// Later... +unsubscribe(); +``` + +### Event Types + +Use discriminated unions with type guards for event handling: + +```typescript +session.on((event) => { + switch (event.type) { + case "user.message": + // Handle user message + break; + case "assistant.message": + console.log(event.data.content); + break; + case "tool.executionStart": + // Tool execution started + break; + case "tool.executionComplete": + // Tool execution completed + break; + case "session.start": + // Session started + break; + case "session.idle": + // Session is idle (processing complete) + break; + case "session.error": + console.error(`Error: ${event.data.message}`); + break; + } +}); +``` + +## Streaming Responses + +### Enabling Streaming + +Set `streaming: true` in SessionConfig: + +```typescript +const session = await client.createSession({ + model: "gpt-5", + streaming: true, +}); +``` + +### Handling Streaming Events + +Handle both delta events (incremental) and final events: + +```typescript +await new Promise((resolve) => { + session.on((event) => { + switch (event.type) { + case "assistant.message.delta": + // Incremental text chunk + process.stdout.write(event.data.deltaContent); + break; + case "assistant.reasoning.delta": + // Incremental reasoning chunk (model-dependent) + process.stdout.write(event.data.deltaContent); + break; + case "assistant.message": + // Final complete message + console.log("\n--- Final ---"); + console.log(event.data.content); + break; + case "assistant.reasoning": + // Final reasoning content + console.log("--- Reasoning ---"); + console.log(event.data.content); + break; + case "session.idle": + resolve(); + break; + } + }); + + session.send({ prompt: "Tell me a story" }); +}); +``` + +Note: Final events (`assistant.message`, `assistant.reasoning`) are ALWAYS sent regardless of streaming setting. + +## Custom Tools + +### Defining Tools with defineTool + +Use `defineTool` for type-safe tool definitions: + +```typescript +import { defineTool } from "@github/copilot-sdk"; + +const session = await client.createSession({ + model: "gpt-5", + tools: [ + defineTool({ + name: "lookup_issue", + description: "Fetch issue details from tracker", + parameters: { + type: "object", + properties: { + id: { type: "string", description: "Issue ID" }, + }, + required: ["id"], + }, + handler: async (args) => { + const issue = await fetchIssue(args.id); + return issue; + }, + }), + ], +}); +``` + +### Using Zod for Parameters + +The SDK supports Zod schemas for parameters: + +```typescript +import { z } from "zod"; + +const session = await client.createSession({ + tools: [ + defineTool({ + name: "get_weather", + description: "Get weather for a location", + parameters: z.object({ + location: z.string().describe("City name"), + units: z.enum(["celsius", "fahrenheit"]).optional(), + }), + handler: async (args) => { + return { temperature: 72, units: args.units || "fahrenheit" }; + }, + }), + ], +}); +``` + +### Tool Return Types + +- Return any JSON-serializable value (automatically wrapped) +- Or return `ToolResultObject` for full control over metadata: + +```typescript +{ + textResultForLlm: string; // Result shown to LLM + resultType: "success" | "failure"; + error?: string; // Internal error (not shown to LLM) + toolTelemetry?: Record; +} +``` + +### Tool Execution Flow + +When Copilot invokes a tool, the client automatically: + +1. Runs your handler function +2. Serializes the return value +3. Responds to the CLI + +## System Message Customization + +### Append Mode (Default - Preserves Guardrails) + +```typescript +const session = await client.createSession({ + model: "gpt-5", + systemMessage: { + mode: "append", + content: ` + +- Always check for security vulnerabilities +- Suggest performance improvements when applicable + +`, + }, +}); +``` + +### Replace Mode (Full Control - Removes Guardrails) + +```typescript +const session = await client.createSession({ + model: "gpt-5", + systemMessage: { + mode: "replace", + content: "You are a helpful assistant.", + }, +}); +``` + +## File Attachments + +Attach files to messages: + +```typescript +await session.send({ + prompt: "Analyze this file", + attachments: [ + { + type: "file", + path: "/path/to/file.ts", + displayName: "My File", + }, + ], +}); +``` + +## Message Delivery Modes + +Use the `mode` property in message options: + +- `"enqueue"` - Queue message for processing +- `"immediate"` - Process message immediately + +```typescript +await session.send({ + prompt: "...", + mode: "enqueue", +}); +``` + +## Multiple Sessions + +Sessions are independent and can run concurrently: + +```typescript +const session1 = await client.createSession({ model: "gpt-5" }); +const session2 = await client.createSession({ model: "claude-sonnet-4.5" }); + +await Promise.all([ + session1.send({ prompt: "Hello from session 1" }), + session2.send({ prompt: "Hello from session 2" }), +]); +``` + +## Bring Your Own Key (BYOK) + +Use custom API providers via `provider`: + +```typescript +const session = await client.createSession({ + provider: { + type: "openai", + baseUrl: "https://api.openai.com/v1", + apiKey: "your-api-key", + }, +}); +``` + +## Session Lifecycle Management + +### Listing Sessions + +```typescript +const sessions = await client.listSessions(); +for (const metadata of sessions) { + console.log(`${metadata.sessionId}: ${metadata.summary}`); +} +``` + +### Deleting Sessions + +```typescript +await client.deleteSession(sessionId); +``` + +### Getting Last Session ID + +```typescript +const lastId = await client.getLastSessionId(); +if (lastId) { + const session = await client.resumeSession(lastId); +} +``` + +### Checking Connection State + +```typescript +const state = client.getState(); +// Returns: "disconnected" | "connecting" | "connected" | "error" +``` + +## Error Handling + +### Standard Exception Handling + +```typescript +try { + const session = await client.createSession(); + await session.send({ prompt: "Hello" }); +} catch (error) { + console.error(`Error: ${error.message}`); +} +``` + +### Session Error Events + +Monitor `session.error` event type for runtime errors: + +```typescript +session.on((event) => { + if (event.type === "session.error") { + console.error(`Session Error: ${event.data.message}`); + } +}); +``` + +## Connectivity Testing + +Use ping to verify server connectivity: + +```typescript +const response = await client.ping("health check"); +console.log(`Server responded at ${new Date(response.timestamp)}`); +``` + +## Resource Cleanup + +### Automatic Cleanup with Try-Finally + +ALWAYS use try-finally or cleanup in a finally block: + +```typescript +const client = new CopilotClient(); +try { + await client.start(); + const session = await client.createSession(); + try { + // Use session... + } finally { + await session.destroy(); + } +} finally { + await client.stop(); +} +``` + +### Cleanup Function Pattern + +```typescript +async function withClient( + fn: (client: CopilotClient) => Promise, +): Promise { + const client = new CopilotClient(); + try { + await client.start(); + return await fn(client); + } finally { + await client.stop(); + } +} + +async function withSession( + client: CopilotClient, + fn: (session: CopilotSession) => Promise, +): Promise { + const session = await client.createSession(); + try { + return await fn(session); + } finally { + await session.destroy(); + } +} + +// Usage +await withClient(async (client) => { + await withSession(client, async (session) => { + await session.send({ prompt: "Hello!" }); + }); +}); +``` + +## Best Practices + +1. **Always use try-finally** for resource cleanup +2. **Use Promises** to wait for session.idle event +3. **Handle session.error** events for robust error handling +4. **Use type guards or switch statements** for event handling +5. **Enable streaming** for better UX in interactive scenarios +6. **Use defineTool** for type-safe tool definitions +7. **Use Zod schemas** for runtime parameter validation +8. **Dispose event subscriptions** when no longer needed +9. **Use systemMessage with mode: "append"** to preserve safety guardrails +10. **Handle both delta and final events** when streaming is enabled +11. **Leverage TypeScript types** for compile-time safety + +## Common Patterns + +### Simple Query-Response + +```typescript +import { CopilotClient } from "@github/copilot-sdk"; + +const client = new CopilotClient(); +try { + await client.start(); + + const session = await client.createSession({ model: "gpt-5" }); + try { + await new Promise((resolve) => { + session.on((event) => { + if (event.type === "assistant.message") { + console.log(event.data.content); + } else if (event.type === "session.idle") { + resolve(); + } + }); + + session.send({ prompt: "What is 2+2?" }); + }); + } finally { + await session.destroy(); + } +} finally { + await client.stop(); +} +``` + +### Multi-Turn Conversation + +```typescript +const session = await client.createSession(); + +async function sendAndWait(prompt: string): Promise { + await new Promise((resolve, reject) => { + const unsubscribe = session.on((event) => { + if (event.type === "assistant.message") { + console.log(event.data.content); + } else if (event.type === "session.idle") { + unsubscribe(); + resolve(); + } else if (event.type === "session.error") { + unsubscribe(); + reject(new Error(event.data.message)); + } + }); + + session.send({ prompt }); + }); +} + +await sendAndWait("What is the capital of France?"); +await sendAndWait("What is its population?"); +``` + +### SendAndWait Helper + +```typescript +// Use built-in sendAndWait for simpler synchronous interaction +const response = await session.sendAndWait({ prompt: "What is 2+2?" }, 60000); + +if (response) { + console.log(response.data.content); +} +``` + +### Tool with Type-Safe Parameters + +```typescript +import { z } from "zod"; +import { defineTool } from "@github/copilot-sdk"; + +interface UserInfo { + id: string; + name: string; + email: string; + role: string; +} + +const session = await client.createSession({ + tools: [ + defineTool({ + name: "get_user", + description: "Retrieve user information", + parameters: z.object({ + userId: z.string().describe("User ID"), + }), + handler: async (args): Promise => { + return { + id: args.userId, + name: "John Doe", + email: "john@example.com", + role: "Developer", + }; + }, + }), + ], +}); +``` + +### Streaming with Progress + +```typescript +let currentMessage = ""; + +const unsubscribe = session.on((event) => { + if (event.type === "assistant.message.delta") { + currentMessage += event.data.deltaContent; + process.stdout.write(event.data.deltaContent); + } else if (event.type === "assistant.message") { + console.log("\n\n=== Complete ==="); + console.log(`Total length: ${event.data.content.length} chars`); + } else if (event.type === "session.idle") { + unsubscribe(); + } +}); + +await session.send({ prompt: "Write a long story" }); +``` + +### Error Recovery + +```typescript +session.on((event) => { + if (event.type === "session.error") { + console.error("Session error:", event.data.message); + // Optionally retry or handle error + } +}); + +try { + await session.send({ prompt: "risky operation" }); +} catch (error) { + // Handle send errors + console.error("Failed to send:", error); +} +``` + +## TypeScript-Specific Features + +### Type Inference + +```typescript +import type { SessionEvent, AssistantMessageEvent } from "@github/copilot-sdk"; + +session.on((event: SessionEvent) => { + if (event.type === "assistant.message") { + // TypeScript knows event is AssistantMessageEvent here + const content: string = event.data.content; + } +}); +``` + +### Generic Helper + +```typescript +async function waitForEvent( + session: CopilotSession, + eventType: T, +): Promise> { + return new Promise((resolve) => { + const unsubscribe = session.on((event) => { + if (event.type === eventType) { + unsubscribe(); + resolve(event as Extract); + } + }); + }); +} + +// Usage +const message = await waitForEvent(session, "assistant.message"); +console.log(message.data.content); +``` diff --git a/instructions/copilot-sdk-python.instructions.md b/instructions/copilot-sdk-python.instructions.md new file mode 100644 index 00000000..e4b08e02 --- /dev/null +++ b/instructions/copilot-sdk-python.instructions.md @@ -0,0 +1,806 @@ +--- +applyTo: "**.py, pyproject.toml, setup.py" +description: "This file provides guidance on building Python applications using GitHub Copilot SDK." +name: "GitHub Copilot SDK Python Instructions" +--- + +## Core Principles + +- The SDK is in technical preview and may have breaking changes +- Requires Python 3.9 or later +- Requires GitHub Copilot CLI installed and in PATH +- Uses async/await patterns throughout (asyncio) +- Supports both async context managers and manual lifecycle management +- Type hints provided for better IDE support + +## Installation + +Always install via pip: + +```bash +pip install copilot-sdk +# or with poetry +poetry add copilot-sdk +# or with uv +uv add copilot-sdk +``` + +## Client Initialization + +### Basic Client Setup + +```python +from copilot import CopilotClient + +async with CopilotClient() as client: + # Use client... + pass +``` + +### Client Configuration Options + +When creating a CopilotClient, use a dict with these keys: + +- `cli_path` - Path to CLI executable (default: "copilot" from PATH or COPILOT_CLI_PATH env var) +- `cli_url` - URL of existing CLI server (e.g., "localhost:8080"). When provided, client won't spawn a process +- `port` - Server port (default: 0 for random) +- `use_stdio` - Use stdio transport instead of TCP (default: True) +- `log_level` - Log level (default: "info") +- `auto_start` - Auto-start server (default: True) +- `auto_restart` - Auto-restart on crash (default: True) +- `cwd` - Working directory for the CLI process (default: os.getcwd()) +- `env` - Environment variables for the CLI process (dict) + +### Manual Server Control + +For explicit control: + +```python +client = CopilotClient({"auto_start": False}) +await client.start() +# Use client... +await client.stop() +``` + +Use `force_stop()` when `stop()` takes too long. + +## Session Management + +### Creating Sessions + +Use a dict for SessionConfig: + +```python +session = await client.create_session({ + "model": "gpt-5", + "streaming": True, + "tools": [...], + "system_message": { ... }, + "available_tools": ["tool1", "tool2"], + "excluded_tools": ["tool3"], + "provider": { ... } +}) +``` + +### Session Config Options + +- `session_id` - Custom session ID (str) +- `model` - Model name ("gpt-5", "claude-sonnet-4.5", etc.) +- `tools` - Custom tools exposed to the CLI (list[Tool]) +- `system_message` - System message customization (dict) +- `available_tools` - Allowlist of tool names (list[str]) +- `excluded_tools` - Blocklist of tool names (list[str]) +- `provider` - Custom API provider configuration (BYOK) (ProviderConfig) +- `streaming` - Enable streaming response chunks (bool) +- `mcp_servers` - MCP server configurations (list) +- `custom_agents` - Custom agent configurations (list) +- `config_dir` - Config directory override (str) +- `skill_directories` - Skill directories (list[str]) +- `disabled_skills` - Disabled skills (list[str]) +- `on_permission_request` - Permission request handler (callable) + +### Resuming Sessions + +```python +session = await client.resume_session("session-id", { + "tools": [my_new_tool] +}) +``` + +### Session Operations + +- `session.session_id` - Get session identifier (str) +- `await session.send({"prompt": "...", "attachments": [...]})` - Send message, returns str (message ID) +- `await session.send_and_wait({"prompt": "..."}, timeout=60.0)` - Send and wait for idle, returns SessionEvent | None +- `await session.abort()` - Abort current processing +- `await session.get_messages()` - Get all events/messages, returns list[SessionEvent] +- `await session.destroy()` - Clean up session + +## Event Handling + +### Event Subscription Pattern + +ALWAYS use asyncio events or futures for waiting on session events: + +```python +import asyncio + +done = asyncio.Event() + +def handler(event): + if event.type == "assistant.message": + print(event.data.content) + elif event.type == "session.idle": + done.set() + +session.on(handler) +await session.send({"prompt": "..."}) +await done.wait() +``` + +### Unsubscribing from Events + +The `on()` method returns a function that unsubscribes: + +```python +unsubscribe = session.on(lambda event: print(event.type)) +# Later... +unsubscribe() +``` + +### Event Types + +Use attribute access for event type checking: + +```python +def handler(event): + if event.type == "user.message": + # Handle user message + pass + elif event.type == "assistant.message": + print(event.data.content) + elif event.type == "tool.executionStart": + # Tool execution started + pass + elif event.type == "tool.executionComplete": + # Tool execution completed + pass + elif event.type == "session.start": + # Session started + pass + elif event.type == "session.idle": + # Session is idle (processing complete) + pass + elif event.type == "session.error": + print(f"Error: {event.data.message}") + +session.on(handler) +``` + +## Streaming Responses + +### Enabling Streaming + +Set `streaming: True` in SessionConfig: + +```python +session = await client.create_session({ + "model": "gpt-5", + "streaming": True +}) +``` + +### Handling Streaming Events + +Handle both delta events (incremental) and final events: + +```python +import asyncio + +done = asyncio.Event() + +def handler(event): + if event.type == "assistant.message.delta": + # Incremental text chunk + print(event.data.delta_content, end="", flush=True) + elif event.type == "assistant.reasoning.delta": + # Incremental reasoning chunk (model-dependent) + print(event.data.delta_content, end="", flush=True) + elif event.type == "assistant.message": + # Final complete message + print("\n--- Final ---") + print(event.data.content) + elif event.type == "assistant.reasoning": + # Final reasoning content + print("--- Reasoning ---") + print(event.data.content) + elif event.type == "session.idle": + done.set() + +session.on(handler) +await session.send({"prompt": "Tell me a story"}) +await done.wait() +``` + +Note: Final events (`assistant.message`, `assistant.reasoning`) are ALWAYS sent regardless of streaming setting. + +## Custom Tools + +### Defining Tools with define_tool + +Use `define_tool` for tool definitions: + +```python +from copilot import define_tool + +async def fetch_issue(issue_id: str): + # Fetch issue from tracker + return {"id": issue_id, "status": "open"} + +session = await client.create_session({ + "model": "gpt-5", + "tools": [ + define_tool( + name="lookup_issue", + description="Fetch issue details from tracker", + parameters={ + "type": "object", + "properties": { + "id": {"type": "string", "description": "Issue ID"} + }, + "required": ["id"] + }, + handler=lambda args, inv: fetch_issue(args["id"]) + ) + ] +}) +``` + +### Using Pydantic for Parameters + +The SDK works well with Pydantic models: + +```python +from pydantic import BaseModel, Field + +class WeatherArgs(BaseModel): + location: str = Field(description="City name") + units: str = Field(default="fahrenheit", description="Temperature units") + +async def get_weather(args: WeatherArgs, inv): + return {"temperature": 72, "units": args.units} + +session = await client.create_session({ + "tools": [ + define_tool( + name="get_weather", + description="Get weather for a location", + parameters=WeatherArgs.model_json_schema(), + handler=lambda args, inv: get_weather(WeatherArgs(**args), inv) + ) + ] +}) +``` + +### Tool Return Types + +- Return any JSON-serializable value (automatically wrapped) +- Or return a ToolResult dict for full control: + +```python +{ + "text_result_for_llm": str, # Result shown to LLM + "result_type": "success" | "failure", + "error": str, # Optional: Internal error (not shown to LLM) + "tool_telemetry": dict # Optional: Telemetry data +} +``` + +### Tool Handler Signature + +Tool handlers receive two arguments: + +- `args` (dict) - The tool arguments passed by the LLM +- `invocation` (ToolInvocation) - Metadata about the invocation + - `invocation.session_id` - Session ID + - `invocation.tool_call_id` - Tool call ID + - `invocation.tool_name` - Tool name + - `invocation.arguments` - Same as args parameter + +### Tool Execution Flow + +When Copilot invokes a tool, the client automatically: + +1. Runs your handler function +2. Serializes the return value +3. Responds to the CLI + +## System Message Customization + +### Append Mode (Default - Preserves Guardrails) + +```python +session = await client.create_session({ + "model": "gpt-5", + "system_message": { + "mode": "append", + "content": """ + +- Always check for security vulnerabilities +- Suggest performance improvements when applicable + +""" + } +}) +``` + +### Replace Mode (Full Control - Removes Guardrails) + +```python +session = await client.create_session({ + "model": "gpt-5", + "system_message": { + "mode": "replace", + "content": "You are a helpful assistant." + } +}) +``` + +## File Attachments + +Attach files to messages: + +```python +await session.send({ + "prompt": "Analyze this file", + "attachments": [ + { + "type": "file", + "path": "/path/to/file.py", + "display_name": "My File" + } + ] +}) +``` + +## Message Delivery Modes + +Use the `mode` key in message options: + +- `"enqueue"` - Queue message for processing +- `"immediate"` - Process message immediately + +```python +await session.send({ + "prompt": "...", + "mode": "enqueue" +}) +``` + +## Multiple Sessions + +Sessions are independent and can run concurrently: + +```python +session1 = await client.create_session({"model": "gpt-5"}) +session2 = await client.create_session({"model": "claude-sonnet-4.5"}) + +await asyncio.gather( + session1.send({"prompt": "Hello from session 1"}), + session2.send({"prompt": "Hello from session 2"}) +) +``` + +## Bring Your Own Key (BYOK) + +Use custom API providers via `provider`: + +```python +session = await client.create_session({ + "provider": { + "type": "openai", + "base_url": "https://api.openai.com/v1", + "api_key": "your-api-key" + } +}) +``` + +## Session Lifecycle Management + +### Listing Sessions + +```python +sessions = await client.list_sessions() +for metadata in sessions: + print(f"{metadata.session_id}: {metadata.summary}") +``` + +### Deleting Sessions + +```python +await client.delete_session(session_id) +``` + +### Getting Last Session ID + +```python +last_id = await client.get_last_session_id() +if last_id: + session = await client.resume_session(last_id) +``` + +### Checking Connection State + +```python +state = client.get_state() +# Returns: "disconnected" | "connecting" | "connected" | "error" +``` + +## Error Handling + +### Standard Exception Handling + +```python +try: + session = await client.create_session() + await session.send({"prompt": "Hello"}) +except Exception as e: + print(f"Error: {e}") +``` + +### Session Error Events + +Monitor `session.error` event type for runtime errors: + +```python +def handler(event): + if event.type == "session.error": + print(f"Session Error: {event.data.message}") + +session.on(handler) +``` + +## Connectivity Testing + +Use ping to verify server connectivity: + +```python +response = await client.ping("health check") +print(f"Server responded at {response['timestamp']}") +``` + +## Resource Cleanup + +### Automatic Cleanup with Context Managers + +ALWAYS use async context managers for automatic cleanup: + +```python +async with CopilotClient() as client: + async with await client.create_session() as session: + # Use session... + await session.send({"prompt": "Hello"}) + # Session automatically destroyed +# Client automatically stopped +``` + +### Manual Cleanup with Try-Finally + +```python +client = CopilotClient() +try: + await client.start() + session = await client.create_session() + try: + # Use session... + pass + finally: + await session.destroy() +finally: + await client.stop() +``` + +## Best Practices + +1. **Always use async context managers** (`async with`) for automatic cleanup +2. **Use asyncio.Event or asyncio.Future** to wait for session.idle event +3. **Handle session.error** events for robust error handling +4. **Use if/elif chains** for event type checking +5. **Enable streaming** for better UX in interactive scenarios +6. **Use define_tool** for tool definitions +7. **Use Pydantic models** for type-safe parameter validation +8. **Dispose event subscriptions** when no longer needed +9. **Use system_message with mode: "append"** to preserve safety guardrails +10. **Handle both delta and final events** when streaming is enabled +11. **Use type hints** for better IDE support and code clarity + +## Common Patterns + +### Simple Query-Response + +```python +from copilot import CopilotClient +import asyncio + +async def main(): + async with CopilotClient() as client: + async with await client.create_session({"model": "gpt-5"}) as session: + done = asyncio.Event() + + def handler(event): + if event.type == "assistant.message": + print(event.data.content) + elif event.type == "session.idle": + done.set() + + session.on(handler) + await session.send({"prompt": "What is 2+2?"}) + await done.wait() + +asyncio.run(main()) +``` + +### Multi-Turn Conversation + +```python +async def send_and_wait(session, prompt: str): + done = asyncio.Event() + result = [] + + def handler(event): + if event.type == "assistant.message": + result.append(event.data.content) + print(event.data.content) + elif event.type == "session.idle": + done.set() + elif event.type == "session.error": + result.append(None) + done.set() + + unsubscribe = session.on(handler) + await session.send({"prompt": prompt}) + await done.wait() + unsubscribe() + + return result[0] if result else None + +async with await client.create_session() as session: + await send_and_wait(session, "What is the capital of France?") + await send_and_wait(session, "What is its population?") +``` + +### SendAndWait Helper + +```python +# Use built-in send_and_wait for simpler synchronous interaction +async with await client.create_session() as session: + response = await session.send_and_wait( + {"prompt": "What is 2+2?"}, + timeout=60.0 + ) + + if response and response.type == "assistant.message": + print(response.data.content) +``` + +### Tool with Dataclass Return Type + +```python +from dataclasses import dataclass, asdict +from copilot import define_tool + +@dataclass +class UserInfo: + id: str + name: str + email: str + role: str + +async def get_user(args, inv) -> dict: + user = UserInfo( + id=args["user_id"], + name="John Doe", + email="john@example.com", + role="Developer" + ) + return asdict(user) + +session = await client.create_session({ + "tools": [ + define_tool( + name="get_user", + description="Retrieve user information", + parameters={ + "type": "object", + "properties": { + "user_id": {"type": "string", "description": "User ID"} + }, + "required": ["user_id"] + }, + handler=get_user + ) + ] +}) +``` + +### Streaming with Progress + +```python +import asyncio + +current_message = [] +done = asyncio.Event() + +def handler(event): + if event.type == "assistant.message.delta": + current_message.append(event.data.delta_content) + print(event.data.delta_content, end="", flush=True) + elif event.type == "assistant.message": + print(f"\n\n=== Complete ===") + print(f"Total length: {len(event.data.content)} chars") + elif event.type == "session.idle": + done.set() + +unsubscribe = session.on(handler) +await session.send({"prompt": "Write a long story"}) +await done.wait() +unsubscribe() +``` + +### Error Recovery + +```python +def handler(event): + if event.type == "session.error": + print(f"Session error: {event.data.message}") + # Optionally retry or handle error + +session.on(handler) + +try: + await session.send({"prompt": "risky operation"}) +except Exception as e: + # Handle send errors + print(f"Failed to send: {e}") +``` + +### Using TypedDict for Type Safety + +```python +from typing import TypedDict, List + +class MessageOptions(TypedDict, total=False): + prompt: str + attachments: List[dict] + mode: str + +class SessionConfig(TypedDict, total=False): + model: str + streaming: bool + tools: List + +# Usage with type hints +options: MessageOptions = { + "prompt": "Hello", + "mode": "enqueue" +} +await session.send(options) + +config: SessionConfig = { + "model": "gpt-5", + "streaming": True +} +session = await client.create_session(config) +``` + +### Async Generator for Streaming + +```python +from typing import AsyncGenerator + +async def stream_response(session, prompt: str) -> AsyncGenerator[str, None]: + """Stream response chunks as an async generator.""" + queue = asyncio.Queue() + done = asyncio.Event() + + def handler(event): + if event.type == "assistant.message.delta": + queue.put_nowait(event.data.delta_content) + elif event.type == "session.idle": + done.set() + + unsubscribe = session.on(handler) + await session.send({"prompt": prompt}) + + while not done.is_set(): + try: + chunk = await asyncio.wait_for(queue.get(), timeout=0.1) + yield chunk + except asyncio.TimeoutError: + continue + + # Drain remaining items + while not queue.empty(): + yield queue.get_nowait() + + unsubscribe() + +# Usage +async for chunk in stream_response(session, "Tell me a story"): + print(chunk, end="", flush=True) +``` + +### Decorator Pattern for Tools + +```python +from typing import Callable, Any +from copilot import define_tool + +def copilot_tool( + name: str, + description: str, + parameters: dict +) -> Callable: + """Decorator to convert a function into a Copilot tool.""" + def decorator(func: Callable) -> Any: + return define_tool( + name=name, + description=description, + parameters=parameters, + handler=lambda args, inv: func(**args) + ) + return decorator + +@copilot_tool( + name="calculate", + description="Perform a calculation", + parameters={ + "type": "object", + "properties": { + "expression": {"type": "string", "description": "Math expression"} + }, + "required": ["expression"] + } +) +def calculate(expression: str) -> float: + return eval(expression) + +session = await client.create_session({"tools": [calculate]}) +``` + +## Python-Specific Features + +### Async Context Manager Protocol + +The SDK implements `__aenter__` and `__aexit__`: + +```python +class CopilotClient: + async def __aenter__(self): + await self.start() + return self + + async def __aexit__(self, exc_type, exc_val, exc_tb): + await self.stop() + return False + +class CopilotSession: + async def __aenter__(self): + return self + + async def __aexit__(self, exc_type, exc_val, exc_tb): + await self.destroy() + return False +``` + +### Dataclass Support + +Event data is available as attributes: + +```python +def handler(event): + # Access event attributes directly + print(event.type) + print(event.data.content) # For assistant.message + print(event.data.delta_content) # For assistant.message.delta +```