{"kind":"AgentDefinition","metadata":{"namespace":"community","name":"copilot-sdk-go","version":"0.1.0"},"spec":{"agents_md":"---\napplyTo: \"**.go, go.mod\"\ndescription: \"This file provides guidance on building Go applications using GitHub Copilot SDK.\"\nname: \"GitHub Copilot SDK Go Instructions\"\n---\n\n## Core Principles\n\n- The SDK is in technical preview and may have breaking changes\n- Requires Go 1.21 or later\n- Requires GitHub Copilot CLI installed and in PATH\n- Uses goroutines and channels for concurrent operations\n- No external dependencies beyond the standard library\n\n## Installation\n\nAlways install via Go modules:\n\n```bash\ngo get github.com/github/copilot-sdk/go\n```\n\n## Client Initialization\n\n### Basic Client Setup\n\n```go\nimport \"github.com/github/copilot-sdk/go\"\n\nclient := copilot.NewClient(nil)\nif err := client.Start(); err != nil {\n    log.Fatal(err)\n}\ndefer client.Stop()\n```\n\n### Client Configuration Options\n\nWhen creating a CopilotClient, use `ClientOptions`:\n\n- `CLIPath` - Path to CLI executable (default: \"copilot\" from PATH)\n- `CLIUrl` - URL of existing CLI server (e.g., \"localhost:8080\"). When provided, client won't spawn a process\n- `Port` - Server port (default: 0 for random)\n- `UseStdio` - Use stdio transport instead of TCP (default: true)\n- `LogLevel` - Log level (default: \"info\")\n- `AutoStart` - Auto-start server (default: true, use pointer: `boolPtr(true)`)\n- `AutoRestart` - Auto-restart on crash (default: true, use pointer: `boolPtr(true)`)\n- `Cwd` - Working directory for the CLI process\n- `Env` - Environment variables for the CLI process ([]string)\n\n### Manual Server Control\n\nFor explicit control:\n\n```go\nautoStart := false\nclient := copilot.NewClient(\u0026copilot.ClientOptions{AutoStart: \u0026autoStart})\nif err := client.Start(); err != nil {\n    log.Fatal(err)\n}\n// Use client...\nclient.Stop()\n```\n\nUse `ForceStop()` when `Stop()` takes too long.\n\n## Session Management\n\n### Creating Sessions\n\nUse `SessionConfig` for configuration:\n\n```go\nsession, err := client.CreateSession(\u0026copilot.SessionConfig{\n\tOnPermissionRequest: copilot.PermissionHandler.ApproveAll,\n    Model: \"gpt-5\",\n    Streaming: true,\n    Tools: []copilot.Tool{...},\n    SystemMessage: \u0026copilot.SystemMessageConfig{ ... },\n    AvailableTools: []string{\"tool1\", \"tool2\"},\n    ExcludedTools: []string{\"tool3\"},\n    Provider: \u0026copilot.ProviderConfig{ ... },\n})\nif err != nil {\n    log.Fatal(err)\n}\n```\n\n### Session Config Options\n\n- `SessionID` - Custom session ID\n- `Model` - Model name (\"gpt-5\", \"claude-sonnet-4.5\", etc.)\n- `Tools` - Custom tools exposed to the CLI ([]Tool)\n- `SystemMessage` - System message customization (\\*SystemMessageConfig)\n- `AvailableTools` - Allowlist of tool names ([]string)\n- `ExcludedTools` - Blocklist of tool names ([]string)\n- `Provider` - Custom API provider configuration (BYOK) (\\*ProviderConfig)\n- `Streaming` - Enable streaming response chunks (bool)\n- `MCPServers` - MCP server configurations\n- `CustomAgents` - Custom agent configurations\n- `ConfigDir` - Config directory override\n- `SkillDirectories` - Skill directories ([]string)\n- `DisabledSkills` - Disabled skills ([]string)\n\n### Resuming Sessions\n\n```go\nsession, err := client.ResumeSession(\"session-id\", \u0026copilot.ResumeSessionConfig{OnPermissionRequest: copilot.PermissionHandler.ApproveAll})\n// Or with options:\nsession, err := client.ResumeSessionWithOptions(\"session-id\", \u0026copilot.ResumeSessionConfig{ ... })\n```\n\n### Session Operations\n\n- `session.SessionID` - Get session identifier (string)\n- `session.Send(copilot.MessageOptions{Prompt: \"...\", Attachments: []copilot.Attachment{...}})` - Send message, returns (messageID string, error)\n- `session.SendAndWait(options, timeout)` - Send and wait for idle, returns (\\*SessionEvent, error)\n- `session.Abort()` - Abort current processing, returns error\n- `session.GetMessages()` - Get all events/messages, returns ([]SessionEvent, error)\n- `session.Destroy()` - Clean up session, returns error\n\n## Event Handling\n\n### Event Subscription Pattern\n\nALWAYS use channels or done signals to wait for session events:\n\n```go\ndone := make(chan struct{})\n\nunsubscribe := session.On(func(evt copilot.SessionEvent) {\n    switch evt.Type {\n    case copilot.AssistantMessage:\n        fmt.Println(*evt.Data.Content)\n    case copilot.SessionIdle:\n        close(done)\n    }\n})\ndefer unsubscribe()\n\nsession.Send(copilot.MessageOptions{Prompt: \"...\"})\n\u003c-done\n```\n\n### Unsubscribing from Events\n\nThe `On()` method returns a function that unsubscribes:\n\n```go\nunsubscribe := session.On(func(evt copilot.SessionEvent) {\n    // handler\n})\n// Later...\nunsubscribe()\n```\n\n### Event Types\n\nUse type switches for event handling:\n\n```go\nsession.On(func(evt copilot.SessionEvent) {\n    switch evt.Type {\n    case copilot.UserMessage:\n        // Handle user message\n    case copilot.AssistantMessage:\n        if evt.Data.Content != nil {\n            fmt.Println(*evt.Data.Content)\n        }\n    case copilot.ToolExecutionStart:\n        // Tool execution started\n    case copilot.ToolExecutionComplete:\n        // Tool execution completed\n    case copilot.SessionStart:\n        // Session started\n    case copilot.SessionIdle:\n        // Session is idle (processing complete)\n    case copilot.SessionError:\n        if evt.Data.Message != nil {\n            fmt.Println(\"Error:\", *evt.Data.Message)\n        }\n    }\n})\n```\n\n## Streaming Responses\n\n### Enabling Streaming\n\nSet `Streaming: true` in SessionConfig:\n\n```go\nsession, err := client.CreateSession(\u0026copilot.SessionConfig{\n\tOnPermissionRequest: copilot.PermissionHandler.ApproveAll,\n    Model: \"gpt-5\",\n    Streaming: true,\n})\n```\n\n### Handling Streaming Events\n\nHandle both delta events (incremental) and final events:\n\n```go\ndone := make(chan struct{})\n\nsession.On(func(evt copilot.SessionEvent) {\n    switch evt.Type {\n    case copilot.AssistantMessageDelta:\n        // Incremental text chunk\n        if evt.Data.DeltaContent != nil {\n            fmt.Print(*evt.Data.DeltaContent)\n        }\n    case copilot.AssistantReasoningDelta:\n        // Incremental reasoning chunk (model-dependent)\n        if evt.Data.DeltaContent != nil {\n            fmt.Print(*evt.Data.DeltaContent)\n        }\n    case copilot.AssistantMessage:\n        // Final complete message\n        fmt.Println(\"\\n--- Final ---\")\n        if evt.Data.Content != nil {\n            fmt.Println(*evt.Data.Content)\n        }\n    case copilot.AssistantReasoning:\n        // Final reasoning content\n        fmt.Println(\"--- Reasoning ---\")\n        if evt.Data.Content != nil {\n            fmt.Println(*evt.Data.Content)\n        }\n    case copilot.SessionIdle:\n        close(done)\n    }\n})\n\nsession.Send(copilot.MessageOptions{Prompt: \"Tell me a story\"})\n\u003c-done\n```\n\nNote: Final events (`AssistantMessage`, `AssistantReasoning`) are ALWAYS sent regardless of streaming setting.\n\n## Custom Tools\n\n### Defining Tools\n\n```go\nsession, err := client.CreateSession(\u0026copilot.SessionConfig{\n\tOnPermissionRequest: copilot.PermissionHandler.ApproveAll,\n    Model: \"gpt-5\",\n    Tools: []copilot.Tool{\n        {\n            Name:        \"lookup_issue\",\n            Description: \"Fetch issue details from tracker\",\n            Parameters: map[string]interface{}{\n                \"type\": \"object\",\n                \"properties\": map[string]interface{}{\n                    \"id\": map[string]interface{}{\n                        \"type\":        \"string\",\n                        \"description\": \"Issue ID\",\n                    },\n                },\n                \"required\": []string{\"id\"},\n            },\n            Handler: func(inv copilot.ToolInvocation) (copilot.ToolResult, error) {\n                args := inv.Arguments.(map[string]interface{})\n                issueID := args[\"id\"].(string)\n\n                issue, err := fetchIssue(issueID)\n                if err != nil {\n                    return copilot.ToolResult{}, err\n                }\n\n                return copilot.ToolResult{\n                    TextResultForLLM: fmt.Sprintf(\"Issue: %v\", issue),\n                    ResultType:       \"success\",\n                    ToolTelemetry:    map[string]interface{}{},\n                }, nil\n            },\n        },\n    },\n})\n```\n\n### Tool Return Types\n\n- Return `ToolResult` struct with fields:\n  - `TextResultForLLM` (string) - Result text for the LLM\n  - `ResultType` (string) - \"success\" or \"failure\"\n  - `Error` (string, optional) - Internal error message (not shown to LLM)\n  - `ToolTelemetry` (map[string]interface{}) - Telemetry data\n\n### Tool Execution Flow\n\nWhen Copilot invokes a tool, the client automatically:\n\n1. Runs your handler function\n2. Returns the ToolResult\n3. Responds to the CLI\n\n## System Message Customization\n\n### Append Mode (Default - Preserves Guardrails)\n\n```go\nsession, err := client.CreateSession(\u0026copilot.SessionConfig{\n\tOnPermissionRequest: copilot.PermissionHandler.ApproveAll,\n    Model: \"gpt-5\",\n    SystemMessage: \u0026copilot.SystemMessageConfig{\n        Mode: \"append\",\n        Content: `\n\u003cworkflow_rules\u003e\n- Always check for security vulnerabilities\n- Suggest performance improvements when applicable\n\u003c/workflow_rules\u003e\n`,\n    },\n})\n```\n\n### Replace Mode (Full Control - Removes Guardrails)\n\n```go\nsession, err := client.CreateSession(\u0026copilot.SessionConfig{\n\tOnPermissionRequest: copilot.PermissionHandler.ApproveAll,\n    Model: \"gpt-5\",\n    SystemMessage: \u0026copilot.SystemMessageConfig{\n        Mode:    \"replace\",\n        Content: \"You are a helpful assistant.\",\n    },\n})\n```\n\n## File Attachments\n\nAttach files to messages using `Attachment`:\n\n```go\nmessageID, err := session.Send(copilot.MessageOptions{\n    Prompt: \"Analyze this file\",\n    Attachments: []copilot.Attachment{\n        {\n            Type:        \"file\",\n            Path:        \"/path/to/file.go\",\n            DisplayName: \"My File\",\n        },\n    },\n})\n```\n\n## Message Delivery Modes\n\nUse the `Mode` field in `MessageOptions`:\n\n- `\"enqueue\"` - Queue message for processing\n- `\"immediate\"` - Process message immediately\n\n```go\nsession.Send(copilot.MessageOptions{\n    Prompt: \"...\",\n    Mode:   \"enqueue\",\n})\n```\n\n## Multiple Sessions\n\nSessions are independent and can run concurrently:\n\n```go\nsession1, _ := client.CreateSession(\u0026copilot.SessionConfig{\n\tOnPermissionRequest: copilot.PermissionHandler.ApproveAll,\n\tModel:               \"gpt-5\",\n})\nsession2, _ := client.CreateSession(\u0026copilot.SessionConfig{\n\tOnPermissionRequest: copilot.PermissionHandler.ApproveAll,\n\tModel:               \"claude-sonnet-4.5\",\n})\n\nsession1.Send(copilot.MessageOptions{Prompt: \"Hello from session 1\"})\nsession2.Send(copilot.MessageOptions{Prompt: \"Hello from session 2\"})\n```\n\n## Bring Your Own Key (BYOK)\n\nUse custom API providers by configuring `ProviderConfig`:\n\n```go\nsession, err := client.CreateSession(\u0026copilot.SessionConfig{\n\tOnPermissionRequest: copilot.PermissionHandler.ApproveAll,\n    Provider: \u0026copilot.ProviderConfig{\n        Type:    \"openai\",\n        BaseURL: \"https://api.openai.com/v1\",\n        APIKey:  \"your-api-key\",\n    },\n})\n```\n\n## Session Lifecycle Management\n\n### Checking Connection State\n\n```go\nstate := client.GetState()\n// Returns: \"disconnected\", \"connecting\", \"connected\", or \"error\"\n```\n\n## Error Handling\n\n### Standard Exception Handling\n\n```go\nsession, err := client.CreateSession(\u0026copilot.SessionConfig{\n\tOnPermissionRequest: copilot.PermissionHandler.ApproveAll,\n})\nif err != nil {\n    log.Fatalf(\"Failed to create session: %v\", err)\n}\n\n_, err = session.Send(copilot.MessageOptions{Prompt: \"Hello\"})\nif err != nil {\n    log.Printf(\"Failed to send: %v\", err)\n}\n```\n\n### Session Error Events\n\nMonitor `SessionError` type for runtime errors:\n\n```go\nsession.On(func(evt copilot.SessionEvent) {\n    if evt.Type == copilot.SessionError {\n        if evt.Data.Message != nil {\n            fmt.Fprintf(os.Stderr, \"Session Error: %s\\n\", *evt.Data.Message)\n        }\n    }\n})\n```\n\n## Connectivity Testing\n\nUse `Ping` to verify server connectivity:\n\n```go\nresp, err := client.Ping(\"test message\")\nif err != nil {\n    log.Printf(\"Server unreachable: %v\", err)\n} else {\n    log.Printf(\"Server responded at %d\", resp.Timestamp)\n}\n```\n\n## Resource Cleanup\n\n### Cleanup with Defer\n\nALWAYS use `defer` for cleanup:\n\n```go\nclient := copilot.NewClient(nil)\nif err := client.Start(); err != nil {\n    log.Fatal(err)\n}\ndefer client.Stop()\n\nsession, err := client.CreateSession(\u0026copilot.SessionConfig{OnPermissionRequest: copilot.PermissionHandler.ApproveAll})\nif err != nil {\n    log.Fatal(err)\n}\ndefer session.Destroy()\n```\n\n### Manual Cleanup\n\nIf not using defer:\n\n```go\nclient := copilot.NewClient(nil)\nerr := client.Start()\nif err != nil {\n    log.Fatal(err)\n}\n\nsession, err := client.CreateSession(\u0026copilot.SessionConfig{OnPermissionRequest: copilot.PermissionHandler.ApproveAll})\nif err != nil {\n    client.Stop()\n    log.Fatal(err)\n}\n\n// Use session...\n\nsession.Destroy()\nerrors := client.Stop()\nfor _, err := range errors {\n    log.Printf(\"Cleanup error: %v\", err)\n}\n```\n\n## Best Practices\n\n1. **Always use `defer`** for cleanup of clients and sessions\n2. **Use channels** to wait for SessionIdle event\n3. **Handle SessionError** events for robust error handling\n4. **Use type switches** for event handling\n5. **Enable streaming** for better UX in interactive scenarios\n6. **Provide descriptive tool names and descriptions** for better model understanding\n7. **Call unsubscribe functions** when no longer needed\n8. **Use SystemMessageConfig with Mode: \"append\"** to preserve safety guardrails\n9. **Handle both delta and final events** when streaming is enabled\n10. **Check nil pointers** in event data (Content, Message, etc. are pointers)\n\n## Common Patterns\n\n### Simple Query-Response\n\n```go\nclient := copilot.NewClient(nil)\nif err := client.Start(); err != nil {\n    log.Fatal(err)\n}\ndefer client.Stop()\n\nsession, err := client.CreateSession(\u0026copilot.SessionConfig{\n\tOnPermissionRequest: copilot.PermissionHandler.ApproveAll,\n\tModel:               \"gpt-5\",\n})\nif err != nil {\n    log.Fatal(err)\n}\ndefer session.Destroy()\n\ndone := make(chan struct{})\n\nsession.On(func(evt copilot.SessionEvent) {\n    if evt.Type == copilot.AssistantMessage \u0026\u0026 evt.Data.Content != nil {\n        fmt.Println(*evt.Data.Content)\n    } else if evt.Type == copilot.SessionIdle {\n        close(done)\n    }\n})\n\nsession.Send(copilot.MessageOptions{Prompt: \"What is 2+2?\"})\n\u003c-done\n```\n\n### Multi-Turn Conversation\n\n```go\nsession, _ := client.CreateSession(\u0026copilot.SessionConfig{OnPermissionRequest: copilot.PermissionHandler.ApproveAll})\ndefer session.Destroy()\n\nsendAndWait := func(prompt string) error {\n    done := make(chan struct{})\n    var eventErr error\n\n    unsubscribe := session.On(func(evt copilot.SessionEvent) {\n        switch evt.Type {\n        case copilot.AssistantMessage:\n            if evt.Data.Content != nil {\n                fmt.Println(*evt.Data.Content)\n            }\n        case copilot.SessionIdle:\n            close(done)\n        case copilot.SessionError:\n            if evt.Data.Message != nil {\n                eventErr = fmt.Errorf(*evt.Data.Message)\n            }\n        }\n    })\n    defer unsubscribe()\n\n    if _, err := session.Send(copilot.MessageOptions{Prompt: prompt}); err != nil {\n        return err\n    }\n    \u003c-done\n    return eventErr\n}\n\nsendAndWait(\"What is the capital of France?\")\nsendAndWait(\"What is its population?\")\n```\n\n### SendAndWait Helper\n\n```go\n// Use built-in SendAndWait for simpler synchronous interaction\nresponse, err := session.SendAndWait(copilot.MessageOptions{\n    Prompt: \"What is 2+2?\",\n}, 0) // 0 uses default 60s timeout\n\nif err != nil {\n    log.Printf(\"Error: %v\", err)\n}\nif response != nil \u0026\u0026 response.Data.Content != nil {\n    fmt.Println(*response.Data.Content)\n}\n```\n\n### Tool with Struct Return Type\n\n```go\ntype UserInfo struct {\n    ID    string `json:\"id\"`\n    Name  string `json:\"name\"`\n    Email string `json:\"email\"`\n    Role  string `json:\"role\"`\n}\n\nsession, _ := client.CreateSession(\u0026copilot.SessionConfig{\n\tOnPermissionRequest: copilot.PermissionHandler.ApproveAll,\n    Tools: []copilot.Tool{\n        {\n            Name:        \"get_user\",\n            Description: \"Retrieve user information\",\n            Parameters: map[string]interface{}{\n                \"type\": \"object\",\n                \"properties\": map[string]interface{}{\n                    \"user_id\": map[string]interface{}{\n                        \"type\":        \"string\",\n                        \"description\": \"User ID\",\n                    },\n                },\n                \"required\": []string{\"user_id\"},\n            },\n            Handler: func(inv copilot.ToolInvocation) (copilot.ToolResult, error) {\n                args := inv.Arguments.(map[string]interface{})\n                userID := args[\"user_id\"].(string)\n\n                user := UserInfo{\n                    ID:    userID,\n                    Name:  \"John Doe\",\n                    Email: \"john@example.com\",\n                    Role:  \"Developer\",\n                }\n\n                jsonBytes, _ := json.Marshal(user)\n                return copilot.ToolResult{\n                    TextResultForLLM: string(jsonBytes),\n                    ResultType:       \"success\",\n                    ToolTelemetry:    map[string]interface{}{},\n                }, nil\n            },\n        },\n    },\n})\n```\n","description":"This file provides guidance on building Go applications using GitHub Copilot SDK.","import":{"commit_sha":"541b7819d8c3545c6df122491af4fa1eae415779","imported_at":"2026-05-18T20:05:35Z","license_text":"MIT License\n\nCopyright GitHub, Inc.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.","owner":"github","repo":"github/awesome-copilot","source_url":"https://github.com/github/awesome-copilot/blob/541b7819d8c3545c6df122491af4fa1eae415779/instructions/copilot-sdk-go.instructions.md"},"manifest":{}},"content_hash":[221,181,193,112,187,152,94,178,13,180,104,174,18,64,220,176,157,237,43,51,42,75,216,238,7,94,147,56,131,82,154,38],"trust_level":"unsigned","yanked":false}
