diff --git a/.github/instructions/instruction-architecture.instructions.md b/.github/instructions/instruction-architecture.instructions.md new file mode 100644 index 000000000000..f379e18f4a01 --- /dev/null +++ b/.github/instructions/instruction-architecture.instructions.md @@ -0,0 +1,13 @@ +--- +applyTo: ".github/instructions/**,.github/agents/**" +--- + +# Editing Copilot content instruction and agent files + +This applies when you add, edit, or remove a Copilot instruction or shared agent file that guides how **content** (articles, data files) is written. It does **not** apply to code instructions or agents owned by the engineering team (for example `code.instructions.md`). + +When it applies, you **must** first read the instruction-architecture doc in full and follow it: + +https://github.com/github/docs-team/blob/main/contributing-to-docs/docs-work/copilot-instruction-architecture.md + +Read the current version every time (from a local `github/docs-team` checkout if you have one, otherwise fetch the URL); do not rely on your memory of it, because it changes. If you cannot access it, say so and stop rather than guessing. diff --git a/assets/images/help/copilot/copilot-sdk/features-agent-loop-diagram-1.png b/assets/images/help/copilot/copilot-sdk/features-agent-loop-diagram-1.png index 09030de7ef9b..99414b8c12b4 100644 Binary files a/assets/images/help/copilot/copilot-sdk/features-agent-loop-diagram-1.png and b/assets/images/help/copilot/copilot-sdk/features-agent-loop-diagram-1.png differ diff --git a/assets/images/help/copilot/copilot-sdk/features-agent-loop-diagram-2.png b/assets/images/help/copilot/copilot-sdk/features-agent-loop-diagram-2.png index 871721c4dcf0..eef0e9a03f4d 100644 Binary files a/assets/images/help/copilot/copilot-sdk/features-agent-loop-diagram-2.png and b/assets/images/help/copilot/copilot-sdk/features-agent-loop-diagram-2.png differ diff --git a/assets/images/help/copilot/copilot-sdk/setup-azure-managed-identity-diagram-0.png b/assets/images/help/copilot/copilot-sdk/setup-azure-managed-identity-diagram-0.png index 44d0054435a4..fe12e2592949 100644 Binary files a/assets/images/help/copilot/copilot-sdk/setup-azure-managed-identity-diagram-0.png and b/assets/images/help/copilot/copilot-sdk/setup-azure-managed-identity-diagram-0.png differ diff --git a/content/copilot/how-tos/copilot-sdk/auth/authenticate.md b/content/copilot/how-tos/copilot-sdk/auth/authenticate.md index 564360595e4c..d0758235e458 100644 --- a/content/copilot/how-tos/copilot-sdk/auth/authenticate.md +++ b/content/copilot/how-tos/copilot-sdk/auth/authenticate.md @@ -23,7 +23,7 @@ contentType: how-tos | [GitHub Signed-in User](#github-signed-in-user) | Interactive apps where users sign in with GitHub | Yes | | [OAuth GitHub App](#oauth-github-app) | Apps acting on behalf of users via OAuth | Yes | | [Environment Variables](#environment-variables) | CI/CD, automation, server-to-server | Yes | -| [AUTOTITLE](/copilot/how-tos/copilot-sdk/auth/byok) | Using your own API keys (Azure AI Foundry, OpenAI, etc.) | No | +| [AUTOTITLE](/copilot/how-tos/copilot-sdk/auth/byok) | Using your own API keys (Azure AI Foundry, OpenAI, and more) | No | ## GitHub signed-in user @@ -216,7 +216,7 @@ client.start().get(); **Supported token types:** * `gho_` - OAuth user access tokens -* `ghu_` - GitHub App user access tokens +* `ghu_` - GitHub App user access tokens * `github_pat_` - Fine-grained personal access tokens **Not supported:** @@ -269,7 +269,7 @@ await client.start() {% endcodetabs %} **When to use:** -* CI/CD pipelines (GitHub Actions, Jenkins, etc.) +* CI/CD pipelines (GitHub Actions, Jenkins, and more) * Automated testing * Server-side applications with service accounts * Development when you don't want to use interactive login diff --git a/content/copilot/how-tos/copilot-sdk/auth/byok.md b/content/copilot/how-tos/copilot-sdk/auth/byok.md index a24aa9f1cf69..ed7cfe3ba9c7 100644 --- a/content/copilot/how-tos/copilot-sdk/auth/byok.md +++ b/content/copilot/how-tos/copilot-sdk/auth/byok.md @@ -41,7 +41,7 @@ import os from copilot import CopilotClient from copilot.session import PermissionHandler -FOUNDRY_MODEL_URL = "https://your-resource.openai.azure.com/openai/v1/" +FOUNDRY_MODEL_URL = "https://.openai.azure.com/openai/v1/" # Set FOUNDRY_API_KEY environment variable async def main(): @@ -79,7 +79,7 @@ asyncio.run(main()) ```typescript import { CopilotClient } from "@github/copilot-sdk"; -const FOUNDRY_MODEL_URL = "https://your-resource.openai.azure.com/openai/v1/"; +const FOUNDRY_MODEL_URL = "https://.openai.azure.com/openai/v1/"; const client = new CopilotClient(); const session = await client.createSession({ @@ -125,7 +125,7 @@ func main() { Model: "gpt-5.2-codex", // Your deployment name Provider: &copilot.ProviderConfig{ Type: "openai", - BaseURL: "https://your-resource.openai.azure.com/openai/v1/", + BaseURL: "https://.openai.azure.com/openai/v1/", WireAPI: "responses", // Use "completions" for older models APIKey: os.Getenv("FOUNDRY_API_KEY"), }, @@ -160,7 +160,7 @@ await using var session = await client.CreateSessionAsync(new SessionConfig Provider = new ProviderConfig { Type = "openai", - BaseUrl = "https://your-resource.openai.azure.com/openai/v1/", + BaseUrl = "https://.openai.azure.com/openai/v1/", WireApi = "responses", // Use "completions" for older models ApiKey = Environment.GetEnvironmentVariable("FOUNDRY_API_KEY"), }, @@ -188,7 +188,7 @@ var session = client.createSession(new SessionConfig() .setOnPermissionRequest(PermissionHandler.APPROVE_ALL) .setProvider(new ProviderConfig() .setType("openai") - .setBaseUrl("https://your-resource.openai.azure.com/openai/v1/") + .setBaseUrl("https://.openai.azure.com/openai/v1/") .setWireApi("responses") // Use "completions" for older models .setApiKey(System.getenv("FOUNDRY_API_KEY"))) ).get(); @@ -213,6 +213,7 @@ client.stop().get(); | `baseUrl` / `base_url` | string | **Required.** API endpoint URL | | `apiKey` / `api_key` | string | API key (optional for local providers like Ollama) | | `bearerToken` / `bearer_token` | string | Bearer token auth (takes precedence over apiKey) | +| `bearerTokenProvider` / `bearer_token_provider` | callback | Returns a bearer token on demand (takes precedence over `apiKey` and `bearerToken`) | | `wireApi` / `wire_api` | `"completions"` \| `"responses"` | Select `"completions"` for broad model compatibility (the Chat Completions API); select `"responses"` for multi-turn state management, tool namespacing, and reasoning support (the Responses API). Anthropic models always use the Messages API regardless of this setting. | | `azure.apiVersion` / `azure.api_version` | string | Azure API version (default: `"2024-10-21"`) | @@ -274,7 +275,7 @@ For Azure AI Foundry deployments with `/openai/v1/` endpoints, use `type: "opena ```typescript provider: { type: "openai", - baseUrl: "https://your-resource.openai.azure.com/openai/v1/", + baseUrl: "https://.openai.azure.com/openai/v1/", apiKey: process.env.FOUNDRY_API_KEY, wireApi: "responses", // For GPT-5 series models } @@ -334,12 +335,14 @@ provider: { ### Bearer token authentication -Some providers require bearer token authentication instead of API keys: +Some providers require bearer token authentication instead of API keys. Supply a static token with `bearerToken`, or supply a `bearerTokenProvider` callback that the GitHub Copilot SDK runtime invokes before outbound provider requests. The callback or identity library it wraps manages token caching and refresh. + +Use `bearerToken` when your application already has a token: ```typescript provider: { type: "openai", - baseUrl: "https://my-custom-endpoint.example.com/v1", + baseUrl: "https://.openai.azure.com/openai/v1/", bearerToken: process.env.MY_BEARER_TOKEN, // Sets Authorization header } ``` @@ -347,6 +350,22 @@ provider: { > [!NOTE] > The `bearerToken` option accepts a **static token string** only. The SDK does not refresh this token automatically. If your token expires, requests will fail and you'll need to create a new session with a fresh token. +Use `bearerTokenProvider` to acquire tokens on demand: + + + +```typescript +provider: { + type: "openai", + baseUrl: "https://my-custom-endpoint.example.com/v1", + bearerTokenProvider: async () => { + return await acquireBearerToken(); + }, +} +``` + +For more details about acquiring and refreshing Microsoft Entra bearer tokens, see [AUTOTITLE](/copilot/how-tos/copilot-sdk/setup/azure-managed-identity). + ## Custom model listing When using BYOK, the CLI server may not know which models your provider supports. You can supply a custom `onListModels` handler at the client level so that `client.listModels()` returns your provider's models in the standard `ModelInfo` format. This lets downstream consumers discover available models without querying the CLI. @@ -531,7 +550,7 @@ import { CopilotClient } from "@github/copilot-sdk"; const client = new CopilotClient(); const session = await client.createSession({ - model: "gpt-4.1", + model: "gpt-5.4", provider: { type: "azure", baseUrl: "https://my-resource.openai.azure.com", @@ -564,7 +583,7 @@ import { CopilotClient } from "@github/copilot-sdk"; const client = new CopilotClient(); const session = await client.createSession({ - model: "gpt-4.1", + model: "gpt-5.4", provider: { type: "openai", baseUrl: "https://your-resource.openai.azure.com/openai/v1/", diff --git a/content/copilot/how-tos/copilot-sdk/features/cloud-sessions.md b/content/copilot/how-tos/copilot-sdk/features/cloud-sessions.md index 522537546bde..13b4c39c1d6a 100644 --- a/content/copilot/how-tos/copilot-sdk/features/cloud-sessions.md +++ b/content/copilot/how-tos/copilot-sdk/features/cloud-sessions.md @@ -252,7 +252,7 @@ Capture the URL by subscribing to `session.info` and filtering by `infoType: "re session.on("session.info", (event) => { if (event.data?.infoType === "remote" && event.data.url) { console.log("Open from web or mobile:", event.data.url); - // e.g. surface in your UI as a shareable link or QR code. + // For example, surface in your UI as a shareable link or QR code. } }); ``` diff --git a/content/copilot/how-tos/copilot-sdk/features/custom-agents.md b/content/copilot/how-tos/copilot-sdk/features/custom-agents.md index 87b42f8122d5..06601dca30e5 100644 --- a/content/copilot/how-tos/copilot-sdk/features/custom-agents.md +++ b/content/copilot/how-tos/copilot-sdk/features/custom-agents.md @@ -43,7 +43,7 @@ const client = new CopilotClient(); await client.start(); const session = await client.createSession({ - model: "gpt-4.1", + model: "gpt-5.4", customAgents: [ { name: "researcher", @@ -75,7 +75,7 @@ await client.start() session = await client.create_session( on_permission_request=lambda req, inv: PermissionDecisionApproveOnce(), - model="gpt-4.1", + model="gpt-5.4", custom_agents=[ { "name": "researcher", @@ -113,7 +113,7 @@ func main() { client.Start(ctx) session, _ := client.CreateSession(ctx, &copilot.SessionConfig{ - Model: "gpt-4.1", + Model: "gpt-5.4", CustomAgents: []copilot.CustomAgentConfig{ { Name: "researcher", @@ -144,7 +144,7 @@ client := copilot.NewClient(nil) client.Start(ctx) session, _ := client.CreateSession(ctx, &copilot.SessionConfig{ - Model: "gpt-4.1", + Model: "gpt-5.4", CustomAgents: []copilot.CustomAgentConfig{ { Name: "researcher", @@ -177,7 +177,7 @@ using GitHub.Copilot.Rpc; await using var client = new CopilotClient(); await using var session = await client.CreateSessionAsync(new SessionConfig { - Model = "gpt-4.1", + Model = "gpt-5.4", CustomAgents = new List { new() @@ -215,7 +215,7 @@ try (var client = new CopilotClient()) { var session = client.createSession( new SessionConfig() - .setModel("gpt-4.1") + .setModel("gpt-5.4") .setCustomAgents(List.of( new CustomAgentConfig() .setName("researcher") @@ -250,10 +250,14 @@ try (var client = new CopilotClient()) { | `mcpServers` | `object` | | MCP server configurations specific to this agent | | `infer` | `boolean` | | Whether the runtime can auto-select this agent (default: `true`) | | `skills` | `string[]` | | Skill names to preload into the agent's context at startup | +| `model` | `string` | | Model identifier to use while this agent runs | +| `reasoningEffort` | `string` | | Reasoning effort to use while this agent runs. When omitted, no override is sent and the backend chooses its default | > [!TIP] > A good `description` helps the runtime match user intent to the right agent. Be specific about the agent's expertise and capabilities. +Set `model` and `reasoningEffort` to override the parent session's model settings while a custom agent runs. When `reasoningEffort` is omitted, the SDK sends no per-agent override and the backend chooses its default. The parent session effort is not inherited, and the SDK does not add a per-agent default. Python uses `reasoning_effort`, .NET uses `ReasoningEffort`, Go uses `ReasoningEffort`, Java uses `setReasoningEffort`, and Rust uses `with_reasoning_effort`. + In addition to per-agent configuration above, you can set `agent` on the **session config** itself to pre-select which custom agent is active when the session starts. See [Selecting an Agent at Session Creation](#selecting-an-agent-at-session-creation) below. | Session Config Property | Type | Description | @@ -429,6 +433,8 @@ By default, all custom agents are available for automatic selection (`infer: tru When a sub-agent runs, the parent session emits lifecycle events. Subscribe to these events to build UIs that visualize agent activity. +Sub-agent-originated session events share the parent session stream and include envelope-level `agentId`. Root/main agent events and session-level events omit `agentId`, so renderers can keep the parent response separate from sub-agent traces by checking the event envelope. + ### Event types | Event | Emitted when | Data | @@ -519,7 +525,7 @@ func main() { client.Start(ctx) session, _ := client.CreateSession(ctx, &copilot.SessionConfig{ - Model: "gpt-4.1", + Model: "gpt-5.4", OnPermissionRequest: func(req copilot.PermissionRequest, inv copilot.PermissionInvocation) (rpc.PermissionDecision, error) { return &rpc.PermissionDecisionApproveOnce{}, nil }, diff --git a/content/copilot/how-tos/copilot-sdk/features/image-input.md b/content/copilot/how-tos/copilot-sdk/features/image-input.md index db4f625e9edd..a94084ae0112 100644 --- a/content/copilot/how-tos/copilot-sdk/features/image-input.md +++ b/content/copilot/how-tos/copilot-sdk/features/image-input.md @@ -44,7 +44,7 @@ const client = new CopilotClient(); await client.start(); const session = await client.createSession({ - model: "gpt-4.1", + model: "gpt-5.4", onPermissionRequest: async () => ({ kind: "approve-once" }), }); @@ -70,7 +70,7 @@ await client.start() session = await client.create_session( on_permission_request=lambda req, inv: PermissionDecisionApproveOnce(), - model="gpt-4.1", + model="gpt-5.4", ) await session.send( @@ -102,7 +102,7 @@ func main() { client.Start(ctx) session, _ := client.CreateSession(ctx, &copilot.SessionConfig{ - Model: "gpt-4.1", + Model: "gpt-5.4", OnPermissionRequest: func(req copilot.PermissionRequest, inv copilot.PermissionInvocation) (rpc.PermissionDecision, error) { return &rpc.PermissionDecisionApproveOnce{}, nil }, @@ -127,7 +127,7 @@ client := copilot.NewClient(nil) client.Start(ctx) session, _ := client.CreateSession(ctx, &copilot.SessionConfig{ - Model: "gpt-4.1", + Model: "gpt-5.4", OnPermissionRequest: func(req copilot.PermissionRequest, inv copilot.PermissionInvocation) (rpc.PermissionDecision, error) { return &rpc.PermissionDecisionApproveOnce{}, nil }, @@ -159,7 +159,7 @@ public static class ImageInputExample await using var client = new CopilotClient(); await using var session = await client.CreateSessionAsync(new SessionConfig { - Model = "gpt-4.1", + Model = "gpt-5.4", OnPermissionRequest = (req, inv) => Task.FromResult(PermissionDecision.ApproveOnce()), }); @@ -187,7 +187,7 @@ using GitHub.Copilot.Rpc; await using var client = new CopilotClient(); await using var session = await client.CreateSessionAsync(new SessionConfig { - Model = "gpt-4.1", + Model = "gpt-5.4", OnPermissionRequest = (req, inv) => Task.FromResult(PermissionDecision.ApproveOnce()), }); @@ -219,7 +219,7 @@ try (var client = new CopilotClient()) { var session = client.createSession( new SessionConfig() - .setModel("gpt-4.1") + .setModel("gpt-5.4") .setOnPermissionRequest(PermissionHandler.APPROVE_ALL) ).get(); @@ -249,7 +249,7 @@ const client = new CopilotClient(); await client.start(); const session = await client.createSession({ - model: "gpt-4.1", + model: "gpt-5.4", onPermissionRequest: async () => ({ kind: "approve-once" }), }); @@ -278,7 +278,7 @@ await client.start() session = await client.create_session( on_permission_request=lambda req, inv: PermissionDecisionApproveOnce(), - model="gpt-4.1", + model="gpt-5.4", ) base64_image_data = "..." # your base64-encoded image @@ -313,7 +313,7 @@ func main() { client.Start(ctx) session, _ := client.CreateSession(ctx, &copilot.SessionConfig{ - Model: "gpt-4.1", + Model: "gpt-5.4", OnPermissionRequest: func(req copilot.PermissionRequest, inv copilot.PermissionInvocation) (rpc.PermissionDecision, error) { return &rpc.PermissionDecisionApproveOnce{}, nil }, @@ -364,7 +364,7 @@ public static class BlobAttachmentExample await using var client = new CopilotClient(); await using var session = await client.CreateSessionAsync(new SessionConfig { - Model = "gpt-4.1", + Model = "gpt-5.4", OnPermissionRequest = (req, inv) => Task.FromResult(PermissionDecision.ApproveOnce()), }); @@ -416,7 +416,7 @@ try (var client = new CopilotClient()) { var session = client.createSession( new SessionConfig() - .setModel("gpt-4.1") + .setModel("gpt-5.4") .setOnPermissionRequest(PermissionHandler.APPROVE_ALL) ).get(); diff --git a/content/copilot/how-tos/copilot-sdk/features/skills.md b/content/copilot/how-tos/copilot-sdk/features/skills.md index 3e2fd99814d4..08acedf9f77d 100644 --- a/content/copilot/how-tos/copilot-sdk/features/skills.md +++ b/content/copilot/how-tos/copilot-sdk/features/skills.md @@ -38,7 +38,7 @@ import { CopilotClient } from "@github/copilot-sdk"; const client = new CopilotClient(); const session = await client.createSession({ - model: "gpt-4.1", + model: "gpt-5.4", skillDirectories: [ "./skills/code-review", "./skills/documentation", @@ -62,7 +62,7 @@ async def main(): session = await client.create_session( on_permission_request=lambda req, inv: PermissionDecisionApproveOnce(), - model="gpt-4.1", + model="gpt-5.4", skill_directories=[ "./skills/code-review", "./skills/documentation", @@ -97,7 +97,7 @@ func main() { defer client.Stop() session, err := client.CreateSession(ctx, &copilot.SessionConfig{ - Model: "gpt-4.1", + Model: "gpt-5.4", SkillDirectories: []string{ "./skills/code-review", "./skills/documentation", @@ -130,7 +130,7 @@ using GitHub.Copilot.Rpc; await using var client = new CopilotClient(); await using var session = await client.CreateSessionAsync(new SessionConfig { - Model = "gpt-4.1", + Model = "gpt-5.4", SkillDirectories = new List { "./skills/code-review", @@ -160,7 +160,7 @@ try (var client = new CopilotClient()) { var session = client.createSession( new SessionConfig() - .setModel("gpt-4.1") + .setModel("gpt-5.4") .setSkillDirectories(List.of( "./skills/code-review", "./skills/documentation" diff --git a/content/copilot/how-tos/copilot-sdk/features/steering-and-queueing.md b/content/copilot/how-tos/copilot-sdk/features/steering-and-queueing.md index 85ef4660148e..ea797ef82f23 100644 --- a/content/copilot/how-tos/copilot-sdk/features/steering-and-queueing.md +++ b/content/copilot/how-tos/copilot-sdk/features/steering-and-queueing.md @@ -41,7 +41,7 @@ const client = new CopilotClient(); await client.start(); const session = await client.createSession({ - model: "gpt-4.1", + model: "gpt-5.4", onPermissionRequest: async () => ({ kind: "approve-once" }), }); @@ -69,7 +69,7 @@ async def main(): session = await client.create_session( on_permission_request=lambda req, inv: PermissionDecisionApproveOnce(), - model="gpt-4.1", + model="gpt-5.4", ) # Start a long-running task @@ -108,7 +108,7 @@ func main() { defer client.Stop() session, err := client.CreateSession(ctx, &copilot.SessionConfig{ - Model: "gpt-4.1", + Model: "gpt-5.4", OnPermissionRequest: func(req copilot.PermissionRequest, inv copilot.PermissionInvocation) (rpc.PermissionDecision, error) { return &rpc.PermissionDecisionApproveOnce{}, nil }, @@ -146,7 +146,7 @@ using GitHub.Copilot.Rpc; await using var client = new CopilotClient(); await using var session = await client.CreateSessionAsync(new SessionConfig { - Model = "gpt-4.1", + Model = "gpt-5.4", OnPermissionRequest = (req, inv) => Task.FromResult(PermissionDecision.ApproveOnce()), }); @@ -177,7 +177,7 @@ try (var client = new CopilotClient()) { var session = client.createSession( new SessionConfig() - .setModel("gpt-4.1") + .setModel("gpt-5.4") .setOnPermissionRequest(PermissionHandler.APPROVE_ALL) ).get(); @@ -221,7 +221,7 @@ const client = new CopilotClient(); await client.start(); const session = await client.createSession({ - model: "gpt-4.1", + model: "gpt-5.4", onPermissionRequest: async () => ({ kind: "approve-once" }), }); @@ -254,7 +254,7 @@ async def main(): session = await client.create_session( on_permission_request=lambda req, inv: PermissionDecisionApproveOnce(), - model="gpt-4.1", + model="gpt-5.4", ) # Send an initial task @@ -293,7 +293,7 @@ func main() { client.Start(ctx) session, _ := client.CreateSession(ctx, &copilot.SessionConfig{ - Model: "gpt-4.1", + Model: "gpt-5.4", OnPermissionRequest: func(req copilot.PermissionRequest, inv copilot.PermissionInvocation) (rpc.PermissionDecision, error) { return &rpc.PermissionDecisionApproveOnce{}, nil }, @@ -349,7 +349,7 @@ public static class QueueingExample await using var client = new CopilotClient(); await using var session = await client.CreateSessionAsync(new SessionConfig { - Model = "gpt-4.1", + Model = "gpt-5.4", OnPermissionRequest = (req, inv) => Task.FromResult(PermissionDecision.ApproveOnce()), }); @@ -409,7 +409,7 @@ try (var client = new CopilotClient()) { var session = client.createSession( new SessionConfig() - .setModel("gpt-4.1") + .setModel("gpt-5.4") .setOnPermissionRequest(PermissionHandler.APPROVE_ALL) ).get(); @@ -451,7 +451,7 @@ You can use both patterns together in a single session. Steering affects the cur ```typescript const session = await client.createSession({ - model: "gpt-4.1", + model: "gpt-5.4", onPermissionRequest: async () => ({ kind: "approve-once" }), }); @@ -477,7 +477,7 @@ await session.send({ ```python session = await client.create_session( on_permission_request=lambda req, inv: PermissionDecisionApproveOnce(), - model="gpt-4.1", + model="gpt-5.4", ) # Start a task diff --git a/content/copilot/how-tos/copilot-sdk/features/streaming-events.md b/content/copilot/how-tos/copilot-sdk/features/streaming-events.md index 65d4cadd7411..2bc4f130b6a0 100644 --- a/content/copilot/how-tos/copilot-sdk/features/streaming-events.md +++ b/content/copilot/how-tos/copilot-sdk/features/streaming-events.md @@ -39,6 +39,7 @@ Every session event, regardless of type, includes these fields: | `id` | `string` (UUID v4) | Unique event identifier | | `timestamp` | `string` (ISO 8601) | When the event was created | | `parentId` | `string \| null` | ID of the previous event in the chain; `null` for the first event | +| `agentId` | `string?` | Sub-agent instance ID for sub-agent-originated events; absent for root/main agent and session-level events | | `ephemeral` | `boolean?` | `true` for transient events; absent or `false` for persisted events | | `type` | `string` | Event type discriminator (see tables below) | | `data` | `object` | Event-specific payload | @@ -106,7 +107,7 @@ func main() { client := copilot.NewClient(nil) session, _ := client.CreateSession(ctx, &copilot.SessionConfig{ - Model: "gpt-4.1", + Model: "gpt-5.4", Streaming: copilot.Bool(true), OnPermissionRequest: func(req copilot.PermissionRequest, inv copilot.PermissionInvocation) (rpc.PermissionDecision, error) { return &rpc.PermissionDecisionApproveOnce{}, nil @@ -188,6 +189,130 @@ session.on(AssistantMessageDeltaEvent.class, event -> > [!TIP] > **(TypeScript)** The TypeScript SDK uses a discriminated union—when you match on `event.type`, the `data` payload is automatically narrowed to the correct shape. +## Render only the parent agent response + +Sub-agent events share the parent session stream and include envelope-level `agentId`. Root/main agent events and session-level events omit `agentId`, so main-chat renderers can ignore assistant events where `agentId` is set and route those events to traces or progress UI instead. + +{% codetabs %} +{% codetab typescript %} + +```typescript +import type { CopilotSession } from "@github/copilot-sdk"; + +export function subscribeParentResponse(session: CopilotSession): void { + session.on("assistant.message_delta", (event) => { + if (!event.agentId) { + process.stdout.write(event.data.deltaContent); + } + }); +} +``` + +{% endcodetab %} +{% codetab python %} + +```python +from copilot import CopilotSession, SessionEvent, SessionEventType +from copilot.session_events import AssistantMessageDeltaData + + +def subscribe_parent_response(session: CopilotSession) -> None: + def handle(event: SessionEvent) -> None: + if event.type == SessionEventType.ASSISTANT_MESSAGE_DELTA and event.agent_id is None: + data = event.data + if isinstance(data, AssistantMessageDeltaData): + print(data.delta_content, end="", flush=True) + + session.on(handle) +``` + +{% endcodetab %} +{% codetab go %} + +```golang +package example + +import ( + "fmt" + + copilot "github.com/github/copilot-sdk/go" +) + +func subscribeParentResponse(session *copilot.Session) { + session.On(func(event copilot.SessionEvent) { + if event.AgentID != nil { + return + } + + if d, ok := event.Data.(*copilot.AssistantMessageDeltaData); ok { + fmt.Print(d.DeltaContent) + } + }) +} +``` + +{% endcodetab %} +{% codetab dotnet %} + +```csharp +using System; +using GitHub.Copilot; + +static class ParentAgentResponseExample +{ + public static void SubscribeParentResponse(CopilotSession session) + { + session.On(evt => + { + if (evt.AgentId is null) + { + Console.Write(evt.Data.DeltaContent); + } + }); + } +} +``` + +{% endcodetab %} +{% codetab java %} + +```java +import com.github.copilot.CopilotSession; +import com.github.copilot.generated.AssistantMessageDeltaEvent; + +final class ParentAgentResponseExample { + static void subscribeParentResponse(CopilotSession session) { + session.on(AssistantMessageDeltaEvent.class, event -> { + if (event.getAgentId() == null) { + System.out.print(event.getData().deltaContent()); + } + }); + } +} +``` + +{% endcodetab %} +{% codetab rust %} + +```rust +use github_copilot_sdk::session::Session; + +async fn subscribe_parent_response(session: &Session) { + let mut events = session.subscribe(); + + while let Ok(event) = events.recv().await { + if event.event_type == "assistant.message_delta" && event.agent_id.is_none() { + if let Some(delta) = event.data.get("deltaContent").and_then(|v| v.as_str()) { + print!("{delta}"); + } + } + } +} +``` + +{% endcodetab %} +{% endcodetabs %} + ## Assistant events These events track the agent's response lifecycle—from turn start through streaming chunks to the final message. @@ -242,7 +367,7 @@ The assistant's complete response for this LLM call. May include tool invocation | `phase` | `string` | | Generation phase (e.g., `"thinking"` vs `"response"`) | | `outputTokens` | `number` | | Actual output token count from the API response | | `interactionId` | `string` | | CAPI interaction ID for telemetry | -| `parentToolCallId` | `string` | | Set when this message originates from a sub-agent | +| `parentToolCallId` | `string` | | Deprecated. Use envelope-level `agentId` for sub-agent attribution | **`ToolRequest` fields:** @@ -261,7 +386,7 @@ Ephemeral. Incremental chunk of the assistant's text response, streamed in real |------------|------|----------|-------------| | `messageId` | `string` | ✅ | Matches the corresponding `assistant.message` event | | `deltaContent` | `string` | ✅ | Text chunk to append to the message | -| `parentToolCallId` | `string` | | Set when originating from a sub-agent | +| `parentToolCallId` | `string` | | Deprecated. Use envelope-level `agentId` for sub-agent attribution | ### `assistant.turn_end` @@ -277,7 +402,7 @@ Ephemeral. Token usage and cost information for an individual API call. | Data Field | Type | Required | Description | |------------|------|----------|-------------| -| `model` | `string` | ✅ | Model identifier (e.g., `"gpt-4.1"`) | +| `model` | `string` | ✅ | Model identifier (e.g., `"gpt-5.4"`) | | `inputTokens` | `number` | | Input tokens consumed | | `outputTokens` | `number` | | Output tokens produced | | `cacheReadTokens` | `number` | | Tokens read from prompt cache | @@ -288,7 +413,7 @@ Ephemeral. Token usage and cost information for an individual API call. | `apiCallId` | `string` | | Completion ID from the provider (e.g., `chatcmpl-abc123`) | | `apiEndpoint` | `"/chat/completions" \| "/v1/messages" \| "/responses" \| "ws:/responses"` | | API endpoint used for the model call; useful for observability and cost attribution. `ws:/responses` is the websocket variant of the responses API | | `providerCallId` | `string` | | GitHub request tracing ID (`x-github-request-id`) | -| `parentToolCallId` | `string` | | Set when usage originates from a sub-agent | +| `parentToolCallId` | `string` | | Deprecated. Use envelope-level `agentId` for sub-agent attribution | | `quotaSnapshots` | `Record` | | Per-quota resource usage, keyed by quota identifier | | `copilotUsage` | `CopilotUsage` | | Itemized token cost breakdown from the API | @@ -315,7 +440,7 @@ Emitted when a tool begins executing. | `arguments` | `object` | | Parsed arguments passed to the tool | | `mcpServerName` | `string` | | MCP server name, when the tool is provided by an MCP server | | `mcpToolName` | `string` | | Original tool name on the MCP server | -| `parentToolCallId` | `string` | | Set when invoked by a sub-agent | +| `parentToolCallId` | `string` | | Deprecated. Use envelope-level `agentId` for sub-agent attribution | ### `tool.execution_partial_result` @@ -349,7 +474,7 @@ Emitted when a tool finishes executing—successfully or with an error. | `result` | `Result` | | Present on success (see below) | | `error` | `{ message, code? }` | | Present on failure | | `toolTelemetry` | `object` | | Tool-specific telemetry (e.g., CodeQL check counts) | -| `parentToolCallId` | `string` | | Set when invoked by a sub-agent | +| `parentToolCallId` | `string` | | Deprecated. Use envelope-level `agentId` for sub-agent attribution | **`Result` fields:** @@ -760,6 +885,8 @@ session.idle → Ready for next message (ephemeral) ## All event types at a glance +This table lists key `data` payload fields. Common envelope fields are documented above. + | Event Type | Ephemeral | Category | Key Data Fields | |------------|-----------|----------|-----------------| | `assistant.turn_start` | | Assistant | `turnId`, `interactionId?` | @@ -768,7 +895,7 @@ session.idle → Ready for next message (ephemeral) | `assistant.reasoning_delta` | ✅ | Assistant | `reasoningId`, `deltaContent` | | `assistant.streaming_delta` | ✅ | Assistant | `totalResponseSizeBytes` | | `assistant.message` | | Assistant | `messageId`, `content`, `toolRequests?`, `outputTokens?`, `phase?` | -| `assistant.message_delta` | ✅ | Assistant | `messageId`, `deltaContent`, `parentToolCallId?` | +| `assistant.message_delta` | ✅ | Assistant | `messageId`, `deltaContent` | | `assistant.turn_end` | | Assistant | `turnId` | | `assistant.usage` | ✅ | Assistant | `model`, `apiEndpoint?`, `inputTokens?`, `outputTokens?`, `cost?`, `duration?` | | `tool.user_requested` | | Tool | `toolCallId`, `toolName`, `arguments?` | diff --git a/content/copilot/how-tos/copilot-sdk/index.md b/content/copilot/how-tos/copilot-sdk/index.md index 227c147609d7..094b1f3411a1 100644 --- a/content/copilot/how-tos/copilot-sdk/index.md +++ b/content/copilot/how-tos/copilot-sdk/index.md @@ -22,3 +22,4 @@ children: + diff --git a/content/copilot/how-tos/copilot-sdk/integrations/microsoft-agent-framework.md b/content/copilot/how-tos/copilot-sdk/integrations/microsoft-agent-framework.md index 299ef507dc71..bf97add8a1f3 100644 --- a/content/copilot/how-tos/copilot-sdk/integrations/microsoft-agent-framework.md +++ b/content/copilot/how-tos/copilot-sdk/integrations/microsoft-agent-framework.md @@ -132,7 +132,7 @@ var client = new CopilotClient(); client.start().get(); var session = client.createSession(new SessionConfig() - .setModel("gpt-4.1") + .setModel("gpt-5.4") .setOnPermissionRequest(PermissionHandler.APPROVE_ALL) ).get(); @@ -228,7 +228,7 @@ const getWeather = DefineTool({ const client = new CopilotClient(); const session = await client.createSession({ - model: "gpt-4.1", + model: "gpt-5.4", tools: [getWeather], onPermissionRequest: async () => ({ kind: "approve-once" }), }); @@ -264,7 +264,7 @@ try (var client = new CopilotClient()) { client.start().get(); var session = client.createSession(new SessionConfig() - .setModel("gpt-4.1") + .setModel("gpt-5.4") .setTools(List.of(getWeather)) .setOnPermissionRequest(PermissionHandler.APPROVE_ALL) ).get(); @@ -307,7 +307,7 @@ AIAgent reviewer = copilotClient.AsAIAgent(new AIAgentOptions // Azure OpenAI agent for generating documentation AIAgent documentor = AIAgent.FromOpenAI(new OpenAIAgentOptions { - Model = "gpt-4.1", + Model = "gpt-5.4", Instructions = "You write clear, concise documentation for code changes.", }); @@ -340,7 +340,7 @@ async def main(): # OpenAI agent for documentation documentor = OpenAIAgent( - model="gpt-4.1", + model="gpt-5.4", instructions="You write clear, concise documentation for code changes.", ) @@ -369,7 +369,7 @@ client.start().get(); // Step 1: Code review session var reviewer = client.createSession(new SessionConfig() - .setModel("gpt-4.1") + .setModel("gpt-5.4") .setOnPermissionRequest(PermissionHandler.APPROVE_ALL) ).get(); @@ -379,7 +379,7 @@ var review = reviewer.sendAndWait(new MessageOptions() // Step 2: Documentation session using review output var documentor = client.createSession(new SessionConfig() - .setModel("gpt-4.1") + .setModel("gpt-5.4") .setOnPermissionRequest(PermissionHandler.APPROVE_ALL) ).get(); @@ -444,12 +444,12 @@ var client = new CopilotClient(); client.start().get(); var securitySession = client.createSession(new SessionConfig() - .setModel("gpt-4.1") + .setModel("gpt-5.4") .setOnPermissionRequest(PermissionHandler.APPROVE_ALL) ).get(); var perfSession = client.createSession(new SessionConfig() - .setModel("gpt-4.1") + .setModel("gpt-5.4") .setOnPermissionRequest(PermissionHandler.APPROVE_ALL) ).get(); @@ -530,7 +530,7 @@ import { CopilotClient } from "@github/copilot-sdk"; const client = new CopilotClient(); const session = await client.createSession({ - model: "gpt-4.1", + model: "gpt-5.4", streaming: true, onPermissionRequest: async () => ({ kind: "approve-once" }), }); @@ -555,7 +555,7 @@ var client = new CopilotClient(); client.start().get(); var session = client.createSession(new SessionConfig() - .setModel("gpt-4.1") + .setModel("gpt-5.4") .setStreaming(true) .setOnPermissionRequest(PermissionHandler.APPROVE_ALL) ).get(); @@ -610,7 +610,7 @@ import { CopilotClient } from "@github/copilot-sdk"; const client = new CopilotClient(); const session = await client.createSession({ - model: "gpt-4.1", + model: "gpt-5.4", onPermissionRequest: async () => ({ kind: "approve-once" }), }); const response = await session.sendAndWait({ prompt: "Explain this code" }); diff --git a/content/copilot/how-tos/copilot-sdk/setup/azure-managed-identity.md b/content/copilot/how-tos/copilot-sdk/setup/azure-managed-identity.md index 7f228f5edcf9..cad294875432 100644 --- a/content/copilot/how-tos/copilot-sdk/setup/azure-managed-identity.md +++ b/content/copilot/how-tos/copilot-sdk/setup/azure-managed-identity.md @@ -2,11 +2,12 @@ title: Azure managed identity with BYOK shortTitle: Azure Managed Identity intro: >- - The Copilot SDK's [AUTOTITLE](/copilot/how-tos/copilot-sdk/auth/byok) accepts static API keys, but - Azure deployments often use **Managed Identity** (Microsoft Entra ID) instead - of long-lived keys. Since the SDK doesn't natively support Microsoft Entra - authentication, you can use a short-lived bearer token via the `bearer_token` - provider config field. + The GitHub Copilot SDK's [AUTOTITLE](/copilot/how-tos/copilot-sdk/auth/byok) supports static API + keys, but Azure deployments often use **Managed Identity** (Microsoft Entra + ID) instead of long-lived keys. The GitHub Copilot SDK is designed to compose + with the Azure Identity SDK for maximum flexibility. Supply a bearer token + provider callback that can fetch fresh tokens on demand using an Azure + Identity SDK API. versions: fpt: '*' ghec: '*' @@ -18,15 +19,17 @@ contentType: how-tos -This guide shows how to use the Azure Identity SDK's `DefaultAzureCredential` API to authenticate with Microsoft Foundry models through the Copilot SDK. +This guide shows how to use Azure Identity SDK APIs to authenticate with Microsoft Foundry models through the GitHub Copilot SDK. Most languages use `DefaultAzureCredential`; Rust uses `DeveloperToolsCredential` locally and `ManagedIdentityCredential` in Azure. ## How it works -Microsoft Foundry's OpenAI-compatible endpoint accepts bearer tokens from Microsoft Entra ID in place of static API keys. The pattern is: +Microsoft Foundry's OpenAI-compatible endpoint (`https://.openai.azure.com/openai/v1/`) accepts bearer tokens from Microsoft Entra ID in place of static API keys. This guide uses a token provider callback so the GitHub Copilot SDK runtime can request fresh tokens on demand. -1. Use `DefaultAzureCredential` to obtain a token for the `https://ai.azure.com/.default` scope -1. Pass the token as the `bearer_token` in the BYOK provider config -1. Refresh the token before it expires (tokens are typically valid for ~1 hour) +Using Python as an example, the flow is: + +1. Configure `DefaultAzureCredential` for your environment. +1. Pass a callback, in `bearer_token_provider` of the BYOK provider configuration, that uses `DefaultAzureCredential` to obtain a token for the `https://ai.azure.com/.default` scope. +1. Let the GitHub Copilot SDK request fresh tokens on demand through that callback. ![Diagram: Sequence diagram showing the described process.](/assets/images/help/copilot/copilot-sdk/setup-azure-managed-identity-diagram-0.png) @@ -34,7 +37,7 @@ Microsoft Foundry's OpenAI-compatible endpoint accepts bearer tokens from Micros ### Prerequisites -Install the Azure Identity and Copilot SDK packages for your language: +Install the Azure Identity and GitHub Copilot SDK packages for your language: {% codetabs %} {% codetab dotnet %} @@ -46,6 +49,35 @@ dotnet add package GitHub.Copilot.SDK dotnet add package Azure.Core ``` +{% endcodetab %} +{% codetab go %} + + + +```bash +go get github.com/github/copilot-sdk/go +go get github.com/Azure/azure-sdk-for-go/sdk/azidentity +``` + +{% endcodetab %} +{% codetab java %} + + + +```xml + + com.github + copilot-sdk-java + ${copilot.sdk.version} + + + + com.azure + azure-identity + ${azure.identity.version} + +``` + {% endcodetab %} {% codetab python %} @@ -55,6 +87,16 @@ dotnet add package Azure.Core pip install github-copilot-sdk azure-identity ``` +{% endcodetab %} +{% codetab rust %} + + + +```bash +cargo add github-copilot-sdk azure_identity azure_core +cargo add tokio --features macros,rt-multi-thread +``` + {% endcodetab %} {% codetab typescript %} @@ -67,9 +109,11 @@ npm install @github/copilot-sdk @azure/identity {% endcodetab %} {% endcodetabs %} -### Basic usage +### Use a token provider callback + +Use this approach when you want the GitHub Copilot SDK runtime to request fresh tokens on demand through a callback that you provide. The Azure Identity SDK handles token caching and refresh timing. -Get a token using `DefaultAzureCredential` and pass it as the bearer token in your provider configuration: +Here are language-specific implementations: {% codetabs %} {% codetab dotnet %} @@ -83,11 +127,8 @@ using GitHub.Copilot; DefaultAzureCredential credential = new( DefaultAzureCredential.DefaultEnvironmentVariableName); -AccessToken token = await credential.GetTokenAsync( - new TokenRequestContext(new[] { "https://ai.azure.com/.default" })); - await using CopilotClient client = new(); -string? foundryUrl = Environment.GetEnvironmentVariable("FOUNDRY_RESOURCE_URL"); +string foundryUrl = Environment.GetEnvironmentVariable("FOUNDRY_RESOURCE_URL")!; await using CopilotSession session = await client.CreateSessionAsync(new SessionConfig { @@ -95,8 +136,13 @@ await using CopilotSession session = await client.CreateSessionAsync(new Session Provider = new ProviderConfig { Type = "openai", - BaseUrl = $"{foundryUrl!.TrimEnd('/')}/openai/v1/", - BearerToken = token.Token, + BaseUrl = $"{foundryUrl}/openai/v1/", + BearerTokenProvider = async _ => + { + AccessToken token = await credential.GetTokenAsync( + new TokenRequestContext(["https://ai.azure.com/.default"])); + return token.Token; + }, WireApi = "responses", }, }); @@ -106,6 +152,133 @@ AssistantMessageEvent? response = await session.SendAndWaitAsync( Console.WriteLine(response?.Data.Content); ``` +{% endcodetab %} +{% codetab go %} + + + +```golang +package main + +import ( + "context" + "fmt" + "log" + "os" + "time" + + "github.com/Azure/azure-sdk-for-go/sdk/azcore/policy" + "github.com/Azure/azure-sdk-for-go/sdk/azidentity" + copilot "github.com/github/copilot-sdk/go" +) +func main() { + opts := azidentity.DefaultAzureCredentialOptions{RequireAzureTokenCredentials: true} + credential, err := azidentity.NewDefaultAzureCredential(&opts) + if err != nil { + log.Fatal(err) + } + + getBearerToken := func(args copilot.ProviderTokenArgs) (string, error) { + token, err := credential.GetToken(context.Background(), policy.TokenRequestOptions{ + Scopes: []string{"https://ai.azure.com/.default"}, + }) + if err != nil { + return "", err + } + return token.Token, nil + } + + client := copilot.NewClient(nil) + if err := client.Start(context.Background()); err != nil { + log.Fatal(err) + } + defer client.Stop() + + foundryURL := os.Getenv("FOUNDRY_RESOURCE_URL") + + session, err := client.CreateSession(context.Background(), &copilot.SessionConfig{ + Model: "gpt-5.5", + Provider: &copilot.ProviderConfig{ + Type: "openai", + BaseURL: fmt.Sprintf("%s/openai/v1/", foundryURL), + BearerTokenProvider: getBearerToken, + WireAPI: "responses", + }, + }) + if err != nil { + log.Fatal(err) + } + defer session.Disconnect() + + ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second) + defer cancel() + + response, err := session.SendAndWait(ctx, copilot.MessageOptions{ + Prompt: "Hello from Managed Identity!", + }) + if err != nil { + log.Fatal(err) + } + + if response != nil { + if data, ok := response.Data.(*copilot.AssistantMessageData); ok { + fmt.Println(data.Content) + } + } +} +``` + +{% endcodetab %} +{% codetab java %} + + + +```java +import com.azure.core.credential.TokenRequestContext; +import com.azure.identity.AzureIdentityEnvVars; +import com.azure.identity.DefaultAzureCredentialBuilder; +import com.github.copilot.CopilotClient; +import com.github.copilot.generated.AssistantMessageEvent; +import com.github.copilot.rpc.BearerTokenProvider; +import com.github.copilot.rpc.MessageOptions; +import com.github.copilot.rpc.ProviderConfig; +import com.github.copilot.rpc.SessionConfig; + +public class ManagedIdentityExample { + public static void main(String[] args) throws Exception { + var credential = new DefaultAzureCredentialBuilder() + .requireEnvVars(AzureIdentityEnvVars.AZURE_TOKEN_CREDENTIALS) + .build(); + BearerTokenProvider tokenProvider = providerArgs -> + credential + .getToken(new TokenRequestContext().addScopes("https://ai.azure.com/.default")) + .map(accessToken -> accessToken.getToken()) + .toFuture(); + String foundryUrl = System.getenv("FOUNDRY_RESOURCE_URL"); + + try (var client = new CopilotClient()) { + client.start().get(); + + var session = client.createSession(new SessionConfig() + .setModel("gpt-5.5") + .setProvider(new ProviderConfig() + .setType("openai") + .setBaseUrl(foundryUrl + "/openai/v1/") + .setBearerTokenProvider(tokenProvider) + .setWireApi("responses"))) + .get(); + + AssistantMessageEvent response = session + .sendAndWait(new MessageOptions().setPrompt("Hello from Managed Identity!")) + .get(); + System.out.println(response.getData().content()); + + session.disconnect().get(); + } + } +} +``` + {% endcodetab %} {% codetab python %} @@ -115,17 +288,15 @@ Console.WriteLine(response?.Data.Content); import asyncio import os -from azure.identity import DefaultAzureCredential +from azure.identity.aio import DefaultAzureCredential from copilot import CopilotClient from copilot.session import PermissionHandler, ProviderConfig -SCOPE = "https://ai.azure.com/.default" - - async def main(): - # Get a token using Managed Identity, Azure CLI, or other credential chain credential = DefaultAzureCredential(require_envvar=True) - token = credential.get_token(SCOPE).token + async def get_bearer_token(_args) -> str: + token = await credential.get_token("https://ai.azure.com/.default") + return token.token foundry_url = os.environ["FOUNDRY_RESOURCE_URL"] @@ -138,7 +309,7 @@ async def main(): provider=ProviderConfig( type="openai", base_url=f"{foundry_url.rstrip('/')}/openai/v1/", - bearer_token=token, # Short-lived bearer token + bearer_token_provider=get_bearer_token, wire_api="responses", ), ) @@ -147,11 +318,74 @@ async def main(): print(response.data.content) await client.stop() + await credential.close() asyncio.run(main()) ``` +{% endcodetab %} +{% codetab rust %} + + + +```rust +use std::sync::Arc; + +use azure_core::credentials::TokenCredential; +use azure_identity::{DeveloperToolsCredential, ManagedIdentityCredential}; +use github_copilot_sdk::{BearerTokenError, Client, ClientOptions, MessageOptions, ProviderTokenArgs}; +use github_copilot_sdk::types::{ProviderConfig, SessionConfig}; + +fn credential_for_environment() -> azure_core::Result> { + match std::env::var("AZURE_TOKEN_CREDENTIALS").as_deref() { + Ok("ManagedIdentityCredential") => Ok(ManagedIdentityCredential::new(None)?), + _ => Ok(DeveloperToolsCredential::new(None)?), + } +} + +#[tokio::main] +async fn main() -> Result<(), Box> { + let credential = credential_for_environment()?; + let foundry_url = std::env::var("FOUNDRY_RESOURCE_URL")?; + + let get_bearer_token = { + let credential = credential.clone(); + move |_args: ProviderTokenArgs| { + let credential = credential.clone(); + async move { + let token = credential + .get_token(&["https://ai.azure.com/.default"], None) + .await + .map_err(|err| BearerTokenError::message(err.to_string()))?; + Ok(token.token.secret().to_string()) + } + } + }; + + let mut provider = ProviderConfig::default(); + provider.provider_type = Some("openai".to_string()); + provider.base_url = format!("{}/openai/v1/", foundry_url.trim_end_matches('/')); + provider.bearer_token_provider = Some(Arc::new(get_bearer_token)); + provider.wire_api = Some("responses".to_string()); + + let mut config = SessionConfig::default(); + config.model = Some("gpt-5.5".to_string()); + config.provider = Some(provider); + + let client = Client::start(ClientOptions::default()).await?; + let session = client.create_session(config).await?; + + session + .send_and_wait(MessageOptions::new("Hello from Managed Identity!")) + .await?; + + session.disconnect().await?; + client.stop().await?; + Ok(()) +} +``` + {% endcodetab %} {% codetab typescript %} @@ -164,9 +398,10 @@ import { CopilotClient } from "@github/copilot-sdk"; const credential = new DefaultAzureCredential({ requiredEnvVars: ["AZURE_TOKEN_CREDENTIALS"], }); -const tokenResponse = await credential.getToken( - "https://ai.azure.com/.default" -); +const getBearerToken = async () => { + const tokenResponse = await credential.getToken("https://ai.azure.com/.default"); + return tokenResponse.token; +}; const client = new CopilotClient(); @@ -175,12 +410,12 @@ const session = await client.createSession({ provider: { type: "openai", baseUrl: `${process.env.FOUNDRY_RESOURCE_URL}/openai/v1/`, - bearerToken: tokenResponse.token, + bearerTokenProvider: getBearerToken, wireApi: "responses", }, }); -const response = await session.sendAndWait({ prompt: "Hello!" }); +const response = await session.sendAndWait({ prompt: "Hello from Managed Identity!" }); console.log(response?.data.content); await client.stop(); @@ -189,71 +424,28 @@ await client.stop(); {% endcodetab %} {% endcodetabs %} -### Token refresh for long-running applications - -Bearer tokens expire (typically after ~1 hour). For servers or long-running agents, refresh the token before creating each session. The following Python example demonstrates this pattern: - - - -```python -from azure.identity import DefaultAzureCredential -from copilot import CopilotClient -from copilot.session import PermissionHandler, ProviderConfig - -SCOPE = "https://ai.azure.com/.default" - - -class ManagedIdentityCopilotAgent: - """Copilot agent that refreshes Microsoft Entra tokens for Microsoft Foundry.""" - - def __init__(self, foundry_url: str, model: str = "gpt-5.5"): - self.foundry_url = foundry_url.rstrip("/") - self.model = model - self.credential = DefaultAzureCredential(require_envvar=True) - self.client = CopilotClient() - - def _get_provider_config(self) -> ProviderConfig: - """Build a ProviderConfig with a fresh bearer token.""" - token = self.credential.get_token(SCOPE).token - return ProviderConfig( - type="openai", - base_url=f"{self.foundry_url}/openai/v1/", - bearer_token=token, - wire_api="responses", - ) - - async def chat(self, prompt: str) -> str: - """Send a prompt and return the response text.""" - # Fresh token for each session - session = await self.client.create_session( - on_permission_request=PermissionHandler.approve_all, - model=self.model, - provider=self._get_provider_config(), - ) - - response = await session.send_and_wait(prompt) - await session.disconnect() - - return response.data.content if response else "" -``` - ## Environment configuration | Variable | Description | Example | |----------|-------------|---------| | `AZURE_TOKEN_CREDENTIALS` | When running in **Azure**, set it to `ManagedIdentityCredential`. When running **locally**, set it to either `dev` or a developer tool credential name, such as `AzureCliCredential`. | `ManagedIdentityCredential` | +| `AZURE_CLIENT_ID` | *Optional.* When running in **Azure**, set this to the client ID of a User-assigned Managed Identity when using `ManagedIdentityCredential`. If not set, Azure uses the System-assigned Managed Identity. | `11111111-2222-3333-4444-555555555555` | | `FOUNDRY_RESOURCE_URL` | Your Microsoft Foundry resource URL | `https://.openai.azure.com` | -No API key environment variable is needed—authentication is handled by `DefaultAzureCredential`, which automatically supports: +No API key environment variable is needed—authentication is handled by Azure Identity credentials. In .NET, Go, Java, Python, and TypeScript, `DefaultAzureCredential` automatically supports: -* **Managed Identity** (system-assigned or user-assigned): for Azure-hosted apps +* **Managed Identity** (System-assigned or User-assigned): for Azure-hosted apps * **Azure CLI** (`az login`): for local development * **Environment variables** (`AZURE_CLIENT_ID`, `AZURE_TENANT_ID`, `AZURE_CLIENT_SECRET`): for service principals * **Workload Identity**: for Kubernetes -See the `DefaultAzureCredential` documentation for the full credential chain: +In .NET, Go, Java, Python, and TypeScript, `ManagedIdentityCredential` reads `AZURE_CLIENT_ID` to select a User-assigned Managed Identity. Rust is an exception in this guide. + +In Rust, use `DeveloperToolsCredential` for local development and `ManagedIdentityCredential` when running in Azure. For other languages, see the `DefaultAzureCredential` documentation for the full credential chain: * [.NET](https://aka.ms/azsdk/net/identity/credential-chains#defaultazurecredential-overview) +* [Go](https://aka.ms/azsdk/go/identity/credential-chains#defaultazurecredential-overview) +* [Java](https://aka.ms/azsdk/java/identity/credential-chains#defaultazurecredential-overview) * [Python](https://aka.ms/azsdk/python/identity/credential-chains#defaultazurecredential-overview) * [TypeScript](https://aka.ms/azsdk/js/identity/credential-chains#defaultazurecredential-overview) @@ -271,4 +463,3 @@ See the `DefaultAzureCredential` documentation for the full credential chain: * [AUTOTITLE](/copilot/how-tos/copilot-sdk/auth/byok): Static API key configuration * [AUTOTITLE](/copilot/how-tos/copilot-sdk/setup/backend-services): Server-side deployment -* [Azure Identity documentation](https://learn.microsoft.com/python/api/overview/azure/identity-readme) diff --git a/content/copilot/how-tos/copilot-sdk/setup/backend-services.md b/content/copilot/how-tos/copilot-sdk/setup/backend-services.md index 674a216528b5..d5f7672802e2 100644 --- a/content/copilot/how-tos/copilot-sdk/setup/backend-services.md +++ b/content/copilot/how-tos/copilot-sdk/setup/backend-services.md @@ -114,7 +114,7 @@ const client = new CopilotClient({ const session = await client.createSession({ sessionId: `user-${userId}-${Date.now()}`, - model: "gpt-4.1", + model: "gpt-5.4", availableTools: ["custom:*"], gitHubToken: user.githubToken, }); @@ -135,7 +135,7 @@ client = CopilotClient( ) await client.start() -session = await client.create_session(on_permission_request=PermissionHandler.approve_all, model="gpt-4.1", session_id=f"user-{user_id}-{int(time.time())}") +session = await client.create_session(on_permission_request=PermissionHandler.approve_all, model="gpt-5.4", session_id=f"user-{user_id}-{int(time.time())}") response = await session.send_and_wait(message) ``` @@ -166,7 +166,7 @@ func main() { session, _ := client.CreateSession(ctx, &copilot.SessionConfig{ SessionID: fmt.Sprintf("user-%s-%d", userID, time.Now().Unix()), - Model: "gpt-4.1", + Model: "gpt-5.4", }) response, _ := session.SendAndWait(ctx, copilot.MessageOptions{Prompt: message}) @@ -183,7 +183,7 @@ defer client.Stop() session, _ := client.CreateSession(ctx, &copilot.SessionConfig{ SessionID: fmt.Sprintf("user-%s-%d", userID, time.Now().Unix()), - Model: "gpt-4.1", + Model: "gpt-5.4", }) response, _ := session.SendAndWait(ctx, copilot.MessageOptions{Prompt: message}) @@ -206,7 +206,7 @@ var client = new CopilotClient(new CopilotClientOptions await using var session = await client.CreateSessionAsync(new SessionConfig { SessionId = $"user-{userId}-{DateTimeOffset.UtcNow.ToUnixTimeSeconds()}", - Model = "gpt-4.1", + Model = "gpt-5.4", }); var response = await session.SendAndWaitAsync( @@ -222,7 +222,7 @@ var client = new CopilotClient(new CopilotClientOptions await using var session = await client.CreateSessionAsync(new SessionConfig { SessionId = $"user-{userId}-{DateTimeOffset.UtcNow.ToUnixTimeSeconds()}", - Model = "gpt-4.1", + Model = "gpt-5.4", }); var response = await session.SendAndWaitAsync( @@ -248,7 +248,7 @@ try { var session = client.createSession(new SessionConfig() .setSessionId(String.format("user-%s-%d", userId, System.currentTimeMillis() / 1000)) - .setModel("gpt-4.1") + .setModel("gpt-5.4") .setOnPermissionRequest(PermissionHandler.APPROVE_ALL) ).get(); @@ -290,7 +290,7 @@ const client = new CopilotClient({ app.post("/chat", authMiddleware, async (req, res) => { const session = await client.createSession({ sessionId: `user-${req.user.id}-chat`, - model: "gpt-4.1", + model: "gpt-5.4", availableTools: ["custom:*"], gitHubToken: req.user.githubToken, }); @@ -313,7 +313,7 @@ const client = new CopilotClient({ }); const session = await client.createSession({ - model: "gpt-4.1", + model: "gpt-5.4", provider: { type: "openai", baseUrl: "https://api.openai.com/v1", @@ -351,7 +351,7 @@ app.post("/api/chat", async (req, res) => { } catch { session = await client.createSession({ sessionId, - model: "gpt-4.1", + model: "gpt-5.4", availableTools: ["custom:*"], gitHubToken: req.user.githubToken, }); @@ -380,7 +380,7 @@ const client = new CopilotClient({ async function processJob(job: Job) { const session = await client.createSession({ sessionId: `job-${job.id}`, - model: "gpt-4.1", + model: "gpt-5.4", }); const response = await session.sendAndWait({ diff --git a/content/copilot/how-tos/copilot-sdk/setup/bundled-cli.md b/content/copilot/how-tos/copilot-sdk/setup/bundled-cli.md index 4c9b04c41185..252d20e94496 100644 --- a/content/copilot/how-tos/copilot-sdk/setup/bundled-cli.md +++ b/content/copilot/how-tos/copilot-sdk/setup/bundled-cli.md @@ -48,7 +48,7 @@ import { CopilotClient } from "@github/copilot-sdk"; const client = new CopilotClient(); -const session = await client.createSession({ model: "gpt-4.1" }); +const session = await client.createSession({ model: "gpt-5.4" }); const response = await session.sendAndWait({ prompt: "Hello!" }); console.log(response?.data.content); @@ -65,7 +65,7 @@ from copilot.session import PermissionHandler client = CopilotClient() await client.start() -session = await client.create_session(on_permission_request=PermissionHandler.approve_all, model="gpt-4.1") +session = await client.create_session(on_permission_request=PermissionHandler.approve_all, model="gpt-5.4") response = await session.send_and_wait("Hello!") print(response.data.content) @@ -97,7 +97,7 @@ func main() { } defer client.Stop() - session, _ := client.CreateSession(ctx, &copilot.SessionConfig{Model: "gpt-4.1"}) + session, _ := client.CreateSession(ctx, &copilot.SessionConfig{Model: "gpt-5.4"}) response, _ := session.SendAndWait(ctx, copilot.MessageOptions{Prompt: "Hello!"}) if d, ok := response.Data.(*copilot.AssistantMessageData); ok { fmt.Println(d.Content) @@ -112,7 +112,7 @@ if err := client.Start(ctx); err != nil { } defer client.Stop() -session, _ := client.CreateSession(ctx, &copilot.SessionConfig{Model: "gpt-4.1"}) +session, _ := client.CreateSession(ctx, &copilot.SessionConfig{Model: "gpt-5.4"}) response, _ := session.SendAndWait(ctx, copilot.MessageOptions{Prompt: "Hello!"}) if d, ok := response.Data.(*copilot.AssistantMessageData); ok { fmt.Println(d.Content) @@ -125,7 +125,7 @@ if d, ok := response.Data.(*copilot.AssistantMessageData); ok { ```csharp await using var client = new CopilotClient(); await using var session = await client.CreateSessionAsync( - new SessionConfig { Model = "gpt-4.1" }); + new SessionConfig { Model = "gpt-5.4" }); var response = await session.SendAndWaitAsync( new MessageOptions { Prompt = "Hello!" }); @@ -149,7 +149,7 @@ var client = new CopilotClient(new CopilotClientOptions() client.start().get(); var session = client.createSession(new SessionConfig() - .setModel("gpt-4.1") + .setModel("gpt-5.4") .setOnPermissionRequest(PermissionHandler.APPROVE_ALL) ).get(); @@ -198,7 +198,7 @@ If you manage your own model provider keys, users don't need GitHub accounts at const client = new CopilotClient(); const session = await client.createSession({ - model: "gpt-4.1", + model: "gpt-5.4", provider: { type: "openai", baseUrl: "https://api.openai.com/v1", @@ -220,7 +220,7 @@ const client = new CopilotClient(); const sessionId = `project-${projectName}`; const session = await client.createSession({ sessionId, - model: "gpt-4.1", + model: "gpt-5.4", }); // User closes app... diff --git a/content/copilot/how-tos/copilot-sdk/setup/choosing-a-setup-path.md b/content/copilot/how-tos/copilot-sdk/setup/choosing-a-setup-path.md index 1fc8528d435e..79d5e41f9608 100644 --- a/content/copilot/how-tos/copilot-sdk/setup/choosing-a-setup-path.md +++ b/content/copilot/how-tos/copilot-sdk/setup/choosing-a-setup-path.md @@ -81,7 +81,7 @@ Use this table to find the right guides based on what you need to do: | Getting started quickly | [AUTOTITLE](/copilot/how-tos/copilot-sdk/setup/bundled-cli) | | Use your own CLI binary or server | [AUTOTITLE](/copilot/how-tos/copilot-sdk/setup/local-cli) | | Users sign in with GitHub | [AUTOTITLE](/copilot/how-tos/copilot-sdk/setup/github-oauth) | -| Use your own model keys (OpenAI, Azure, etc.) | [AUTOTITLE](/copilot/how-tos/copilot-sdk/auth/byok) | +| Use your own model keys (OpenAI, Azure, and more) | [AUTOTITLE](/copilot/how-tos/copilot-sdk/auth/byok) | | Azure BYOK with Managed Identity (no API keys) | [AUTOTITLE](/copilot/how-tos/copilot-sdk/setup/azure-managed-identity) | | Run the SDK on a server | [AUTOTITLE](/copilot/how-tos/copilot-sdk/setup/backend-services) | | Configure SDK options for concurrent users | [AUTOTITLE](/copilot/how-tos/copilot-sdk/setup/multi-tenancy) | diff --git a/content/copilot/how-tos/copilot-sdk/setup/github-oauth.md b/content/copilot/how-tos/copilot-sdk/setup/github-oauth.md index ddb3c29df73b..326ebc3001cf 100644 --- a/content/copilot/how-tos/copilot-sdk/setup/github-oauth.md +++ b/content/copilot/how-tos/copilot-sdk/setup/github-oauth.md @@ -95,7 +95,7 @@ function createClientForUser(userToken: string): CopilotClient { const client = createClientForUser("gho_user_access_token"); const session = await client.createSession({ sessionId: `user-${userId}-session`, - model: "gpt-4.1", + model: "gpt-5.4", }); const response = await session.sendAndWait({ prompt: "Hello!" }); @@ -118,7 +118,7 @@ def create_client_for_user(user_token: str) -> CopilotClient: client = create_client_for_user("gho_user_access_token") await client.start() -session = await client.create_session(on_permission_request=PermissionHandler.approve_all, model="gpt-4.1", session_id=f"user-{user_id}-session") +session = await client.create_session(on_permission_request=PermissionHandler.approve_all, model="gpt-5.4", session_id=f"user-{user_id}-session") response = await session.send_and_wait("Hello!") ``` @@ -152,7 +152,7 @@ func main() { session, _ := client.CreateSession(ctx, &copilot.SessionConfig{ SessionID: fmt.Sprintf("user-%s-session", userID), - Model: "gpt-4.1", + Model: "gpt-5.4", }) response, _ := session.SendAndWait(ctx, copilot.MessageOptions{Prompt: "Hello!"}) _ = response @@ -174,7 +174,7 @@ defer client.Stop() session, _ := client.CreateSession(ctx, &copilot.SessionConfig{ SessionID: fmt.Sprintf("user-%s-session", userID), - Model: "gpt-4.1", + Model: "gpt-5.4", }) response, _ := session.SendAndWait(ctx, copilot.MessageOptions{Prompt: "Hello!"}) ``` @@ -198,7 +198,7 @@ await using var client = CreateClientForUser("gho_user_access_token"); await using var session = await client.CreateSessionAsync(new SessionConfig { SessionId = $"user-{userId}-session", - Model = "gpt-4.1", + Model = "gpt-5.4", }); var response = await session.SendAndWaitAsync( @@ -218,7 +218,7 @@ await using var client = CreateClientForUser("gho_user_access_token"); await using var session = await client.CreateSessionAsync(new SessionConfig { SessionId = $"user-{userId}-session", - Model = "gpt-4.1", + Model = "gpt-5.4", }); var response = await session.SendAndWaitAsync( @@ -248,7 +248,7 @@ var userId = "user1"; try (var client = createClientForUser("gho_user_access_token")) { var session = client.createSession(new SessionConfig() .setSessionId(String.format("user-%s-session", userId)) - .setModel("gpt-4.1") + .setModel("gpt-5.4") .setOnPermissionRequest(PermissionHandler.APPROVE_ALL) ).get(); diff --git a/content/copilot/how-tos/copilot-sdk/setup/local-cli.md b/content/copilot/how-tos/copilot-sdk/setup/local-cli.md index 81adbda1a09e..ff7d85572dfe 100644 --- a/content/copilot/how-tos/copilot-sdk/setup/local-cli.md +++ b/content/copilot/how-tos/copilot-sdk/setup/local-cli.md @@ -44,7 +44,7 @@ const client = new CopilotClient({ cliPath: "/usr/local/bin/copilot", }); -const session = await client.createSession({ model: "gpt-4.1" }); +const session = await client.createSession({ model: "gpt-5.4" }); const response = await session.sendAndWait({ prompt: "Hello!" }); console.log(response?.data.content); @@ -64,7 +64,7 @@ client = CopilotClient({ }) await client.start() -session = await client.create_session(on_permission_request=PermissionHandler.approve_all, model="gpt-4.1") +session = await client.create_session(on_permission_request=PermissionHandler.approve_all, model="gpt-5.4") response = await session.send_and_wait("Hello!") if response: match response.data: @@ -101,7 +101,7 @@ func main() { } defer client.Stop() - session, _ := client.CreateSession(ctx, &copilot.SessionConfig{Model: "gpt-4.1"}) + session, _ := client.CreateSession(ctx, &copilot.SessionConfig{Model: "gpt-5.4"}) response, _ := session.SendAndWait(ctx, copilot.MessageOptions{Prompt: "Hello!"}) if response != nil { if d, ok := response.Data.(*copilot.AssistantMessageData); ok { @@ -120,7 +120,7 @@ if err := client.Start(ctx); err != nil { } defer client.Stop() -session, _ := client.CreateSession(ctx, &copilot.SessionConfig{Model: "gpt-4.1"}) +session, _ := client.CreateSession(ctx, &copilot.SessionConfig{Model: "gpt-5.4"}) response, _ := session.SendAndWait(ctx, copilot.MessageOptions{Prompt: "Hello!"}) if response != nil { if d, ok := response.Data.(*copilot.AssistantMessageData); ok { @@ -139,7 +139,7 @@ var client = new CopilotClient(new CopilotClientOptions }); await using var session = await client.CreateSessionAsync( - new SessionConfig { Model = "gpt-4.1" }); + new SessionConfig { Model = "gpt-5.4" }); var response = await session.SendAndWaitAsync( new MessageOptions { Prompt = "Hello!" }); @@ -187,7 +187,7 @@ Sessions default to ephemeral. To create resumable sessions, provide your own se // Create a named session const session = await client.createSession({ sessionId: "my-project-analysis", - model: "gpt-4.1", + model: "gpt-5.4", }); // Later, resume it diff --git a/content/copilot/how-tos/copilot-sdk/setup/multi-tenancy.md b/content/copilot/how-tos/copilot-sdk/setup/multi-tenancy.md index 9fbe6d3870c3..0ca22cf23b7f 100644 --- a/content/copilot/how-tos/copilot-sdk/setup/multi-tenancy.md +++ b/content/copilot/how-tos/copilot-sdk/setup/multi-tenancy.md @@ -63,7 +63,7 @@ const client = new CopilotClient({ const session = await client.createSession({ sessionId: `user-${user.id}-${crypto.randomUUID()}`, - model: "gpt-4.1", + model: "gpt-5.4", availableTools: ["custom:lookupOrder", "custom:createTicket"], gitHubToken: user.githubToken, }); @@ -86,7 +86,7 @@ await client.start() session = await client.create_session( session_id=f"user-{user.id}-{request_id}", - model="gpt-4.1", + model="gpt-5.4", available_tools=["custom:lookupOrder", "custom:createTicket"], github_token=user.github_token, on_permission_request=PermissionHandler.approve_all, @@ -127,7 +127,7 @@ func main() { session, err := client.CreateSession(ctx, &copilot.SessionConfig{ SessionID: fmt.Sprintf("user-%s-%s", user.ID, requestID), - Model: "gpt-4.1", + Model: "gpt-5.4", AvailableTools: []string{"custom:lookupOrder", "custom:createTicket"}, GitHubToken: user.GitHubToken, }) @@ -146,7 +146,7 @@ client := copilot.NewClient(&copilot.ClientOptions{ session, err := client.CreateSession(ctx, &copilot.SessionConfig{ SessionID: fmt.Sprintf("user-%s-%s", user.ID, requestID), - Model: "gpt-4.1", + Model: "gpt-5.4", AvailableTools: []string{"custom:lookupOrder", "custom:createTicket"}, GitHubToken: user.GitHubToken, }) @@ -174,7 +174,7 @@ var client = new CopilotClient(new CopilotClientOptions await using var session = await client.CreateSessionAsync(new SessionConfig { SessionId = $"user-{user.Id}-{requestId}", - Model = "gpt-4.1", + Model = "gpt-5.4", AvailableTools = ["custom:lookupOrder", "custom:createTicket"], GitHubToken = user.GitHubToken, }); @@ -192,7 +192,7 @@ var client = new CopilotClient(new CopilotClientOptions await using var session = await client.CreateSessionAsync(new SessionConfig { SessionId = $"user-{user.Id}-{requestId}", - Model = "gpt-4.1", + Model = "gpt-5.4", AvailableTools = ["custom:lookupOrder", "custom:createTicket"], GitHubToken = user.GitHubToken, }); @@ -225,7 +225,7 @@ public class MultiTenancyExample { var session = client.createSession(new SessionConfig() .setSessionId("user-" + user.id() + "-" + requestId) - .setModel("gpt-4.1") + .setModel("gpt-5.4") .setAvailableTools(List.of("custom:lookupOrder", "custom:createTicket")) .setGitHubToken(user.gitHubToken()) ).get(); @@ -243,7 +243,7 @@ var client = new CopilotClient(new CopilotClientOptions() var session = client.createSession(new SessionConfig() .setSessionId("user-" + user.id() + "-" + requestId) - .setModel("gpt-4.1") + .setModel("gpt-5.4") .setAvailableTools(List.of("custom:lookupOrder", "custom:createTicket")) .setGitHubToken(user.gitHubToken()) ).get(); @@ -275,7 +275,7 @@ let client = Client::start( let session = client.create_session( SessionConfig::default() .with_session_id(format!("user-{}-{request_id}", user.id)) - .with_model("gpt-4.1") + .with_model("gpt-5.4") .with_available_tools(["custom:lookupOrder", "custom:createTicket"]) .with_github_token(user.github_token), ).await?; @@ -366,7 +366,7 @@ Set `gitHubToken` on each session to scope GitHub auth to the requesting user. T ```typescript const session = await client.createSession({ sessionId: `user-${user.id}-support`, - model: "gpt-4.1", + model: "gpt-5.4", availableTools: ["custom:*"], gitHubToken: user.githubToken, }); diff --git a/content/copilot/how-tos/copilot-sdk/setup/scaling.md b/content/copilot/how-tos/copilot-sdk/setup/scaling.md index d45441808b01..0339a9df91b2 100644 --- a/content/copilot/how-tos/copilot-sdk/setup/scaling.md +++ b/content/copilot/how-tos/copilot-sdk/setup/scaling.md @@ -222,7 +222,7 @@ app.post("/chat", async (req, res) => { const session = await client.createSession({ sessionId: `user-${req.user.id}-chat`, - model: "gpt-4.1", + model: "gpt-5.4", }); const response = await session.sendAndWait({ prompt: req.body.message }); @@ -272,7 +272,7 @@ class SessionManager { // Create or resume const session = await client.createSession({ sessionId, - model: "gpt-4.1", + model: "gpt-5.4", }); this.activeSessions.set(sessionId, session); @@ -300,7 +300,7 @@ For stateless API endpoints where each request is independent: ```typescript app.post("/api/analyze", async (req, res) => { const session = await client.createSession({ - model: "gpt-4.1", + model: "gpt-5.4", }); try { @@ -325,7 +325,7 @@ app.post("/api/chat/start", async (req, res) => { const session = await client.createSession({ sessionId, - model: "gpt-4.1", + model: "gpt-5.4", infiniteSessions: { enabled: true, backgroundCompactionThreshold: 0.80, diff --git a/content/copilot/how-tos/copilot-sdk/troubleshooting/debugging.md b/content/copilot/how-tos/copilot-sdk/troubleshooting/debugging.md index 72d819632cd5..301053a71553 100644 --- a/content/copilot/how-tos/copilot-sdk/troubleshooting/debugging.md +++ b/content/copilot/how-tos/copilot-sdk/troubleshooting/debugging.md @@ -193,7 +193,7 @@ var client = new CopilotClient(new CopilotClientOptions **Solution:** -1. Install the CLI: [Installation guide](/copilot/how-tos/copilot-cli/set-up-copilot-cli/install-copilot-cli) +1. Install the CLI: [Installation guide](/copilot/how-tos/set-up/install-copilot-cli) 1. Verify installation: diff --git a/content/copilot/how-tos/copilot-sdk/troubleshooting/mcp-debugging.md b/content/copilot/how-tos/copilot-sdk/troubleshooting/mcp-debugging.md index e6e751f7df2c..963ed27e1800 100644 --- a/content/copilot/how-tos/copilot-sdk/troubleshooting/mcp-debugging.md +++ b/content/copilot/how-tos/copilot-sdk/troubleshooting/mcp-debugging.md @@ -477,10 +477,10 @@ When opening an issue or asking for help, collect: * [ ] SDK language and version * [ ] CLI version (`copilot --version`) -* [ ] MCP server type (Node.js, Python, .NET, Go, Rust, etc.) +* [ ] MCP server type (Node.js, Python, .NET, Go, Rust, and more) * [ ] Full MCP server configuration (redact secrets) * [ ] Result of manual `initialize` test -* [ ] Result of manual `tools/list` test +* [ ] Result of manual `tools/list` test * [ ] Debug logs from SDK * [ ] Any error messages diff --git a/content/copilot/reference/copilot-usage-metrics/example-schema.md b/content/copilot/reference/copilot-usage-metrics/example-schema.md index 9d2a996ed5b1..ee535d09ac00 100644 --- a/content/copilot/reference/copilot-usage-metrics/example-schema.md +++ b/content/copilot/reference/copilot-usage-metrics/example-schema.md @@ -323,7 +323,100 @@ The following user-teams report examples are returned by the `user-teams-1-day` The following repository-level report example is returned in the NDJSON files downloaded from the `repos-1-day` endpoints. Each row represents one repository with pull request activity on the requested day. Both enterprise- and organization-scoped rows populate `organization_id` (the organization that owns each repository). Enterprise-scoped rows also populate `enterprise_id`, and organization-scoped rows populate `enterprise_id` only for organizations owned by an enterprise. For the field reference, see [AUTOTITLE](/copilot/reference/copilot-usage-metrics/copilot-usage-metrics#repository-level-fields-api-only). ```json copy -{"day":"2026-07-14","enterprise_id":"1001","organization_id":"2002","repo_id":900000001,"repo_owner_name":"octodemo-metrics","repo_name":"example-service-alpha","repo_visibility":"INTERNAL","pull_requests":{"total_reviewed":1,"total_created":1,"total_created_by_copilot":1,"total_reviewed_by_copilot":1,"total_merged":1,"median_minutes_to_merge":372.62,"total_suggestions":0,"total_applied_suggestions":0,"total_merged_created_by_copilot":1,"median_minutes_to_merge_copilot_authored":372.62,"total_copilot_suggestions":0,"total_copilot_applied_suggestions":0,"total_merged_reviewed_by_copilot":1,"median_minutes_to_merge_copilot_reviewed":372.62,"copilot_suggestions_by_comment_type":[]}} -{"day":"2026-07-14","enterprise_id":"1001","organization_id":"2002","repo_id":900000003,"repo_owner_name":"octodemo-metrics","repo_name":"example-service-gamma","repo_visibility":"INTERNAL","pull_requests":{"total_reviewed":1,"total_created":0,"total_created_by_copilot":0,"total_reviewed_by_copilot":1,"total_merged":1,"median_minutes_to_merge":1020.53,"total_suggestions":0,"total_applied_suggestions":1,"total_merged_created_by_copilot":0,"total_copilot_suggestions":0,"total_copilot_applied_suggestions":1,"total_merged_reviewed_by_copilot":1,"median_minutes_to_merge_copilot_reviewed":1020.53,"copilot_suggestions_by_comment_type":[{"comment_type":"spelling","total_copilot_suggestions":0,"total_copilot_applied_suggestions":1}]}} -{"day":"2026-07-14","organization_id":"3003","enterprise_id":"","repo_id":900000010,"repo_owner_name":"octodemo-metrics","repo_name":"example-service-delta","repo_visibility":"PRIVATE","pull_requests":{"total_reviewed":1,"total_created":0,"total_created_by_copilot":0,"total_reviewed_by_copilot":1,"total_merged":2,"median_minutes_to_merge":1332.96,"total_suggestions":1,"total_applied_suggestions":2,"total_merged_created_by_copilot":1,"median_minutes_to_merge_copilot_authored":1329.47,"total_copilot_suggestions":1,"total_copilot_applied_suggestions":2,"total_merged_reviewed_by_copilot":2,"median_minutes_to_merge_copilot_reviewed":1332.96,"copilot_suggestions_by_comment_type":[{"comment_type":"documentation","total_copilot_suggestions":1,"total_copilot_applied_suggestions":1},{"comment_type":"spelling","total_copilot_suggestions":0,"total_copilot_applied_suggestions":1}]}} +[ + { + "day": "2026-07-14", + "enterprise_id": "1001", + "organization_id": "2002", + "repo_id": 900000001, + "repo_owner_name": "octodemo-metrics", + "repo_name": "example-service-alpha", + "repo_visibility": "INTERNAL", + "pull_requests": { + "total_reviewed": 1, + "total_created": 1, + "total_created_by_copilot": 1, + "total_reviewed_by_copilot": 1, + "total_merged": 1, + "median_minutes_to_merge": 372.62, + "total_suggestions": 0, + "total_applied_suggestions": 0, + "total_merged_created_by_copilot": 1, + "median_minutes_to_merge_copilot_authored": 372.62, + "total_copilot_suggestions": 0, + "total_copilot_applied_suggestions": 0, + "total_merged_reviewed_by_copilot": 1, + "median_minutes_to_merge_copilot_reviewed": 372.62, + "copilot_suggestions_by_comment_type": [] + } + }, + { + "day": "2026-07-14", + "enterprise_id": "1001", + "organization_id": "2002", + "repo_id": 900000003, + "repo_owner_name": "octodemo-metrics", + "repo_name": "example-service-gamma", + "repo_visibility": "INTERNAL", + "pull_requests": { + "total_reviewed": 1, + "total_created": 0, + "total_created_by_copilot": 0, + "total_reviewed_by_copilot": 1, + "total_merged": 1, + "median_minutes_to_merge": 1020.53, + "total_suggestions": 0, + "total_applied_suggestions": 1, + "total_merged_created_by_copilot": 0, + "total_copilot_suggestions": 0, + "total_copilot_applied_suggestions": 1, + "total_merged_reviewed_by_copilot": 1, + "median_minutes_to_merge_copilot_reviewed": 1020.53, + "copilot_suggestions_by_comment_type": [ + { + "comment_type": "spelling", + "total_copilot_suggestions": 0, + "total_copilot_applied_suggestions": 1 + } + ] + } + }, + { + "day": "2026-07-14", + "organization_id": "3003", + "enterprise_id": "", + "repo_id": 900000010, + "repo_owner_name": "octodemo-metrics", + "repo_name": "example-service-delta", + "repo_visibility": "PRIVATE", + "pull_requests": { + "total_reviewed": 1, + "total_created": 0, + "total_created_by_copilot": 0, + "total_reviewed_by_copilot": 1, + "total_merged": 2, + "median_minutes_to_merge": 1332.96, + "total_suggestions": 1, + "total_applied_suggestions": 2, + "total_merged_created_by_copilot": 1, + "median_minutes_to_merge_copilot_authored": 1329.47, + "total_copilot_suggestions": 1, + "total_copilot_applied_suggestions": 2, + "total_merged_reviewed_by_copilot": 2, + "median_minutes_to_merge_copilot_reviewed": 1332.96, + "copilot_suggestions_by_comment_type": [ + { + "comment_type": "documentation", + "total_copilot_suggestions": 1, + "total_copilot_applied_suggestions": 1 + }, + { + "comment_type": "spelling", + "total_copilot_suggestions": 0, + "total_copilot_applied_suggestions": 1 + } + ] + } + } +] ``` diff --git a/content/get-started/start-your-journey/connecting-to-your-code-locally.md b/content/get-started/start-your-journey/connecting-to-your-code-locally.md new file mode 100644 index 000000000000..6c38e9930245 --- /dev/null +++ b/content/get-started/start-your-journey/connecting-to-your-code-locally.md @@ -0,0 +1,70 @@ +--- +title: 'Connecting to your code locally' +shortTitle: 'Connect locally' +intro: 'Connect {% data variables.product.prodname_desktop %} to your account and clone your repository to edit files locally.' +category: + - Learn to code +contentType: get-started +versions: + fpt: '*' + ghes: '*' + ghec: '*' +--- + +So far, you've worked in the browser. In this tutorial, you'll use {% data variables.product.prodname_desktop %} to clone your repository to your computer so you can edit files in your own code editor. + +{% data reusables.enterprise.url-substitute-note %} + +## Prerequisites + +* A `stargazers-log` repository. If you haven't created it yet, see [AUTOTITLE](/get-started/start-your-journey/creating-a-repository-for-your-project-on-github). +* {% data variables.product.prodname_desktop %} installed on your computer. + +## Installing {% data variables.product.prodname_desktop %} + +If you don't have {% data variables.product.prodname_desktop %} yet, install it before you continue. [Download {% data variables.product.prodname_desktop %}](https://desktop.github.com/?ref_product=desktop&ref_type=engagement&ref_style=button). + +> [!TIP] +> {% data variables.product.prodname_docs %} contains documentation for all of {% data variables.product.company_short %}'s plans, and where relevant, tools and operating systems. To find content that matches your setup, use the **Version** dropdown in the top left corner of an article to select your plan, and select the tool or operating system tabs below the article's introduction. + +## Signing in to {% data variables.product.prodname_desktop %} + +Sign in so that {% data variables.product.prodname_desktop %} can access your repositories. + +1. Open {% data variables.product.prodname_desktop %}. +1. From the welcome screen, click {% ifversion fpt or ghec %}**Sign in to {% data variables.product.prodname_dotcom_the_website %}**{% else %}**Sign in to {% data variables.product.prodname_enterprise %}**{% endif %}. +1. Follow the prompts to authenticate with your {% data variables.product.github %} account and authorize the app. +1. Follow the steps to configure Git with your name and email address, which will be used for commits you make in {% data variables.product.prodname_desktop %}. + +If you don't see the welcome screen, follow the steps in [AUTOTITLE](/desktop/configuring-and-customizing-github-desktop/configuring-basic-settings-in-github-desktop) to sign in and manage your settings from the {% data variables.product.prodname_desktop %} menu. + +## Cloning your repository + +Cloning creates a copy of your repository on your computer that stays connected to the version on {% data variables.product.github %}. + +1. From the "Let's get started!" screen, select **`stargazers-log`** from the list of "Your repositories". +1. Directly below the repositories list, click **Clone OWNER/stargazers-log**, where OWNER is your {% data variables.product.github %} personal account. +1. Accept the default repository URL and local path, and click **Clone**. + +If you don't see the "Let's get started!" screen, follow the steps in [Cloning a repository](/desktop/adding-and-cloning-repositories/cloning-and-forking-repositories-from-github-desktop#cloning-a-repository) to clone your `stargazers-log` repository. + +## Opening your code in an editor + +With your repository cloned, open it in a code editor to start working on the files. + +1. In {% data variables.product.prodname_desktop %}, select **Repository**, then click **Open in EDITOR**. + * If you haven't set an editor yet, install one such as [{% data variables.product.prodname_vscode %}](https://code.visualstudio.com/) ({% data variables.product.prodname_vscode_shortname %}), then choose it in the **Advanced** section of {% data variables.product.prodname_desktop %} settings. + +1. Confirm that you can see your `index.html` and `README.md` files in the editor. + +## What you accomplished + +| Task | Outcome | +| ---- | ------- | +| Signed in to {% data variables.product.prodname_desktop %} | You connected {% data variables.product.prodname_desktop %} to your account. | +| Cloned your repository | You created a local copy of `stargazers-log` on your computer. | +| Opened your code | You opened the website files in a code editor, ready to make changes. | + +## Next steps + +* Now that your code is on your computer, build the first feature for your website. Continue to [AUTOTITLE](/get-started/start-your-journey/writing-and-storing-your-code). diff --git a/content/get-started/start-your-journey/creating-a-repository-for-your-project-on-github.md b/content/get-started/start-your-journey/creating-a-repository-for-your-project-on-github.md new file mode 100644 index 000000000000..5050e71e535d --- /dev/null +++ b/content/get-started/start-your-journey/creating-a-repository-for-your-project-on-github.md @@ -0,0 +1,96 @@ +--- +title: 'Creating a repository for your project on GitHub' +shortTitle: 'Create your repository' +intro: 'Create a repository on {% data variables.product.github %} to store your code, track its history, and build a software project you can share.' +allowTitleToDifferFromFilename: true +category: + - Learn to code +contentType: get-started +versions: + fpt: '*' + ghes: '*' + ghec: '*' +--- + +In this tutorial, you'll create the repository you'll use throughout this series. You'll build a small software project called `stargazers-log`. By the end of the journey, you'll plan work, write code, review changes, and deploy your code to a live website. + +`stargazers-log` is a simple website that tracks and displays repositories you have starred. It helps you build a personal catalog of tools and code examples you care about. Starring repositories also helps you bookmark projects for later and show appreciation to maintainers. + +{% data reusables.enterprise.url-substitute-note %} + +## Prerequisites + +{% ifversion fpt or ghec %} + +* An account on {% data variables.product.github %}. To sign up, go to [https://github.com/signup](https://github.com/signup?ref_product=github&ref_type=engagement&ref_style=text). + +{% else %} + +* An account on your {% data variables.product.prodname_ghe_server %} instance. + +{% endif %} + +## What is a repository? + +A repository is where you keep code and files for a software project on {% data variables.product.github %}. It stores your files, tracks each change as a commit, and gives collaborators a shared place to work. Most software projects—from a single web page to a large application—live in their own repository. + +## Creating your repository + +Follow these steps to create the repository for this series. + +{% ifversion fpt or ghec %} + +1. In the upper-right corner of any page on {% data variables.product.github %}, select {% octicon "plus" aria-label="Create new" %}, then click **New repository**. + + Alternatively, go to [new repository page on {% data variables.product.prodname_dotcom_the_website %}](https://github.com/new?ref_product=github&ref_type=engagement&ref_style=text) to open the new repository form directly. + +{% else %} + +1. On {% data variables.location.product_location %}, in the upper-right corner of any page, select {% octicon "plus" aria-label="Create new" %}, then click **New repository**. + +{% endif %} + +1. Use the **Owner** dropdown menu to select your personal account to own the repository. +1. In the **Repository name** field, type `stargazers-log`. +1. In the **Description** field, type a short description, such as "A log of the repositories I've starred." +1. Use the **Choose visibility** dropdown menu to select **Public** so you can publish your software project to a live site later in this series. +1. Select **Add README**. This gives your repository a starting file and a place to describe your software project. +1. You do not need to add a `.gitignore` or license file for this software project, so leave those options unchanged. +1. Click **Create repository**. + +## Adding a starter web page + +Your software project will grow into a small web page, so add an `index.html` file to hold its content. + +1. On the main page of your `stargazers-log` repository above the list of files, click **{% octicon "plus" aria-hidden="true" aria-label="plus" %}**, then click **{% octicon "plus" aria-hidden="true" aria-label="plus" %} Create new file**. +1. In the file name field, type `index.html`. +1. In the file editor, add the following starter content. + + ```html copy + + + + + Stargazers log + + +

Stargazers log

+

A log of the repositories I've starred.

+ + + ``` + +1. Click **Commit changes**. +1. In the dialog that opens, keep the default option to commit directly to the `main` branch, then click **Commit changes**. + +## What you accomplished + +| Task | Outcome | +| ---- | ------- | +| Created a repository | You created `stargazers-log` to store your software project on {% data variables.product.github %}. | +| Added a README | You gave your software project a place to describe itself. | +| Added a web page | You created `index.html` as the starting point for your site. | + +## Next steps + +* Now that your software project has a home, plan the work you want to do. Continue to [AUTOTITLE](/get-started/start-your-journey/planning-your-work). diff --git a/content/get-started/start-your-journey/deploying-your-website-automatically.md b/content/get-started/start-your-journey/deploying-your-website-automatically.md new file mode 100644 index 000000000000..dce6e3dd9098 --- /dev/null +++ b/content/get-started/start-your-journey/deploying-your-website-automatically.md @@ -0,0 +1,129 @@ +--- +title: 'Deploying your website automatically' +shortTitle: 'Deploy automatically' +intro: 'Automate your code deployment with {% data variables.product.prodname_actions %} and {% data variables.product.prodname_pages %} to publish updates to a live site with every push to the main branch.' +category: + - Learn to code +contentType: get-started +versions: + fpt: '*' + ghes: '*' + ghec: '*' +--- + +Your feature is merged, so now you can share it. In this tutorial, you'll set up automatic deployment so every push to `main` publishes your software project to a live website. + +> [!NOTE] +> The URL of your {% data variables.product.prodname_pages %} site depends on which version of {% data variables.product.github %} you use. +> * On {% data variables.product.prodname_dotcom_the_website %}, your site is published at `YOUR-USERNAME.github.io/REPOSITORY`. +> * If you use {% data variables.product.github %} with data residency on {% data variables.enterprise.data_residency_site %}, your site is published at `pages.SUBDOMAIN.ghe.com/YOUR-USERNAME/REPOSITORY`, where `SUBDOMAIN` is your enterprise's subdomain. +> * On {% data variables.product.prodname_ghe_server %}, your site is published at `pages.HOSTNAME/YOUR-USERNAME/REPOSITORY`, where `HOSTNAME` is the hostname of your {% data variables.product.prodname_ghe_server %} instance. + +## Prerequisites + +* A `stargazers-log` repository with your merged feature. If you haven't merged it yet, see [AUTOTITLE](/get-started/start-your-journey/reviewing-your-proposed-changes). + +## Enabling {% data variables.product.prodname_pages %} + +Set up {% data variables.product.prodname_pages %} to host your site, and use {% data variables.product.prodname_actions %} to build and publish it. + +1. On {% data variables.product.github %}, navigate to your `stargazers-log` repository. +1. Under your repository name, click **Settings**. +1. In the sidebar, in the "Code and automation" section, click **Pages**. +1. Under "Build and deployment", from the **Source** dropdown, select **{% data variables.product.prodname_actions %}**. + +Leave the other settings at their defaults. Next, you'll create **your own workflow** to build and deploy your site automatically. + +## Creating a deployment workflow + +A workflow is a set of automated steps that {% data variables.product.prodname_actions %} runs for you. Add one that builds and deploys your site whenever you push to `main`. + +1. In your repository, create a file named `.github/workflows/deploy.yml`. + 1. On the main page of your `stargazers-log` repository above the list of files, click **{% octicon "plus" aria-hidden="true" aria-label="plus" %}**, then click **{% octicon "plus" aria-hidden="true" aria-label="plus" %} Create new file**. + 1. In the file name field, type `.github/workflows/deploy.yml`. +1. Add the following workflow content. + + ```yaml copy + name: Deploy to GitHub Pages + + on: + push: + branches: + - main + + permissions: + contents: read + pages: write + id-token: write + + jobs: + deploy: + runs-on: ubuntu-latest + environment: + name: github-pages + url: {% raw %}${{ steps.deployment.outputs.page_url }}{% endraw %} + steps: + - name: Checkout + uses: {% data reusables.actions.action-checkout %} + - name: Setup Pages + uses: actions/configure-pages@v5 + - name: Upload artifact + {%- ifversion fpt or ghec %} + uses: actions/upload-pages-artifact@v4 + {%- elsif ghes %} + uses: actions/upload-pages-artifact@v2 + {%- endif %} + with: + path: '.' + - name: Deploy to GitHub Pages + {%- ifversion fpt or ghec %} + id: deployment + uses: actions/deploy-pages@v4 + {%- elsif ghes %} + id: deployment + uses: actions/deploy-pages@v2 + {%- endif %} + ``` + +1. Commit the file directly to the `main` branch. + +## Understanding the workflow + +Each part of the workflow has a job to do: + +* **`on`** tells {% data variables.product.prodname_actions %} to run the workflow on every push to `main`. +* **`permissions`** grant the workflow the access it needs to publish to {% data variables.product.prodname_pages %}. +* **`environment`** connects the job to your {% data variables.product.prodname_pages %} site and exposes the published URL as {% raw %}`${{ steps.deployment.outputs.page_url }}`{% endraw %}. +* **`steps`** check out your code, prepare {% data variables.product.prodname_pages %}, upload your files as an artifact, and deploy them to your live site. + +## Viewing your live site + +After you commit the workflow, {% data variables.product.prodname_actions %} runs it automatically. + +1. In your repository, click the **Actions** tab to watch the workflow run. +1. Click the latest workflow run to see a summary of the job details. +1. When the run completes, open your published site at {% ifversion fpt or ghec %}`https://YOUR-USERNAME.github.io/stargazers-log/`{% elsif ghes %}`https://pages.HOSTNAME/YOUR-USERNAME/stargazers-log/`{% endif %}. Replace `YOUR-USERNAME` with your username{% ifversion ghes %}, and replace `HOSTNAME` with your {% data variables.product.prodname_ghe_server %} hostname{% endif %}. + * For example, if your {% data variables.product.github %} account username is `octocat`, your site is at {% ifversion fpt or ghec %}`https://octocat.github.io/stargazers-log/`{% elsif ghes %}`https://pages.HOSTNAME/octocat/stargazers-log/`{% endif %}. + +From now on, every push to `main` redeploys your site with your latest changes. + +## What you built + +Across this series, you built a complete software project and practiced the {% data variables.product.github %} workflow: + +| Stage | What you learned | +| ----- | ---------------- | +| Creating your software project | Repositories, README files | +| Planning your work | Issues, Projects (project boards) | +| Connecting locally | {% data variables.product.prodname_desktop %}, cloning | +| Writing and storing code | Branches, commits, pull requests, {% data variables.product.prodname_copilot_short %} | +| Reviewing changes | Pull request reviews, {% data variables.product.prodname_copilot_short %} | +| Deploying automatically | {% data variables.product.prodname_actions %}, {% data variables.product.prodname_pages %} | + +## Next steps + +* Expand your understanding of Git and {% data variables.product.github %}. For more information, see [AUTOTITLE](/get-started/start-your-journey/git-and-github-learning-resources). +* Explore {% data variables.copilot.copilot_chat_short %} to learn faster and get help as you code. For more information, see [AUTOTITLE]({% ifversion ghes %}/enterprise-cloud@latest/{% endif %}/copilot/concepts/chat){% ifversion ghes %} in the {% data variables.product.prodname_ghe_cloud %} documentation{% endif %}. +* Go deeper with AI and learn how agents can act like a practical coding partner by turning ideas into small actionable steps, generating examples, and handling repetitive work. For more information, see [AUTOTITLE]({% ifversion ghes %}/enterprise-cloud@latest/{% endif %}/copilot/concepts/agents){% ifversion ghes %} in the {% data variables.product.prodname_ghe_cloud %} documentation{% endif %}. +* Learn more about automating your software projects with {% data variables.product.prodname_actions %} workflows. For more information, see [AUTOTITLE](/actions/get-started/understand-github-actions). +* Add a custom domain or explore more ways to publish your website with {% data variables.product.prodname_pages %}. For more information, see [AUTOTITLE](/pages/getting-started-with-github-pages). diff --git a/content/get-started/start-your-journey/index.md b/content/get-started/start-your-journey/index.md index 7ece3a127f89..6ef3ba478a60 100644 --- a/content/get-started/start-your-journey/index.md +++ b/content/get-started/start-your-journey/index.md @@ -14,6 +14,12 @@ children: - /downloading-files-from-github - /uploading-a-project-to-github - /git-and-github-learning-resources + - /creating-a-repository-for-your-project-on-github + - /planning-your-work + - /connecting-to-your-code-locally + - /writing-and-storing-your-code + - /reviewing-your-proposed-changes + - /deploying-your-website-automatically redirect_from: - /github/getting-started-with-github/quickstart - /get-started/quickstart @@ -24,11 +30,11 @@ journeyTracks: description: 'Master the fundamentals of {% data variables.product.github %} and Git.' guides: - href: '/get-started/start-your-journey/what-is-github' - - href: '/get-started/start-your-journey/creating-an-account-on-github' - - href: '/get-started/start-your-journey/hello-world' - - href: '/get-started/start-your-journey/setting-up-your-profile' - - href: '/get-started/start-your-journey/finding-inspiration-on-github' - - href: '/get-started/start-your-journey/downloading-files-from-github' - - href: '/get-started/start-your-journey/uploading-a-project-to-github' + - href: '/get-started/start-your-journey/creating-a-repository-for-your-project-on-github' + - href: '/get-started/start-your-journey/planning-your-work' + - href: '/get-started/start-your-journey/connecting-to-your-code-locally' + - href: '/get-started/start-your-journey/writing-and-storing-your-code' + - href: '/get-started/start-your-journey/reviewing-your-proposed-changes' + - href: '/get-started/start-your-journey/deploying-your-website-automatically' - href: '/get-started/start-your-journey/git-and-github-learning-resources' --- diff --git a/content/get-started/start-your-journey/planning-your-work.md b/content/get-started/start-your-journey/planning-your-work.md new file mode 100644 index 000000000000..f2a5d2f69350 --- /dev/null +++ b/content/get-started/start-your-journey/planning-your-work.md @@ -0,0 +1,72 @@ +--- +title: 'Planning your work' +shortTitle: 'Plan your work' +intro: 'Organize your work and track your progress so you always know what to build next.' +category: + - Learn to code +contentType: get-started +versions: + fpt: '*' + ghes: '*' + ghec: '*' +--- + +Before you write code, capture what you want to build and track your progress. In this tutorial, you'll plan and organize work for your website's first feature using {% data variables.product.prodname_github_issues %} and {% data variables.product.prodname_projects_v2 %}. + +## Prerequisites + +* A `stargazers-log` repository. If you haven't created it yet, see [AUTOTITLE](/get-started/start-your-journey/creating-a-repository-for-your-project-on-github). + +## Creating an issue + +Issues track ideas, tasks, and bugs for your software project. Create one for the first feature you'll build: a list of starred repositories on your web page. + +1. On the main page of your `stargazers-log` repository, click **Issues**. +1. Click **New issue**. +1. In the **Add a title** field, type `Display a list of starred repositories`. +1. In the description field, add a few details about what you want to build, such as "Show a list of starred repositories on the home page." +1. Click **Create**. + +## Creating a project board and importing your issue + +{% data variables.product.prodname_projects_v2 %} give you visual tables, boards, and roadmaps to organize and track issues for your software projects. In this tutorial, you'll create a user project board for `stargazers-log`. + +1. On {% data variables.product.github %}, in the top right corner of {% data variables.product.prodname_dotcom %}, click your profile picture, then click **Profile**. +1. Click **Projects**. +1. Click **New project**. +1. Under **Templates**, select **Board**. +1. In the project name field, type `Stargazers log`. +1. Leave **Import items from a repository** checked, to import the issue you recently created to the board. +1. Click **Create project**. + +## Moving work across the board + +The board's columns represent stages of your work. As you make progress, move each item to show its status. + +1. On your project board, find the `Display a list of starred repositories` item in the **Todo** column. +1. Drag the item into the **In Progress** column to show that you've started the work. + +You'll return to the board later to move the item to **Done** once your feature is live. + +## Adding follow up issues + +Create one or more issues to describe ideas and features for your website that you want to work on later. For example, you might create an issue to: + +* Add a search feature to your website +* Improve the styling of the starred repositories list +* Allow filtering of starred repositories by programming language, such as JavaScript, Python, or Go + +Add each new issue to your project board, and move it into **In Progress** once you start working on it. + +## What you accomplished + +| Task | Outcome | +| ---- | ------- | +| Created an issue | You captured your first feature as a trackable task. | +| Created a project board | You set up a board to organize and track your work. | +| Tracked your progress | You added the issue to the board and moved it into progress. | +| Added more issues | You created new issues to capture future work for your website. | + +## Next steps + +* With your work planned, bring your repository to your computer so you can start writing, storing, and updating your website code. Continue to [AUTOTITLE](/get-started/start-your-journey/connecting-to-your-code-locally). diff --git a/content/get-started/start-your-journey/reviewing-your-proposed-changes.md b/content/get-started/start-your-journey/reviewing-your-proposed-changes.md new file mode 100644 index 000000000000..5f308592ac25 --- /dev/null +++ b/content/get-started/start-your-journey/reviewing-your-proposed-changes.md @@ -0,0 +1,104 @@ +--- +title: 'Reviewing your proposed changes' +shortTitle: 'Review changes' +intro: 'Review your own pull request before you merge to catch bugs and improve quality, with an optional AI assist from {% data variables.product.prodname_copilot_short %}.' +category: + - Learn to code +contentType: get-started +versions: + fpt: '*' + ghes: '*' + ghec: '*' +--- + +A pull request is a chance to look at your work with fresh eyes before it becomes part of `main`. In this tutorial, you'll review your own changes, apply improvements, and merge the first feature for your website. + +## Prerequisites + +* An open pull request for your `add-starred-list` branch. If you haven't opened one yet, see [AUTOTITLE](/get-started/start-your-journey/writing-and-storing-your-code). + +## Why review code? + +Reviewing changes before you merge helps you catch bugs, spot missing error handling, and improve readability while the work is still fresh. Even when you work alone, a deliberate review builds a habit that keeps your software projects healthy and makes collaboration easier later. + +## Reviewing your changes in the pull request + +The **Files changed** tab in a pull request on {% data variables.product.github %} shows a diff of everything your branch adds or modifies. Review it carefully. + +1. Click [View your pull requests](https://github.com/pulls?ref_product=github&ref_type=engagement&ref_style=button) to go to your pull requests inbox on {% data variables.product.github %}. Alternatively, navigate to your `stargazers-log` repository and click the **Pull requests** tab. +1. Click on the title to open your pull request for the `add-starred-list` branch. +1. Click the **Files changed** tab. +1. Read the diff for each file, looking for: + * Bugs or logic errors, such as a mismatched file name in `script.js`. + * Missing error handling, such as what happens if `events.json` fails to load. + * Accessibility issues, such as missing text alternatives or unclear labels. + * Anything unclear that a future reader might struggle with. +1. To note something you want to change, hover over a line, click {% octicon "plus" aria-label="Add a comment" %}, type a comment, then click **Comment**. + +## Optional: Getting an AI second opinion + +You can also ask {% data variables.copilot.copilot_chat_short %} in your editor to review the same changes and offer a second perspective. + +1. In your editor, open {% data variables.copilot.copilot_chat_short %}. +1. Ask it to review your changes with a prompt like the following. + + ```text copy + Review my changes for bugs, missing error handling, and accessibility issues. + ``` + +1. Read the suggestions and decide which ones improve your feature. You're always in control of which changes to make. + +> [!TIP] +> If you enjoyed using {% data variables.product.prodname_copilot_short %} to review your own code, you can [sign up for a paid plan](https://github.com/github-copilot/signup?ref_product=copilot&ref_type=purchase&ref_style=text&ref_plan=pro) to get additional AI credits and {% data variables.copilot.copilot_code-review_short %}, which can review your pull requests automatically when you add {% data variables.product.prodname_copilot_short %} as a reviewer. +> +> For more information, see [AUTOTITLE]({% ifversion ghes %}/enterprise-cloud@latest{% endif %}/copilot/how-tos/set-up/set-up-for-self) and [AUTOTITLE]({% ifversion ghes %}/enterprise-cloud@latest{% endif %}/copilot/concepts/agents/code-review){% ifversion ghes %} in the {% data variables.product.prodname_ghe_cloud %} documentation{% endif %}. + +## Applying feedback + +Turn your review notes and any {% data variables.copilot.copilot_chat_short %} suggestions you agree with into updates. + +1. In your editor, update the affected files to address what you found. +1. In {% data variables.product.prodname_desktop %}, enter a commit message such as `Address review feedback`, then click **Commit # files to add-starred-list**. +1. Click **Push origin** to update your pull request with the new commit. + +## Merging the pull request + +Once you're satisfied with your changes, merge them into `main`. + +1. On {% data variables.product.github %}, return to your pull request and refresh the page. +1. Click the **Conversation** tab. +1. Click **Merge pull request**, then click **Confirm merge**. +1. Click **Delete branch** to clean up your `add-starred-list` branch. + +## Checking your progress + +Update your project board to reflect the completed work. + +1. On {% data variables.product.github %}, navigate to your `Stargazers log` project board from the **Projects** tab in your repository. +1. Drag the `Display a list of starred repositories` item from **In Progress** to **Done**. + +If you have more features to build, create new issues and move them to **In Progress** when you start the next cycle. + +## The complete workflow + +You've now run through the full developer workflow: + +* Planned the work with an issue and a project board. +* Created a branch and built your feature. +* Opened a pull request. +* Reviewed your own changes (optionally with help from {% data variables.product.prodname_copilot_short %}). +* Merged your feature into `main`. +* Updated your project board to reflect your progress. + +## What you accomplished + +| Task | Outcome | +| ---- | ------- | +| Reviewed changes in the Files changed tab | You opened the pull request's Files changed tab and read the diff. | +| Applied feedback and got an optional AI assist | You applied changes, and possibly asked {% data variables.copilot.copilot_chat_short %} for a review. | +| Merged your feature | You merged your changes into `main`. | +| Updated your progress | You moved the issue to **Done** on your project board. | + +## Next steps + +* Publish your software project to a live URL that updates your website automatically. Continue to [AUTOTITLE](/get-started/start-your-journey/deploying-your-website-automatically). diff --git a/content/get-started/start-your-journey/what-is-github.md b/content/get-started/start-your-journey/what-is-github.md index 79c108f7667a..5cf776012297 100644 --- a/content/get-started/start-your-journey/what-is-github.md +++ b/content/get-started/start-your-journey/what-is-github.md @@ -51,7 +51,7 @@ Some of the most common reasons people use {% data variables.product.github %} a * Write code, review code, manage security vulnerabilities and updates. * Collaborate and socialize on projects. * Contribute to open source software. -* Issue tracking and project management. +* Track and manage projects. * Automate software development workflows like CI/CD, testing, and deployments. * Showcase and share work. * Publish and release software packages. @@ -63,9 +63,9 @@ Some of the most common reasons people use {% data variables.product.github %} a If you're new to {% data variables.product.github %} and unfamiliar with Git, we recommend working through the articles in the [AUTOTITLE](/get-started/start-your-journey) category. The articles show you how to perform common tasks software developers do using {% data variables.product.github %}, such as: -* **Create a project** and store the code on {% data variables.product.github %}. +* **Create a repository for your software project** and store the code on {% data variables.product.github %}, see [AUTOTITLE](/get-started/start-your-journey/creating-a-repository-for-your-project-on-github). * **Plan your work** by creating issues and tracking your project work. -* **Connect to your project on your local machine** and use the {% data variables.product.prodname_desktop %} application to manage code changes with Git. -* **Update your project code**, optionally pair with an AI assistant, and sync your changes with {% data variables.product.github %}. +* **Connect to your code on your local machine** and use the {% data variables.product.prodname_desktop %} application to manage code changes with Git. +* **Write and store your code**, optionally pair with an AI assistant, and sync your changes with {% data variables.product.github %}. * **Review your proposed code changes** by creating pull requests and reviewing your own changes before you merge. -* **Deploy your project** using automated deployment workflows, allowing you to publish a basic website. +* **Deploy your website** using automated deployment workflows, allowing you to publish a basic website for your software project. diff --git a/content/get-started/start-your-journey/writing-and-storing-your-code.md b/content/get-started/start-your-journey/writing-and-storing-your-code.md new file mode 100644 index 000000000000..5f4d4911e950 --- /dev/null +++ b/content/get-started/start-your-journey/writing-and-storing-your-code.md @@ -0,0 +1,156 @@ +--- +title: 'Writing and storing your code' +shortTitle: 'Write and store code' +intro: 'Follow the core developer workflow to create a branch, write code, and open a pull request using {% data variables.product.prodname_copilot_short %} and {% data variables.product.prodname_desktop %}.' +category: + - Learn to code +contentType: get-started +versions: + fpt: '*' + ghes: '*' + ghec: '*' +--- + +Now you'll build the website feature you planned: a page that lists starred repositories. Along the way, you'll follow the core developer workflow—setting up a space for your work, making and saving your changes, and proposing them for review—using branches, commits, and a pull request. + +## Prerequisites + +* A local clone of your `stargazers-log` repository. If you haven't set this up yet, see [AUTOTITLE](/get-started/start-your-journey/connecting-to-your-code-locally). + +## Creating a branch + +A branch lets you work on your feature in its own space, without changing the `main` copy of your code until you're ready. + +1. Open {% data variables.product.prodname_desktop %}. +1. At the top of the app, click **Current Branch**, then click **New Branch**. +1. Name the branch `add-starred-list`, then click **Create Branch**. +1. Click **Publish branch** to make the branch available on {% data variables.product.github %}. + +## Building the website's code + +Your feature needs a few files: sample data, a stylesheet, a script to display the data, and an updated home page. + +You can write these files yourself or ask {% data variables.product.prodname_copilot_short %}, an AI assistant, to generate them. Every {% data variables.product.github %} account includes access to {% data variables.copilot.copilot_free %} and an allowance of AI credits, so you can start using {% data variables.product.prodname_copilot_short %} right away. + +To learn about the {% data variables.product.prodname_copilot_short %} plans available and how usage works, see [AUTOTITLE]({% ifversion ghes %}/enterprise-cloud@latest{% endif %}/copilot/get-started/plans){% ifversion ghes %} in the {% data variables.product.prodname_ghe_cloud %} documentation{% endif %}. + +Open {% data variables.copilot.copilot_chat_short %} in your editor and ask it to create the files with a prompt like the following. + + +```text copy +Create a starred repositories page for my software project. Add: +- events.json with sample data for a few starred repositories +- style.css to style a simple list +- script.js to fetch events.json and render the list +- an update to index.html that links style.css and script.js + +Use the code written in https://docs.github.com/get-started/start-your-journey/creating-and-changing-your-code. +``` + + + +Open {% data variables.copilot.copilot_chat_short %} in {% data variables.product.prodname_vscode_shortname %} {% octicon "link-external" height:16 aria-label="link-external" %} +

+ +Review what {% data variables.product.prodname_copilot_short %} generates, then adjust the files to match the examples below. + +If you didn't use {% data variables.product.prodname_copilot_short %}, use your code editor to create the files below in the `add-starred-list` branch. + +1. Create a file named `events.json` with the following sample data. Save the file. + + ```json copy + [ + { "name": "octocat/Hello-World", "starred": "2024-01-15" }, + { "name": "github/docs", "starred": "2024-02-02" }, + { "name": "octo-org/octo-repo", "starred": "2024-03-21" } + ] + ``` + +1. Create a file named `style.css` with the following styles. Save the file. + + ```scss copy + body { + font-family: sans-serif; + margin: 2rem; + } + + ul { + list-style: none; + padding: 0; + } + + li { + padding: 0.5rem 0; + border-bottom: 1px solid #ddd; + } + ``` + +1. Create a file named `script.js` with the following code. Save the file. + + ```javascript copy + fetch("events.json") + .then((response) => response.json()) + .then((events) => { + const list = document.querySelector("#starred"); + events.forEach((event) => { + const item = document.createElement("li"); + item.textContent = `${event.name} — starred ${event.starred}`; + list.appendChild(item); + }); + }); + ``` + +1. Update `index.html` to link the new files and hold the list. Save the file. + + ```html copy + + + + + Stargazers log + + + +

Stargazers log

+

A log of the repositories I've starred.

+
    + + + + ``` + +## Committing and pushing your changes + +Committing saves a snapshot of your changes, and pushing sends them to {% data variables.product.github %} where they will be stored in your repository. + +1. In {% data variables.product.prodname_desktop %}, review your changed files in the left sidebar. +1. At the bottom of the sidebar, in the **Summary** field, type a commit message such as `Add starred repositories list`. +1. Click **Commit 4 files to add-starred-list**. +1. Click **Push origin** to send your commit to {% data variables.product.github %}. + +## Opening a pull request + +A pull request proposes your branch's changes for review before they merge into `main`. + +1. In {% data variables.product.prodname_desktop %}, click **Preview Pull Request**, then click **Create Pull Request**. Your browser opens to the pull request form on {% data variables.product.github %}. +1. Review the title and description, then add a note about what your feature does. +1. Click **Create pull request**. + +## What you accomplished + +| Task | Outcome | +| ---- | ------- | +| Created a branch | You made an isolated space for your feature with `add-starred-list`. | +| Wrote the code | You added the feature's files to your branch, with an optional assist from {% data variables.copilot.copilot_chat_short %}. | +| Pushed your changes | You stored your branch's changes on {% data variables.product.github %}. | +| Opened a pull request | You proposed your changes for review. | + +## Further reading + +* [AUTOTITLE]({% ifversion ghes %}/enterprise-cloud@latest{% endif %}/copilot/concepts/billing/individual-plans) +* [AUTOTITLE]({% ifversion ghes %}/enterprise-cloud@latest{% endif %}/copilot/concepts/billing/usage-based-billing-for-individuals) +* [AUTOTITLE]({% ifversion ghes %}/enterprise-cloud@latest{% endif %}/copilot/how-tos/manage-and-track-spending/monitor-ai-usage){% ifversion ghes %} in the {% data variables.product.prodname_ghe_cloud %} documentation{% endif %} + +## Next steps + +* Before you merge, review your own changes to catch issues early. Continue to [AUTOTITLE](/get-started/start-your-journey/reviewing-your-proposed-changes). diff --git a/content/repositories/viewing-activity-and-data-for-your-repository/viewing-a-projects-contributors.md b/content/repositories/viewing-activity-and-data-for-your-repository/viewing-a-projects-contributors.md index efc0b40fed88..48af4a4655e3 100644 --- a/content/repositories/viewing-activity-and-data-for-your-repository/viewing-a-projects-contributors.md +++ b/content/repositories/viewing-activity-and-data-for-your-repository/viewing-a-projects-contributors.md @@ -47,3 +47,9 @@ If you don't appear in a repository's contributors graph, it may be because: If all your commits in the repository are on non-default branches, you won't be in the contributors graph. For example, commits on the `gh-pages` branch aren't included in the graph unless `gh-pages` is the repository's default branch. To have your commits merged into the default branch, you can create a pull request. For more information, see [AUTOTITLE](/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/about-pull-requests). If the email address you used to author the commits is not connected to your {% data variables.product.github %} account, your commits won't be linked to your account, and you won't appear in the contributors graph. For more information, see [AUTOTITLE](/account-and-profile/how-tos/email-preferences/setting-your-commit-email-address) and [AUTOTITLE](/account-and-profile/how-tos/email-preferences/adding-an-email-address-to-your-github-account). + +### Contributor data is stale after history changes + +After force-pushing, rewriting history, or deleting commits, repository contributor displays and statistics can take about 24 hours to refresh. + +If you're a repository owner and contributor data is still incorrect after waiting about 24 hours, contact {% data variables.contact.github_support %}. For more information about creating a support ticket, see [AUTOTITLE](/support/contacting-github-support/creating-a-support-ticket). diff --git a/data/reusables/enterprise-migration-tool/github-pat-required-scopes.md b/data/reusables/enterprise-migration-tool/github-pat-required-scopes.md index 11420c06a13e..591a3d5cd2b8 100644 --- a/data/reusables/enterprise-migration-tool/github-pat-required-scopes.md +++ b/data/reusables/enterprise-migration-tool/github-pat-required-scopes.md @@ -8,4 +8,4 @@ Task | Organization owner | Migrator Assigning the migrator role for repository migrations | `admin:org` | {% octicon "dash" aria-label="Not applicable" %} Running a repository migration (destination organization) | `repo`, `admin:org`, `workflow` | `repo`, `read:org`, `workflow` Downloading a migration log | `repo`, `admin:org`, `workflow` | `repo`, `read:org`, `workflow` -Reclaiming mannequins | `admin:org` | {% octicon "dash" aria-label="Not applicable" %} +Reclaiming mannequins | `repo`, `admin:org`, `workflow` | {% octicon "dash" aria-label="Not applicable" %} diff --git a/data/reusables/enterprise/url-substitute-note.md b/data/reusables/enterprise/url-substitute-note.md new file mode 100644 index 000000000000..a98dbd77cfe4 --- /dev/null +++ b/data/reusables/enterprise/url-substitute-note.md @@ -0,0 +1,2 @@ +> [!NOTE] +> As you follow this series, if you use {% data variables.enterprise.data_residency %} or {% data variables.product.prodname_ghe_server %}, you will need to substitute references and links to {% data variables.product.prodname_dotcom_the_website %} with your enterprise's dedicated URL.