diff --git a/docs/auth/byok.md b/docs/auth/byok.md index 13ad8b055..abab53ae0 100644 --- a/docs/auth/byok.md +++ b/docs/auth/byok.md @@ -23,7 +23,7 @@ Azure AI Foundry (formerly Azure OpenAI) is a common BYOK deployment target for ```python import asyncio import os -from copilot import CopilotClient +from copilot import CopilotClient, PermissionHandler FOUNDRY_MODEL_URL = "https://your-resource.openai.azure.com/openai/v1/" # Set FOUNDRY_API_KEY environment variable @@ -32,14 +32,11 @@ async def main(): client = CopilotClient() await client.start() - session = await client.create_session({ - "model": "gpt-5.2-codex", # Your deployment name - "provider": { - "type": "openai", - "base_url": FOUNDRY_MODEL_URL, - "wire_api": "responses", # Use "completions" for older models - "api_key": os.environ["FOUNDRY_API_KEY"], - }, + session = await client.create_session(PermissionHandler.approve_all, "gpt-5.2-codex", provider={ + "type": "openai", + "base_url": FOUNDRY_MODEL_URL, + "wire_api": "responses", # Use "completions" for older models + "api_key": os.environ["FOUNDRY_API_KEY"], }) done = asyncio.Event() diff --git a/docs/getting-started.md b/docs/getting-started.md index 56c6a9c46..a4482640d 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -129,13 +129,13 @@ Create `main.py`: ```python import asyncio -from copilot import CopilotClient +from copilot import CopilotClient, PermissionHandler async def main(): client = CopilotClient() await client.start() - session = await client.create_session({"model": "gpt-4.1"}) + session = await client.create_session(PermissionHandler.approve_all, "gpt-4.1") response = await session.send_and_wait({"prompt": "What is 2 + 2?"}) print(response.data.content) @@ -274,17 +274,14 @@ Update `main.py`: ```python import asyncio import sys -from copilot import CopilotClient +from copilot import CopilotClient, PermissionHandler from copilot.generated.session_events import SessionEventType async def main(): client = CopilotClient() await client.start() - session = await client.create_session({ - "model": "gpt-4.1", - "streaming": True, - }) + session = await client.create_session(PermissionHandler.approve_all, "gpt-4.1", streaming=True) # Listen for response chunks def handle_event(event): @@ -565,7 +562,7 @@ Update `main.py`: import asyncio import random import sys -from copilot import CopilotClient +from copilot import CopilotClient, PermissionHandler from copilot.tools import define_tool from copilot.generated.session_events import SessionEventType from pydantic import BaseModel, Field @@ -588,11 +585,7 @@ async def main(): client = CopilotClient() await client.start() - session = await client.create_session({ - "model": "gpt-4.1", - "streaming": True, - "tools": [get_weather], - }) + session = await client.create_session(PermissionHandler.approve_all, "gpt-4.1", streaming=True, tools=[get_weather]) def handle_event(event): if event.type == SessionEventType.ASSISTANT_MESSAGE_DELTA: @@ -837,7 +830,7 @@ Create `weather_assistant.py`: import asyncio import random import sys -from copilot import CopilotClient +from copilot import CopilotClient, PermissionHandler from copilot.tools import define_tool from copilot.generated.session_events import SessionEventType from pydantic import BaseModel, Field @@ -857,11 +850,7 @@ async def main(): client = CopilotClient() await client.start() - session = await client.create_session({ - "model": "gpt-4.1", - "streaming": True, - "tools": [get_weather], - }) + session = await client.create_session(PermissionHandler.approve_all, "gpt-4.1", streaming=True, tools=[get_weather]) def handle_event(event): if event.type == SessionEventType.ASSISTANT_MESSAGE_DELTA: @@ -1218,7 +1207,7 @@ client = CopilotClient({ await client.start() # Use the client normally -session = await client.create_session({"on_permission_request": PermissionHandler.approve_all}) +session = await client.create_session(PermissionHandler.approve_all) # ... ``` diff --git a/docs/guides/session-persistence.md b/docs/guides/session-persistence.md index 527f5ecc7..de58ecd35 100644 --- a/docs/guides/session-persistence.md +++ b/docs/guides/session-persistence.md @@ -46,16 +46,13 @@ await session.sendAndWait({ prompt: "Analyze my codebase" }); ### Python ```python -from copilot import CopilotClient +from copilot import CopilotClient, PermissionHandler client = CopilotClient() await client.start() # Create a session with a meaningful ID -session = await client.create_session({ - "session_id": "user-123-task-456", - "model": "gpt-5.2-codex", -}) +session = await client.create_session(PermissionHandler.approve_all, "gpt-5.2-codex", session_id="user-123-task-456") # Do some work... await session.send_and_wait({"prompt": "Analyze my codebase"}) diff --git a/docs/guides/setup/azure-managed-identity.md b/docs/guides/setup/azure-managed-identity.md index bfafc6f91..c0296fe28 100644 --- a/docs/guides/setup/azure-managed-identity.md +++ b/docs/guides/setup/azure-managed-identity.md @@ -42,7 +42,7 @@ import asyncio import os from azure.identity import DefaultAzureCredential -from copilot import CopilotClient, ProviderConfig, SessionConfig +from copilot import CopilotClient, PermissionHandler COGNITIVE_SERVICES_SCOPE = "https://cognitiveservices.azure.com/.default" @@ -58,15 +58,14 @@ async def main(): await client.start() session = await client.create_session( - SessionConfig( - model="gpt-4.1", - provider=ProviderConfig( - type="openai", - base_url=f"{foundry_url.rstrip('/')}/openai/v1/", - bearer_token=token, # Short-lived bearer token - wire_api="responses", - ), - ) + PermissionHandler.approve_all, + "gpt-4.1", + provider={ + "type": "openai", + "base_url": f"{foundry_url.rstrip('/')}/openai/v1/", + "bearer_token": token, # Short-lived bearer token + "wire_api": "responses", + }, ) response = await session.send_and_wait({"prompt": "Hello from Managed Identity!"}) @@ -84,7 +83,7 @@ Bearer tokens expire (typically after ~1 hour). For servers or long-running agen ```python from azure.identity import DefaultAzureCredential -from copilot import CopilotClient, ProviderConfig, SessionConfig +from copilot import CopilotClient, PermissionHandler COGNITIVE_SERVICES_SCOPE = "https://cognitiveservices.azure.com/.default" @@ -98,24 +97,21 @@ class ManagedIdentityCopilotAgent: self.credential = DefaultAzureCredential() self.client = CopilotClient() - def _get_session_config(self) -> SessionConfig: - """Build a SessionConfig with a fresh bearer token.""" + def _get_provider_config(self) -> dict: + """Build a provider config dict with a fresh bearer token.""" token = self.credential.get_token(COGNITIVE_SERVICES_SCOPE).token - return SessionConfig( - model=self.model, - provider=ProviderConfig( - type="openai", - base_url=f"{self.foundry_url}/openai/v1/", - bearer_token=token, - wire_api="responses", - ), - ) + return { + "type": "openai", + "base_url": f"{self.foundry_url}/openai/v1/", + "bearer_token": token, + "wire_api": "responses", + } async def chat(self, prompt: str) -> str: """Send a prompt and return the response text.""" # Fresh token for each session - config = self._get_session_config() - session = await self.client.create_session(config) + provider = self._get_provider_config() + session = await self.client.create_session(PermissionHandler.approve_all, self.model, provider=provider) response = await session.send_and_wait({"prompt": prompt}) await session.destroy() diff --git a/docs/guides/setup/backend-services.md b/docs/guides/setup/backend-services.md index c9bc13f8d..81b552db5 100644 --- a/docs/guides/setup/backend-services.md +++ b/docs/guides/setup/backend-services.md @@ -111,17 +111,14 @@ res.json({ content: response?.data.content }); Python ```python -from copilot import CopilotClient +from copilot import CopilotClient, PermissionHandler client = CopilotClient({ "cli_url": "localhost:4321", }) await client.start() -session = await client.create_session({ - "session_id": f"user-{user_id}-{int(time.time())}", - "model": "gpt-4.1", -}) +session = await client.create_session(PermissionHandler.approve_all, "gpt-4.1", session_id=f"user-{user_id}-{int(time.time())}") response = await session.send_and_wait({"prompt": message}) ``` diff --git a/docs/guides/setup/bundled-cli.md b/docs/guides/setup/bundled-cli.md index 6daf57b56..2429d5104 100644 --- a/docs/guides/setup/bundled-cli.md +++ b/docs/guides/setup/bundled-cli.md @@ -85,7 +85,7 @@ await client.stop(); Python ```python -from copilot import CopilotClient +from copilot import CopilotClient, PermissionHandler from pathlib import Path client = CopilotClient({ @@ -93,7 +93,7 @@ client = CopilotClient({ }) await client.start() -session = await client.create_session({"model": "gpt-4.1"}) +session = await client.create_session(PermissionHandler.approve_all, "gpt-4.1") response = await session.send_and_wait({"prompt": "Hello!"}) print(response.data.content) diff --git a/docs/guides/setup/byok.md b/docs/guides/setup/byok.md index 5b8b8a460..2ca844259 100644 --- a/docs/guides/setup/byok.md +++ b/docs/guides/setup/byok.md @@ -93,18 +93,15 @@ await client.stop(); ```python import os -from copilot import CopilotClient +from copilot import CopilotClient, PermissionHandler client = CopilotClient() await client.start() -session = await client.create_session({ - "model": "gpt-4.1", - "provider": { - "type": "openai", - "base_url": "https://api.openai.com/v1", - "api_key": os.environ["OPENAI_API_KEY"], - }, +session = await client.create_session(PermissionHandler.approve_all, "gpt-4.1", provider={ + "type": "openai", + "base_url": "https://api.openai.com/v1", + "api_key": os.environ["OPENAI_API_KEY"], }) response = await session.send_and_wait({"prompt": "Hello!"}) diff --git a/docs/guides/setup/github-oauth.md b/docs/guides/setup/github-oauth.md index 07251c8fb..94d859e3a 100644 --- a/docs/guides/setup/github-oauth.md +++ b/docs/guides/setup/github-oauth.md @@ -145,7 +145,7 @@ const response = await session.sendAndWait({ prompt: "Hello!" }); Python ```python -from copilot import CopilotClient +from copilot import CopilotClient, PermissionHandler def create_client_for_user(user_token: str) -> CopilotClient: return CopilotClient({ @@ -157,10 +157,7 @@ def create_client_for_user(user_token: str) -> CopilotClient: client = create_client_for_user("gho_user_access_token") await client.start() -session = await client.create_session({ - "session_id": f"user-{user_id}-session", - "model": "gpt-4.1", -}) +session = await client.create_session(PermissionHandler.approve_all, "gpt-4.1", session_id=f"user-{user_id}-session") response = await session.send_and_wait({"prompt": "Hello!"}) ``` diff --git a/docs/guides/setup/local-cli.md b/docs/guides/setup/local-cli.md index a5fa906b8..4923d6710 100644 --- a/docs/guides/setup/local-cli.md +++ b/docs/guides/setup/local-cli.md @@ -51,12 +51,12 @@ await client.stop(); Python ```python -from copilot import CopilotClient +from copilot import CopilotClient, PermissionHandler client = CopilotClient() await client.start() -session = await client.create_session({"model": "gpt-4.1"}) +session = await client.create_session(PermissionHandler.approve_all, "gpt-4.1") response = await session.send_and_wait({"prompt": "Hello!"}) print(response.data.content) diff --git a/docs/guides/skills.md b/docs/guides/skills.md index b2ea3ae7a..d87341b79 100644 --- a/docs/guides/skills.md +++ b/docs/guides/skills.md @@ -48,14 +48,14 @@ async def main(): client = CopilotClient() await client.start() - session = await client.create_session({ - "model": "gpt-4.1", - "skill_directories": [ + session = await client.create_session( + lambda req, inv: {"kind": "approved"}, + "gpt-4.1", + skill_directories=[ "./skills/code-review", "./skills/documentation", ], - "on_permission_request": lambda req: {"kind": "approved"}, - }) + ) # Copilot now has access to skills in those directories await session.send_and_wait({"prompt": "Review this code for security issues"}) @@ -159,10 +159,13 @@ const session = await client.createSession({ Python ```python -session = await client.create_session({ - "skill_directories": ["./skills"], - "disabled_skills": ["experimental-feature", "deprecated-tool"], -}) +from copilot import PermissionHandler + +session = await client.create_session( + PermissionHandler.approve_all, + skill_directories=["./skills"], + disabled_skills=["experimental-feature", "deprecated-tool"], +) ``` diff --git a/docs/hooks/error-handling.md b/docs/hooks/error-handling.md index 0f705868d..b8e15f102 100644 --- a/docs/hooks/error-handling.md +++ b/docs/hooks/error-handling.md @@ -107,15 +107,15 @@ const session = await client.createSession({ Python ```python +from copilot import PermissionHandler + async def on_error_occurred(input_data, invocation): print(f"[{invocation['session_id']}] Error: {input_data['error']}") print(f" Context: {input_data['errorContext']}") print(f" Recoverable: {input_data['recoverable']}") return None -session = await client.create_session({ - "hooks": {"on_error_occurred": on_error_occurred} -}) +session = await client.create_session(PermissionHandler.approve_all, hooks={"on_error_occurred": on_error_occurred}) ``` diff --git a/docs/hooks/overview.md b/docs/hooks/overview.md index a51ef0464..e54a57237 100644 --- a/docs/hooks/overview.md +++ b/docs/hooks/overview.md @@ -53,7 +53,7 @@ const session = await client.createSession({ Python ```python -from copilot import CopilotClient +from copilot import CopilotClient, PermissionHandler async def main(): client = CopilotClient() @@ -70,13 +70,11 @@ async def main(): async def on_session_start(input_data, invocation): return {"additionalContext": "User prefers concise answers."} - session = await client.create_session({ - "hooks": { + session = await client.create_session(PermissionHandler.approve_all, hooks={ "on_pre_tool_use": on_pre_tool_use, "on_post_tool_use": on_post_tool_use, "on_session_start": on_session_start, - } - }) + }) ``` diff --git a/docs/hooks/post-tool-use.md b/docs/hooks/post-tool-use.md index 0021e20a0..797458961 100644 --- a/docs/hooks/post-tool-use.md +++ b/docs/hooks/post-tool-use.md @@ -106,15 +106,15 @@ const session = await client.createSession({ Python ```python +from copilot import PermissionHandler + async def on_post_tool_use(input_data, invocation): print(f"[{invocation['session_id']}] Tool: {input_data['toolName']}") print(f" Args: {input_data['toolArgs']}") print(f" Result: {input_data['toolResult']}") return None # Pass through unchanged -session = await client.create_session({ - "hooks": {"on_post_tool_use": on_post_tool_use} -}) +session = await client.create_session(PermissionHandler.approve_all, hooks={"on_post_tool_use": on_post_tool_use}) ``` diff --git a/docs/hooks/pre-tool-use.md b/docs/hooks/pre-tool-use.md index ac12df4fa..7fb369cc6 100644 --- a/docs/hooks/pre-tool-use.md +++ b/docs/hooks/pre-tool-use.md @@ -114,14 +114,14 @@ const session = await client.createSession({ Python ```python +from copilot import PermissionHandler + async def on_pre_tool_use(input_data, invocation): print(f"[{invocation['session_id']}] Calling {input_data['toolName']}") print(f" Args: {input_data['toolArgs']}") return {"permissionDecision": "allow"} -session = await client.create_session({ - "hooks": {"on_pre_tool_use": on_pre_tool_use} -}) +session = await client.create_session(PermissionHandler.approve_all, hooks={"on_pre_tool_use": on_pre_tool_use}) ``` diff --git a/docs/hooks/session-lifecycle.md b/docs/hooks/session-lifecycle.md index 74f4666f4..50a6b3ba2 100644 --- a/docs/hooks/session-lifecycle.md +++ b/docs/hooks/session-lifecycle.md @@ -113,6 +113,8 @@ Package manager: ${projectInfo.packageManager} Python ```python +from copilot import PermissionHandler + async def on_session_start(input_data, invocation): print(f"Session {invocation['session_id']} started ({input_data['source']})") @@ -126,9 +128,7 @@ Package manager: {project_info['packageManager']} """.strip() } -session = await client.create_session({ - "hooks": {"on_session_start": on_session_start} -}) +session = await client.create_session(PermissionHandler.approve_all, hooks={"on_session_start": on_session_start}) ``` @@ -309,6 +309,8 @@ const session = await client.createSession({ Python ```python +from copilot import PermissionHandler + session_start_times = {} async def on_session_start(input_data, invocation): @@ -328,12 +330,10 @@ async def on_session_end(input_data, invocation): session_start_times.pop(invocation["session_id"], None) return None -session = await client.create_session({ - "hooks": { +session = await client.create_session(PermissionHandler.approve_all, hooks={ "on_session_start": on_session_start, "on_session_end": on_session_end, - } -}) + }) ``` diff --git a/docs/hooks/user-prompt-submitted.md b/docs/hooks/user-prompt-submitted.md index 3205b95cd..4a2bda0ed 100644 --- a/docs/hooks/user-prompt-submitted.md +++ b/docs/hooks/user-prompt-submitted.md @@ -102,13 +102,13 @@ const session = await client.createSession({ Python ```python +from copilot import PermissionHandler + async def on_user_prompt_submitted(input_data, invocation): print(f"[{invocation['session_id']}] User: {input_data['prompt']}") return None -session = await client.create_session({ - "hooks": {"on_user_prompt_submitted": on_user_prompt_submitted} -}) +session = await client.create_session(PermissionHandler.approve_all, hooks={"on_user_prompt_submitted": on_user_prompt_submitted}) ``` diff --git a/docs/mcp/overview.md b/docs/mcp/overview.md index aa2fba668..637079c78 100644 --- a/docs/mcp/overview.md +++ b/docs/mcp/overview.md @@ -59,32 +59,29 @@ const session = await client.createSession({ ```python import asyncio -from copilot import CopilotClient +from copilot import CopilotClient, PermissionHandler async def main(): client = CopilotClient() await client.start() - session = await client.create_session({ - "model": "gpt-5", - "mcp_servers": { - # Local MCP server (stdio) - "my-local-server": { - "type": "local", - "command": "python", - "args": ["./mcp_server.py"], - "env": {"DEBUG": "true"}, - "cwd": "./servers", - "tools": ["*"], - "timeout": 30000, - }, - # Remote MCP server (HTTP) - "github": { - "type": "http", - "url": "https://api.githubcopilot.com/mcp/", - "headers": {"Authorization": "Bearer ${TOKEN}"}, - "tools": ["*"], - }, + session = await client.create_session(PermissionHandler.approve_all, "gpt-5", mcp_servers={ + # Local MCP server (stdio) + "my-local-server": { + "type": "local", + "command": "python", + "args": ["./mcp_server.py"], + "env": {"DEBUG": "true"}, + "cwd": "./servers", + "tools": ["*"], + "timeout": 30000, + }, + # Remote MCP server (HTTP) + "github": { + "type": "http", + "url": "https://api.githubcopilot.com/mcp/", + "headers": {"Authorization": "Bearer ${TOKEN}"}, + "tools": ["*"], }, }) diff --git a/python/README.md b/python/README.md index aa82e0c34..867af45f0 100644 --- a/python/README.md +++ b/python/README.md @@ -25,7 +25,7 @@ python chat.py ```python import asyncio -from copilot import CopilotClient +from copilot import CopilotClient, PermissionHandler async def main(): # Create and start client @@ -33,7 +33,7 @@ async def main(): await client.start() # Create a session - session = await client.create_session({"model": "gpt-5"}) + session = await client.create_session(PermissionHandler.approve_all, "gpt-5") # Wait for response using session.idle event done = asyncio.Event() @@ -80,7 +80,7 @@ client = CopilotClient({ }) await client.start() -session = await client.create_session({"model": "gpt-5"}) +session = await client.create_session(PermissionHandler.approve_all, "gpt-5") def on_event(event): print(f"Event: {event['type']}") @@ -107,18 +107,31 @@ await client.stop() - `github_token` (str): GitHub token for authentication. When provided, takes priority over other auth methods. - `use_logged_in_user` (bool): Whether to use logged-in user for authentication (default: True, but False when `github_token` is provided). Cannot be used with `cli_url`. -**SessionConfig Options (for `create_session`):** +**`create_session` Parameters:** -- `model` (str): Model to use ("gpt-5", "claude-sonnet-4.5", etc.). **Required when using custom provider.** +- `on_permission_request` (callable): **Required.** Handler for permission requests from the server. +- `model` (str): Model to use ("gpt-5", "claude-sonnet-4.5", etc.). + +The parameters below are keyword-only: + +- `session_id` (str): Custom session ID for resuming or identifying sessions. +- `client_name` (str): Client name to identify the application using the SDK. Included in the User-Agent header for API requests. - `reasoning_effort` (str): Reasoning effort level for models that support it ("low", "medium", "high", "xhigh"). Use `list_models()` to check which models support this option. -- `session_id` (str): Custom session ID -- `tools` (list): Custom tools exposed to the CLI -- `system_message` (dict): System message configuration -- `streaming` (bool): Enable streaming delta events -- `provider` (dict): Custom API provider configuration (BYOK). See [Custom Providers](#custom-providers) section. -- `infinite_sessions` (dict): Automatic context compaction configuration +- `tools` (list): Custom tools exposed to the CLI. +- `system_message` (dict): System message configuration. +- `available_tools` (list[str]): List of tool names to allow. Takes precedence over `excluded_tools`. +- `excluded_tools` (list[str]): List of tool names to disable. Ignored if `available_tools` is set. - `on_user_input_request` (callable): Handler for user input requests from the agent (enables ask_user tool). See [User Input Requests](#user-input-requests) section. - `hooks` (dict): Hook handlers for session lifecycle events. See [Session Hooks](#session-hooks) section. +- `working_directory` (str): Working directory for the session. Tool operations will be relative to this directory. +- `provider` (dict): Custom API provider configuration (BYOK). See [Custom Providers](#custom-providers) section. +- `streaming` (bool): Enable streaming delta events. +- `mcp_servers` (dict): MCP server configurations for the session. +- `custom_agents` (list): Custom agent configurations for the session. +- `config_dir` (str): Override the default configuration directory location. +- `skill_directories` (list[str]): Directories to load skills from. +- `disabled_skills` (list[str]): List of skill names to disable. +- `infinite_sessions` (dict): Automatic context compaction configuration. **Session Lifecycle Methods:** @@ -155,7 +168,7 @@ Define tools with automatic JSON schema generation using the `@define_tool` deco ```python from pydantic import BaseModel, Field -from copilot import CopilotClient, define_tool +from copilot import CopilotClient, define_tool, PermissionHandler class LookupIssueParams(BaseModel): id: str = Field(description="Issue identifier") @@ -165,10 +178,11 @@ async def lookup_issue(params: LookupIssueParams) -> str: issue = await fetch_issue(params.id) return issue.summary -session = await client.create_session({ - "model": "gpt-5", - "tools": [lookup_issue], -}) +session = await client.create_session( + PermissionHandler.approve_all, + "gpt-5", + tools=[lookup_issue], +) ``` > **Note:** When using `from __future__ import annotations`, define Pydantic models at module level (not inside functions). @@ -178,7 +192,7 @@ session = await client.create_session({ For users who prefer manual schema definition: ```python -from copilot import CopilotClient, Tool +from copilot import CopilotClient, Tool, PermissionHandler async def lookup_issue(invocation): issue_id = invocation["arguments"]["id"] @@ -189,9 +203,10 @@ async def lookup_issue(invocation): "sessionLog": f"Fetched issue {issue_id}", } -session = await client.create_session({ - "model": "gpt-5", - "tools": [ +session = await client.create_session( + PermissionHandler.approve_all, + "gpt-5", + tools=[ Tool( name="lookup_issue", description="Fetch issue details from our tracker", @@ -205,7 +220,7 @@ session = await client.create_session({ handler=lookup_issue, ) ], -}) +) ``` The SDK automatically handles `tool.call`, executes your handler (sync or async), and responds with the final result when the tool completes. @@ -238,16 +253,17 @@ Enable streaming to receive assistant response chunks as they're generated: ```python import asyncio -from copilot import CopilotClient +from copilot import CopilotClient, PermissionHandler async def main(): client = CopilotClient() await client.start() - session = await client.create_session({ - "model": "gpt-5", - "streaming": True - }) + session = await client.create_session( + PermissionHandler.approve_all, + "gpt-5", + streaming=True, + ) # Use asyncio.Event to wait for completion done = asyncio.Event() @@ -298,27 +314,29 @@ By default, sessions use **infinite sessions** which automatically manage contex ```python # Default: infinite sessions enabled with default thresholds -session = await client.create_session({"model": "gpt-5"}) +session = await client.create_session(PermissionHandler.approve_all, "gpt-5") # Access the workspace path for checkpoints and files print(session.workspace_path) # => ~/.copilot/session-state/{session_id}/ # Custom thresholds -session = await client.create_session({ - "model": "gpt-5", - "infinite_sessions": { +session = await client.create_session( + PermissionHandler.approve_all, + "gpt-5", + infinite_sessions={ "enabled": True, "background_compaction_threshold": 0.80, # Start compacting at 80% context usage "buffer_exhaustion_threshold": 0.95, # Block at 95% until compaction completes }, -}) +) # Disable infinite sessions -session = await client.create_session({ - "model": "gpt-5", - "infinite_sessions": {"enabled": False}, -}) +session = await client.create_session( + PermissionHandler.approve_all, + "gpt-5", + infinite_sessions={"enabled": False}, +) ``` When enabled, sessions emit compaction events: @@ -342,14 +360,15 @@ The SDK supports custom OpenAI-compatible API providers (BYOK - Bring Your Own K **Example with Ollama:** ```python -session = await client.create_session({ - "model": "deepseek-coder-v2:16b", # Required when using custom provider - "provider": { +session = await client.create_session( + PermissionHandler.approve_all, + "deepseek-coder-v2:16b", # Model to use with the custom provider + provider={ "type": "openai", "base_url": "http://localhost:11434/v1", # Ollama endpoint # api_key not required for Ollama }, -}) +) await session.send({"prompt": "Hello!"}) ``` @@ -359,14 +378,15 @@ await session.send({"prompt": "Hello!"}) ```python import os -session = await client.create_session({ - "model": "gpt-4", - "provider": { +session = await client.create_session( + PermissionHandler.approve_all, + "gpt-4", + provider={ "type": "openai", "base_url": "https://my-api.example.com/v1", "api_key": os.environ["MY_API_KEY"], }, -}) +) ``` **Example with Azure OpenAI:** @@ -374,9 +394,10 @@ session = await client.create_session({ ```python import os -session = await client.create_session({ - "model": "gpt-4", - "provider": { +session = await client.create_session( + PermissionHandler.approve_all, + "gpt-4", + provider={ "type": "azure", # Must be "azure" for Azure endpoints, NOT "openai" "base_url": "https://my-resource.openai.azure.com", # Just the host, no path "api_key": os.environ["AZURE_OPENAI_KEY"], @@ -384,11 +405,10 @@ session = await client.create_session({ "api_version": "2024-10-21", }, }, -}) +) ``` > **Important notes:** -> - When using a custom provider, the `model` parameter is **required**. The SDK will throw an error if no model is specified. > - For Azure OpenAI endpoints (`*.openai.azure.com`), you **must** use `type: "azure"`, not `type: "openai"`. > - The `base_url` should be just the host (e.g., `https://my-resource.openai.azure.com`). Do **not** include `/openai/v1` in the URL - the SDK handles path construction automatically. @@ -401,21 +421,22 @@ async def handle_user_input(request, invocation): # request["question"] - The question to ask # request.get("choices") - Optional list of choices for multiple choice # request.get("allowFreeform", True) - Whether freeform input is allowed - + print(f"Agent asks: {request['question']}") if request.get("choices"): print(f"Choices: {', '.join(request['choices'])}") - + # Return the user's response return { "answer": "User's answer here", "wasFreeform": True, # Whether the answer was freeform (not from choices) } -session = await client.create_session({ - "model": "gpt-5", - "on_user_input_request": handle_user_input, -}) +session = await client.create_session( + PermissionHandler.approve_all, + "gpt-5", + on_user_input_request=handle_user_input, +) ``` ## Session Hooks @@ -459,9 +480,10 @@ async def on_error_occurred(input, invocation): "errorHandling": "retry", # "retry", "skip", or "abort" } -session = await client.create_session({ - "model": "gpt-5", - "hooks": { +session = await client.create_session( + PermissionHandler.approve_all, + "gpt-5", + hooks={ "on_pre_tool_use": on_pre_tool_use, "on_post_tool_use": on_post_tool_use, "on_user_prompt_submitted": on_user_prompt_submitted, @@ -469,7 +491,7 @@ session = await client.create_session({ "on_session_end": on_session_end, "on_error_occurred": on_error_occurred, }, -}) +) ``` **Available hooks:** diff --git a/python/copilot/__init__.py b/python/copilot/__init__.py index f5f7ed0b1..d65c32b2d 100644 --- a/python/copilot/__init__.py +++ b/python/copilot/__init__.py @@ -27,7 +27,6 @@ PingResponse, ProviderConfig, ResumeSessionConfig, - SessionConfig, SessionContext, SessionEvent, SessionListFilter, @@ -63,7 +62,6 @@ "PingResponse", "ProviderConfig", "ResumeSessionConfig", - "SessionConfig", "SessionContext", "SessionEvent", "SessionListFilter", diff --git a/python/copilot/client.py b/python/copilot/client.py index 774569afb..043d1c00c 100644 --- a/python/copilot/client.py +++ b/python/copilot/client.py @@ -5,10 +5,10 @@ to the Copilot CLI server and provides session management capabilities. Example: - >>> from copilot import CopilotClient + >>> from copilot import CopilotClient, PermissionHandler >>> >>> async with CopilotClient() as client: - ... session = await client.create_session() + ... session = await client.create_session(PermissionHandler.approve_all) ... await session.send({"prompt": "Hello!"}) """ @@ -34,20 +34,27 @@ CustomAgentConfig, GetAuthStatusResponse, GetStatusResponse, + InfiniteSessionConfig, + MCPServerConfig, ModelInfo, PingResponse, ProviderConfig, + ReasoningEffort, ResumeSessionConfig, - SessionConfig, + SessionHooks, SessionLifecycleEvent, SessionLifecycleEventType, SessionLifecycleHandler, SessionListFilter, SessionMetadata, StopError, + SystemMessageConfig, + Tool, ToolHandler, ToolInvocation, ToolResult, + UserInputHandler, + _PermissionHandlerFn, ) @@ -417,7 +424,30 @@ async def force_stop(self) -> None: if not self._is_external_server: self._actual_port = None - async def create_session(self, config: SessionConfig) -> CopilotSession: + async def create_session( + self, + on_permission_request: _PermissionHandlerFn, + model: str | None = None, + *, + session_id: str | None = None, + client_name: str | None = None, + reasoning_effort: ReasoningEffort | None = None, + tools: list[Tool] | None = None, + system_message: SystemMessageConfig | None = None, + available_tools: list[str] | None = None, + excluded_tools: list[str] | None = None, + on_user_input_request: UserInputHandler | None = None, + hooks: SessionHooks | None = None, + working_directory: str | None = None, + provider: ProviderConfig | None = None, + streaming: bool | None = None, + mcp_servers: dict[str, MCPServerConfig] | None = None, + custom_agents: list[CustomAgentConfig] | None = None, + config_dir: str | None = None, + skill_directories: list[str] | None = None, + disabled_skills: list[str] | None = None, + infinite_sessions: InfiniteSessionConfig | None = None, + ) -> CopilotSession: """ Create a new conversation session with the Copilot CLI. @@ -426,8 +456,26 @@ async def create_session(self, config: SessionConfig) -> CopilotSession: automatically start the connection. Args: - config: Optional configuration for the session, including model selection, - custom tools, system messages, and more. + on_permission_request: Handler for permission requests from the server. + model: Model to use for this session. + session_id: Custom session ID. + client_name: Client name to identify the application using the SDK. + reasoning_effort: Reasoning effort level ("low", "medium", "high", "xhigh"). + tools: Custom tools exposed to the CLI. + system_message: System message configuration. + available_tools: List of tool names to allow (takes precedence over excluded_tools). + excluded_tools: List of tool names to disable (ignored if available_tools is set). + on_user_input_request: Handler for user input requests (enables ask_user tool). + hooks: Hook handlers for intercepting session lifecycle events. + working_directory: Working directory for the session. + provider: Custom provider configuration (BYOK - Bring Your Own Key). + streaming: Enable streaming of assistant message and reasoning chunks. + mcp_servers: MCP server configurations for the session. + custom_agents: Custom agent configurations for the session. + config_dir: Override the default configuration directory location. + skill_directories: Directories to load skills from. + disabled_skills: List of skill names to disable. + infinite_sessions: Infinite session configuration for persistent workspaces. Returns: A :class:`CopilotSession` instance for the new session. @@ -436,34 +484,28 @@ async def create_session(self, config: SessionConfig) -> CopilotSession: RuntimeError: If the client is not connected and auto_start is disabled. Example: - >>> # Basic session - >>> config = {"on_permission_request": PermissionHandler.approve_all} - >>> session = await client.create_session(config) + >>> session = await client.create_session(PermissionHandler.approve_all) >>> >>> # Session with model and streaming - >>> session = await client.create_session({ - ... "on_permission_request": PermissionHandler.approve_all, - ... "model": "gpt-4", - ... "streaming": True - ... }) + >>> session = await client.create_session( + ... PermissionHandler.approve_all, + ... "gpt-4", + ... streaming=True, + ... ) """ + if not on_permission_request or not callable(on_permission_request): + raise ValueError( + "A valid on_permission_request handler is required. " + "Use PermissionHandler.approve_all or provide a custom handler." + ) + if not self._client: if self.options["auto_start"]: await self.start() else: raise RuntimeError("Client not connected. Call start() first.") - cfg = config - - if not cfg.get("on_permission_request"): - raise ValueError( - "An on_permission_request handler is required when creating a session. " - "For example, to allow all permissions, use " - '{"on_permission_request": PermissionHandler.approve_all}.' - ) - tool_defs = [] - tools = cfg.get("tools") if tools: for tool in tools: definition = { @@ -475,89 +517,60 @@ async def create_session(self, config: SessionConfig) -> CopilotSession: tool_defs.append(definition) payload: dict[str, Any] = {} - if cfg.get("model"): - payload["model"] = cfg["model"] - if cfg.get("session_id"): - payload["sessionId"] = cfg["session_id"] - if cfg.get("client_name"): - payload["clientName"] = cfg["client_name"] - if cfg.get("reasoning_effort"): - payload["reasoningEffort"] = cfg["reasoning_effort"] + if model: + payload["model"] = model + if session_id: + payload["sessionId"] = session_id + if client_name: + payload["clientName"] = client_name + if reasoning_effort: + payload["reasoningEffort"] = reasoning_effort if tool_defs: payload["tools"] = tool_defs - # Add system message configuration if provided - system_message = cfg.get("system_message") if system_message: payload["systemMessage"] = system_message - # Add tool filtering options - available_tools = cfg.get("available_tools") if available_tools is not None: payload["availableTools"] = available_tools - excluded_tools = cfg.get("excluded_tools") if excluded_tools: payload["excludedTools"] = excluded_tools - # Always enable permission request callback (deny by default if no handler provided) - on_permission_request = cfg.get("on_permission_request") payload["requestPermission"] = True - # Enable user input request callback if handler provided - on_user_input_request = cfg.get("on_user_input_request") if on_user_input_request: payload["requestUserInput"] = True - # Enable hooks callback if any hook handler provided - hooks = cfg.get("hooks") if hooks and any(hooks.values()): payload["hooks"] = True - # Add working directory if provided - working_directory = cfg.get("working_directory") if working_directory: payload["workingDirectory"] = working_directory - # Add streaming option if provided - streaming = cfg.get("streaming") if streaming is not None: payload["streaming"] = streaming - # Add provider configuration if provided - provider = cfg.get("provider") if provider: payload["provider"] = self._convert_provider_to_wire_format(provider) - # Add MCP servers configuration if provided - mcp_servers = cfg.get("mcp_servers") if mcp_servers: payload["mcpServers"] = mcp_servers payload["envValueMode"] = "direct" - # Add custom agents configuration if provided - custom_agents = cfg.get("custom_agents") if custom_agents: payload["customAgents"] = [ self._convert_custom_agent_to_wire_format(agent) for agent in custom_agents ] - # Add config directory override if provided - config_dir = cfg.get("config_dir") if config_dir: payload["configDir"] = config_dir - # Add skill directories configuration if provided - skill_directories = cfg.get("skill_directories") if skill_directories: payload["skillDirectories"] = skill_directories - # Add disabled skills configuration if provided - disabled_skills = cfg.get("disabled_skills") if disabled_skills: payload["disabledSkills"] = disabled_skills - # Add infinite sessions configuration if provided - infinite_sessions = cfg.get("infinite_sessions") if infinite_sessions: wire_config: dict[str, Any] = {} if "enabled" in infinite_sessions: diff --git a/python/copilot/types.py b/python/copilot/types.py index 142aee474..959f8319c 100644 --- a/python/copilot/types.py +++ b/python/copilot/types.py @@ -463,55 +463,6 @@ class InfiniteSessionConfig(TypedDict, total=False): buffer_exhaustion_threshold: float -# Configuration for creating a session -class SessionConfig(TypedDict, total=False): - """Configuration for creating a session""" - - session_id: str # Optional custom session ID - # Client name to identify the application using the SDK. - # Included in the User-Agent header for API requests. - client_name: str - model: str # Model to use for this session. Use client.list_models() to see available models. - # Reasoning effort level for models that support it. - # Only valid for models where capabilities.supports.reasoning_effort is True. - reasoning_effort: ReasoningEffort - tools: list[Tool] - system_message: SystemMessageConfig # System message configuration - # List of tool names to allow (takes precedence over excluded_tools) - available_tools: list[str] - # List of tool names to disable (ignored if available_tools is set) - excluded_tools: list[str] - # Handler for permission requests from the server - on_permission_request: _PermissionHandlerFn - # Handler for user input requests from the agent (enables ask_user tool) - on_user_input_request: UserInputHandler - # Hook handlers for intercepting session lifecycle events - hooks: SessionHooks - # Working directory for the session. Tool operations will be relative to this directory. - working_directory: str - # Custom provider configuration (BYOK - Bring Your Own Key) - provider: ProviderConfig - # Enable streaming of assistant message and reasoning chunks - # When True, assistant.message_delta and assistant.reasoning_delta events - # with delta_content are sent as the response is generated - streaming: bool - # MCP server configurations for the session - mcp_servers: dict[str, MCPServerConfig] - # Custom agent configurations for the session - custom_agents: list[CustomAgentConfig] - # Override the default configuration directory location. - # When specified, the session will use this directory for storing config and state. - config_dir: str - # Directories to load skills from - skill_directories: list[str] - # List of skill names to disable - disabled_skills: list[str] - # Infinite session configuration for persistent workspaces and automatic compaction. - # When enabled (default), sessions automatically manage context limits and persist state. - # Set to {"enabled": False} to disable. - infinite_sessions: InfiniteSessionConfig - - # Azure-specific provider options class AzureProviderOptions(TypedDict, total=False): """Azure-specific provider configuration""" diff --git a/python/e2e/test_agent_and_compact_rpc.py b/python/e2e/test_agent_and_compact_rpc.py index a960c8426..58b3972c7 100644 --- a/python/e2e/test_agent_and_compact_rpc.py +++ b/python/e2e/test_agent_and_compact_rpc.py @@ -19,23 +19,21 @@ async def test_should_list_available_custom_agents(self): try: await client.start() session = await client.create_session( - { - "on_permission_request": PermissionHandler.approve_all, - "custom_agents": [ - { - "name": "test-agent", - "display_name": "Test Agent", - "description": "A test agent", - "prompt": "You are a test agent.", - }, - { - "name": "another-agent", - "display_name": "Another Agent", - "description": "Another test agent", - "prompt": "You are another agent.", - }, - ], - } + PermissionHandler.approve_all, + custom_agents=[ + { + "name": "test-agent", + "display_name": "Test Agent", + "description": "A test agent", + "prompt": "You are a test agent.", + }, + { + "name": "another-agent", + "display_name": "Another Agent", + "description": "Another test agent", + "prompt": "You are another agent.", + }, + ], ) result = await session.rpc.agent.list() @@ -59,17 +57,15 @@ async def test_should_return_null_when_no_agent_is_selected(self): try: await client.start() session = await client.create_session( - { - "on_permission_request": PermissionHandler.approve_all, - "custom_agents": [ - { - "name": "test-agent", - "display_name": "Test Agent", - "description": "A test agent", - "prompt": "You are a test agent.", - } - ], - } + PermissionHandler.approve_all, + custom_agents=[ + { + "name": "test-agent", + "display_name": "Test Agent", + "description": "A test agent", + "prompt": "You are a test agent.", + } + ], ) result = await session.rpc.agent.get_current() @@ -88,17 +84,15 @@ async def test_should_select_and_get_current_agent(self): try: await client.start() session = await client.create_session( - { - "on_permission_request": PermissionHandler.approve_all, - "custom_agents": [ - { - "name": "test-agent", - "display_name": "Test Agent", - "description": "A test agent", - "prompt": "You are a test agent.", - } - ], - } + PermissionHandler.approve_all, + custom_agents=[ + { + "name": "test-agent", + "display_name": "Test Agent", + "description": "A test agent", + "prompt": "You are a test agent.", + } + ], ) # Select the agent @@ -127,17 +121,15 @@ async def test_should_deselect_current_agent(self): try: await client.start() session = await client.create_session( - { - "on_permission_request": PermissionHandler.approve_all, - "custom_agents": [ - { - "name": "test-agent", - "display_name": "Test Agent", - "description": "A test agent", - "prompt": "You are a test agent.", - } - ], - } + PermissionHandler.approve_all, + custom_agents=[ + { + "name": "test-agent", + "display_name": "Test Agent", + "description": "A test agent", + "prompt": "You are a test agent.", + } + ], ) # Select then deselect @@ -160,9 +152,7 @@ async def test_should_return_empty_list_when_no_custom_agents_configured(self): try: await client.start() - session = await client.create_session( - {"on_permission_request": PermissionHandler.approve_all} - ) + session = await client.create_session(PermissionHandler.approve_all) result = await session.rpc.agent.list() assert result.agents == [] @@ -177,9 +167,7 @@ class TestSessionCompactionRpc: @pytest.mark.asyncio async def test_should_compact_session_history_after_messages(self, ctx: E2ETestContext): """Test compacting session history via RPC.""" - session = await ctx.client.create_session( - {"on_permission_request": PermissionHandler.approve_all} - ) + session = await ctx.client.create_session(PermissionHandler.approve_all) # Send a message to create some history await session.send_and_wait({"prompt": "What is 2+2?"}) diff --git a/python/e2e/test_ask_user.py b/python/e2e/test_ask_user.py index f409e460c..9942e4e20 100644 --- a/python/e2e/test_ask_user.py +++ b/python/e2e/test_ask_user.py @@ -30,10 +30,8 @@ async def on_user_input_request(request, invocation): } session = await ctx.client.create_session( - { - "on_user_input_request": on_user_input_request, - "on_permission_request": PermissionHandler.approve_all, - } + PermissionHandler.approve_all, + on_user_input_request=on_user_input_request, ) await session.send_and_wait( @@ -69,10 +67,8 @@ async def on_user_input_request(request, invocation): } session = await ctx.client.create_session( - { - "on_user_input_request": on_user_input_request, - "on_permission_request": PermissionHandler.approve_all, - } + PermissionHandler.approve_all, + on_user_input_request=on_user_input_request, ) await session.send_and_wait( @@ -110,10 +106,8 @@ async def on_user_input_request(request, invocation): } session = await ctx.client.create_session( - { - "on_user_input_request": on_user_input_request, - "on_permission_request": PermissionHandler.approve_all, - } + PermissionHandler.approve_all, + on_user_input_request=on_user_input_request, ) response = await session.send_and_wait( diff --git a/python/e2e/test_client.py b/python/e2e/test_client.py index cc5d31ac6..59386335c 100644 --- a/python/e2e/test_client.py +++ b/python/e2e/test_client.py @@ -51,7 +51,7 @@ async def test_should_return_errors_on_failed_cleanup(self): client = CopilotClient({"cli_path": CLI_PATH}) try: - await client.create_session({"on_permission_request": PermissionHandler.approve_all}) + await client.create_session(PermissionHandler.approve_all) # Kill the server process to force cleanup to fail process = client._process @@ -69,7 +69,7 @@ async def test_should_return_errors_on_failed_cleanup(self): async def test_should_force_stop_without_cleanup(self): client = CopilotClient({"cli_path": CLI_PATH}) - await client.create_session({"on_permission_request": PermissionHandler.approve_all}) + await client.create_session(PermissionHandler.approve_all) await client.force_stop() assert client.get_state() == "disconnected" @@ -206,9 +206,7 @@ async def test_should_report_error_with_stderr_when_cli_fails_to_start(self): # Verify subsequent calls also fail (don't hang) with pytest.raises(Exception) as exc_info2: - session = await client.create_session( - {"on_permission_request": PermissionHandler.approve_all} - ) + session = await client.create_session(PermissionHandler.approve_all) await session.send("test") # Error message varies by platform (EINVAL on Windows, EPIPE on Linux) error_msg = str(exc_info2.value).lower() diff --git a/python/e2e/test_compaction.py b/python/e2e/test_compaction.py index 5447b4bad..6df20bf02 100644 --- a/python/e2e/test_compaction.py +++ b/python/e2e/test_compaction.py @@ -17,16 +17,14 @@ async def test_should_trigger_compaction_with_low_threshold_and_emit_events( ): # Create session with very low compaction thresholds to trigger compaction quickly session = await ctx.client.create_session( - { - "infinite_sessions": { - "enabled": True, - # Trigger background compaction at 0.5% context usage (~1000 tokens) - "background_compaction_threshold": 0.005, - # Block at 1% to ensure compaction runs - "buffer_exhaustion_threshold": 0.01, - }, - "on_permission_request": PermissionHandler.approve_all, - } + PermissionHandler.approve_all, + infinite_sessions={ + "enabled": True, + # Trigger background compaction at 0.5% context usage (~1000 tokens) + "background_compaction_threshold": 0.005, + # Block at 1% to ensure compaction runs + "buffer_exhaustion_threshold": 0.01, + }, ) compaction_start_events = [] @@ -72,10 +70,8 @@ async def test_should_not_emit_compaction_events_when_infinite_sessions_disabled self, ctx: E2ETestContext ): session = await ctx.client.create_session( - { - "infinite_sessions": {"enabled": False}, - "on_permission_request": PermissionHandler.approve_all, - } + PermissionHandler.approve_all, + infinite_sessions={"enabled": False}, ) compaction_events = [] diff --git a/python/e2e/test_hooks.py b/python/e2e/test_hooks.py index 8278fb33c..a4d432b10 100644 --- a/python/e2e/test_hooks.py +++ b/python/e2e/test_hooks.py @@ -24,10 +24,8 @@ async def on_pre_tool_use(input_data, invocation): return {"permissionDecision": "allow"} session = await ctx.client.create_session( - { - "hooks": {"on_pre_tool_use": on_pre_tool_use}, - "on_permission_request": PermissionHandler.approve_all, - } + PermissionHandler.approve_all, + hooks={"on_pre_tool_use": on_pre_tool_use}, ) # Create a file for the model to read @@ -57,10 +55,8 @@ async def on_post_tool_use(input_data, invocation): return None session = await ctx.client.create_session( - { - "hooks": {"on_post_tool_use": on_post_tool_use}, - "on_permission_request": PermissionHandler.approve_all, - } + PermissionHandler.approve_all, + hooks={"on_post_tool_use": on_post_tool_use}, ) # Create a file for the model to read @@ -95,13 +91,11 @@ async def on_post_tool_use(input_data, invocation): return None session = await ctx.client.create_session( - { - "hooks": { - "on_pre_tool_use": on_pre_tool_use, - "on_post_tool_use": on_post_tool_use, - }, - "on_permission_request": PermissionHandler.approve_all, - } + PermissionHandler.approve_all, + hooks={ + "on_pre_tool_use": on_pre_tool_use, + "on_post_tool_use": on_post_tool_use, + }, ) write_file(ctx.work_dir, "both.txt", "Testing both hooks!") @@ -132,10 +126,8 @@ async def on_pre_tool_use(input_data, invocation): return {"permissionDecision": "deny"} session = await ctx.client.create_session( - { - "hooks": {"on_pre_tool_use": on_pre_tool_use}, - "on_permission_request": PermissionHandler.approve_all, - } + PermissionHandler.approve_all, + hooks={"on_pre_tool_use": on_pre_tool_use}, ) # Create a file diff --git a/python/e2e/test_mcp_and_agents.py b/python/e2e/test_mcp_and_agents.py index b29a54827..c8659f1c1 100644 --- a/python/e2e/test_mcp_and_agents.py +++ b/python/e2e/test_mcp_and_agents.py @@ -33,7 +33,7 @@ async def test_should_accept_mcp_server_configuration_on_session_create( } session = await ctx.client.create_session( - {"mcp_servers": mcp_servers, "on_permission_request": PermissionHandler.approve_all} + PermissionHandler.approve_all, mcp_servers=mcp_servers ) assert session.session_id is not None @@ -50,9 +50,7 @@ async def test_should_accept_mcp_server_configuration_on_session_resume( ): """Test that MCP server configuration is accepted on session resume""" # Create a session first - session1 = await ctx.client.create_session( - {"on_permission_request": PermissionHandler.approve_all} - ) + session1 = await ctx.client.create_session(PermissionHandler.approve_all) session_id = session1.session_id await session1.send_and_wait({"prompt": "What is 1+1?"}) @@ -95,10 +93,7 @@ async def test_should_pass_literal_env_values_to_mcp_server_subprocess( } session = await ctx.client.create_session( - { - "mcp_servers": mcp_servers, - "on_permission_request": PermissionHandler.approve_all, - } + PermissionHandler.approve_all, mcp_servers=mcp_servers ) assert session.session_id is not None @@ -131,7 +126,7 @@ async def test_should_accept_custom_agent_configuration_on_session_create( ] session = await ctx.client.create_session( - {"custom_agents": custom_agents, "on_permission_request": PermissionHandler.approve_all} + PermissionHandler.approve_all, custom_agents=custom_agents ) assert session.session_id is not None @@ -148,9 +143,7 @@ async def test_should_accept_custom_agent_configuration_on_session_resume( ): """Test that custom agent configuration is accepted on session resume""" # Create a session first - session1 = await ctx.client.create_session( - {"on_permission_request": PermissionHandler.approve_all} - ) + session1 = await ctx.client.create_session(PermissionHandler.approve_all) session_id = session1.session_id await session1.send_and_wait({"prompt": "What is 1+1?"}) @@ -203,11 +196,9 @@ async def test_should_accept_both_mcp_servers_and_custom_agents(self, ctx: E2ETe ] session = await ctx.client.create_session( - { - "mcp_servers": mcp_servers, - "custom_agents": custom_agents, - "on_permission_request": PermissionHandler.approve_all, - } + PermissionHandler.approve_all, + mcp_servers=mcp_servers, + custom_agents=custom_agents, ) assert session.session_id is not None diff --git a/python/e2e/test_permissions.py b/python/e2e/test_permissions.py index c116053ba..a4242f46c 100644 --- a/python/e2e/test_permissions.py +++ b/python/e2e/test_permissions.py @@ -27,7 +27,7 @@ def on_permission_request( # Approve the permission return {"kind": "approved"} - session = await ctx.client.create_session({"on_permission_request": on_permission_request}) + session = await ctx.client.create_session(on_permission_request) write_file(ctx.work_dir, "test.txt", "original content") @@ -53,7 +53,7 @@ def on_permission_request( # Deny all permissions return {"kind": "denied-interactively-by-user"} - session = await ctx.client.create_session({"on_permission_request": on_permission_request}) + session = await ctx.client.create_session(on_permission_request) original_content = "protected content" write_file(ctx.work_dir, "protected.txt", original_content) @@ -76,7 +76,7 @@ async def test_should_deny_tool_operations_when_handler_explicitly_denies( def deny_all(request, invocation): return {"kind": "denied-no-approval-rule-and-could-not-request-from-user"} - session = await ctx.client.create_session({"on_permission_request": deny_all}) + session = await ctx.client.create_session(deny_all) denied_events = [] done_event = asyncio.Event() @@ -107,9 +107,7 @@ async def test_should_deny_tool_operations_when_handler_explicitly_denies_after_ self, ctx: E2ETestContext ): """Test that tool operations are denied after resume when handler explicitly denies""" - session1 = await ctx.client.create_session( - {"on_permission_request": PermissionHandler.approve_all} - ) + session1 = await ctx.client.create_session(PermissionHandler.approve_all) session_id = session1.session_id await session1.send_and_wait({"prompt": "What is 1+1?"}) @@ -145,9 +143,7 @@ def on_event(event): async def test_should_work_with_approve_all_permission_handler(self, ctx: E2ETestContext): """Test that sessions work with approve-all permission handler""" - session = await ctx.client.create_session( - {"on_permission_request": PermissionHandler.approve_all} - ) + session = await ctx.client.create_session(PermissionHandler.approve_all) message = await session.send_and_wait({"prompt": "What is 2+2?"}) @@ -168,7 +164,7 @@ async def on_permission_request( await asyncio.sleep(0.01) return {"kind": "approved"} - session = await ctx.client.create_session({"on_permission_request": on_permission_request}) + session = await ctx.client.create_session(on_permission_request) await session.send_and_wait({"prompt": "Run 'echo test' and tell me what happens"}) @@ -181,9 +177,7 @@ async def test_should_resume_session_with_permission_handler(self, ctx: E2ETestC permission_requests = [] # Create initial session - session1 = await ctx.client.create_session( - {"on_permission_request": PermissionHandler.approve_all} - ) + session1 = await ctx.client.create_session(PermissionHandler.approve_all) session_id = session1.session_id await session1.send_and_wait({"prompt": "What is 1+1?"}) @@ -213,7 +207,7 @@ def on_permission_request( ) -> PermissionRequestResult: raise RuntimeError("Handler error") - session = await ctx.client.create_session({"on_permission_request": on_permission_request}) + session = await ctx.client.create_session(on_permission_request) message = await session.send_and_wait( {"prompt": "Run 'echo test'. If you can't, say 'failed'."} @@ -240,7 +234,7 @@ def on_permission_request( assert len(request["toolCallId"]) > 0 return {"kind": "approved"} - session = await ctx.client.create_session({"on_permission_request": on_permission_request}) + session = await ctx.client.create_session(on_permission_request) await session.send_and_wait({"prompt": "Run 'echo test'"}) diff --git a/python/e2e/test_rpc.py b/python/e2e/test_rpc.py index 240cd3730..96101759c 100644 --- a/python/e2e/test_rpc.py +++ b/python/e2e/test_rpc.py @@ -78,7 +78,7 @@ class TestSessionRpc: async def test_should_call_session_rpc_model_get_current(self, ctx: E2ETestContext): """Test calling session.rpc.model.getCurrent""" session = await ctx.client.create_session( - {"model": "claude-sonnet-4.5", "on_permission_request": PermissionHandler.approve_all} + PermissionHandler.approve_all, "claude-sonnet-4.5" ) result = await session.rpc.model.get_current() @@ -92,7 +92,7 @@ async def test_should_call_session_rpc_model_switch_to(self, ctx: E2ETestContext from copilot.generated.rpc import SessionModelSwitchToParams session = await ctx.client.create_session( - {"model": "claude-sonnet-4.5", "on_permission_request": PermissionHandler.approve_all} + PermissionHandler.approve_all, "claude-sonnet-4.5" ) # Get initial model @@ -116,9 +116,7 @@ async def test_get_and_set_session_mode(self): try: await client.start() - session = await client.create_session( - {"on_permission_request": PermissionHandler.approve_all} - ) + session = await client.create_session(PermissionHandler.approve_all) # Get initial mode (default should be interactive) initial = await session.rpc.mode.get() @@ -152,9 +150,7 @@ async def test_read_update_and_delete_plan(self): try: await client.start() - session = await client.create_session( - {"on_permission_request": PermissionHandler.approve_all} - ) + session = await client.create_session(PermissionHandler.approve_all) # Initially plan should not exist initial = await session.rpc.plan.read() @@ -195,9 +191,7 @@ async def test_create_list_and_read_workspace_files(self): try: await client.start() - session = await client.create_session( - {"on_permission_request": PermissionHandler.approve_all} - ) + session = await client.create_session(PermissionHandler.approve_all) # Initially no files initial_files = await session.rpc.workspace.list_files() diff --git a/python/e2e/test_session.py b/python/e2e/test_session.py index 4842d7829..438a5abca 100644 --- a/python/e2e/test_session.py +++ b/python/e2e/test_session.py @@ -14,9 +14,7 @@ class TestSessions: async def test_should_create_and_destroy_sessions(self, ctx: E2ETestContext): - session = await ctx.client.create_session( - {"model": "fake-test-model", "on_permission_request": PermissionHandler.approve_all} - ) + session = await ctx.client.create_session(PermissionHandler.approve_all, "fake-test-model") assert session.session_id messages = await session.get_messages() @@ -31,9 +29,7 @@ async def test_should_create_and_destroy_sessions(self, ctx: E2ETestContext): await session.get_messages() async def test_should_have_stateful_conversation(self, ctx: E2ETestContext): - session = await ctx.client.create_session( - {"on_permission_request": PermissionHandler.approve_all} - ) + session = await ctx.client.create_session(PermissionHandler.approve_all) assistant_message = await session.send_and_wait({"prompt": "What is 1+1?"}) assert assistant_message is not None @@ -50,10 +46,8 @@ async def test_should_create_a_session_with_appended_systemMessage_config( ): system_message_suffix = "End each response with the phrase 'Have a nice day!'" session = await ctx.client.create_session( - { - "system_message": {"mode": "append", "content": system_message_suffix}, - "on_permission_request": PermissionHandler.approve_all, - } + PermissionHandler.approve_all, + system_message={"mode": "append", "content": system_message_suffix}, ) await session.send({"prompt": "What is your full name?"}) @@ -72,10 +66,8 @@ async def test_should_create_a_session_with_replaced_systemMessage_config( ): test_system_message = "You are an assistant called Testy McTestface. Reply succinctly." session = await ctx.client.create_session( - { - "system_message": {"mode": "replace", "content": test_system_message}, - "on_permission_request": PermissionHandler.approve_all, - } + PermissionHandler.approve_all, + system_message={"mode": "replace", "content": test_system_message}, ) await session.send({"prompt": "What is your full name?"}) @@ -90,10 +82,8 @@ async def test_should_create_a_session_with_replaced_systemMessage_config( async def test_should_create_a_session_with_availableTools(self, ctx: E2ETestContext): session = await ctx.client.create_session( - { - "available_tools": ["view", "edit"], - "on_permission_request": PermissionHandler.approve_all, - } + PermissionHandler.approve_all, + available_tools=["view", "edit"], ) await session.send({"prompt": "What is 1+1?"}) @@ -109,7 +99,7 @@ async def test_should_create_a_session_with_availableTools(self, ctx: E2ETestCon async def test_should_create_a_session_with_excludedTools(self, ctx: E2ETestContext): session = await ctx.client.create_session( - {"excluded_tools": ["view"], "on_permission_request": PermissionHandler.approve_all} + PermissionHandler.approve_all, excluded_tools=["view"] ) await session.send({"prompt": "What is 1+1?"}) @@ -132,9 +122,9 @@ async def test_should_handle_multiple_concurrent_sessions(self, ctx: E2ETestCont import asyncio s1, s2, s3 = await asyncio.gather( - ctx.client.create_session({"on_permission_request": PermissionHandler.approve_all}), - ctx.client.create_session({"on_permission_request": PermissionHandler.approve_all}), - ctx.client.create_session({"on_permission_request": PermissionHandler.approve_all}), + ctx.client.create_session(PermissionHandler.approve_all), + ctx.client.create_session(PermissionHandler.approve_all), + ctx.client.create_session(PermissionHandler.approve_all), ) # All sessions should have unique IDs @@ -156,9 +146,7 @@ async def test_should_handle_multiple_concurrent_sessions(self, ctx: E2ETestCont async def test_should_resume_a_session_using_the_same_client(self, ctx: E2ETestContext): # Create initial session - session1 = await ctx.client.create_session( - {"on_permission_request": PermissionHandler.approve_all} - ) + session1 = await ctx.client.create_session(PermissionHandler.approve_all) session_id = session1.session_id answer = await session1.send_and_wait({"prompt": "What is 1+1?"}) assert answer is not None @@ -174,9 +162,7 @@ async def test_should_resume_a_session_using_the_same_client(self, ctx: E2ETestC async def test_should_resume_a_session_using_a_new_client(self, ctx: E2ETestContext): # Create initial session - session1 = await ctx.client.create_session( - {"on_permission_request": PermissionHandler.approve_all} - ) + session1 = await ctx.client.create_session(PermissionHandler.approve_all) session_id = session1.session_id answer = await session1.send_and_wait({"prompt": "What is 1+1?"}) assert answer is not None @@ -219,13 +205,9 @@ async def test_should_list_sessions(self, ctx: E2ETestContext): import asyncio # Create a couple of sessions and send messages to persist them - session1 = await ctx.client.create_session( - {"on_permission_request": PermissionHandler.approve_all} - ) + session1 = await ctx.client.create_session(PermissionHandler.approve_all) await session1.send_and_wait({"prompt": "Say hello"}) - session2 = await ctx.client.create_session( - {"on_permission_request": PermissionHandler.approve_all} - ) + session2 = await ctx.client.create_session(PermissionHandler.approve_all) await session2.send_and_wait({"prompt": "Say goodbye"}) # Small delay to ensure session files are written to disk @@ -262,9 +244,7 @@ async def test_should_delete_session(self, ctx: E2ETestContext): import asyncio # Create a session and send a message to persist it - session = await ctx.client.create_session( - {"on_permission_request": PermissionHandler.approve_all} - ) + session = await ctx.client.create_session(PermissionHandler.approve_all) await session.send_and_wait({"prompt": "Hello"}) session_id = session.session_id @@ -300,21 +280,19 @@ def get_secret_number_handler(invocation): } session = await ctx.client.create_session( - { - "tools": [ - Tool( - name="get_secret_number", - description="Gets the secret number", - handler=get_secret_number_handler, - parameters={ - "type": "object", - "properties": {"key": {"type": "string", "description": "Key"}}, - "required": ["key"], - }, - ) - ], - "on_permission_request": PermissionHandler.approve_all, - } + PermissionHandler.approve_all, + tools=[ + Tool( + name="get_secret_number", + description="Gets the secret number", + handler=get_secret_number_handler, + parameters={ + "type": "object", + "properties": {"key": {"type": "string", "description": "Key"}}, + "required": ["key"], + }, + ) + ], ) answer = await session.send_and_wait({"prompt": "What is the secret number for key ALPHA?"}) @@ -323,37 +301,31 @@ def get_secret_number_handler(invocation): async def test_should_create_session_with_custom_provider(self, ctx: E2ETestContext): session = await ctx.client.create_session( - { - "provider": { - "type": "openai", - "base_url": "https://api.openai.com/v1", - "api_key": "fake-key", - }, - "on_permission_request": PermissionHandler.approve_all, - } + PermissionHandler.approve_all, + provider={ + "type": "openai", + "base_url": "https://api.openai.com/v1", + "api_key": "fake-key", + }, ) assert session.session_id async def test_should_create_session_with_azure_provider(self, ctx: E2ETestContext): session = await ctx.client.create_session( - { - "provider": { - "type": "azure", - "base_url": "https://my-resource.openai.azure.com", - "api_key": "fake-key", - "azure": { - "api_version": "2024-02-15-preview", - }, + PermissionHandler.approve_all, + provider={ + "type": "azure", + "base_url": "https://my-resource.openai.azure.com", + "api_key": "fake-key", + "azure": { + "api_version": "2024-02-15-preview", }, - "on_permission_request": PermissionHandler.approve_all, - } + }, ) assert session.session_id async def test_should_resume_session_with_custom_provider(self, ctx: E2ETestContext): - session = await ctx.client.create_session( - {"on_permission_request": PermissionHandler.approve_all} - ) + session = await ctx.client.create_session(PermissionHandler.approve_all) session_id = session.session_id # Resume the session with a provider @@ -374,9 +346,7 @@ async def test_should_resume_session_with_custom_provider(self, ctx: E2ETestCont async def test_should_abort_a_session(self, ctx: E2ETestContext): import asyncio - session = await ctx.client.create_session( - {"on_permission_request": PermissionHandler.approve_all} - ) + session = await ctx.client.create_session(PermissionHandler.approve_all) # Set up event listeners BEFORE sending to avoid race conditions wait_for_tool_start = asyncio.create_task( @@ -422,9 +392,7 @@ async def test_should_receive_streaming_delta_events_when_streaming_is_enabled( ): import asyncio - session = await ctx.client.create_session( - {"streaming": True, "on_permission_request": PermissionHandler.approve_all} - ) + session = await ctx.client.create_session(PermissionHandler.approve_all, streaming=True) delta_contents = [] done_event = asyncio.Event() @@ -465,9 +433,7 @@ def on_event(event): async def test_should_pass_streaming_option_to_session_creation(self, ctx: E2ETestContext): # Verify that the streaming option is accepted without errors - session = await ctx.client.create_session( - {"streaming": True, "on_permission_request": PermissionHandler.approve_all} - ) + session = await ctx.client.create_session(PermissionHandler.approve_all, streaming=True) assert session.session_id @@ -479,9 +445,7 @@ async def test_should_pass_streaming_option_to_session_creation(self, ctx: E2ETe async def test_should_receive_session_events(self, ctx: E2ETestContext): import asyncio - session = await ctx.client.create_session( - {"on_permission_request": PermissionHandler.approve_all} - ) + session = await ctx.client.create_session(PermissionHandler.approve_all) received_events = [] idle_event = asyncio.Event() @@ -517,10 +481,7 @@ async def test_should_create_session_with_custom_config_dir(self, ctx: E2ETestCo custom_config_dir = os.path.join(ctx.home_dir, "custom-config") session = await ctx.client.create_session( - { - "config_dir": custom_config_dir, - "on_permission_request": PermissionHandler.approve_all, - } + PermissionHandler.approve_all, config_dir=custom_config_dir ) assert session.session_id diff --git a/python/e2e/test_skills.py b/python/e2e/test_skills.py index 10d32695c..3eaa1cda4 100644 --- a/python/e2e/test_skills.py +++ b/python/e2e/test_skills.py @@ -56,10 +56,7 @@ async def test_should_load_and_apply_skill_from_skilldirectories(self, ctx: E2ET """Test that skills are loaded and applied from skillDirectories""" skills_dir = create_skill_dir(ctx.work_dir) session = await ctx.client.create_session( - { - "skill_directories": [skills_dir], - "on_permission_request": PermissionHandler.approve_all, - } + PermissionHandler.approve_all, skill_directories=[skills_dir] ) assert session.session_id is not None @@ -77,11 +74,9 @@ async def test_should_not_apply_skill_when_disabled_via_disabledskills( """Test that disabledSkills prevents skill from being applied""" skills_dir = create_skill_dir(ctx.work_dir) session = await ctx.client.create_session( - { - "skill_directories": [skills_dir], - "disabled_skills": ["test-skill"], - "on_permission_request": PermissionHandler.approve_all, - } + PermissionHandler.approve_all, + skill_directories=[skills_dir], + disabled_skills=["test-skill"], ) assert session.session_id is not None @@ -104,9 +99,7 @@ async def test_should_apply_skill_on_session_resume_with_skilldirectories( skills_dir = create_skill_dir(ctx.work_dir) # Create a session without skills first - session1 = await ctx.client.create_session( - {"on_permission_request": PermissionHandler.approve_all} - ) + session1 = await ctx.client.create_session(PermissionHandler.approve_all) session_id = session1.session_id # First message without skill - marker should not appear diff --git a/python/e2e/test_tools.py b/python/e2e/test_tools.py index e4a9f5f06..81b0533f3 100644 --- a/python/e2e/test_tools.py +++ b/python/e2e/test_tools.py @@ -18,9 +18,7 @@ async def test_invokes_built_in_tools(self, ctx: E2ETestContext): with open(readme_path, "w") as f: f.write("# ELIZA, the only chatbot you'll ever need") - session = await ctx.client.create_session( - {"on_permission_request": PermissionHandler.approve_all} - ) + session = await ctx.client.create_session(PermissionHandler.approve_all) await session.send({"prompt": "What's the first line of README.md in this directory?"}) assistant_message = await get_final_assistant_message(session) @@ -35,7 +33,7 @@ def encrypt_string(params: EncryptParams, invocation: ToolInvocation) -> str: return params.input.upper() session = await ctx.client.create_session( - {"tools": [encrypt_string], "on_permission_request": PermissionHandler.approve_all} + PermissionHandler.approve_all, tools=[encrypt_string] ) await session.send({"prompt": "Use encrypt_string to encrypt this string: Hello"}) @@ -48,7 +46,7 @@ def get_user_location() -> str: raise Exception("Melbourne") session = await ctx.client.create_session( - {"tools": [get_user_location], "on_permission_request": PermissionHandler.approve_all} + PermissionHandler.approve_all, tools=[get_user_location] ) await session.send( @@ -112,9 +110,7 @@ def db_query(params: DbQueryParams, invocation: ToolInvocation) -> list[City]: City(countryId=12, cityName="San Lorenzo", population=204356), ] - session = await ctx.client.create_session( - {"tools": [db_query], "on_permission_request": PermissionHandler.approve_all} - ) + session = await ctx.client.create_session(PermissionHandler.approve_all, tools=[db_query]) expected_session_id = session.session_id await session.send( @@ -147,12 +143,7 @@ def on_permission_request(request, invocation): permission_requests.append(request) return {"kind": "approved"} - session = await ctx.client.create_session( - { - "tools": [encrypt_string], - "on_permission_request": on_permission_request, - } - ) + session = await ctx.client.create_session(on_permission_request, tools=[encrypt_string]) await session.send({"prompt": "Use encrypt_string to encrypt this string: Hello"}) assistant_message = await get_final_assistant_message(session) @@ -178,12 +169,7 @@ def encrypt_string(params: EncryptParams, invocation: ToolInvocation) -> str: def on_permission_request(request, invocation): return {"kind": "denied-interactively-by-user"} - session = await ctx.client.create_session( - { - "tools": [encrypt_string], - "on_permission_request": on_permission_request, - } - ) + session = await ctx.client.create_session(on_permission_request, tools=[encrypt_string]) await session.send({"prompt": "Use encrypt_string to encrypt this string: Hello"}) await get_final_assistant_message(session) diff --git a/python/samples/chat.py b/python/samples/chat.py index eb781e4e2..c04b148dd 100644 --- a/python/samples/chat.py +++ b/python/samples/chat.py @@ -9,11 +9,7 @@ async def main(): client = CopilotClient() await client.start() - session = await client.create_session( - { - "on_permission_request": PermissionHandler.approve_all, - } - ) + session = await client.create_session(PermissionHandler.approve_all) def on_event(event): output = None diff --git a/python/test_client.py b/python/test_client.py index c6ad027f5..a7e61ca03 100644 --- a/python/test_client.py +++ b/python/test_client.py @@ -16,8 +16,18 @@ async def test_create_session_raises_without_permission_handler(self): client = CopilotClient({"cli_path": CLI_PATH}) await client.start() try: - with pytest.raises(ValueError, match="on_permission_request.*is required"): - await client.create_session({}) + with pytest.raises(TypeError, match="on_permission_request"): + await client.create_session() # type: ignore[call-arg] + finally: + await client.force_stop() + + @pytest.mark.asyncio + async def test_create_session_raises_with_none_permission_handler(self): + client = CopilotClient({"cli_path": CLI_PATH}) + await client.start() + try: + with pytest.raises(ValueError, match="on_permission_request handler is required"): + await client.create_session(None) # type: ignore[arg-type] finally: await client.force_stop() @@ -26,9 +36,7 @@ async def test_resume_session_raises_without_permission_handler(self): client = CopilotClient({"cli_path": CLI_PATH}) await client.start() try: - session = await client.create_session( - {"on_permission_request": PermissionHandler.approve_all} - ) + session = await client.create_session(PermissionHandler.approve_all) with pytest.raises(ValueError, match="on_permission_request.*is required"): await client.resume_session(session.session_id, {}) finally: @@ -42,9 +50,7 @@ async def test_returns_failure_when_tool_not_registered(self): await client.start() try: - session = await client.create_session( - {"on_permission_request": PermissionHandler.approve_all} - ) + session = await client.create_session(PermissionHandler.approve_all) response = await client._handle_tool_call_request( { @@ -191,9 +197,7 @@ async def mock_request(method, params): return await original_request(method, params) client._client.request = mock_request - await client.create_session( - {"client_name": "my-app", "on_permission_request": PermissionHandler.approve_all} - ) + await client.create_session(PermissionHandler.approve_all, client_name="my-app") assert captured["session.create"]["clientName"] == "my-app" finally: await client.force_stop() @@ -204,9 +208,7 @@ async def test_resume_session_forwards_client_name(self): await client.start() try: - session = await client.create_session( - {"on_permission_request": PermissionHandler.approve_all} - ) + session = await client.create_session(PermissionHandler.approve_all) captured = {} original_request = client._client.request diff --git a/test/scenarios/auth/byok-anthropic/python/main.py b/test/scenarios/auth/byok-anthropic/python/main.py index 7f5e5834c..34e3be00a 100644 --- a/test/scenarios/auth/byok-anthropic/python/main.py +++ b/test/scenarios/auth/byok-anthropic/python/main.py @@ -1,7 +1,7 @@ import asyncio import os import sys -from copilot import CopilotClient +from copilot import CopilotClient, PermissionHandler ANTHROPIC_API_KEY = os.environ.get("ANTHROPIC_API_KEY") ANTHROPIC_MODEL = os.environ.get("ANTHROPIC_MODEL", "claude-sonnet-4-20250514") @@ -19,19 +19,20 @@ async def main(): client = CopilotClient(opts) try: - session = await client.create_session({ - "model": ANTHROPIC_MODEL, - "provider": { + session = await client.create_session( + PermissionHandler.approve_all, + ANTHROPIC_MODEL, + provider={ "type": "anthropic", "base_url": ANTHROPIC_BASE_URL, "api_key": ANTHROPIC_API_KEY, }, - "available_tools": [], - "system_message": { + available_tools=[], + system_message={ "mode": "replace", "content": "You are a helpful assistant. Answer concisely.", }, - }) + ) response = await session.send_and_wait( {"prompt": "What is the capital of France?"} diff --git a/test/scenarios/auth/byok-azure/python/main.py b/test/scenarios/auth/byok-azure/python/main.py index 5376cac28..bcf8f74c4 100644 --- a/test/scenarios/auth/byok-azure/python/main.py +++ b/test/scenarios/auth/byok-azure/python/main.py @@ -1,7 +1,7 @@ import asyncio import os import sys -from copilot import CopilotClient +from copilot import CopilotClient, PermissionHandler AZURE_OPENAI_ENDPOINT = os.environ.get("AZURE_OPENAI_ENDPOINT") AZURE_OPENAI_API_KEY = os.environ.get("AZURE_OPENAI_API_KEY") @@ -20,9 +20,10 @@ async def main(): client = CopilotClient(opts) try: - session = await client.create_session({ - "model": AZURE_OPENAI_MODEL, - "provider": { + session = await client.create_session( + PermissionHandler.approve_all, + AZURE_OPENAI_MODEL, + provider={ "type": "azure", "base_url": AZURE_OPENAI_ENDPOINT, "api_key": AZURE_OPENAI_API_KEY, @@ -30,12 +31,12 @@ async def main(): "api_version": AZURE_API_VERSION, }, }, - "available_tools": [], - "system_message": { + available_tools=[], + system_message={ "mode": "replace", "content": "You are a helpful assistant. Answer concisely.", }, - }) + ) response = await session.send_and_wait( {"prompt": "What is the capital of France?"} diff --git a/test/scenarios/auth/byok-ollama/python/main.py b/test/scenarios/auth/byok-ollama/python/main.py index 0f9df7f54..8b1d33131 100644 --- a/test/scenarios/auth/byok-ollama/python/main.py +++ b/test/scenarios/auth/byok-ollama/python/main.py @@ -1,7 +1,7 @@ import asyncio import os import sys -from copilot import CopilotClient +from copilot import CopilotClient, PermissionHandler OLLAMA_BASE_URL = os.environ.get("OLLAMA_BASE_URL", "http://localhost:11434/v1") OLLAMA_MODEL = os.environ.get("OLLAMA_MODEL", "llama3.2:3b") @@ -18,18 +18,19 @@ async def main(): client = CopilotClient(opts) try: - session = await client.create_session({ - "model": OLLAMA_MODEL, - "provider": { + session = await client.create_session( + PermissionHandler.approve_all, + OLLAMA_MODEL, + provider={ "type": "openai", "base_url": OLLAMA_BASE_URL, }, - "available_tools": [], - "system_message": { + available_tools=[], + system_message={ "mode": "replace", "content": COMPACT_SYSTEM_PROMPT, }, - }) + ) response = await session.send_and_wait( {"prompt": "What is the capital of France?"} diff --git a/test/scenarios/auth/byok-openai/python/main.py b/test/scenarios/auth/byok-openai/python/main.py index 651a92cd6..961257689 100644 --- a/test/scenarios/auth/byok-openai/python/main.py +++ b/test/scenarios/auth/byok-openai/python/main.py @@ -1,7 +1,7 @@ import asyncio import os import sys -from copilot import CopilotClient +from copilot import CopilotClient, PermissionHandler OPENAI_BASE_URL = os.environ.get("OPENAI_BASE_URL", "https://api.openai.com/v1") OPENAI_MODEL = os.environ.get("OPENAI_MODEL", "claude-haiku-4.5") @@ -19,14 +19,15 @@ async def main(): client = CopilotClient(opts) try: - session = await client.create_session({ - "model": OPENAI_MODEL, - "provider": { + session = await client.create_session( + PermissionHandler.approve_all, + OPENAI_MODEL, + provider={ "type": "openai", "base_url": OPENAI_BASE_URL, "api_key": OPENAI_API_KEY, }, - }) + ) response = await session.send_and_wait( {"prompt": "What is the capital of France?"} diff --git a/test/scenarios/auth/gh-app/python/main.py b/test/scenarios/auth/gh-app/python/main.py index 4568c82b2..ee47d53a0 100644 --- a/test/scenarios/auth/gh-app/python/main.py +++ b/test/scenarios/auth/gh-app/python/main.py @@ -4,7 +4,7 @@ import time import urllib.request -from copilot import CopilotClient +from copilot import CopilotClient, PermissionHandler DEVICE_CODE_URL = "https://github.com/login/device/code" @@ -84,7 +84,7 @@ async def main(): client = CopilotClient(opts) try: - session = await client.create_session({"model": "claude-haiku-4.5"}) + session = await client.create_session(PermissionHandler.approve_all, "claude-haiku-4.5") response = await session.send_and_wait({"prompt": "What is the capital of France?"}) if response: print(response.data.content) diff --git a/test/scenarios/bundling/app-backend-to-server/python/main.py b/test/scenarios/bundling/app-backend-to-server/python/main.py index 218505f4a..34283a17c 100644 --- a/test/scenarios/bundling/app-backend-to-server/python/main.py +++ b/test/scenarios/bundling/app-backend-to-server/python/main.py @@ -5,7 +5,7 @@ import urllib.request from flask import Flask, request, jsonify -from copilot import CopilotClient +from copilot import CopilotClient, PermissionHandler app = Flask(__name__) @@ -16,7 +16,7 @@ async def ask_copilot(prompt: str) -> str: client = CopilotClient({"cli_url": CLI_URL}) try: - session = await client.create_session({"model": "claude-haiku-4.5"}) + session = await client.create_session(PermissionHandler.approve_all, "claude-haiku-4.5") response = await session.send_and_wait({"prompt": prompt}) diff --git a/test/scenarios/bundling/app-direct-server/python/main.py b/test/scenarios/bundling/app-direct-server/python/main.py index 05aaa9270..a47c926e9 100644 --- a/test/scenarios/bundling/app-direct-server/python/main.py +++ b/test/scenarios/bundling/app-direct-server/python/main.py @@ -1,6 +1,6 @@ import asyncio import os -from copilot import CopilotClient +from copilot import CopilotClient, PermissionHandler async def main(): @@ -9,7 +9,7 @@ async def main(): }) try: - session = await client.create_session({"model": "claude-haiku-4.5"}) + session = await client.create_session(PermissionHandler.approve_all, "claude-haiku-4.5") response = await session.send_and_wait( {"prompt": "What is the capital of France?"} diff --git a/test/scenarios/bundling/container-proxy/python/main.py b/test/scenarios/bundling/container-proxy/python/main.py index 05aaa9270..a47c926e9 100644 --- a/test/scenarios/bundling/container-proxy/python/main.py +++ b/test/scenarios/bundling/container-proxy/python/main.py @@ -1,6 +1,6 @@ import asyncio import os -from copilot import CopilotClient +from copilot import CopilotClient, PermissionHandler async def main(): @@ -9,7 +9,7 @@ async def main(): }) try: - session = await client.create_session({"model": "claude-haiku-4.5"}) + session = await client.create_session(PermissionHandler.approve_all, "claude-haiku-4.5") response = await session.send_and_wait( {"prompt": "What is the capital of France?"} diff --git a/test/scenarios/bundling/fully-bundled/python/main.py b/test/scenarios/bundling/fully-bundled/python/main.py index 138bb5646..cfe12901f 100644 --- a/test/scenarios/bundling/fully-bundled/python/main.py +++ b/test/scenarios/bundling/fully-bundled/python/main.py @@ -1,6 +1,6 @@ import asyncio import os -from copilot import CopilotClient +from copilot import CopilotClient, PermissionHandler async def main(): @@ -10,7 +10,7 @@ async def main(): client = CopilotClient(opts) try: - session = await client.create_session({"model": "claude-haiku-4.5"}) + session = await client.create_session(PermissionHandler.approve_all, "claude-haiku-4.5") response = await session.send_and_wait( {"prompt": "What is the capital of France?"} diff --git a/test/scenarios/callbacks/hooks/python/main.py b/test/scenarios/callbacks/hooks/python/main.py index a00c18af7..abbef404e 100644 --- a/test/scenarios/callbacks/hooks/python/main.py +++ b/test/scenarios/callbacks/hooks/python/main.py @@ -1,6 +1,6 @@ import asyncio import os -from copilot import CopilotClient +from copilot import CopilotClient, PermissionHandler hook_log: list[str] = [] @@ -47,18 +47,16 @@ async def main(): try: session = await client.create_session( - { - "model": "claude-haiku-4.5", - "on_permission_request": auto_approve_permission, - "hooks": { - "on_session_start": on_session_start, - "on_session_end": on_session_end, - "on_pre_tool_use": on_pre_tool_use, - "on_post_tool_use": on_post_tool_use, - "on_user_prompt_submitted": on_user_prompt_submitted, - "on_error_occurred": on_error_occurred, - }, - } + auto_approve_permission, + "claude-haiku-4.5", + hooks={ + "on_session_start": on_session_start, + "on_session_end": on_session_end, + "on_pre_tool_use": on_pre_tool_use, + "on_post_tool_use": on_post_tool_use, + "on_user_prompt_submitted": on_user_prompt_submitted, + "on_error_occurred": on_error_occurred, + }, ) response = await session.send_and_wait( diff --git a/test/scenarios/callbacks/permissions/python/main.py b/test/scenarios/callbacks/permissions/python/main.py index 2da5133fa..0827c8e17 100644 --- a/test/scenarios/callbacks/permissions/python/main.py +++ b/test/scenarios/callbacks/permissions/python/main.py @@ -1,6 +1,6 @@ import asyncio import os -from copilot import CopilotClient +from copilot import CopilotClient, PermissionHandler # Track which tools requested permission permission_log: list[str] = [] @@ -23,11 +23,9 @@ async def main(): try: session = await client.create_session( - { - "model": "claude-haiku-4.5", - "on_permission_request": log_permission, - "hooks": {"on_pre_tool_use": auto_approve_tool}, - } + log_permission, + "claude-haiku-4.5", + hooks={"on_pre_tool_use": auto_approve_tool}, ) response = await session.send_and_wait( diff --git a/test/scenarios/callbacks/user-input/python/main.py b/test/scenarios/callbacks/user-input/python/main.py index fb36eda5c..cef027b20 100644 --- a/test/scenarios/callbacks/user-input/python/main.py +++ b/test/scenarios/callbacks/user-input/python/main.py @@ -1,6 +1,6 @@ import asyncio import os -from copilot import CopilotClient +from copilot import CopilotClient, PermissionHandler input_log: list[str] = [] @@ -27,12 +27,10 @@ async def main(): try: session = await client.create_session( - { - "model": "claude-haiku-4.5", - "on_permission_request": auto_approve_permission, - "on_user_input_request": handle_user_input, - "hooks": {"on_pre_tool_use": auto_approve_tool}, - } + auto_approve_permission, + "claude-haiku-4.5", + on_user_input_request=handle_user_input, + hooks={"on_pre_tool_use": auto_approve_tool}, ) response = await session.send_and_wait( diff --git a/test/scenarios/modes/default/python/main.py b/test/scenarios/modes/default/python/main.py index 0abc6b709..4f708957b 100644 --- a/test/scenarios/modes/default/python/main.py +++ b/test/scenarios/modes/default/python/main.py @@ -1,6 +1,6 @@ import asyncio import os -from copilot import CopilotClient +from copilot import CopilotClient, PermissionHandler async def main(): @@ -10,9 +10,7 @@ async def main(): client = CopilotClient(opts) try: - session = await client.create_session({ - "model": "claude-haiku-4.5", - }) + session = await client.create_session(PermissionHandler.approve_all, "claude-haiku-4.5") response = await session.send_and_wait({"prompt": "Use the grep tool to search for the word 'SDK' in README.md and show the matching lines."}) if response: diff --git a/test/scenarios/modes/minimal/python/main.py b/test/scenarios/modes/minimal/python/main.py index 74a98ba0e..73d2d7fc7 100644 --- a/test/scenarios/modes/minimal/python/main.py +++ b/test/scenarios/modes/minimal/python/main.py @@ -1,6 +1,6 @@ import asyncio import os -from copilot import CopilotClient +from copilot import CopilotClient, PermissionHandler async def main(): @@ -10,14 +10,15 @@ async def main(): client = CopilotClient(opts) try: - session = await client.create_session({ - "model": "claude-haiku-4.5", - "available_tools": [], - "system_message": { + session = await client.create_session( + PermissionHandler.approve_all, + "claude-haiku-4.5", + available_tools=[], + system_message={ "mode": "replace", "content": "You have no tools. Respond with text only.", }, - }) + ) response = await session.send_and_wait({"prompt": "Use the grep tool to search for 'SDK' in README.md."}) if response: diff --git a/test/scenarios/prompts/attachments/python/main.py b/test/scenarios/prompts/attachments/python/main.py index acf9c7af1..048eddaf6 100644 --- a/test/scenarios/prompts/attachments/python/main.py +++ b/test/scenarios/prompts/attachments/python/main.py @@ -1,6 +1,6 @@ import asyncio import os -from copilot import CopilotClient +from copilot import CopilotClient, PermissionHandler SYSTEM_PROMPT = """You are a helpful assistant. Answer questions about attached files concisely.""" @@ -13,11 +13,10 @@ async def main(): try: session = await client.create_session( - { - "model": "claude-haiku-4.5", - "system_message": {"mode": "replace", "content": SYSTEM_PROMPT}, - "available_tools": [], - } + PermissionHandler.approve_all, + "claude-haiku-4.5", + system_message={"mode": "replace", "content": SYSTEM_PROMPT}, + available_tools=[], ) sample_file = os.path.join(os.path.dirname(__file__), "..", "sample-data.txt") diff --git a/test/scenarios/prompts/reasoning-effort/python/main.py b/test/scenarios/prompts/reasoning-effort/python/main.py index 74444e7bf..45095ae29 100644 --- a/test/scenarios/prompts/reasoning-effort/python/main.py +++ b/test/scenarios/prompts/reasoning-effort/python/main.py @@ -1,6 +1,6 @@ import asyncio import os -from copilot import CopilotClient +from copilot import CopilotClient, PermissionHandler async def main(): @@ -10,15 +10,16 @@ async def main(): client = CopilotClient(opts) try: - session = await client.create_session({ - "model": "claude-opus-4.6", - "reasoning_effort": "low", - "available_tools": [], - "system_message": { + session = await client.create_session( + PermissionHandler.approve_all, + "claude-opus-4.6", + reasoning_effort="low", + available_tools=[], + system_message={ "mode": "replace", "content": "You are a helpful assistant. Answer concisely.", }, - }) + ) response = await session.send_and_wait( {"prompt": "What is the capital of France?"} diff --git a/test/scenarios/prompts/system-message/python/main.py b/test/scenarios/prompts/system-message/python/main.py index a3bfccdcf..90df1729b 100644 --- a/test/scenarios/prompts/system-message/python/main.py +++ b/test/scenarios/prompts/system-message/python/main.py @@ -1,6 +1,6 @@ import asyncio import os -from copilot import CopilotClient +from copilot import CopilotClient, PermissionHandler PIRATE_PROMPT = """You are a pirate. Always respond in pirate speak. Say 'Arrr!' in every response. Use nautical terms and pirate slang throughout.""" @@ -13,11 +13,10 @@ async def main(): try: session = await client.create_session( - { - "model": "claude-haiku-4.5", - "system_message": {"mode": "replace", "content": PIRATE_PROMPT}, - "available_tools": [], - } + PermissionHandler.approve_all, + "claude-haiku-4.5", + system_message={"mode": "replace", "content": PIRATE_PROMPT}, + available_tools=[], ) response = await session.send_and_wait( diff --git a/test/scenarios/sessions/concurrent-sessions/python/main.py b/test/scenarios/sessions/concurrent-sessions/python/main.py index 171a202e4..52446baf9 100644 --- a/test/scenarios/sessions/concurrent-sessions/python/main.py +++ b/test/scenarios/sessions/concurrent-sessions/python/main.py @@ -1,6 +1,6 @@ import asyncio import os -from copilot import CopilotClient +from copilot import CopilotClient, PermissionHandler PIRATE_PROMPT = "You are a pirate. Always say Arrr!" ROBOT_PROMPT = "You are a robot. Always say BEEP BOOP!" @@ -15,18 +15,16 @@ async def main(): try: session1, session2 = await asyncio.gather( client.create_session( - { - "model": "claude-haiku-4.5", - "system_message": {"mode": "replace", "content": PIRATE_PROMPT}, - "available_tools": [], - } + PermissionHandler.approve_all, + "claude-haiku-4.5", + system_message={"mode": "replace", "content": PIRATE_PROMPT}, + available_tools=[], ), client.create_session( - { - "model": "claude-haiku-4.5", - "system_message": {"mode": "replace", "content": ROBOT_PROMPT}, - "available_tools": [], - } + PermissionHandler.approve_all, + "claude-haiku-4.5", + system_message={"mode": "replace", "content": ROBOT_PROMPT}, + available_tools=[], ), ) diff --git a/test/scenarios/sessions/infinite-sessions/python/main.py b/test/scenarios/sessions/infinite-sessions/python/main.py index fe39a7117..13fbb1623 100644 --- a/test/scenarios/sessions/infinite-sessions/python/main.py +++ b/test/scenarios/sessions/infinite-sessions/python/main.py @@ -1,6 +1,6 @@ import asyncio import os -from copilot import CopilotClient +from copilot import CopilotClient, PermissionHandler async def main(): @@ -10,19 +10,20 @@ async def main(): client = CopilotClient(opts) try: - session = await client.create_session({ - "model": "claude-haiku-4.5", - "available_tools": [], - "system_message": { + session = await client.create_session( + PermissionHandler.approve_all, + "claude-haiku-4.5", + available_tools=[], + system_message={ "mode": "replace", "content": "You are a helpful assistant. Answer concisely in one sentence.", }, - "infinite_sessions": { + infinite_sessions={ "enabled": True, "background_compaction_threshold": 0.80, "buffer_exhaustion_threshold": 0.95, }, - }) + ) prompts = [ "What is the capital of France?", diff --git a/test/scenarios/sessions/session-resume/python/main.py b/test/scenarios/sessions/session-resume/python/main.py index b65370b97..cb176b92c 100644 --- a/test/scenarios/sessions/session-resume/python/main.py +++ b/test/scenarios/sessions/session-resume/python/main.py @@ -1,6 +1,6 @@ import asyncio import os -from copilot import CopilotClient +from copilot import CopilotClient, PermissionHandler async def main(): @@ -12,10 +12,9 @@ async def main(): try: # 1. Create a session session = await client.create_session( - { - "model": "claude-haiku-4.5", - "available_tools": [], - } + PermissionHandler.approve_all, + "claude-haiku-4.5", + available_tools=[], ) # 2. Send the secret word diff --git a/test/scenarios/sessions/streaming/python/main.py b/test/scenarios/sessions/streaming/python/main.py index 2bbc94e78..bbe99ba23 100644 --- a/test/scenarios/sessions/streaming/python/main.py +++ b/test/scenarios/sessions/streaming/python/main.py @@ -1,6 +1,6 @@ import asyncio import os -from copilot import CopilotClient +from copilot import CopilotClient, PermissionHandler async def main(): @@ -11,10 +11,9 @@ async def main(): try: session = await client.create_session( - { - "model": "claude-haiku-4.5", - "streaming": True, - } + PermissionHandler.approve_all, + "claude-haiku-4.5", + streaming=True, ) chunk_count = 0 diff --git a/test/scenarios/tools/custom-agents/python/main.py b/test/scenarios/tools/custom-agents/python/main.py index d4e416716..bb1e8cc2d 100644 --- a/test/scenarios/tools/custom-agents/python/main.py +++ b/test/scenarios/tools/custom-agents/python/main.py @@ -1,6 +1,6 @@ import asyncio import os -from copilot import CopilotClient +from copilot import CopilotClient, PermissionHandler async def main(): @@ -11,18 +11,17 @@ async def main(): try: session = await client.create_session( - { - "model": "claude-haiku-4.5", - "custom_agents": [ - { - "name": "researcher", - "display_name": "Research Agent", - "description": "A research agent that can only read and search files, not modify them", - "tools": ["grep", "glob", "view"], - "prompt": "You are a research assistant. You can search and read files but cannot modify anything. When asked about your capabilities, list the tools you have access to.", - }, - ], - } + PermissionHandler.approve_all, + "claude-haiku-4.5", + custom_agents=[ + { + "name": "researcher", + "display_name": "Research Agent", + "description": "A research agent that can only read and search files, not modify them", + "tools": ["grep", "glob", "view"], + "prompt": "You are a research assistant. You can search and read files but cannot modify anything. When asked about your capabilities, list the tools you have access to.", + }, + ], ) response = await session.send_and_wait( diff --git a/test/scenarios/tools/mcp-servers/python/main.py b/test/scenarios/tools/mcp-servers/python/main.py index 81d2e39ba..ba7494839 100644 --- a/test/scenarios/tools/mcp-servers/python/main.py +++ b/test/scenarios/tools/mcp-servers/python/main.py @@ -1,6 +1,6 @@ import asyncio import os -from copilot import CopilotClient +from copilot import CopilotClient, PermissionHandler async def main(): @@ -22,8 +22,7 @@ async def main(): "args": args, } - session_config = { - "model": "claude-haiku-4.5", + session_kwargs = { "available_tools": [], "system_message": { "mode": "replace", @@ -31,9 +30,11 @@ async def main(): }, } if mcp_servers: - session_config["mcp_servers"] = mcp_servers + session_kwargs["mcp_servers"] = mcp_servers - session = await client.create_session(session_config) + session = await client.create_session( + PermissionHandler.approve_all, "claude-haiku-4.5", **session_kwargs + ) response = await session.send_and_wait( {"prompt": "What is the capital of France?"} diff --git a/test/scenarios/tools/no-tools/python/main.py b/test/scenarios/tools/no-tools/python/main.py index d857183c0..5a3079e1c 100644 --- a/test/scenarios/tools/no-tools/python/main.py +++ b/test/scenarios/tools/no-tools/python/main.py @@ -1,6 +1,6 @@ import asyncio import os -from copilot import CopilotClient +from copilot import CopilotClient, PermissionHandler SYSTEM_PROMPT = """You are a minimal assistant with no tools available. You cannot execute code, read files, edit files, search, or perform any actions. @@ -16,11 +16,10 @@ async def main(): try: session = await client.create_session( - { - "model": "claude-haiku-4.5", - "system_message": {"mode": "replace", "content": SYSTEM_PROMPT}, - "available_tools": [], - } + PermissionHandler.approve_all, + "claude-haiku-4.5", + system_message={"mode": "replace", "content": SYSTEM_PROMPT}, + available_tools=[], ) response = await session.send_and_wait( diff --git a/test/scenarios/tools/skills/python/main.py b/test/scenarios/tools/skills/python/main.py index 5adb74b76..ab4ee08f9 100644 --- a/test/scenarios/tools/skills/python/main.py +++ b/test/scenarios/tools/skills/python/main.py @@ -15,14 +15,12 @@ async def main(): skills_dir = str(Path(__file__).resolve().parent.parent / "sample-skills") session = await client.create_session( - { - "model": "claude-haiku-4.5", - "skill_directories": [skills_dir], - "on_permission_request": lambda _: {"kind": "approved"}, - "hooks": { - "on_pre_tool_use": lambda _: {"permission_decision": "allow"}, - }, - } + lambda _, __: {"kind": "approved"}, + "claude-haiku-4.5", + skill_directories=[skills_dir], + hooks={ + "on_pre_tool_use": lambda _, __: {"permissionDecision": "allow"}, + }, ) response = await session.send_and_wait( diff --git a/test/scenarios/tools/tool-filtering/python/main.py b/test/scenarios/tools/tool-filtering/python/main.py index 174be620e..66e8baee3 100644 --- a/test/scenarios/tools/tool-filtering/python/main.py +++ b/test/scenarios/tools/tool-filtering/python/main.py @@ -1,6 +1,6 @@ import asyncio import os -from copilot import CopilotClient +from copilot import CopilotClient, PermissionHandler SYSTEM_PROMPT = """You are a helpful assistant. You have access to a limited set of tools. When asked about your tools, list exactly which tools you have available.""" @@ -13,11 +13,10 @@ async def main(): try: session = await client.create_session( - { - "model": "claude-haiku-4.5", - "system_message": {"mode": "replace", "content": SYSTEM_PROMPT}, - "available_tools": ["grep", "glob", "view"], - } + PermissionHandler.approve_all, + "claude-haiku-4.5", + system_message={"mode": "replace", "content": SYSTEM_PROMPT}, + available_tools=["grep", "glob", "view"], ) response = await session.send_and_wait( diff --git a/test/scenarios/tools/virtual-filesystem/python/main.py b/test/scenarios/tools/virtual-filesystem/python/main.py index b150c1a2a..526411228 100644 --- a/test/scenarios/tools/virtual-filesystem/python/main.py +++ b/test/scenarios/tools/virtual-filesystem/python/main.py @@ -53,13 +53,11 @@ async def main(): try: session = await client.create_session( - { - "model": "claude-haiku-4.5", - "available_tools": [], - "tools": [create_file, read_file, list_files], - "on_permission_request": auto_approve_permission, - "hooks": {"on_pre_tool_use": auto_approve_tool}, - } + auto_approve_permission, + "claude-haiku-4.5", + available_tools=[], + tools=[create_file, read_file, list_files], + hooks={"on_pre_tool_use": auto_approve_tool}, ) response = await session.send_and_wait( diff --git a/test/scenarios/transport/reconnect/python/main.py b/test/scenarios/transport/reconnect/python/main.py index e8aecea50..5e76db1e2 100644 --- a/test/scenarios/transport/reconnect/python/main.py +++ b/test/scenarios/transport/reconnect/python/main.py @@ -1,7 +1,7 @@ import asyncio import os import sys -from copilot import CopilotClient +from copilot import CopilotClient, PermissionHandler async def main(): @@ -12,7 +12,7 @@ async def main(): try: # First session print("--- Session 1 ---") - session1 = await client.create_session({"model": "claude-haiku-4.5"}) + session1 = await client.create_session(PermissionHandler.approve_all, "claude-haiku-4.5") response1 = await session1.send_and_wait( {"prompt": "What is the capital of France?"} @@ -29,7 +29,7 @@ async def main(): # Second session — tests that the server accepts new sessions print("--- Session 2 ---") - session2 = await client.create_session({"model": "claude-haiku-4.5"}) + session2 = await client.create_session(PermissionHandler.approve_all, "claude-haiku-4.5") response2 = await session2.send_and_wait( {"prompt": "What is the capital of France?"} diff --git a/test/scenarios/transport/stdio/python/main.py b/test/scenarios/transport/stdio/python/main.py index 138bb5646..cfe12901f 100644 --- a/test/scenarios/transport/stdio/python/main.py +++ b/test/scenarios/transport/stdio/python/main.py @@ -1,6 +1,6 @@ import asyncio import os -from copilot import CopilotClient +from copilot import CopilotClient, PermissionHandler async def main(): @@ -10,7 +10,7 @@ async def main(): client = CopilotClient(opts) try: - session = await client.create_session({"model": "claude-haiku-4.5"}) + session = await client.create_session(PermissionHandler.approve_all, "claude-haiku-4.5") response = await session.send_and_wait( {"prompt": "What is the capital of France?"} diff --git a/test/scenarios/transport/tcp/python/main.py b/test/scenarios/transport/tcp/python/main.py index 05aaa9270..a47c926e9 100644 --- a/test/scenarios/transport/tcp/python/main.py +++ b/test/scenarios/transport/tcp/python/main.py @@ -1,6 +1,6 @@ import asyncio import os -from copilot import CopilotClient +from copilot import CopilotClient, PermissionHandler async def main(): @@ -9,7 +9,7 @@ async def main(): }) try: - session = await client.create_session({"model": "claude-haiku-4.5"}) + session = await client.create_session(PermissionHandler.approve_all, "claude-haiku-4.5") response = await session.send_and_wait( {"prompt": "What is the capital of France?"}