From 697c692034b51659c84c63e64e2a4b92b736b6f0 Mon Sep 17 00:00:00 2001 From: Hirofumi Horikawa Date: Thu, 19 Mar 2026 20:58:02 +0000 Subject: [PATCH 1/3] fix: add onPermissionRequest handler to sessions across various documentation and examples --- docs/auth/byok.md | 11 +++++-- docs/features/custom-agents.md | 3 ++ docs/features/mcp.md | 6 ++-- docs/features/session-persistence.md | 25 +++++++------- docs/features/skills.md | 1 + docs/features/steering-and-queueing.md | 2 +- docs/getting-started.md | 15 ++++++--- docs/hooks/error-handling.md | 38 +++++++++++++--------- docs/hooks/index.md | 6 +++- docs/hooks/post-tool-use.md | 25 ++++++++------ docs/hooks/pre-tool-use.md | 10 ++++-- docs/hooks/session-lifecycle.md | 45 +++++++++++++++----------- docs/hooks/user-prompt-submitted.md | 24 +++++++++----- docs/setup/azure-managed-identity.md | 3 +- docs/setup/backend-services.md | 11 +++++-- docs/setup/bundled-cli.md | 6 ++-- docs/setup/github-oauth.md | 3 +- docs/setup/local-cli.md | 5 +-- docs/setup/scaling.md | 4 +++ docs/troubleshooting/compatibility.md | 1 + docs/troubleshooting/debugging.md | 5 +-- nodejs/README.md | 23 ++++++++++--- nodejs/examples/basic-example.ts | 4 +-- 23 files changed, 182 insertions(+), 94 deletions(-) diff --git a/docs/auth/byok.md b/docs/auth/byok.md index 8d9650280..a0c576f33 100644 --- a/docs/auth/byok.md +++ b/docs/auth/byok.md @@ -63,7 +63,7 @@ asyncio.run(main()) Node.js / TypeScript ```typescript -import { CopilotClient } from "@github/copilot-sdk"; +import { CopilotClient, approveAll } from "@github/copilot-sdk"; const FOUNDRY_MODEL_URL = "https://your-resource.openai.azure.com/openai/v1/"; @@ -76,6 +76,7 @@ const session = await client.createSession({ wireApi: "responses", // Use "completions" for older models apiKey: process.env.FOUNDRY_API_KEY, }, + onPermissionRequest: approveAll }); session.on("assistant.message", (event) => { @@ -457,12 +458,14 @@ When using BYOK, the `model` parameter is **required**: // ❌ Error: Model required with custom provider const session = await client.createSession({ provider: { type: "openai", baseUrl: "..." }, + onPermissionRequest: async () => ({ kind: "approved" }), }); // ✅ Correct: Model specified const session = await client.createSession({ model: "gpt-4", // Required! provider: { type: "openai", baseUrl: "..." }, + onPermissionRequest: async () => ({ kind: "approved" }), }); ``` @@ -472,7 +475,7 @@ For Azure OpenAI endpoints (`*.openai.azure.com`), use the correct type: ```typescript -import { CopilotClient } from "@github/copilot-sdk"; +import { CopilotClient, approveAll } from "@github/copilot-sdk"; const client = new CopilotClient(); const session = await client.createSession({ @@ -481,6 +484,7 @@ const session = await client.createSession({ type: "azure", baseUrl: "https://my-resource.openai.azure.com", }, + onPermissionRequest: approveAll }); ``` @@ -503,7 +507,7 @@ However, if your Azure AI Foundry deployment provides an OpenAI-compatible endpo ```typescript -import { CopilotClient } from "@github/copilot-sdk"; +import { CopilotClient, approveAll } from "@github/copilot-sdk"; const client = new CopilotClient(); const session = await client.createSession({ @@ -512,6 +516,7 @@ const session = await client.createSession({ type: "openai", baseUrl: "https://your-resource.openai.azure.com/openai/v1/", }, + onPermissionRequest: approveAll }); ``` diff --git a/docs/features/custom-agents.md b/docs/features/custom-agents.md index 47712d9cf..6cf1ce7d5 100644 --- a/docs/features/custom-agents.md +++ b/docs/features/custom-agents.md @@ -248,6 +248,7 @@ const session = await client.createSession({ }, ], agent: "researcher", // Pre-select the researcher agent + onPermissionRequest: async () => ({ kind: "approved" }), }); ``` @@ -639,6 +640,7 @@ const session = await client.createSession({ prompt: "You handle complex multi-step tasks using any available tools.", }, ], + onPermissionRequest: async () => ({ kind: "approved" }), }); ``` @@ -663,6 +665,7 @@ const session = await client.createSession({ }, }, ], + onPermissionRequest: async () => ({ kind: "approved" }), }); ``` diff --git a/docs/features/mcp.md b/docs/features/mcp.md index 62465c0bd..e91184a74 100644 --- a/docs/features/mcp.md +++ b/docs/features/mcp.md @@ -28,7 +28,7 @@ The SDK supports two types of MCP servers: ### Node.js / TypeScript ```typescript -import { CopilotClient } from "@github/copilot-sdk"; +import { CopilotClient, approveAll } from "@github/copilot-sdk"; const client = new CopilotClient(); const session = await client.createSession({ @@ -52,6 +52,7 @@ const session = await client.createSession({ tools: ["*"], }, }, + onPermissionRequest: approveAll }); ``` @@ -162,7 +163,7 @@ await using var session = await client.CreateSessionAsync(new SessionConfig Here's a complete working example using the official [`@modelcontextprotocol/server-filesystem`](https://www.npmjs.com/package/@modelcontextprotocol/server-filesystem) MCP server: ```typescript -import { CopilotClient } from "@github/copilot-sdk"; +import { CopilotClient, approveAll } from "@github/copilot-sdk"; async function main() { const client = new CopilotClient(); @@ -177,6 +178,7 @@ async function main() { tools: ["*"], }, }, + onPermissionRequest: approveAll }); console.log("Session created:", session.sessionId); diff --git a/docs/features/session-persistence.md b/docs/features/session-persistence.md index 3b0e9f69b..bed790936 100644 --- a/docs/features/session-persistence.md +++ b/docs/features/session-persistence.md @@ -26,7 +26,7 @@ The key to resumable sessions is providing your own `session_id`. Without one, t ### TypeScript ```typescript -import { CopilotClient } from "@github/copilot-sdk"; +import { CopilotClient, approveAll } from "@github/copilot-sdk"; const client = new CopilotClient(); @@ -34,6 +34,7 @@ const client = new CopilotClient(); const session = await client.createSession({ sessionId: "user-123-task-456", model: "gpt-5.2-codex", + onPermissionRequest: approveAll }); // Do some work... @@ -134,10 +135,10 @@ flowchart LR subgraph Day1["Day 1"] A1[Client A:
createSession] --> A2[Work...] end - + A2 --> S[(💾 Storage:
~/.copilot/session-state/)] S --> B1 - + subgraph Day2["Day 2"] B1[Client B:
resumeSession] --> B2[Continue] end @@ -275,6 +276,7 @@ const session = await client.createSession({ apiKey: process.env.AZURE_OPENAI_KEY, deploymentId: "my-gpt-deployment", }, + onPermissionRequest: async () => ({ kind: "approved" }), }); // When resuming, you MUST re-provide the provider config @@ -376,7 +378,7 @@ const repoSessions = await client.listSessions({ repository: "owner/repo" }); async function cleanupExpiredSessions(maxAgeMs: number) { const sessions = await client.listSessions(); const now = Date.now(); - + for (const session of sessions) { const age = now - new Date(session.createdAt).getTime(); if (age > maxAgeMs) { @@ -398,7 +400,7 @@ When a task completes, disconnect from the session explicitly rather than waitin try { // Do work... await session.sendAndWait({ prompt: "Complete the task" }); - + // Task complete — release in-memory resources (session can be resumed later) await session.disconnect(); } catch (error) { @@ -495,11 +497,11 @@ async function resumeSessionWithAuth( ): Promise { // Parse user from session ID const [sessionUserId] = sessionId.split("-"); - + if (sessionUserId !== currentUserId) { throw new Error("Access denied: session belongs to another user"); } - + return client.resumeSession(sessionId); } ``` @@ -533,10 +535,10 @@ flowchart LR subgraph Before["Container A"] CLI1[CLI + Session X] end - + CLI1 --> |persist| Azure[(☁️ Azure File Share)] Azure --> |restore| CLI2 - + subgraph After["Container B (restart)"] CLI2[CLI + Session X] end @@ -556,6 +558,7 @@ const session = await client.createSession({ backgroundCompactionThreshold: 0.80, // Start compaction at 80% context bufferExhaustionThreshold: 0.95, // Block at 95% if needed }, + onPermissionRequest: async () => ({ kind: "approved" }), }); ``` @@ -586,11 +589,11 @@ async function withSessionLock( ): Promise { const lockKey = `session-lock:${sessionId}`; const acquired = await redis.set(lockKey, "locked", "NX", "EX", 300); - + if (!acquired) { throw new Error("Session is in use by another client"); } - + try { return await fn(); } finally { diff --git a/docs/features/skills.md b/docs/features/skills.md index 466c637ff..f2f397260 100644 --- a/docs/features/skills.md +++ b/docs/features/skills.md @@ -151,6 +151,7 @@ Disable specific skills while keeping others active: const session = await client.createSession({ skillDirectories: ["./skills"], disabledSkills: ["experimental-feature", "deprecated-tool"], + onPermissionRequest: async () => ({ kind: "approved" }), }); ``` diff --git a/docs/features/steering-and-queueing.md b/docs/features/steering-and-queueing.md index 7da349e1c..afee189e0 100644 --- a/docs/features/steering-and-queueing.md +++ b/docs/features/steering-and-queueing.md @@ -41,7 +41,7 @@ Steering sends a message that is injected directly into the agent's current turn Node.js / TypeScript ```typescript -import { CopilotClient } from "@github/copilot-sdk"; +import { CopilotClient, approveAll } from "@github/copilot-sdk"; const client = new CopilotClient(); await client.start(); diff --git a/docs/getting-started.md b/docs/getting-started.md index 6c0aee72e..2cd3740fa 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -102,10 +102,10 @@ Create a new file and add the following code. This is the simplest way to use th Create `index.ts`: ```typescript -import { CopilotClient } from "@github/copilot-sdk"; +import { CopilotClient, approveAll } from "@github/copilot-sdk"; const client = new CopilotClient(); -const session = await client.createSession({ model: "gpt-4.1" }); +const session = await client.createSession({ model: "gpt-4.1", onPermissionRequest: approveAll }); const response = await session.sendAndWait({ prompt: "What is 2 + 2?" }); console.log(response?.data.content); @@ -249,7 +249,7 @@ const client = new CopilotClient(); const session = await client.createSession({ model: "gpt-4.1", streaming: true, -}); +}, onPermissionRequest: approveAll); // Listen for response chunks session.on("assistant.message_delta", (event) => { @@ -595,7 +595,7 @@ Now for the powerful part. Let's give Copilot the ability to call your code by d Update `index.ts`: ```typescript -import { CopilotClient, defineTool } from "@github/copilot-sdk"; +import { CopilotClient, defineTool, approveAll } from "@github/copilot-sdk"; // Define a tool that Copilot can call const getWeather = defineTool("get_weather", { @@ -622,6 +622,7 @@ const session = await client.createSession({ model: "gpt-4.1", streaming: true, tools: [getWeather], + onPermissionRequest: approveAll }); session.on("assistant.message_delta", (event) => { @@ -845,7 +846,7 @@ Let's put it all together into a useful interactive assistant: Node.js / TypeScript ```typescript -import { CopilotClient, defineTool } from "@github/copilot-sdk"; +import { CopilotClient, defineTool, approveAll } from "@github/copilot-sdk"; import * as readline from "readline"; const getWeather = defineTool("get_weather", { @@ -870,6 +871,7 @@ const session = await client.createSession({ model: "gpt-4.1", streaming: true, tools: [getWeather], + onPermissionRequest: approveAll }); session.on("assistant.message_delta", (event) => { @@ -1211,6 +1213,7 @@ const session = await client.createSession({ url: "https://api.githubcopilot.com/mcp/", }, }, + onPermissionRequest: async () => ({ kind: "approved" }), }); ``` @@ -1228,6 +1231,7 @@ const session = await client.createSession({ description: "Reviews pull requests for best practices", prompt: "You are an expert code reviewer. Focus on security, performance, and maintainability.", }], + onPermissionRequest: async () => ({ kind: "approved" }), }); ``` @@ -1242,6 +1246,7 @@ const session = await client.createSession({ systemMessage: { content: "You are a helpful assistant for our engineering team. Always be concise.", }, + onPermissionRequest: async () => ({ kind: "approved" }), }); ``` diff --git a/docs/hooks/error-handling.md b/docs/hooks/error-handling.md index a67906ac9..87bd631f9 100644 --- a/docs/hooks/error-handling.md +++ b/docs/hooks/error-handling.md @@ -137,6 +137,7 @@ const session = await client.createSession({ return null; }, }, + onPermissionRequest: async () => ({ kind: "approved" }), }); ``` @@ -270,10 +271,11 @@ const session = await client.createSession({ cwd: input.cwd, }, }); - + return null; }, }, + onPermissionRequest: async () => ({ kind: "approved" }), }); ``` @@ -291,16 +293,17 @@ const session = await client.createSession({ hooks: { onErrorOccurred: async (input) => { const friendlyMessage = ERROR_MESSAGES[input.errorContext]; - + if (friendlyMessage) { return { userNotification: friendlyMessage, }; } - + return null; }, }, + onPermissionRequest: async () => ({ kind: "approved" }), }); ``` @@ -318,6 +321,7 @@ const session = await client.createSession({ return null; }, }, + onPermissionRequest: async () => ({ kind: "approved" }), }); ``` @@ -337,7 +341,7 @@ The tool failed. Here are some recovery suggestions: `.trim(), }; } - + if (input.errorContext === "model_call" && input.error.includes("rate")) { return { errorHandling: "retry", @@ -345,10 +349,11 @@ The tool failed. Here are some recovery suggestions: userNotification: "Rate limit hit. Retrying...", }; } - + return null; }, }, + onPermissionRequest: async () => ({ kind: "approved" }), }); ``` @@ -367,27 +372,28 @@ const session = await client.createSession({ hooks: { onErrorOccurred: async (input, invocation) => { const key = `${input.errorContext}:${input.error.substring(0, 50)}`; - + const existing = errorStats.get(key) || { count: 0, lastOccurred: 0, contexts: [], }; - + existing.count++; existing.lastOccurred = input.timestamp; existing.contexts.push(invocation.sessionId); - + errorStats.set(key, existing); - + // Alert if error is recurring if (existing.count >= 5) { console.warn(`Recurring error detected: ${key} (${existing.count} times)`); } - + return null; }, }, + onPermissionRequest: async () => ({ kind: "approved" }), }); ``` @@ -408,10 +414,11 @@ const session = await client.createSession({ timestamp: new Date(input.timestamp).toISOString(), }); } - + return null; }, }, + onPermissionRequest: async () => ({ kind: "approved" }), }); ``` @@ -428,17 +435,17 @@ const session = await client.createSession({ sessionContext.set(invocation.sessionId, ctx); return { permissionDecision: "allow" }; }, - + onUserPromptSubmitted: async (input, invocation) => { const ctx = sessionContext.get(invocation.sessionId) || {}; ctx.lastPrompt = input.prompt.substring(0, 100); sessionContext.set(invocation.sessionId, ctx); return null; }, - + onErrorOccurred: async (input, invocation) => { const ctx = sessionContext.get(invocation.sessionId); - + console.error(`Error in session ${invocation.sessionId}:`); console.error(` Error: ${input.error}`); console.error(` Context: ${input.errorContext}`); @@ -448,10 +455,11 @@ const session = await client.createSession({ if (ctx?.lastPrompt) { console.error(` Last prompt: ${ctx.lastPrompt}...`); } - + return null; }, }, + onPermissionRequest: async () => ({ kind: "approved" }), }); ``` diff --git a/docs/hooks/index.md b/docs/hooks/index.md index d83b11b2f..997c23a5a 100644 --- a/docs/hooks/index.md +++ b/docs/hooks/index.md @@ -25,7 +25,7 @@ Hooks allow you to intercept and customize the behavior of Copilot sessions at k Node.js / TypeScript ```typescript -import { CopilotClient } from "@github/copilot-sdk"; +import { CopilotClient, approveAll } from "@github/copilot-sdk"; const client = new CopilotClient(); @@ -44,6 +44,7 @@ const session = await client.createSession({ return { additionalContext: "User prefers concise answers." }; }, }, + onPermissionRequest: approveAll }); ``` @@ -181,6 +182,7 @@ const session = await client.createSession({ return null; }, }, + onPermissionRequest: async () => ({ kind: "approved" }), }); ``` @@ -201,6 +203,7 @@ const session = await client.createSession({ return { permissionDecision: "allow" }; }, }, + onPermissionRequest: async () => ({ kind: "approved" }), }); ``` @@ -216,6 +219,7 @@ const session = await client.createSession({ }; }, }, + onPermissionRequest: async () => ({ kind: "approved" }), }); ``` diff --git a/docs/hooks/post-tool-use.md b/docs/hooks/post-tool-use.md index 029e9eb2f..afda6f428 100644 --- a/docs/hooks/post-tool-use.md +++ b/docs/hooks/post-tool-use.md @@ -136,6 +136,7 @@ const session = await client.createSession({ return null; // Pass through unchanged }, }, + onPermissionRequest: async () => ({ kind: "approved" }), }); ``` @@ -267,7 +268,7 @@ const session = await client.createSession({ for (const pattern of SENSITIVE_PATTERNS) { redacted = redacted.replace(pattern, "[REDACTED]"); } - + if (redacted !== input.toolResult) { return { modifiedResult: redacted }; } @@ -275,6 +276,7 @@ const session = await client.createSession({ return null; }, }, + onPermissionRequest: async () => ({ kind: "approved" }), }); ``` @@ -287,7 +289,7 @@ const session = await client.createSession({ hooks: { onPostToolUse: async (input) => { const resultStr = JSON.stringify(input.toolResult); - + if (resultStr.length > MAX_RESULT_LENGTH) { return { modifiedResult: { @@ -301,6 +303,7 @@ const session = await client.createSession({ return null; }, }, + onPermissionRequest: async () => ({ kind: "approved" }), }); ``` @@ -316,17 +319,18 @@ const session = await client.createSession({ additionalContext: "Tip: If the file doesn't exist, consider creating it or checking the path.", }; } - + // If shell command failed, add debugging hint if (input.toolName === "shell" && input.toolResult?.exitCode !== 0) { return { additionalContext: "The command failed. Check if required dependencies are installed.", }; } - + return null; }, }, + onPermissionRequest: async () => ({ kind: "approved" }), }); ``` @@ -349,6 +353,7 @@ const session = await client.createSession({ return null; }, }, + onPermissionRequest: async () => ({ kind: "approved" }), }); ``` @@ -377,13 +382,14 @@ const session = await client.createSession({ result: input.toolResult, success: !input.toolResult?.error, }); - + // Optionally persist to database/file await saveAuditLog(auditLog); - + return null; }, }, + onPermissionRequest: async () => ({ kind: "approved" }), }); ``` @@ -397,10 +403,10 @@ const session = await client.createSession({ onPostToolUse: async (input) => { if (NOISY_TOOLS.includes(input.toolName)) { // Summarize instead of showing full result - const items = Array.isArray(input.toolResult) - ? input.toolResult + const items = Array.isArray(input.toolResult) + ? input.toolResult : input.toolResult?.items || []; - + return { modifiedResult: { summary: `Found ${items.length} items`, @@ -411,6 +417,7 @@ const session = await client.createSession({ return null; }, }, + onPermissionRequest: async () => ({ kind: "approved" }), }); ``` diff --git a/docs/hooks/pre-tool-use.md b/docs/hooks/pre-tool-use.md index e1bb97495..5ad161ab9 100644 --- a/docs/hooks/pre-tool-use.md +++ b/docs/hooks/pre-tool-use.md @@ -144,6 +144,7 @@ const session = await client.createSession({ return { permissionDecision: "allow" }; }, }, + onPermissionRequest: async () => ({ kind: "approved" }), }); ``` @@ -278,6 +279,7 @@ const session = await client.createSession({ return { permissionDecision: "allow" }; }, }, + onPermissionRequest: async () => ({ kind: "approved" }), }); ``` @@ -301,6 +303,7 @@ const session = await client.createSession({ return { permissionDecision: "allow" }; }, }, + onPermissionRequest: async () => ({ kind: "approved" }), }); ``` @@ -314,10 +317,10 @@ const session = await client.createSession({ onPreToolUse: async (input) => { if (input.toolName === "read_file" || input.toolName === "write_file") { const args = input.toolArgs as { path: string }; - const isAllowed = ALLOWED_DIRECTORIES.some(dir => + const isAllowed = ALLOWED_DIRECTORIES.some(dir => args.path.startsWith(dir) ); - + if (!isAllowed) { return { permissionDecision: "deny", @@ -328,6 +331,7 @@ const session = await client.createSession({ return { permissionDecision: "allow" }; }, }, + onPermissionRequest: async () => ({ kind: "approved" }), }); ``` @@ -345,6 +349,7 @@ const session = await client.createSession({ }; }, }, + onPermissionRequest: async () => ({ kind: "approved" }), }); ``` @@ -363,6 +368,7 @@ const session = await client.createSession({ return { permissionDecision: "allow" }; }, }, + onPermissionRequest: async () => ({ kind: "approved" }), }); ``` diff --git a/docs/hooks/session-lifecycle.md b/docs/hooks/session-lifecycle.md index 4efd33ccc..9d5732cc0 100644 --- a/docs/hooks/session-lifecycle.md +++ b/docs/hooks/session-lifecycle.md @@ -131,9 +131,9 @@ const session = await client.createSession({ hooks: { onSessionStart: async (input, invocation) => { console.log(`Session ${invocation.sessionId} started (${input.source})`); - + const projectInfo = await detectProjectType(input.cwd); - + return { additionalContext: ` This is a ${projectInfo.type} project. @@ -143,6 +143,7 @@ Package manager: ${projectInfo.packageManager} }; }, }, + onPermissionRequest: async () => ({ kind: "approved" }), }); ``` @@ -156,9 +157,9 @@ from copilot import PermissionHandler async def on_session_start(input_data, invocation): print(f"Session {invocation['session_id']} started ({input_data['source']})") - + project_info = await detect_project_type(input_data["cwd"]) - + return { "additionalContext": f""" This is a {project_info['type']} project. @@ -181,7 +182,7 @@ const session = await client.createSession({ if (input.source === "resume") { // Load previous session state const previousState = await loadSessionState(invocation.sessionId); - + return { additionalContext: ` Session resumed. Previous context: @@ -193,6 +194,7 @@ Session resumed. Previous context: return null; }, }, + onPermissionRequest: async () => ({ kind: "approved" }), }); ``` @@ -203,9 +205,9 @@ const session = await client.createSession({ hooks: { onSessionStart: async () => { const preferences = await loadUserPreferences(); - + const contextParts = []; - + if (preferences.language) { contextParts.push(`Preferred language: ${preferences.language}`); } @@ -215,12 +217,13 @@ const session = await client.createSession({ if (preferences.verbosity === "concise") { contextParts.push("Keep responses brief and to the point."); } - + return { additionalContext: contextParts.join("\n"), }; }, }, + onPermissionRequest: async () => ({ kind: "approved" }), }); ``` @@ -351,17 +354,18 @@ const session = await client.createSession({ onSessionEnd: async (input, invocation) => { const startTime = sessionStartTimes.get(invocation.sessionId); const duration = startTime ? input.timestamp - startTime : 0; - + await recordMetrics({ sessionId: invocation.sessionId, duration, endReason: input.reason, }); - + sessionStartTimes.delete(invocation.sessionId); return null; }, }, + onPermissionRequest: async () => ({ kind: "approved" }), }); ``` @@ -382,13 +386,13 @@ async def on_session_start(input_data, invocation): async def on_session_end(input_data, invocation): start_time = session_start_times.get(invocation["session_id"]) duration = input_data["timestamp"] - start_time if start_time else 0 - + await record_metrics({ "session_id": invocation["session_id"], "duration": duration, "end_reason": input_data["reason"], }) - + session_start_times.pop(invocation["session_id"], None) return None @@ -413,7 +417,7 @@ const session = await client.createSession({ }, onSessionEnd: async (input, invocation) => { const resources = sessionResources.get(invocation.sessionId); - + if (resources) { // Clean up temp files for (const file of resources.tempFiles) { @@ -421,11 +425,12 @@ const session = await client.createSession({ } sessionResources.delete(invocation.sessionId); } - + console.log(`Session ${invocation.sessionId} ended: ${input.reason}`); return null; }, }, + onPermissionRequest: async () => ({ kind: "approved" }), }); ``` @@ -446,6 +451,7 @@ const session = await client.createSession({ return null; }, }, + onPermissionRequest: approveAll }); ``` @@ -457,10 +463,10 @@ const sessionData: Record { - sessionData[invocation.sessionId] = { - prompts: 0, - tools: 0, - startTime: input.timestamp + sessionData[invocation.sessionId] = { + prompts: 0, + tools: 0, + startTime: input.timestamp }; return null; }, @@ -482,11 +488,12 @@ Session Summary: Tool calls: ${data.tools} End reason: ${input.reason} `.trim()); - + delete sessionData[invocation.sessionId]; return null; }, }, + onPermissionRequest: async () => ({ kind: "approved" }), }); ``` diff --git a/docs/hooks/user-prompt-submitted.md b/docs/hooks/user-prompt-submitted.md index 2aca7f1ce..c952f7018 100644 --- a/docs/hooks/user-prompt-submitted.md +++ b/docs/hooks/user-prompt-submitted.md @@ -132,6 +132,7 @@ const session = await client.createSession({ return null; // Pass through unchanged }, }, + onPermissionRequest: async () => ({ kind: "approved" }), }); ``` @@ -243,7 +244,7 @@ const session = await client.createSession({ hooks: { onUserPromptSubmitted: async (input) => { const projectInfo = await getProjectInfo(); - + return { additionalContext: ` Project: ${projectInfo.name} @@ -253,6 +254,7 @@ Framework: ${projectInfo.framework} }; }, }, + onPermissionRequest: async () => ({ kind: "approved" }), }); ``` @@ -280,6 +282,7 @@ const session = await client.createSession({ return null; }, }, + onPermissionRequest: async () => ({ kind: "approved" }), }); ``` @@ -307,6 +310,7 @@ const session = await client.createSession({ return null; }, }, + onPermissionRequest: async () => ({ kind: "approved" }), }); ``` @@ -328,6 +332,7 @@ const session = await client.createSession({ return null; }, }, + onPermissionRequest: async () => ({ kind: "approved" }), }); ``` @@ -344,24 +349,25 @@ const session = await client.createSession({ hooks: { onUserPromptSubmitted: async (input) => { const prefs: UserPreferences = await loadUserPreferences(); - + const contextParts = []; - + if (prefs.codeStyle === "concise") { contextParts.push("User prefers concise code with minimal comments."); } else { contextParts.push("User prefers verbose code with detailed comments."); } - + if (prefs.experienceLevel === "beginner") { contextParts.push("Explain concepts in simple terms."); } - + return { additionalContext: contextParts.join(" "), }; }, }, + onPermissionRequest: async () => ({ kind: "approved" }), }); ``` @@ -376,23 +382,24 @@ const session = await client.createSession({ hooks: { onUserPromptSubmitted: async (input) => { const now = Date.now(); - + // Remove timestamps outside the window while (promptTimestamps.length > 0 && promptTimestamps[0] < now - RATE_WINDOW) { promptTimestamps.shift(); } - + if (promptTimestamps.length >= RATE_LIMIT) { return { reject: true, rejectReason: `Rate limit exceeded. Please wait before sending more prompts.`, }; } - + promptTimestamps.push(now); return null; }, }, + onPermissionRequest: async () => ({ kind: "approved" }), }); ``` @@ -429,6 +436,7 @@ const session = await client.createSession({ return null; }, }, + onPermissionRequest: async () => ({ kind: "approved" }), }); ``` diff --git a/docs/setup/azure-managed-identity.md b/docs/setup/azure-managed-identity.md index 40d87c5ba..c3d1821c1 100644 --- a/docs/setup/azure-managed-identity.md +++ b/docs/setup/azure-managed-identity.md @@ -124,7 +124,7 @@ class ManagedIdentityCopilotAgent: ```typescript import { DefaultAzureCredential } from "@azure/identity"; -import { CopilotClient } from "@github/copilot-sdk"; +import { CopilotClient, approveAll } from "@github/copilot-sdk"; const credential = new DefaultAzureCredential(); const tokenResponse = await credential.getToken( @@ -141,6 +141,7 @@ const session = await client.createSession({ bearerToken: tokenResponse.token, wireApi: "responses", }, + onPermissionRequest: approveAll }); const response = await session.sendAndWait({ prompt: "Hello!" }); diff --git a/docs/setup/backend-services.md b/docs/setup/backend-services.md index 735adf4ff..26857abea 100644 --- a/docs/setup/backend-services.md +++ b/docs/setup/backend-services.md @@ -90,7 +90,7 @@ Restart=always Node.js / TypeScript ```typescript -import { CopilotClient } from "@github/copilot-sdk"; +import { CopilotClient, approveAll } from "@github/copilot-sdk"; const client = new CopilotClient({ cliUrl: "localhost:4321", @@ -99,6 +99,7 @@ const client = new CopilotClient({ const session = await client.createSession({ sessionId: `user-${userId}-${Date.now()}`, model: "gpt-4.1", + onPermissionRequest: approveAll }); const response = await session.sendAndWait({ prompt: req.body.message }); @@ -265,6 +266,7 @@ app.post("/chat", authMiddleware, async (req, res) => { const session = await client.createSession({ sessionId: `user-${req.user.id}-chat`, model: "gpt-4.1", + onPermissionRequest: async () => ({ kind: "approved" }), }); const response = await session.sendAndWait({ @@ -291,6 +293,7 @@ const session = await client.createSession({ baseUrl: "https://api.openai.com/v1", apiKey: process.env.OPENAI_API_KEY, }, + onPermissionRequest: async () => ({ kind: "approved" }), }); ``` @@ -316,7 +319,7 @@ flowchart TB ```typescript import express from "express"; -import { CopilotClient } from "@github/copilot-sdk"; +import { CopilotClient, approveAll } from "@github/copilot-sdk"; const app = express(); app.use(express.json()); @@ -337,6 +340,7 @@ app.post("/api/chat", async (req, res) => { session = await client.createSession({ sessionId, model: "gpt-4.1", + onPermissionRequest: approveAll }); } @@ -353,7 +357,7 @@ app.listen(3000); ### Background Worker ```typescript -import { CopilotClient } from "@github/copilot-sdk"; +import { CopilotClient, approveAll } from "@github/copilot-sdk"; const client = new CopilotClient({ cliUrl: process.env.CLI_URL || "localhost:4321", @@ -364,6 +368,7 @@ async function processJob(job: Job) { const session = await client.createSession({ sessionId: `job-${job.id}`, model: "gpt-4.1", + onPermissionRequest: approveAll }); const response = await session.sendAndWait({ diff --git a/docs/setup/bundled-cli.md b/docs/setup/bundled-cli.md index cdfe6df81..7dd887cca 100644 --- a/docs/setup/bundled-cli.md +++ b/docs/setup/bundled-cli.md @@ -64,7 +64,7 @@ npm install @github/copilot Node.js / TypeScript ```typescript -import { CopilotClient } from "@github/copilot-sdk"; +import { CopilotClient, approveAll } from "@github/copilot-sdk"; import path from "path"; const client = new CopilotClient({ @@ -72,7 +72,7 @@ const client = new CopilotClient({ cliPath: path.join(__dirname, "vendor", "copilot"), }); -const session = await client.createSession({ model: "gpt-4.1" }); +const session = await client.createSession({ model: "gpt-4.1", onPermissionRequest: approveAll }); const response = await session.sendAndWait({ prompt: "Hello!" }); console.log(response?.data.content); @@ -228,6 +228,7 @@ const session = await client.createSession({ baseUrl: "https://api.openai.com/v1", apiKey: process.env.OPENAI_API_KEY, }, + onPermissionRequest: async () => ({ kind: "approved" }), }); ``` @@ -247,6 +248,7 @@ const sessionId = `project-${projectName}`; const session = await client.createSession({ sessionId, model: "gpt-4.1", + onPermissionRequest: async () => ({ kind: "approved" }), }); // User closes app... diff --git a/docs/setup/github-oauth.md b/docs/setup/github-oauth.md index 81d2b25a2..1db796ec7 100644 --- a/docs/setup/github-oauth.md +++ b/docs/setup/github-oauth.md @@ -119,7 +119,7 @@ Create a SDK client for each authenticated user, passing their token: Node.js / TypeScript ```typescript -import { CopilotClient } from "@github/copilot-sdk"; +import { CopilotClient, approveAll } from "@github/copilot-sdk"; // Create a client for an authenticated user function createClientForUser(userToken: string): CopilotClient { @@ -134,6 +134,7 @@ const client = createClientForUser("gho_user_access_token"); const session = await client.createSession({ sessionId: `user-${userId}-session`, model: "gpt-4.1", + onPermissionRequest: approveAll }); const response = await session.sendAndWait({ prompt: "Hello!" }); diff --git a/docs/setup/local-cli.md b/docs/setup/local-cli.md index bb95a4d38..3f11402fc 100644 --- a/docs/setup/local-cli.md +++ b/docs/setup/local-cli.md @@ -34,10 +34,10 @@ The default configuration requires no options at all: Node.js / TypeScript ```typescript -import { CopilotClient } from "@github/copilot-sdk"; +import { CopilotClient, approveAll } from "@github/copilot-sdk"; const client = new CopilotClient(); -const session = await client.createSession({ model: "gpt-4.1" }); +const session = await client.createSession({ model: "gpt-4.1", onPermissionRequest: approveAll }); const response = await session.sendAndWait({ prompt: "Hello!" }); console.log(response?.data.content); @@ -196,6 +196,7 @@ With the local CLI, sessions default to ephemeral. To create resumable sessions, const session = await client.createSession({ sessionId: "my-project-analysis", model: "gpt-4.1", + onPermissionRequest: async () => ({ kind: "approved" }), }); // Later, resume it diff --git a/docs/setup/scaling.md b/docs/setup/scaling.md index 325d9244d..957836b1b 100644 --- a/docs/setup/scaling.md +++ b/docs/setup/scaling.md @@ -323,6 +323,7 @@ app.post("/chat", async (req, res) => { const session = await client.createSession({ sessionId: `user-${req.user.id}-chat`, model: "gpt-4.1", + onPermissionRequest: async () => ({ kind: "approved" }), }); const response = await session.sendAndWait({ prompt: req.body.message }); @@ -403,6 +404,7 @@ class SessionManager { const session = await client.createSession({ sessionId, model: "gpt-4.1", + onPermissionRequest: async () => ({ kind: "approved" }), }); this.activeSessions.set(sessionId, session); @@ -449,6 +451,7 @@ For stateless API endpoints where each request is independent: app.post("/api/analyze", async (req, res) => { const session = await client.createSession({ model: "gpt-4.1", + onPermissionRequest: async () => ({ kind: "approved" }), }); try { @@ -478,6 +481,7 @@ app.post("/api/chat/start", async (req, res) => { enabled: true, backgroundCompactionThreshold: 0.80, }, + onPermissionRequest: async () => ({ kind: "approved" }), }); res.json({ sessionId }); diff --git a/docs/troubleshooting/compatibility.md b/docs/troubleshooting/compatibility.md index 1a322b88c..2ce76525a 100644 --- a/docs/troubleshooting/compatibility.md +++ b/docs/troubleshooting/compatibility.md @@ -219,6 +219,7 @@ const session = await client.createSession({ backgroundCompactionThreshold: 0.80, // Start background compaction at 80% context utilization bufferExhaustionThreshold: 0.95, // Block and compact at 95% context utilization }, + onPermissionRequest: async () => ({ kind: "approved" }), }); // Manual compaction (experimental) diff --git a/docs/troubleshooting/debugging.md b/docs/troubleshooting/debugging.md index 146d3fd5a..9ba94eb2b 100644 --- a/docs/troubleshooting/debugging.md +++ b/docs/troubleshooting/debugging.md @@ -395,8 +395,9 @@ const client = new CopilotClient({ ```typescript const session = await client.createSession({ tools: [myTool], + onPermissionRequest: async () => ({ kind: "approved" }), }); - + // Check registered tools console.log("Registered tools:", session.getTools?.()); ``` @@ -424,7 +425,7 @@ const client = new CopilotClient({ handler: async (args) => { // Must return something JSON-serializable return { success: true, data: "result" }; - + // Don't return undefined or non-serializable objects } ``` diff --git a/nodejs/README.md b/nodejs/README.md index e9d23c529..1c9f5579d 100644 --- a/nodejs/README.md +++ b/nodejs/README.md @@ -341,6 +341,7 @@ Enable streaming to receive assistant response chunks as they're generated: const session = await client.createSession({ model: "gpt-5", streaming: true, + onPermissionRequest: async () => ({ kind: "approved" }), }); // Wait for completion using typed event handlers @@ -408,7 +409,7 @@ You can let the CLI call back into your process when the model needs capabilitie ```ts import { z } from "zod"; -import { CopilotClient, defineTool } from "@github/copilot-sdk"; +import { CopilotClient, defineTool, approveAll } from "@github/copilot-sdk"; const session = await client.createSession({ model: "gpt-5", @@ -424,6 +425,7 @@ const session = await client.createSession({ }, }), ], + onPermissionRequest: approveAll }); ``` @@ -470,6 +472,7 @@ const session = await client.createSession({ `, }, + onPermissionRequest: async () => ({ kind: "approved" }), }); ``` @@ -484,6 +487,7 @@ const session = await client.createSession({ mode: "replace", content: "You are a helpful assistant.", }, + onPermissionRequest: async () => ({ kind: "approved" }), }); ``` @@ -493,7 +497,7 @@ By default, sessions use **infinite sessions** which automatically manage contex ```typescript // Default: infinite sessions enabled with default thresholds -const session = await client.createSession({ model: "gpt-5" }); +const session = await client.createSession({ model: "gpt-5",onPermissionRequest: async () => ({ kind: "approved" }), }); // Access the workspace path for checkpoints and files console.log(session.workspacePath); @@ -507,12 +511,14 @@ const session = await client.createSession({ backgroundCompactionThreshold: 0.80, // Start compacting at 80% context usage bufferExhaustionThreshold: 0.95, // Block at 95% until compaction completes }, + onPermissionRequest: async () => ({ kind: "approved" }), }); // Disable infinite sessions const session = await client.createSession({ model: "gpt-5", infiniteSessions: { enabled: false }, + onPermissionRequest: approveAll }); ``` @@ -524,8 +530,8 @@ When enabled, sessions emit compaction events: ### Multiple Sessions ```typescript -const session1 = await client.createSession({ model: "gpt-5" }); -const session2 = await client.createSession({ model: "claude-sonnet-4.5" }); +const session1 = await client.createSession({ model: "gpt-5", onPermissionRequest: async () => ({ kind: "approved" }), }); +const session2 = await client.createSession({ model: "claude-sonnet-4.5", onPermissionRequest: async () => ({ kind: "approved" }), }); // Both sessions are independent await session1.sendAndWait({ prompt: "Hello from session 1" }); @@ -538,6 +544,7 @@ await session2.sendAndWait({ prompt: "Hello from session 2" }); const session = await client.createSession({ sessionId: "my-custom-session-id", model: "gpt-5", + onPermissionRequest: async () => ({ kind: "approved" }), }); ``` @@ -579,6 +586,7 @@ const session = await client.createSession({ baseUrl: "http://localhost:11434/v1", // Ollama endpoint // apiKey not required for Ollama }, + onPermissionRequest: async () => ({ kind: "approved" }), }); await session.sendAndWait({ prompt: "Hello!" }); @@ -594,6 +602,7 @@ const session = await client.createSession({ baseUrl: "https://my-api.example.com/v1", apiKey: process.env.MY_API_KEY, }, + onPermissionRequest: async () => ({ kind: "approved" }), }); ``` @@ -610,6 +619,7 @@ const session = await client.createSession({ apiVersion: "2024-10-21", }, }, + onPermissionRequest: async () => ({ kind: "approved" }), }); ``` @@ -685,7 +695,7 @@ const session = await client.createSession({ Provide your own function to inspect each request and apply custom logic: ```typescript -import type { PermissionRequest, PermissionRequestResult } from "@github/copilot-sdk"; +import type { PermissionRequest, PermissionRequestResult, approveAll } from "@github/copilot-sdk"; const session = await client.createSession({ model: "gpt-5", @@ -712,6 +722,7 @@ const session = await client.createSession({ return { kind: "approved" }; }, + onPermissionRequest: approveAll }); ``` @@ -762,6 +773,7 @@ const session = await client.createSession({ wasFreeform: true, // Whether the answer was freeform (not from choices) }; }, + onPermissionRequest: async () => ({ kind: "approved" }), }); ``` @@ -822,6 +834,7 @@ const session = await client.createSession({ }; }, }, + onPermissionRequest: async () => ({ kind: "approved" }), }); ``` diff --git a/nodejs/examples/basic-example.ts b/nodejs/examples/basic-example.ts index c20a85af0..dc4d002c3 100644 --- a/nodejs/examples/basic-example.ts +++ b/nodejs/examples/basic-example.ts @@ -3,7 +3,7 @@ *--------------------------------------------------------------------------------------------*/ import { z } from "zod"; -import { CopilotClient, defineTool } from "../src/index.js"; +import { CopilotClient, defineTool, approveAll } from "../src/index.js"; console.log("🚀 Starting Copilot SDK Example\n"); @@ -22,7 +22,7 @@ const lookupFactTool = defineTool("lookup_fact", { // Create client - will auto-start CLI server (searches PATH for "copilot") const client = new CopilotClient({ logLevel: "info" }); -const session = await client.createSession({ tools: [lookupFactTool] }); +const session = await client.createSession({ tools: [lookupFactTool], onPermissionRequest: approveAll }); console.log(`✅ Session created: ${session.sessionId}\n`); // Listen to events From ccb6d880b849f551df1e1fab70d4ef032a0e456a Mon Sep 17 00:00:00 2001 From: Hirofumi Horikawa Date: Fri, 20 Mar 2026 20:30:39 +0900 Subject: [PATCH 2/3] fix: use imported `approveAll` for `onPermissionRequest` --- CHANGELOG.md | 8 ++- docs/auth/byok.md | 10 ++- docs/features/custom-agents.md | 19 +++-- docs/features/hooks.md | 72 ++++++++++++++----- docs/features/image-input.md | 8 +-- docs/features/session-persistence.md | 10 ++- docs/features/skills.md | 19 +++-- docs/features/steering-and-queueing.md | 11 +-- docs/getting-started.md | 17 +++-- docs/hooks/error-handling.md | 40 ++++++++--- docs/hooks/index.md | 15 +++- docs/hooks/post-tool-use.md | 35 +++++++-- docs/hooks/pre-tool-use.md | 30 ++++++-- docs/hooks/session-lifecycle.md | 33 +++++++-- docs/hooks/user-prompt-submitted.md | 40 ++++++++--- .../integrations/microsoft-agent-framework.md | 14 ++-- docs/setup/backend-services.md | 9 ++- docs/setup/bundled-cli.md | 8 ++- docs/setup/local-cli.md | 5 +- docs/setup/scaling.md | 22 ++++-- docs/troubleshooting/compatibility.md | 8 ++- docs/troubleshooting/debugging.md | 5 +- nodejs/README.md | 70 +++++++++++++----- 23 files changed, 393 insertions(+), 115 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ac5712aa5..8bb74e3e8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,10 @@ See [GitHub Releases](https://github.com/github/copilot-sdk/releases) for the fu SDK applications written against the v3 API now also work when connected to a v2 CLI server, with no code changes required. The SDK detects the server's protocol version and automatically adapts v2 `tool.call` and `permission.request` messages into the same user-facing handlers used by v3. ([#706](https://github.com/github/copilot-sdk/pull/706)) ```ts +import { CopilotClient, approveAll } from "@github/copilot-sdk"; + +const client = new CopilotClient(); + const session = await client.createSession({ tools: [myTool], // unchanged — works with v2 and v3 servers onPermissionRequest: approveAll, @@ -84,7 +88,9 @@ if result.Kind == copilot.PermissionKindApproved { /* ... */ } Applications can now override built-in tools such as `grep`, `edit_file`, or `read_file`. To do this, register a custom tool with the same name and set the override flag. Without the flag, the runtime will return an error if the name clashes with a built-in. ([#636](https://github.com/github/copilot-sdk/pull/636)) ```ts -import { defineTool } from "@github/copilot-sdk"; +import { CopilotClient, defineTool, approveAll } from "@github/copilot-sdk"; + +const client = new CopilotClient(); const session = await client.createSession({ tools: [defineTool("grep", { diff --git a/docs/auth/byok.md b/docs/auth/byok.md index a0c576f33..04154e07e 100644 --- a/docs/auth/byok.md +++ b/docs/auth/byok.md @@ -456,16 +456,22 @@ When using BYOK, the `model` parameter is **required**: ```typescript // ❌ Error: Model required with custom provider +import { CopilotClient, approveAll } from "@github/copilot-sdk"; + +const client = new CopilotClient(); const session = await client.createSession({ provider: { type: "openai", baseUrl: "..." }, - onPermissionRequest: async () => ({ kind: "approved" }), + onPermissionRequest: approveAll, }); // ✅ Correct: Model specified +import { CopilotClient, approveAll } from "@github/copilot-sdk"; + +const client = new CopilotClient(); const session = await client.createSession({ model: "gpt-4", // Required! provider: { type: "openai", baseUrl: "..." }, - onPermissionRequest: async () => ({ kind: "approved" }), + onPermissionRequest: approveAll, }); ``` diff --git a/docs/features/custom-agents.md b/docs/features/custom-agents.md index 6cf1ce7d5..aee06d5cb 100644 --- a/docs/features/custom-agents.md +++ b/docs/features/custom-agents.md @@ -31,7 +31,7 @@ Pass `customAgents` when creating a session. Each agent needs at minimum a `name Node.js / TypeScript ```typescript -import { CopilotClient } from "@github/copilot-sdk"; +import { CopilotClient, approveAll } from "@github/copilot-sdk"; const client = new CopilotClient(); await client.start(); @@ -54,7 +54,7 @@ const session = await client.createSession({ prompt: "You are a code editor. Make minimal, surgical changes to files as requested.", }, ], - onPermissionRequest: async () => ({ kind: "approved" }), + onPermissionRequest: approveAll, }); ``` @@ -236,6 +236,9 @@ This is equivalent to calling `session.rpc.agent.select()` after creation, but a ```typescript +import { CopilotClient, approveAll } from "@github/copilot-sdk"; + +const client = new CopilotClient(); const session = await client.createSession({ customAgents: [ { @@ -248,7 +251,7 @@ const session = await client.createSession({ }, ], agent: "researcher", // Pre-select the researcher agent - onPermissionRequest: async () => ({ kind: "approved" }), + onPermissionRequest: approveAll, }); ``` @@ -619,6 +622,9 @@ session.on((event) => { Use the `tools` property to restrict which tools an agent can access. This is essential for security and for keeping agents focused: ```typescript +import { CopilotClient, approveAll } from "@github/copilot-sdk"; + +const client = new CopilotClient(); const session = await client.createSession({ customAgents: [ { @@ -640,7 +646,7 @@ const session = await client.createSession({ prompt: "You handle complex multi-step tasks using any available tools.", }, ], - onPermissionRequest: async () => ({ kind: "approved" }), + onPermissionRequest: approveAll, }); ``` @@ -651,6 +657,9 @@ const session = await client.createSession({ Each custom agent can have its own MCP (Model Context Protocol) servers, giving it access to specialized data sources: ```typescript +import { CopilotClient, approveAll } from "@github/copilot-sdk"; + +const client = new CopilotClient(); const session = await client.createSession({ customAgents: [ { @@ -665,7 +674,7 @@ const session = await client.createSession({ }, }, ], - onPermissionRequest: async () => ({ kind: "approved" }), + onPermissionRequest: approveAll, }); ``` diff --git a/docs/features/hooks.md b/docs/features/hooks.md index 1a01c5f1a..5af4e60f5 100644 --- a/docs/features/hooks.md +++ b/docs/features/hooks.md @@ -38,7 +38,7 @@ Pass a `hooks` object when you create (or resume) a session. Every example below Node.js / TypeScript ```typescript -import { CopilotClient } from "@github/copilot-sdk"; +import { CopilotClient, approveAll } from "@github/copilot-sdk"; const client = new CopilotClient(); await client.start(); @@ -50,7 +50,7 @@ const session = await client.createSession({ onPostToolUse: async (input, invocation) => { /* ... */ }, // ... add only the hooks you need }, - onPermissionRequest: async () => ({ kind: "approved" }), + onPermissionRequest: approveAll, }); ``` @@ -209,6 +209,9 @@ Use `onPreToolUse` to build a permission layer that decides which tools the agen Node.js / TypeScript ```typescript +import { CopilotClient, approveAll } from "@github/copilot-sdk"; + +const client = new CopilotClient(); const READ_ONLY_TOOLS = ["read_file", "glob", "grep", "view"]; const session = await client.createSession({ @@ -224,7 +227,7 @@ const session = await client.createSession({ return { permissionDecision: "allow" }; }, }, - onPermissionRequest: async () => ({ kind: "approved" }), + onPermissionRequest: approveAll, }); ``` @@ -383,6 +386,9 @@ var session = await client.CreateSessionAsync(new SessionConfig ### Restrict file access to specific directories ```typescript +import { CopilotClient, approveAll } from "@github/copilot-sdk"; + +const client = new CopilotClient(); const ALLOWED_DIRS = ["/home/user/projects", "/tmp"]; const session = await client.createSession({ @@ -403,13 +409,16 @@ const session = await client.createSession({ return { permissionDecision: "allow" }; }, }, - onPermissionRequest: async () => ({ kind: "approved" }), + onPermissionRequest: approveAll, }); ``` ### Ask the user before destructive operations ```typescript +import { CopilotClient, approveAll } from "@github/copilot-sdk"; + +const client = new CopilotClient(); const DESTRUCTIVE_TOOLS = ["delete_file", "shell", "bash"]; const session = await client.createSession({ @@ -421,7 +430,7 @@ const session = await client.createSession({ return { permissionDecision: "allow" }; }, }, - onPermissionRequest: async () => ({ kind: "approved" }), + onPermissionRequest: approveAll, }); ``` @@ -439,6 +448,8 @@ Combine `onPreToolUse`, `onPostToolUse`, and the session lifecycle hooks to buil Node.js / TypeScript ```typescript +import { CopilotClient, approveAll } from "@github/copilot-sdk"; + interface AuditEntry { timestamp: number; sessionId: string; @@ -451,6 +462,7 @@ interface AuditEntry { const auditLog: AuditEntry[] = []; +const client = new CopilotClient(); const session = await client.createSession({ hooks: { onSessionStart: async (input, invocation) => { @@ -505,7 +517,7 @@ const session = await client.createSession({ return null; }, }, - onPermissionRequest: async () => ({ kind: "approved" }), + onPermissionRequest: approveAll, }); ``` @@ -584,6 +596,9 @@ session = await client.create_session( ### Redact secrets from tool results ```typescript +import { CopilotClient, approveAll } from "@github/copilot-sdk"; + +const client = new CopilotClient(); const SECRET_PATTERNS = [ /(?:api[_-]?key|token|secret|password)\s*[:=]\s*["']?[\w\-\.]+["']?/gi, ]; @@ -603,7 +618,7 @@ const session = await client.createSession({ : null; }, }, - onPermissionRequest: async () => ({ kind: "approved" }), + onPermissionRequest: approveAll, }); ``` @@ -620,7 +635,9 @@ Hooks fire in your application's process, so you can trigger any side-effect — ```typescript import notifier from "node-notifier"; // npm install node-notifier +import { CopilotClient, approveAll } from "@github/copilot-sdk"; +const client = new CopilotClient(); const session = await client.createSession({ hooks: { onSessionEnd: async (input, invocation) => { @@ -638,7 +655,7 @@ const session = await client.createSession({ return null; }, }, - onPermissionRequest: async () => ({ kind: "approved" }), + onPermissionRequest: approveAll, }); ``` @@ -681,7 +698,9 @@ session = await client.create_session( ```typescript import { exec } from "node:child_process"; +import { CopilotClient, approveAll } from "@github/copilot-sdk"; +const client = new CopilotClient(); const session = await client.createSession({ hooks: { onPostToolUse: async (input) => { @@ -694,13 +713,16 @@ const session = await client.createSession({ return null; }, }, - onPermissionRequest: async () => ({ kind: "approved" }), + onPermissionRequest: approveAll, }); ``` ### Post to Slack on errors ```typescript +import { CopilotClient, approveAll } from "@github/copilot-sdk"; + +const client = new CopilotClient(); const SLACK_WEBHOOK_URL = process.env.SLACK_WEBHOOK_URL!; const session = await client.createSession({ @@ -718,7 +740,7 @@ const session = await client.createSession({ return null; }, }, - onPermissionRequest: async () => ({ kind: "approved" }), + onPermissionRequest: approveAll, }); ``` @@ -731,6 +753,9 @@ Use `onSessionStart` and `onUserPromptSubmitted` to automatically inject context ### Inject project metadata at session start ```typescript +import { CopilotClient, approveAll } from "@github/copilot-sdk"; + +const client = new CopilotClient(); const session = await client.createSession({ hooks: { onSessionStart: async (input) => { @@ -746,13 +771,16 @@ const session = await client.createSession({ }; }, }, - onPermissionRequest: async () => ({ kind: "approved" }), + onPermissionRequest: approveAll, }); ``` ### Expand shorthand commands in prompts ```typescript +import { CopilotClient, approveAll } from "@github/copilot-sdk"; + +const client = new CopilotClient(); const SHORTCUTS: Record = { "/fix": "Find and fix all errors in the current file", "/test": "Write comprehensive unit tests for this code", @@ -772,7 +800,7 @@ const session = await client.createSession({ return null; }, }, - onPermissionRequest: async () => ({ kind: "approved" }), + onPermissionRequest: approveAll, }); ``` @@ -785,6 +813,9 @@ The `onErrorOccurred` hook gives you a chance to react to failures — whether t ### Retry transient model errors ```typescript +import { CopilotClient, approveAll } from "@github/copilot-sdk"; + +const client = new CopilotClient(); const session = await client.createSession({ hooks: { onErrorOccurred: async (input) => { @@ -798,13 +829,16 @@ const session = await client.createSession({ return null; }, }, - onPermissionRequest: async () => ({ kind: "approved" }), + onPermissionRequest: approveAll, }); ``` ### Friendly error messages ```typescript +import { CopilotClient, approveAll } from "@github/copilot-sdk"; + +const client = new CopilotClient(); const FRIENDLY_MESSAGES: Record = { model_call: "The AI model is temporarily unavailable. Please try again.", tool_execution: "A tool encountered an error. Check inputs and try again.", @@ -819,7 +853,7 @@ const session = await client.createSession({ }; }, }, - onPermissionRequest: async () => ({ kind: "approved" }), + onPermissionRequest: approveAll, }); ``` @@ -833,6 +867,9 @@ Track how long sessions run, how many tools are invoked, and why sessions end Node.js / TypeScript ```typescript +import { CopilotClient, approveAll } from "@github/copilot-sdk"; + +const client = new CopilotClient(); const metrics = new Map(); const session = await client.createSession({ @@ -867,7 +904,7 @@ const session = await client.createSession({ return null; }, }, - onPermissionRequest: async () => ({ kind: "approved" }), + onPermissionRequest: approveAll, }); ``` @@ -925,6 +962,9 @@ session = await client.create_session( Hooks compose naturally. A single `hooks` object can handle permissions **and** auditing **and** notifications — each hook does its own job. ```typescript +import { CopilotClient, approveAll } from "@github/copilot-sdk"; + +const client = new CopilotClient(); const session = await client.createSession({ hooks: { onSessionStart: async (input) => { @@ -951,7 +991,7 @@ const session = await client.createSession({ return null; }, }, - onPermissionRequest: async () => ({ kind: "approved" }), + onPermissionRequest: approveAll, }); ``` diff --git a/docs/features/image-input.md b/docs/features/image-input.md index acec80d4a..093f62f89 100644 --- a/docs/features/image-input.md +++ b/docs/features/image-input.md @@ -41,14 +41,14 @@ Attach an image file to any message using the file attachment type. The path mus Node.js / TypeScript ```typescript -import { CopilotClient } from "@github/copilot-sdk"; +import { CopilotClient, approveAll } from "@github/copilot-sdk"; const client = new CopilotClient(); await client.start(); const session = await client.createSession({ model: "gpt-4.1", - onPermissionRequest: async () => ({ kind: "approved" }), + onPermissionRequest: approveAll, }); await session.send({ @@ -227,14 +227,14 @@ When you already have image data in memory (e.g., a screenshot captured by your Node.js / TypeScript ```typescript -import { CopilotClient } from "@github/copilot-sdk"; +import { CopilotClient, approveAll } from "@github/copilot-sdk"; const client = new CopilotClient(); await client.start(); const session = await client.createSession({ model: "gpt-4.1", - onPermissionRequest: async () => ({ kind: "approved" }), + onPermissionRequest: approveAll, }); const base64ImageData = "..."; // your base64-encoded image diff --git a/docs/features/session-persistence.md b/docs/features/session-persistence.md index bed790936..dc1fc5062 100644 --- a/docs/features/session-persistence.md +++ b/docs/features/session-persistence.md @@ -266,6 +266,9 @@ const session = await client.resumeSession("user-123-task-456", { When using your own API keys, you must re-provide the provider configuration when resuming. API keys are never persisted to disk for security reasons. ```typescript +import { CopilotClient, approveAll } from "@github/copilot-sdk"; + +const client = new CopilotClient(); // Original session with BYOK const session = await client.createSession({ sessionId: "user-123-task-456", @@ -276,7 +279,7 @@ const session = await client.createSession({ apiKey: process.env.AZURE_OPENAI_KEY, deploymentId: "my-gpt-deployment", }, - onPermissionRequest: async () => ({ kind: "approved" }), + onPermissionRequest: approveAll, }); // When resuming, you MUST re-provide the provider config @@ -551,6 +554,9 @@ flowchart LR For workflows that might exceed context limits, enable infinite sessions with automatic compaction: ```typescript +import { CopilotClient, approveAll } from "@github/copilot-sdk"; + +const client = new CopilotClient(); const session = await client.createSession({ sessionId: "long-workflow-123", infiniteSessions: { @@ -558,7 +564,7 @@ const session = await client.createSession({ backgroundCompactionThreshold: 0.80, // Start compaction at 80% context bufferExhaustionThreshold: 0.95, // Block at 95% if needed }, - onPermissionRequest: async () => ({ kind: "approved" }), + onPermissionRequest: approveAll, }); ``` diff --git a/docs/features/skills.md b/docs/features/skills.md index f2f397260..8f2fd5895 100644 --- a/docs/features/skills.md +++ b/docs/features/skills.md @@ -20,7 +20,7 @@ Specify directories containing skills when creating a session: Node.js / TypeScript ```typescript -import { CopilotClient } from "@github/copilot-sdk"; +import { CopilotClient, approveAll } from "@github/copilot-sdk"; const client = new CopilotClient(); const session = await client.createSession({ @@ -29,7 +29,7 @@ const session = await client.createSession({ "./skills/code-review", "./skills/documentation", ], - onPermissionRequest: async () => ({ kind: "approved" }), + onPermissionRequest: approveAll, }); // Copilot now has access to skills in those directories @@ -148,10 +148,13 @@ Disable specific skills while keeping others active: Node.js / TypeScript ```typescript +import { CopilotClient, approveAll } from "@github/copilot-sdk"; + +const client = new CopilotClient(); const session = await client.createSession({ skillDirectories: ["./skills"], disabledSkills: ["experimental-feature", "deprecated-tool"], - onPermissionRequest: async () => ({ kind: "approved" }), + onPermissionRequest: approveAll, }); ``` @@ -320,6 +323,9 @@ The markdown body contains the instructions that are injected into the session c Skills work alongside custom agents: ```typescript +import { CopilotClient, approveAll } from "@github/copilot-sdk"; + +const client = new CopilotClient(); const session = await client.createSession({ skillDirectories: ["./skills/security"], customAgents: [{ @@ -327,7 +333,7 @@ const session = await client.createSession({ description: "Security-focused code reviewer", prompt: "Focus on OWASP Top 10 vulnerabilities", }], - onPermissionRequest: async () => ({ kind: "approved" }), + onPermissionRequest: approveAll, }); ``` @@ -336,6 +342,9 @@ const session = await client.createSession({ Skills can complement MCP server capabilities: ```typescript +import { CopilotClient, approveAll } from "@github/copilot-sdk"; + +const client = new CopilotClient(); const session = await client.createSession({ skillDirectories: ["./skills/database"], mcpServers: { @@ -346,7 +355,7 @@ const session = await client.createSession({ tools: ["*"], }, }, - onPermissionRequest: async () => ({ kind: "approved" }), + onPermissionRequest: approveAll, }); ``` diff --git a/docs/features/steering-and-queueing.md b/docs/features/steering-and-queueing.md index afee189e0..c68e5692a 100644 --- a/docs/features/steering-and-queueing.md +++ b/docs/features/steering-and-queueing.md @@ -48,7 +48,7 @@ await client.start(); const session = await client.createSession({ model: "gpt-4.1", - onPermissionRequest: async () => ({ kind: "approved" }), + onPermissionRequest: approveAll, }); // Start a long-running task @@ -195,14 +195,14 @@ Queueing buffers messages to be processed sequentially after the current turn fi Node.js / TypeScript ```typescript -import { CopilotClient } from "@github/copilot-sdk"; +import { CopilotClient, approveAll } from "@github/copilot-sdk"; const client = new CopilotClient(); await client.start(); const session = await client.createSession({ model: "gpt-4.1", - onPermissionRequest: async () => ({ kind: "approved" }), + onPermissionRequest: approveAll, }); // Send an initial task @@ -404,9 +404,12 @@ You can use both patterns together in a single session. Steering affects the cur Node.js / TypeScript ```typescript +import { CopilotClient, approveAll } from "@github/copilot-sdk"; + +const client = new CopilotClient(); const session = await client.createSession({ model: "gpt-4.1", - onPermissionRequest: async () => ({ kind: "approved" }), + onPermissionRequest: approveAll, }); // Start a task diff --git a/docs/getting-started.md b/docs/getting-started.md index 2cd3740fa..efdba59af 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -243,7 +243,7 @@ Right now, you wait for the complete response before seeing anything. Let's make Update `index.ts`: ```typescript -import { CopilotClient } from "@github/copilot-sdk"; +import { CopilotClient, approveAll } from "@github/copilot-sdk"; const client = new CopilotClient(); const session = await client.createSession({ @@ -1206,6 +1206,9 @@ Now that you've got the basics, here are more powerful features to explore: MCP (Model Context Protocol) servers provide pre-built tools. Connect to GitHub's MCP server to give Copilot access to repositories, issues, and pull requests: ```typescript +import { CopilotClient, approveAll } from "@github/copilot-sdk"; + +const client = new CopilotClient(); const session = await client.createSession({ mcpServers: { github: { @@ -1213,7 +1216,7 @@ const session = await client.createSession({ url: "https://api.githubcopilot.com/mcp/", }, }, - onPermissionRequest: async () => ({ kind: "approved" }), + onPermissionRequest: approveAll, }); ``` @@ -1224,6 +1227,9 @@ const session = await client.createSession({ Define specialized AI personas for specific tasks: ```typescript +import { CopilotClient, approveAll } from "@github/copilot-sdk"; + +const client = new CopilotClient(); const session = await client.createSession({ customAgents: [{ name: "pr-reviewer", @@ -1231,7 +1237,7 @@ const session = await client.createSession({ description: "Reviews pull requests for best practices", prompt: "You are an expert code reviewer. Focus on security, performance, and maintainability.", }], - onPermissionRequest: async () => ({ kind: "approved" }), + onPermissionRequest: approveAll, }); ``` @@ -1242,11 +1248,14 @@ const session = await client.createSession({ Control the AI's behavior and personality: ```typescript +import { CopilotClient, approveAll } from "@github/copilot-sdk"; + +const client = new CopilotClient(); const session = await client.createSession({ systemMessage: { content: "You are a helpful assistant for our engineering team. Always be concise.", }, - onPermissionRequest: async () => ({ kind: "approved" }), + onPermissionRequest: approveAll, }); ``` diff --git a/docs/hooks/error-handling.md b/docs/hooks/error-handling.md index 87bd631f9..3418ae0e7 100644 --- a/docs/hooks/error-handling.md +++ b/docs/hooks/error-handling.md @@ -128,6 +128,9 @@ Return `null` or `undefined` to use default error handling. Otherwise, return an Node.js / TypeScript ```typescript +import { CopilotClient, approveAll } from "@github/copilot-sdk"; + +const client = new CopilotClient(); const session = await client.createSession({ hooks: { onErrorOccurred: async (input, invocation) => { @@ -137,7 +140,7 @@ const session = await client.createSession({ return null; }, }, - onPermissionRequest: async () => ({ kind: "approved" }), + onPermissionRequest: approveAll, }); ``` @@ -256,6 +259,9 @@ var session = await client.CreateSessionAsync(new SessionConfig ```typescript import { captureException } from "@sentry/node"; // or your monitoring service +import { CopilotClient, approveAll } from "@github/copilot-sdk"; + +const client = new CopilotClient(); const session = await client.createSession({ hooks: { @@ -275,13 +281,16 @@ const session = await client.createSession({ return null; }, }, - onPermissionRequest: async () => ({ kind: "approved" }), + onPermissionRequest: approveAll, }); ``` ### User-Friendly Error Messages ```typescript +import { CopilotClient, approveAll } from "@github/copilot-sdk"; + +const client = new CopilotClient(); const ERROR_MESSAGES: Record = { "model_call": "There was an issue communicating with the AI model. Please try again.", "tool_execution": "A tool failed to execute. Please check your inputs and try again.", @@ -303,13 +312,16 @@ const session = await client.createSession({ return null; }, }, - onPermissionRequest: async () => ({ kind: "approved" }), + onPermissionRequest: approveAll, }); ``` ### Suppress Non-Critical Errors ```typescript +import { CopilotClient, approveAll } from "@github/copilot-sdk"; + +const client = new CopilotClient(); const session = await client.createSession({ hooks: { onErrorOccurred: async (input) => { @@ -321,13 +333,16 @@ const session = await client.createSession({ return null; }, }, - onPermissionRequest: async () => ({ kind: "approved" }), + onPermissionRequest: approveAll, }); ``` ### Add Recovery Context ```typescript +import { CopilotClient, approveAll } from "@github/copilot-sdk"; + +const client = new CopilotClient(); const session = await client.createSession({ hooks: { onErrorOccurred: async (input) => { @@ -353,19 +368,22 @@ The tool failed. Here are some recovery suggestions: return null; }, }, - onPermissionRequest: async () => ({ kind: "approved" }), + onPermissionRequest: approveAll, }); ``` ### Track Error Patterns ```typescript +import { CopilotClient, approveAll } from "@github/copilot-sdk"; + interface ErrorStats { count: number; lastOccurred: number; contexts: string[]; } +const client = new CopilotClient(); const errorStats = new Map(); const session = await client.createSession({ @@ -393,13 +411,16 @@ const session = await client.createSession({ return null; }, }, - onPermissionRequest: async () => ({ kind: "approved" }), + onPermissionRequest: approveAll, }); ``` ### Alert on Critical Errors ```typescript +import { CopilotClient, approveAll } from "@github/copilot-sdk"; + +const client = new CopilotClient(); const CRITICAL_CONTEXTS = ["system", "model_call"]; const session = await client.createSession({ @@ -418,13 +439,16 @@ const session = await client.createSession({ return null; }, }, - onPermissionRequest: async () => ({ kind: "approved" }), + onPermissionRequest: approveAll, }); ``` ### Combine with Other Hooks for Context ```typescript +import { CopilotClient, approveAll } from "@github/copilot-sdk"; + +const client = new CopilotClient(); const sessionContext = new Map(); const session = await client.createSession({ @@ -459,7 +483,7 @@ const session = await client.createSession({ return null; }, }, - onPermissionRequest: async () => ({ kind: "approved" }), + onPermissionRequest: approveAll, }); ``` diff --git a/docs/hooks/index.md b/docs/hooks/index.md index 997c23a5a..9434b3d94 100644 --- a/docs/hooks/index.md +++ b/docs/hooks/index.md @@ -171,6 +171,9 @@ This allows hooks to maintain state or perform session-specific logic. ### Logging All Tool Calls ```typescript +import { CopilotClient, approveAll } from "@github/copilot-sdk"; + +const client = new CopilotClient(); const session = await client.createSession({ hooks: { onPreToolUse: async (input) => { @@ -182,13 +185,16 @@ const session = await client.createSession({ return null; }, }, - onPermissionRequest: async () => ({ kind: "approved" }), + onPermissionRequest: approveAll, }); ``` ### Blocking Dangerous Tools ```typescript +import { CopilotClient, approveAll } from "@github/copilot-sdk"; + +const client = new CopilotClient(); const BLOCKED_TOOLS = ["shell", "bash", "exec"]; const session = await client.createSession({ @@ -203,13 +209,16 @@ const session = await client.createSession({ return { permissionDecision: "allow" }; }, }, - onPermissionRequest: async () => ({ kind: "approved" }), + onPermissionRequest: approveAll, }); ``` ### Adding User Context ```typescript +import { CopilotClient, approveAll } from "@github/copilot-sdk"; + +const client = new CopilotClient(); const session = await client.createSession({ hooks: { onSessionStart: async () => { @@ -219,7 +228,7 @@ const session = await client.createSession({ }; }, }, - onPermissionRequest: async () => ({ kind: "approved" }), + onPermissionRequest: approveAll, }); ``` diff --git a/docs/hooks/post-tool-use.md b/docs/hooks/post-tool-use.md index afda6f428..51cc6521f 100644 --- a/docs/hooks/post-tool-use.md +++ b/docs/hooks/post-tool-use.md @@ -127,6 +127,9 @@ Return `null` or `undefined` to pass through the result unchanged. Otherwise, re Node.js / TypeScript ```typescript +import { CopilotClient, approveAll } from "@github/copilot-sdk"; + +const client = new CopilotClient(); const session = await client.createSession({ hooks: { onPostToolUse: async (input, invocation) => { @@ -136,7 +139,7 @@ const session = await client.createSession({ return null; // Pass through unchanged }, }, - onPermissionRequest: async () => ({ kind: "approved" }), + onPermissionRequest: approveAll, }); ``` @@ -254,6 +257,9 @@ var session = await client.CreateSessionAsync(new SessionConfig ### Redact Sensitive Data ```typescript +import { CopilotClient, approveAll } from "@github/copilot-sdk"; + +const client = new CopilotClient(); const SENSITIVE_PATTERNS = [ /api[_-]?key["\s:=]+["']?[\w-]+["']?/gi, /password["\s:=]+["']?[\w-]+["']?/gi, @@ -276,13 +282,16 @@ const session = await client.createSession({ return null; }, }, - onPermissionRequest: async () => ({ kind: "approved" }), + onPermissionRequest: approveAll, }); ``` ### Truncate Large Results ```typescript +import { CopilotClient, approveAll } from "@github/copilot-sdk"; + +const client = new CopilotClient(); const MAX_RESULT_LENGTH = 10000; const session = await client.createSession({ @@ -303,13 +312,16 @@ const session = await client.createSession({ return null; }, }, - onPermissionRequest: async () => ({ kind: "approved" }), + onPermissionRequest: approveAll, }); ``` ### Add Context Based on Results ```typescript +import { CopilotClient, approveAll } from "@github/copilot-sdk"; + +const client = new CopilotClient(); const session = await client.createSession({ hooks: { onPostToolUse: async (input) => { @@ -330,13 +342,16 @@ const session = await client.createSession({ return null; }, }, - onPermissionRequest: async () => ({ kind: "approved" }), + onPermissionRequest: approveAll, }); ``` ### Filter Error Stack Traces ```typescript +import { CopilotClient, approveAll } from "@github/copilot-sdk"; + +const client = new CopilotClient(); const session = await client.createSession({ hooks: { onPostToolUse: async (input) => { @@ -353,13 +368,15 @@ const session = await client.createSession({ return null; }, }, - onPermissionRequest: async () => ({ kind: "approved" }), + onPermissionRequest: approveAll, }); ``` ### Audit Trail for Compliance ```typescript +import { CopilotClient, approveAll } from "@github/copilot-sdk"; + interface AuditEntry { timestamp: number; sessionId: string; @@ -369,6 +386,7 @@ interface AuditEntry { success: boolean; } +const client = new CopilotClient(); const auditLog: AuditEntry[] = []; const session = await client.createSession({ @@ -389,13 +407,16 @@ const session = await client.createSession({ return null; }, }, - onPermissionRequest: async () => ({ kind: "approved" }), + onPermissionRequest: approveAll, }); ``` ### Suppress Noisy Results ```typescript +import { CopilotClient, approveAll } from "@github/copilot-sdk"; + +const client = new CopilotClient(); const NOISY_TOOLS = ["list_directory", "search_codebase"]; const session = await client.createSession({ @@ -417,7 +438,7 @@ const session = await client.createSession({ return null; }, }, - onPermissionRequest: async () => ({ kind: "approved" }), + onPermissionRequest: approveAll, }); ``` diff --git a/docs/hooks/pre-tool-use.md b/docs/hooks/pre-tool-use.md index 5ad161ab9..ab0587bfe 100644 --- a/docs/hooks/pre-tool-use.md +++ b/docs/hooks/pre-tool-use.md @@ -136,6 +136,9 @@ Return `null` or `undefined` to allow the tool to execute with no changes. Other Node.js / TypeScript ```typescript +import { CopilotClient, approveAll } from "@github/copilot-sdk"; + +const client = new CopilotClient(); const session = await client.createSession({ hooks: { onPreToolUse: async (input, invocation) => { @@ -144,7 +147,7 @@ const session = await client.createSession({ return { permissionDecision: "allow" }; }, }, - onPermissionRequest: async () => ({ kind: "approved" }), + onPermissionRequest: approveAll, }); ``` @@ -265,6 +268,9 @@ var session = await client.CreateSessionAsync(new SessionConfig ### Block Specific Tools ```typescript +import { CopilotClient, approveAll } from "@github/copilot-sdk"; + +const client = new CopilotClient(); const BLOCKED_TOOLS = ["shell", "bash", "write_file", "delete_file"]; const session = await client.createSession({ @@ -279,13 +285,16 @@ const session = await client.createSession({ return { permissionDecision: "allow" }; }, }, - onPermissionRequest: async () => ({ kind: "approved" }), + onPermissionRequest: approveAll, }); ``` ### Modify Tool Arguments ```typescript +import { CopilotClient, approveAll } from "@github/copilot-sdk"; + +const client = new CopilotClient(); const session = await client.createSession({ hooks: { onPreToolUse: async (input) => { @@ -303,13 +312,16 @@ const session = await client.createSession({ return { permissionDecision: "allow" }; }, }, - onPermissionRequest: async () => ({ kind: "approved" }), + onPermissionRequest: approveAll, }); ``` ### Restrict File Access to Specific Directories ```typescript +import { CopilotClient, approveAll } from "@github/copilot-sdk"; + +const client = new CopilotClient(); const ALLOWED_DIRECTORIES = ["/home/user/projects", "/tmp"]; const session = await client.createSession({ @@ -331,13 +343,16 @@ const session = await client.createSession({ return { permissionDecision: "allow" }; }, }, - onPermissionRequest: async () => ({ kind: "approved" }), + onPermissionRequest: approveAll, }); ``` ### Suppress Verbose Tool Output ```typescript +import { CopilotClient, approveAll } from "@github/copilot-sdk"; + +const client = new CopilotClient(); const VERBOSE_TOOLS = ["list_directory", "search_files"]; const session = await client.createSession({ @@ -349,13 +364,16 @@ const session = await client.createSession({ }; }, }, - onPermissionRequest: async () => ({ kind: "approved" }), + onPermissionRequest: approveAll, }); ``` ### Add Context Based on Tool ```typescript +import { CopilotClient, approveAll } from "@github/copilot-sdk"; + +const client = new CopilotClient(); const session = await client.createSession({ hooks: { onPreToolUse: async (input) => { @@ -368,7 +386,7 @@ const session = await client.createSession({ return { permissionDecision: "allow" }; }, }, - onPermissionRequest: async () => ({ kind: "approved" }), + onPermissionRequest: approveAll, }); ``` diff --git a/docs/hooks/session-lifecycle.md b/docs/hooks/session-lifecycle.md index 9d5732cc0..006583406 100644 --- a/docs/hooks/session-lifecycle.md +++ b/docs/hooks/session-lifecycle.md @@ -127,6 +127,9 @@ public delegate Task SessionStartHandler( Node.js / TypeScript ```typescript +import { CopilotClient, approveAll } from "@github/copilot-sdk"; + +const client = new CopilotClient(); const session = await client.createSession({ hooks: { onSessionStart: async (input, invocation) => { @@ -143,7 +146,7 @@ Package manager: ${projectInfo.packageManager} }; }, }, - onPermissionRequest: async () => ({ kind: "approved" }), + onPermissionRequest: approveAll, }); ``` @@ -176,6 +179,9 @@ session = await client.create_session(on_permission_request=PermissionHandler.ap #### Handle Session Resume ```typescript +import { CopilotClient, approveAll } from "@github/copilot-sdk"; + +const client = new CopilotClient(); const session = await client.createSession({ hooks: { onSessionStart: async (input, invocation) => { @@ -194,13 +200,16 @@ Session resumed. Previous context: return null; }, }, - onPermissionRequest: async () => ({ kind: "approved" }), + onPermissionRequest: approveAll, }); ``` #### Load User Preferences ```typescript +import { CopilotClient, approveAll } from "@github/copilot-sdk"; + +const client = new CopilotClient(); const session = await client.createSession({ hooks: { onSessionStart: async () => { @@ -223,7 +232,7 @@ const session = await client.createSession({ }; }, }, - onPermissionRequest: async () => ({ kind: "approved" }), + onPermissionRequest: approveAll, }); ``` @@ -343,6 +352,9 @@ public delegate Task SessionEndHandler( Node.js / TypeScript ```typescript +import { CopilotClient, approveAll } from "@github/copilot-sdk"; + +const client = new CopilotClient(); const sessionStartTimes = new Map(); const session = await client.createSession({ @@ -365,7 +377,7 @@ const session = await client.createSession({ return null; }, }, - onPermissionRequest: async () => ({ kind: "approved" }), + onPermissionRequest: approveAll, }); ``` @@ -407,6 +419,9 @@ session = await client.create_session(on_permission_request=PermissionHandler.ap #### Clean Up Resources ```typescript +import { CopilotClient, approveAll } from "@github/copilot-sdk"; + +const client = new CopilotClient(); const sessionResources = new Map(); const session = await client.createSession({ @@ -430,13 +445,16 @@ const session = await client.createSession({ return null; }, }, - onPermissionRequest: async () => ({ kind: "approved" }), + onPermissionRequest: approveAll, }); ``` #### Save Session State for Resume ```typescript +import { CopilotClient, approveAll } from "@github/copilot-sdk"; + +const client = new CopilotClient(); const session = await client.createSession({ hooks: { onSessionEnd: async (input, invocation) => { @@ -458,6 +476,9 @@ const session = await client.createSession({ #### Log Session Summary ```typescript +import { CopilotClient, approveAll } from "@github/copilot-sdk"; + +const client = new CopilotClient(); const sessionData: Record = {}; const session = await client.createSession({ @@ -493,7 +514,7 @@ Session Summary: return null; }, }, - onPermissionRequest: async () => ({ kind: "approved" }), + onPermissionRequest: approveAll, }); ``` diff --git a/docs/hooks/user-prompt-submitted.md b/docs/hooks/user-prompt-submitted.md index c952f7018..502d6616f 100644 --- a/docs/hooks/user-prompt-submitted.md +++ b/docs/hooks/user-prompt-submitted.md @@ -125,6 +125,9 @@ Return `null` or `undefined` to use the prompt unchanged. Otherwise, return an o Node.js / TypeScript ```typescript +import { CopilotClient, approveAll } from "@github/copilot-sdk"; + +const client = new CopilotClient(); const session = await client.createSession({ hooks: { onUserPromptSubmitted: async (input, invocation) => { @@ -132,7 +135,7 @@ const session = await client.createSession({ return null; // Pass through unchanged }, }, - onPermissionRequest: async () => ({ kind: "approved" }), + onPermissionRequest: approveAll, }); ``` @@ -240,6 +243,9 @@ var session = await client.CreateSessionAsync(new SessionConfig ### Add Project Context ```typescript +import { CopilotClient, approveAll } from "@github/copilot-sdk"; + +const client = new CopilotClient(); const session = await client.createSession({ hooks: { onUserPromptSubmitted: async (input) => { @@ -254,13 +260,16 @@ Framework: ${projectInfo.framework} }; }, }, - onPermissionRequest: async () => ({ kind: "approved" }), + onPermissionRequest: approveAll, }); ``` ### Expand Shorthand Commands ```typescript +import { CopilotClient, approveAll } from "@github/copilot-sdk"; + +const client = new CopilotClient(); const SHORTCUTS: Record = { "/fix": "Please fix the errors in the code", "/explain": "Please explain this code in detail", @@ -282,13 +291,16 @@ const session = await client.createSession({ return null; }, }, - onPermissionRequest: async () => ({ kind: "approved" }), + onPermissionRequest: approveAll, }); ``` ### Content Filtering ```typescript +import { CopilotClient, approveAll } from "@github/copilot-sdk"; + +const client = new CopilotClient(); const BLOCKED_PATTERNS = [ /password\s*[:=]/i, /api[_-]?key\s*[:=]/i, @@ -310,13 +322,16 @@ const session = await client.createSession({ return null; }, }, - onPermissionRequest: async () => ({ kind: "approved" }), + onPermissionRequest: approveAll, }); ``` ### Enforce Prompt Length Limits ```typescript +import { CopilotClient, approveAll } from "@github/copilot-sdk"; + +const client = new CopilotClient(); const MAX_PROMPT_LENGTH = 10000; const session = await client.createSession({ @@ -332,19 +347,22 @@ const session = await client.createSession({ return null; }, }, - onPermissionRequest: async () => ({ kind: "approved" }), + onPermissionRequest: approveAll, }); ``` ### Add User Preferences ```typescript +import { CopilotClient, approveAll } from "@github/copilot-sdk"; + interface UserPreferences { codeStyle: "concise" | "verbose"; preferredLanguage: string; experienceLevel: "beginner" | "intermediate" | "expert"; } +const client = new CopilotClient(); const session = await client.createSession({ hooks: { onUserPromptSubmitted: async (input) => { @@ -367,13 +385,16 @@ const session = await client.createSession({ }; }, }, - onPermissionRequest: async () => ({ kind: "approved" }), + onPermissionRequest: approveAll, }); ``` ### Rate Limiting ```typescript +import { CopilotClient, approveAll } from "@github/copilot-sdk"; + +const client = new CopilotClient(); const promptTimestamps: number[] = []; const RATE_LIMIT = 10; // prompts const RATE_WINDOW = 60000; // 1 minute @@ -399,13 +420,16 @@ const session = await client.createSession({ return null; }, }, - onPermissionRequest: async () => ({ kind: "approved" }), + onPermissionRequest: approveAll, }); ``` ### Prompt Templates ```typescript +import { CopilotClient, approveAll } from "@github/copilot-sdk"; + +const client = new CopilotClient(); const TEMPLATES: Record string> = { "bug:": (desc) => `I found a bug: ${desc} @@ -436,7 +460,7 @@ const session = await client.createSession({ return null; }, }, - onPermissionRequest: async () => ({ kind: "approved" }), + onPermissionRequest: approveAll, }); ``` diff --git a/docs/integrations/microsoft-agent-framework.md b/docs/integrations/microsoft-agent-framework.md index 8e794759b..87fd24a09 100644 --- a/docs/integrations/microsoft-agent-framework.md +++ b/docs/integrations/microsoft-agent-framework.md @@ -159,8 +159,9 @@ You can also use Copilot SDK's native tool definition alongside MAF tools: Node.js / TypeScript (standalone SDK) ```typescript -import { CopilotClient, DefineTool } from "@github/copilot-sdk"; +import { CopilotClient, DefineTool, approveAll } from "@github/copilot-sdk"; +const client = new CopilotClient(); const getWeather = DefineTool({ name: "GetWeather", description: "Get the current weather for a given location.", @@ -168,11 +169,10 @@ const getWeather = DefineTool({ execute: async ({ location }) => `The weather in ${location} is sunny, 25°C.`, }); -const client = new CopilotClient(); const session = await client.createSession({ model: "gpt-4.1", tools: [getWeather], - onPermissionRequest: async () => ({ kind: "approved" }), + onPermissionRequest: approveAll, }); await session.sendAndWait({ prompt: "What's the weather like in Seattle?" }); @@ -351,13 +351,13 @@ You can also stream directly through the Copilot SDK without MAF: Node.js / TypeScript (standalone SDK) ```typescript -import { CopilotClient } from "@github/copilot-sdk"; +import { CopilotClient, approveAll } from "@github/copilot-sdk"; const client = new CopilotClient(); const session = await client.createSession({ model: "gpt-4.1", streaming: true, - onPermissionRequest: async () => ({ kind: "approved" }), + onPermissionRequest: approveAll, }); session.on("assistant.message_delta", (event) => { @@ -401,12 +401,12 @@ Use the MAF wrapper when you need to compose Copilot with other providers in orc ```typescript // Standalone SDK — full control, simpler setup -import { CopilotClient } from "@github/copilot-sdk"; +import { CopilotClient, approveAll } from "@github/copilot-sdk"; const client = new CopilotClient(); const session = await client.createSession({ model: "gpt-4.1", - onPermissionRequest: async () => ({ kind: "approved" }), + onPermissionRequest: approveAll, }); const response = await session.sendAndWait({ prompt: "Explain this code" }); ``` diff --git a/docs/setup/backend-services.md b/docs/setup/backend-services.md index 26857abea..b235019c7 100644 --- a/docs/setup/backend-services.md +++ b/docs/setup/backend-services.md @@ -255,6 +255,9 @@ copilot --headless --port 4321 Pass individual user tokens when creating sessions. See [GitHub OAuth](./github-oauth.md) for the full flow. ```typescript +import { CopilotClient, approveAll } from "@github/copilot-sdk"; + +const client = new CopilotClient(); // Your API receives user tokens from your auth layer app.post("/chat", authMiddleware, async (req, res) => { const client = new CopilotClient({ @@ -266,7 +269,7 @@ app.post("/chat", authMiddleware, async (req, res) => { const session = await client.createSession({ sessionId: `user-${req.user.id}-chat`, model: "gpt-4.1", - onPermissionRequest: async () => ({ kind: "approved" }), + onPermissionRequest: approveAll, }); const response = await session.sendAndWait({ @@ -282,6 +285,8 @@ app.post("/chat", authMiddleware, async (req, res) => { Use your own API keys for the model provider. See [BYOK](../auth/byok.md) for details. ```typescript +import { CopilotClient, approveAll } from "@github/copilot-sdk"; + const client = new CopilotClient({ cliUrl: "localhost:4321", }); @@ -293,7 +298,7 @@ const session = await client.createSession({ baseUrl: "https://api.openai.com/v1", apiKey: process.env.OPENAI_API_KEY, }, - onPermissionRequest: async () => ({ kind: "approved" }), + onPermissionRequest: approveAll, }); ``` diff --git a/docs/setup/bundled-cli.md b/docs/setup/bundled-cli.md index 7dd887cca..427177255 100644 --- a/docs/setup/bundled-cli.md +++ b/docs/setup/bundled-cli.md @@ -217,6 +217,8 @@ const client = new CopilotClient({ If you manage your own model provider keys, users don't need GitHub accounts at all: ```typescript +import { CopilotClient, approveAll } from "@github/copilot-sdk"; + const client = new CopilotClient({ cliPath: path.join(__dirname, "vendor", "copilot"), }); @@ -228,7 +230,7 @@ const session = await client.createSession({ baseUrl: "https://api.openai.com/v1", apiKey: process.env.OPENAI_API_KEY, }, - onPermissionRequest: async () => ({ kind: "approved" }), + onPermissionRequest: approveAll, }); ``` @@ -239,6 +241,8 @@ See the **[BYOK guide](../auth/byok.md)** for full details. Bundled apps typically want named sessions so users can resume conversations: ```typescript +import { CopilotClient, approveAll } from "@github/copilot-sdk"; + const client = new CopilotClient({ cliPath: path.join(__dirname, "vendor", "copilot"), }); @@ -248,7 +252,7 @@ const sessionId = `project-${projectName}`; const session = await client.createSession({ sessionId, model: "gpt-4.1", - onPermissionRequest: async () => ({ kind: "approved" }), + onPermissionRequest: approveAll, }); // User closes app... diff --git a/docs/setup/local-cli.md b/docs/setup/local-cli.md index 3f11402fc..97ba8c700 100644 --- a/docs/setup/local-cli.md +++ b/docs/setup/local-cli.md @@ -192,11 +192,14 @@ The SDK picks these up automatically — no code changes needed. With the local CLI, sessions default to ephemeral. To create resumable sessions, provide your own session ID: ```typescript +import { CopilotClient, approveAll } from "@github/copilot-sdk"; + +const client = new CopilotClient(); // Create a named session const session = await client.createSession({ sessionId: "my-project-analysis", model: "gpt-4.1", - onPermissionRequest: async () => ({ kind: "approved" }), + onPermissionRequest: approveAll, }); // Later, resume it diff --git a/docs/setup/scaling.md b/docs/setup/scaling.md index 957836b1b..0fbb544d6 100644 --- a/docs/setup/scaling.md +++ b/docs/setup/scaling.md @@ -279,6 +279,8 @@ flowchart TB ```typescript // Route sessions to CLI servers +import { CopilotClient, approveAll } from "@github/copilot-sdk"; + class CLILoadBalancer { private servers: string[]; private currentIndex = 0; @@ -310,6 +312,8 @@ class CLILoadBalancer { } } +const client = new CopilotClient(); + const lb = new CLILoadBalancer([ "cli-1:4321", "cli-2:4321", @@ -323,7 +327,7 @@ app.post("/chat", async (req, res) => { const session = await client.createSession({ sessionId: `user-${req.user.id}-chat`, model: "gpt-4.1", - onPermissionRequest: async () => ({ kind: "approved" }), + onPermissionRequest: approveAll, }); const response = await session.sendAndWait({ prompt: req.body.message }); @@ -380,6 +384,10 @@ flowchart TB **Session lifecycle management** is key to vertical scaling: ```typescript +import { CopilotClient, approveAll } from "@github/copilot-sdk"; + +const client = new CopilotClient(); + // Limit concurrent active sessions class SessionManager { private activeSessions = new Map(); @@ -404,7 +412,7 @@ class SessionManager { const session = await client.createSession({ sessionId, model: "gpt-4.1", - onPermissionRequest: async () => ({ kind: "approved" }), + onPermissionRequest: approveAll, }); this.activeSessions.set(sessionId, session); @@ -448,10 +456,13 @@ flowchart LR For stateless API endpoints where each request is independent: ```typescript +import { CopilotClient, approveAll } from "@github/copilot-sdk"; + +const client = new CopilotClient(); app.post("/api/analyze", async (req, res) => { const session = await client.createSession({ model: "gpt-4.1", - onPermissionRequest: async () => ({ kind: "approved" }), + onPermissionRequest: approveAll, }); try { @@ -470,6 +481,9 @@ app.post("/api/analyze", async (req, res) => { For conversational interfaces or long-running workflows: ```typescript +import { CopilotClient, approveAll } from "@github/copilot-sdk"; + +const client = new CopilotClient(); // Create a resumable session app.post("/api/chat/start", async (req, res) => { const sessionId = `user-${req.user.id}-${Date.now()}`; @@ -481,7 +495,7 @@ app.post("/api/chat/start", async (req, res) => { enabled: true, backgroundCompactionThreshold: 0.80, }, - onPermissionRequest: async () => ({ kind: "approved" }), + onPermissionRequest: approveAll, }); res.json({ sessionId }); diff --git a/docs/troubleshooting/compatibility.md b/docs/troubleshooting/compatibility.md index 2ce76525a..3dfc38e28 100644 --- a/docs/troubleshooting/compatibility.md +++ b/docs/troubleshooting/compatibility.md @@ -189,6 +189,9 @@ The SDK uses a **deny-by-default** permission model. All permission requests (fi Instead of `--allow-all-paths` or `--yolo`, use the permission handler: ```typescript +import { CopilotClient, approveAll } from "@github/copilot-sdk"; + +const client = new CopilotClient(); const session = await client.createSession({ onPermissionRequest: approveAll, }); @@ -213,13 +216,16 @@ Instead of `/compact`, configure automatic compaction or trigger it manually: ```typescript // Automatic compaction via config +import { CopilotClient, approveAll } from "@github/copilot-sdk"; + +const client = new CopilotClient(); const session = await client.createSession({ infiniteSessions: { enabled: true, backgroundCompactionThreshold: 0.80, // Start background compaction at 80% context utilization bufferExhaustionThreshold: 0.95, // Block and compact at 95% context utilization }, - onPermissionRequest: async () => ({ kind: "approved" }), + onPermissionRequest: approveAll, }); // Manual compaction (experimental) diff --git a/docs/troubleshooting/debugging.md b/docs/troubleshooting/debugging.md index 9ba94eb2b..ec877709c 100644 --- a/docs/troubleshooting/debugging.md +++ b/docs/troubleshooting/debugging.md @@ -393,9 +393,12 @@ const client = new CopilotClient({ 1. **Verify tool registration:** ```typescript + import { CopilotClient, approveAll } from "@github/copilot-sdk"; + + const client = new CopilotClient(); const session = await client.createSession({ tools: [myTool], - onPermissionRequest: async () => ({ kind: "approved" }), + onPermissionRequest: approveAll, }); // Check registered tools diff --git a/nodejs/README.md b/nodejs/README.md index 1c9f5579d..581f4ecae 100644 --- a/nodejs/README.md +++ b/nodejs/README.md @@ -338,10 +338,13 @@ await session.send({ prompt: "What does the most recent jpg in this directory po Enable streaming to receive assistant response chunks as they're generated: ```typescript +import { CopilotClient, approveAll } from "@github/copilot-sdk"; + +const client = new CopilotClient(); const session = await client.createSession({ model: "gpt-5", streaming: true, - onPermissionRequest: async () => ({ kind: "approved" }), + onPermissionRequest: approveAll, }); // Wait for completion using typed event handlers @@ -462,6 +465,9 @@ defineTool("safe_lookup", { Control the system prompt using `systemMessage` in session config: ```typescript +import { CopilotClient, approveAll } from "@github/copilot-sdk"; + +const client = new CopilotClient(); const session = await client.createSession({ model: "gpt-5", systemMessage: { @@ -472,7 +478,7 @@ const session = await client.createSession({ `, }, - onPermissionRequest: async () => ({ kind: "approved" }), + onPermissionRequest: approveAll, }); ``` @@ -481,13 +487,16 @@ The SDK auto-injects environment context, tool instructions, and security guardr For full control (removes all guardrails), use `mode: "replace"`: ```typescript +import { CopilotClient, approveAll } from "@github/copilot-sdk"; + +const client = new CopilotClient(); const session = await client.createSession({ model: "gpt-5", systemMessage: { mode: "replace", content: "You are a helpful assistant.", }, - onPermissionRequest: async () => ({ kind: "approved" }), + onPermissionRequest: approveAll, }); ``` @@ -496,8 +505,12 @@ const session = await client.createSession({ By default, sessions use **infinite sessions** which automatically manage context window limits through background compaction and persist state to a workspace directory. ```typescript +import { CopilotClient, approveAll } from "@github/copilot-sdk"; + +const client = new CopilotClient(); + // Default: infinite sessions enabled with default thresholds -const session = await client.createSession({ model: "gpt-5",onPermissionRequest: async () => ({ kind: "approved" }), }); +const session = await client.createSession({ model: "gpt-5",onPermissionRequest: approveAll, }); // Access the workspace path for checkpoints and files console.log(session.workspacePath); @@ -511,7 +524,7 @@ const session = await client.createSession({ backgroundCompactionThreshold: 0.80, // Start compacting at 80% context usage bufferExhaustionThreshold: 0.95, // Block at 95% until compaction completes }, - onPermissionRequest: async () => ({ kind: "approved" }), + onPermissionRequest: approveAll, }); // Disable infinite sessions @@ -530,8 +543,11 @@ When enabled, sessions emit compaction events: ### Multiple Sessions ```typescript -const session1 = await client.createSession({ model: "gpt-5", onPermissionRequest: async () => ({ kind: "approved" }), }); -const session2 = await client.createSession({ model: "claude-sonnet-4.5", onPermissionRequest: async () => ({ kind: "approved" }), }); +import { CopilotClient, approveAll } from "@github/copilot-sdk"; + +const client = new CopilotClient(); +const session1 = await client.createSession({ model: "gpt-5", onPermissionRequest: approveAll, }); +const session2 = await client.createSession({ model: "claude-sonnet-4.5", onPermissionRequest: approveAll, }); // Both sessions are independent await session1.sendAndWait({ prompt: "Hello from session 1" }); @@ -541,10 +557,13 @@ await session2.sendAndWait({ prompt: "Hello from session 2" }); ### Custom Session IDs ```typescript +import { CopilotClient, approveAll } from "@github/copilot-sdk"; + +const client = new CopilotClient(); const session = await client.createSession({ sessionId: "my-custom-session-id", model: "gpt-5", - onPermissionRequest: async () => ({ kind: "approved" }), + onPermissionRequest: approveAll, }); ``` @@ -579,6 +598,9 @@ The SDK supports custom OpenAI-compatible API providers (BYOK - Bring Your Own K **Example with Ollama:** ```typescript +import { CopilotClient, approveAll } from "@github/copilot-sdk"; + +const client = new CopilotClient(); const session = await client.createSession({ model: "deepseek-coder-v2:16b", // Required when using custom provider provider: { @@ -586,7 +608,7 @@ const session = await client.createSession({ baseUrl: "http://localhost:11434/v1", // Ollama endpoint // apiKey not required for Ollama }, - onPermissionRequest: async () => ({ kind: "approved" }), + onPermissionRequest: approveAll, }); await session.sendAndWait({ prompt: "Hello!" }); @@ -595,6 +617,9 @@ await session.sendAndWait({ prompt: "Hello!" }); **Example with custom OpenAI-compatible API:** ```typescript +import { CopilotClient, approveAll } from "@github/copilot-sdk"; + +const client = new CopilotClient(); const session = await client.createSession({ model: "gpt-4", provider: { @@ -602,13 +627,16 @@ const session = await client.createSession({ baseUrl: "https://my-api.example.com/v1", apiKey: process.env.MY_API_KEY, }, - onPermissionRequest: async () => ({ kind: "approved" }), + onPermissionRequest: approveAll, }); ``` **Example with Azure OpenAI:** ```typescript +import { CopilotClient, approveAll } from "@github/copilot-sdk"; + +const client = new CopilotClient(); const session = await client.createSession({ model: "gpt-4", provider: { @@ -619,7 +647,7 @@ const session = await client.createSession({ apiVersion: "2024-10-21", }, }, - onPermissionRequest: async () => ({ kind: "approved" }), + onPermissionRequest: approveAll, }); ``` @@ -684,6 +712,7 @@ Use the built-in `approveAll` helper to allow every tool call without any checks ```typescript import { CopilotClient, approveAll } from "@github/copilot-sdk"; +const client = new CopilotClient(); const session = await client.createSession({ model: "gpt-5", onPermissionRequest: approveAll, @@ -695,8 +724,9 @@ const session = await client.createSession({ Provide your own function to inspect each request and apply custom logic: ```typescript -import type { PermissionRequest, PermissionRequestResult, approveAll } from "@github/copilot-sdk"; +import type { CopilotClient, PermissionRequest, PermissionRequestResult } from "@github/copilot-sdk"; +const client = new CopilotClient(); const session = await client.createSession({ model: "gpt-5", onPermissionRequest: (request: PermissionRequest, invocation): PermissionRequestResult => { @@ -721,8 +751,7 @@ const session = await client.createSession({ } return { kind: "approved" }; - }, - onPermissionRequest: approveAll + } }); ``` @@ -741,6 +770,9 @@ const session = await client.createSession({ Pass `onPermissionRequest` when resuming a session too — it is required: ```typescript +import { CopilotClient, approveAll } from "@github/copilot-sdk"; + +const client = new CopilotClient(); const session = await client.resumeSession("session-id", { onPermissionRequest: approveAll, }); @@ -755,6 +787,9 @@ To let a specific custom tool bypass the permission prompt entirely, set `skipPe Enable the agent to ask questions to the user using the `ask_user` tool by providing an `onUserInputRequest` handler: ```typescript +import { CopilotClient, approveAll } from "@github/copilot-sdk"; + +const client = new CopilotClient(); const session = await client.createSession({ model: "gpt-5", onUserInputRequest: async (request, invocation) => { @@ -773,7 +808,7 @@ const session = await client.createSession({ wasFreeform: true, // Whether the answer was freeform (not from choices) }; }, - onPermissionRequest: async () => ({ kind: "approved" }), + onPermissionRequest: approveAll, }); ``` @@ -782,6 +817,9 @@ const session = await client.createSession({ Hook into session lifecycle events by providing handlers in the `hooks` configuration: ```typescript +import { CopilotClient, approveAll } from "@github/copilot-sdk"; + +const client = new CopilotClient(); const session = await client.createSession({ model: "gpt-5", hooks: { @@ -834,7 +872,7 @@ const session = await client.createSession({ }; }, }, - onPermissionRequest: async () => ({ kind: "approved" }), + onPermissionRequest: approveAll, }); ``` From ea7a567eb26b584a156d13b9ed39597273ae074c Mon Sep 17 00:00:00 2001 From: Hirofumi Horikawa Date: Fri, 20 Mar 2026 20:45:35 +0900 Subject: [PATCH 3/3] fix some example code --- docs/auth/byok.md | 6 ++---- docs/getting-started.md | 3 ++- nodejs/README.md | 3 +++ 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/docs/auth/byok.md b/docs/auth/byok.md index 04154e07e..51e858eab 100644 --- a/docs/auth/byok.md +++ b/docs/auth/byok.md @@ -455,19 +455,17 @@ Some Copilot features may behave differently with BYOK: When using BYOK, the `model` parameter is **required**: ```typescript -// ❌ Error: Model required with custom provider import { CopilotClient, approveAll } from "@github/copilot-sdk"; const client = new CopilotClient(); + +// ❌ Error: Model required with custom provider const session = await client.createSession({ provider: { type: "openai", baseUrl: "..." }, onPermissionRequest: approveAll, }); // ✅ Correct: Model specified -import { CopilotClient, approveAll } from "@github/copilot-sdk"; - -const client = new CopilotClient(); const session = await client.createSession({ model: "gpt-4", // Required! provider: { type: "openai", baseUrl: "..." }, diff --git a/docs/getting-started.md b/docs/getting-started.md index efdba59af..9a25fafcf 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -249,7 +249,8 @@ const client = new CopilotClient(); const session = await client.createSession({ model: "gpt-4.1", streaming: true, -}, onPermissionRequest: approveAll); + onPermissionRequest: approveAll +}); // Listen for response chunks session.on("assistant.message_delta", (event) => { diff --git a/nodejs/README.md b/nodejs/README.md index 581f4ecae..df2435531 100644 --- a/nodejs/README.md +++ b/nodejs/README.md @@ -60,6 +60,9 @@ await client.stop(); Sessions also support `Symbol.asyncDispose` for use with [`await using`](https://github.com/tc39/proposal-explicit-resource-management) (TypeScript 5.2+/Node.js 18.0+): ```typescript +import { CopilotClient, approveAll } from "@github/copilot-sdk"; + +const client = new CopilotClient(); await using session = await client.createSession({ model: "gpt-5", onPermissionRequest: approveAll }); // session is automatically disconnected when leaving scope ```