From 581abadfeb9f7fc27a849728008b91f059533af2 Mon Sep 17 00:00:00 2001 From: Steve Sanderson Date: Fri, 22 May 2026 11:19:38 +0100 Subject: [PATCH 01/21] Phase L: emit discriminated unions in Python codegen + drop hand-written PermissionRequestResult MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Brings Python in line with TS/Rust/.NET/Go which all emit per-variant types for nyOf-of-\ discriminated unions in the schemas (PermissionRequest, PermissionDecision, AuthInfo, SendAttachment, ToolExecutionCompleteContent, TaskInfo, SystemNotification, etc.). Previously the Python codegen merged each one into a single flat dataclass with every variant's fields collapsed to Optional — see `scripts/codegen/python.ts:897-901` for the old remap table that fronted the merged blob as `PermissionRequest`. Codegen changes (`scripts/codegen/python.ts`) * `tryEmitPyRefBasedDiscriminatedUnion` in the hand-written session-events pipeline emits each variant as its own `@dataclass`, plus a `Name = VariantA | VariantB | ...` union alias and a `_load_Name(obj)` dispatcher that switches on the discriminator (matches `findPyDiscriminator`). * `postProcessRefBasedDiscriminatedUnionsForPython` does the equivalent on the quicktype-emitted RPC types: detects each `\`-based discriminated union, deletes the merged flat class quicktype produced, emits the union alias and dispatcher, and rewrites every `Name.from_dict(x)` / `to_class(Name, x)` call (including in RPC method wrappers generated later) to route through the dispatcher. * Acronym resolution table (`Api → API`, `Mcp → MCP`, `Cli → CLI`, etc.) to map schema names to the actual class names quicktype emits. * `postProcessDiscriminatorDefaultsForPython` replaces the dataclass-level `kind` field on each variant with a class-level `ClassVar[str]` constant. Users construct variants with no discriminator arg required: `PermissionDecisionApproveOnce()` instead of `PermissionDecisionApproveOnce(kind=PermissionDecisionApproveOnceKind.APPROVE_ONCE)`. `from_dict` / `to_dict` are rewritten in lock-step. Hand-written SDK changes * `copilot.session.PermissionRequestResult` becomes a type alias for `PermissionDecision | PermissionNoResult` (mirrors TS `nodejs/src/types.ts:883`). The hand-written `PermissionRequestResult` dataclass and the `_decision_from_result` mapper are gone — handlers now return the generated variant directly. `PermissionNoResult` is a tiny hand-written sentinel for the v1-protocol case. * `PermissionHandler.approve_all` returns `PermissionDecisionApproveOnce()`. * `CopilotSession._execute_permission_and_respond` passes the returned decision straight through to the RPC; v2 servers reject `PermissionNoResult` with a clear `ValueError`. * `CopilotClient._handle_permission_request_v2` uses `_load_PermissionRequest` and serialises the variant result with `.to_dict()`. Tests and scenarios updated in lock-step. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- python/copilot/client.py | 22 +- python/copilot/generated/rpc.py | 2748 +++++------------ python/copilot/generated/session_events.py | 2315 ++++++++++---- python/copilot/session.py | 52 +- python/e2e/test_multi_client_e2e.py | 17 +- python/e2e/test_pending_work_resume_e2e.py | 14 +- python/e2e/test_permissions_e2e.py | 29 +- python/e2e/test_suspend_e2e.py | 7 +- python/e2e/test_tools_e2e.py | 9 +- python/test_client.py | 14 +- python/test_event_forward_compatibility.py | 43 +- python/test_rpc_generated.py | 4 +- scripts/codegen/python.ts | 548 +++- test/scenarios/callbacks/hooks/python/main.py | 4 +- .../callbacks/permissions/python/main.py | 4 +- .../callbacks/user-input/python/main.py | 4 +- test/scenarios/tools/skills/python/main.py | 4 +- .../tools/virtual-filesystem/python/main.py | 4 +- 18 files changed, 3141 insertions(+), 2701 deletions(-) diff --git a/python/copilot/client.py b/python/copilot/client.py index 7af3fb39f..6527e3cdc 100644 --- a/python/copilot/client.py +++ b/python/copilot/client.py @@ -39,6 +39,7 @@ from .generated.rpc import ( ClientSessionApiHandlers, ConnectRequest, + PermissionDecisionUserNotAvailable, RemoteSessionMode, ServerRpc, _InternalServerRpc, @@ -46,8 +47,8 @@ register_client_session_api_handlers, ) from .generated.session_events import ( - PermissionRequest, SessionEvent, + _load_PermissionRequest, session_event_from_dict, ) from .session import ( @@ -61,6 +62,7 @@ ExitPlanModeHandler, InfiniteSessionConfig, MCPServerConfig, + PermissionNoResult, ProviderConfig, ReasoningEffort, SectionTransformFn, @@ -3193,22 +3195,14 @@ async def _handle_permission_request_v2(self, params: dict) -> dict: raise ValueError(f"unknown session {session_id}") try: - perm_request = PermissionRequest.from_dict(permission_request) + perm_request = _load_PermissionRequest(permission_request) result = await session._handle_permission_request(perm_request) - if result.kind == "no-result": + if isinstance(result, PermissionNoResult): raise ValueError(NO_RESULT_PERMISSION_V2_ERROR) - return {"result": {"kind": result.kind}} + return {"result": result.to_dict()} except ValueError as exc: if str(exc) == NO_RESULT_PERMISSION_V2_ERROR: raise - return { - "result": { - "kind": "user-not-available", - } - } + return {"result": PermissionDecisionUserNotAvailable().to_dict()} except Exception: # pylint: disable=broad-except - return { - "result": { - "kind": "user-not-available", - } - } + return {"result": PermissionDecisionUserNotAvailable().to_dict()} diff --git a/python/copilot/generated/rpc.py b/python/copilot/generated/rpc.py index ce9f98f2a..929aa79e6 100644 --- a/python/copilot/generated/rpc.py +++ b/python/copilot/generated/rpc.py @@ -2,8 +2,9 @@ AUTO-GENERATED FILE - DO NOT EDIT Generated from: api.schema.json """ +from __future__ import annotations -from typing import TYPE_CHECKING +from typing import ClassVar, TYPE_CHECKING from .session_events import AbortReason, EmbeddedBlobResourceContents, EmbeddedTextResourceContents, McpServerSource, McpServerStatus, PermissionPromptRequest, PermissionRule, ReasoningSummary, SessionEvent, SessionMode, ShutdownType, SkillSource, UserToolSessionApproval @@ -384,6 +385,31 @@ def to_dict(self) -> dict: result["includeSkills"] = from_union([from_bool, from_none], self.include_skills) return result +# Experimental: this type is part of an experimental API and may change or be removed. +@dataclass +class CommandsRespondToQueuedCommandRequest: + """Queued-command request ID and the result indicating whether the host executed it (and + whether to stop processing further queued commands). + """ + request_id: str + """Request ID from the `command.queued` event the host is responding to.""" + + result: QueuedCommandResult + """Result of the queued command execution.""" + + @staticmethod + def from_dict(obj: Any) -> 'CommandsRespondToQueuedCommandRequest': + assert isinstance(obj, dict) + request_id = from_str(obj.get("requestId")) + result = _load_QueuedCommandResult(obj.get("result")) + return CommandsRespondToQueuedCommandRequest(request_id, result) + + def to_dict(self) -> dict: + result: dict = {} + result["requestId"] = from_str(self.request_id) + result["result"] = (self.result).to_dict() + return result + # Experimental: this type is part of an experimental API and may change or be removed. @dataclass class CommandsRespondToQueuedCommandResult: @@ -2446,6 +2472,30 @@ class PermissionDecisionDeniedNoApprovalRuleAndCouldNotRequestFromUserKind(Enum) class PermissionDecisionRejectKind(Enum): REJECT = "reject" +# Experimental: this type is part of an experimental API and may change or be removed. +@dataclass +class PermissionDecisionRequest: + """Pending permission request ID and the decision to apply (approve/reject and scope).""" + + request_id: str + """Request ID of the pending permission request""" + + result: PermissionDecision + """The client's response to the pending permission prompt""" + + @staticmethod + def from_dict(obj: Any) -> 'PermissionDecisionRequest': + assert isinstance(obj, dict) + request_id = from_str(obj.get("requestId")) + result = _load_PermissionDecision(obj.get("result")) + return PermissionDecisionRequest(request_id, result) + + def to_dict(self) -> dict: + result: dict = {} + result["requestId"] = from_str(self.request_id) + result["result"] = (self.result).to_dict() + return result + class PermissionDecisionUserNotAvailableKind(Enum): USER_NOT_AVAILABLE = "user-not-available" @@ -3224,7 +3274,7 @@ def to_dict(self) -> dict: class QueuedCommandHandled: """Schema for the `QueuedCommandHandled` type.""" - handled: bool + handled: ClassVar[str] = "true" """The host actually executed the queued command.""" stop_processing_queue: bool | None = None @@ -3235,13 +3285,12 @@ class QueuedCommandHandled: @staticmethod def from_dict(obj: Any) -> 'QueuedCommandHandled': assert isinstance(obj, dict) - handled = from_bool(obj.get("handled")) stop_processing_queue = from_union([from_bool, from_none], obj.get("stopProcessingQueue")) - return QueuedCommandHandled(handled, stop_processing_queue) + return QueuedCommandHandled(stop_processing_queue) def to_dict(self) -> dict: result: dict = {} - result["handled"] = from_bool(self.handled) + result["handled"] = self.handled if self.stop_processing_queue is not None: result["stopProcessingQueue"] = from_union([from_bool, from_none], self.stop_processing_queue) return result @@ -3251,7 +3300,7 @@ def to_dict(self) -> dict: class QueuedCommandNotHandled: """Schema for the `QueuedCommandNotHandled` type.""" - handled: bool + handled: ClassVar[str] = "false" """The host did not execute the queued command. Unblocks the queue without claiming the command was processed (e.g. when the handler threw before completing). """ @@ -3259,12 +3308,11 @@ class QueuedCommandNotHandled: @staticmethod def from_dict(obj: Any) -> 'QueuedCommandNotHandled': assert isinstance(obj, dict) - handled = from_bool(obj.get("handled")) - return QueuedCommandNotHandled(handled) + return QueuedCommandNotHandled() def to_dict(self) -> dict: result: dict = {} - result["handled"] = from_bool(self.handled) + result["handled"] = self.handled return result # Experimental: this type is part of an experimental API and may change or be removed. @@ -4237,6 +4285,31 @@ def to_dict(self) -> dict: result["skipped"] = from_list(from_str, self.skipped) return result +# Experimental: this type is part of an experimental API and may change or be removed. +@dataclass +class SessionSetCredentialsParams: + """New auth credentials to install on the session. Omit to leave credentials unchanged.""" + + credentials: AuthInfo | None = None + """The new auth credentials to install on the session. When omitted or `undefined`, the call + is a no-op and the session's existing credentials are preserved. The runtime stores the + value verbatim and uses it for outbound model/API requests; it does NOT re-validate or + re-fetch the associated Copilot user response. Several variants carry secret material; + treat this method's params as containing secrets at rest and in transit. + """ + + @staticmethod + def from_dict(obj: Any) -> 'SessionSetCredentialsParams': + assert isinstance(obj, dict) + credentials = from_union([_load_AuthInfo, from_none], obj.get("credentials")) + return SessionSetCredentialsParams(credentials) + + def to_dict(self) -> dict: + result: dict = {} + if self.credentials is not None: + result["credentials"] = from_union([lambda x: (x).to_dict(), from_none], self.credentials) + return result + # Experimental: this type is part of an experimental API and may change or be removed. @dataclass class SessionSetCredentialsResult: @@ -5196,6 +5269,25 @@ class TaskInfoType(Enum): AGENT = "agent" SHELL = "shell" +# Experimental: this type is part of an experimental API and may change or be removed. +@dataclass +class TaskList: + """Background tasks currently tracked by the session.""" + + tasks: list[TaskInfo] + """Currently tracked tasks""" + + @staticmethod + def from_dict(obj: Any) -> 'TaskList': + assert isinstance(obj, dict) + tasks = from_list(_load_TaskInfo, obj.get("tasks")) + return TaskList(tasks) + + def to_dict(self) -> dict: + result: dict = {} + result["tasks"] = from_list(lambda x: (x).to_dict(), self.tasks) + return result + class TaskShellInfoType(Enum): SHELL = "shell" @@ -5237,6 +5329,29 @@ def to_dict(self) -> dict: result["cancelled"] = from_bool(self.cancelled) return result +# Experimental: this type is part of an experimental API and may change or be removed. +@dataclass +class TasksGetCurrentPromotableResult: + """The first sync-waiting task that can currently be promoted to background mode.""" + + task: TaskInfo | None = None + """The first sync-waiting task (agent first, then shell) that can currently be promoted to + background mode. Omitted if no such task exists. The returned task is guaranteed to have + executionMode='sync' and canPromoteToBackground=true at the time of the call. + """ + + @staticmethod + def from_dict(obj: Any) -> 'TasksGetCurrentPromotableResult': + assert isinstance(obj, dict) + task = from_union([_load_TaskInfo, from_none], obj.get("task")) + return TasksGetCurrentPromotableResult(task) + + def to_dict(self) -> dict: + result: dict = {} + if self.task is not None: + result["task"] = from_union([lambda x: (x).to_dict(), from_none], self.task) + return result + # Experimental: this type is part of an experimental API and may change or be removed. @dataclass class TasksGetProgressRequest: @@ -5256,6 +5371,30 @@ def to_dict(self) -> dict: result["id"] = from_str(self.id) return result +# Experimental: this type is part of an experimental API and may change or be removed. +@dataclass +class TasksPromoteCurrentToBackgroundResult: + """The promoted task as it now exists in background mode, omitted if no promotable task was + waiting. + """ + task: TaskInfo | None = None + """The promoted task as it now exists in background mode, omitted if no promotable task was + waiting. Atomic operation: avoids the race window of getCurrentPromotable + + promoteToBackground. + """ + + @staticmethod + def from_dict(obj: Any) -> 'TasksPromoteCurrentToBackgroundResult': + assert isinstance(obj, dict) + task = from_union([_load_TaskInfo, from_none], obj.get("task")) + return TasksPromoteCurrentToBackgroundResult(task) + + def to_dict(self) -> dict: + result: dict = {} + if self.task is not None: + result["task"] = from_union([lambda x: (x).to_dict(), from_none], self.task) + return result + # Experimental: this type is part of an experimental API and may change or be removed. @dataclass class TasksPromoteToBackgroundRequest: @@ -6312,7 +6451,7 @@ class SendAttachmentDirectory: path: str """Absolute directory path""" - type: SlashCommandInputCompletion + type: ClassVar[str] = "directory" """Attachment type discriminator""" @staticmethod @@ -6320,14 +6459,13 @@ def from_dict(obj: Any) -> 'SendAttachmentDirectory': assert isinstance(obj, dict) display_name = from_str(obj.get("displayName")) path = from_str(obj.get("path")) - type = SlashCommandInputCompletion(obj.get("type")) - return SendAttachmentDirectory(display_name, path, type) + return SendAttachmentDirectory(display_name, path) def to_dict(self) -> dict: result: dict = {} result["displayName"] = from_str(self.display_name) result["path"] = from_str(self.path) - result["type"] = to_enum(SlashCommandInputCompletion, self.type) + result["type"] = self.type return result # Experimental: this type is part of an experimental API and may change or be removed. @@ -6790,7 +6928,7 @@ class ExternalToolTextResultForLlmContentAudio: mime_type: str """MIME type of the audio (e.g., audio/wav, audio/mpeg)""" - type: ExternalToolTextResultForLlmContentAudioType + type: ClassVar[str] = "audio" """Content block type discriminator""" @staticmethod @@ -6798,14 +6936,13 @@ def from_dict(obj: Any) -> 'ExternalToolTextResultForLlmContentAudio': assert isinstance(obj, dict) data = from_str(obj.get("data")) mime_type = from_str(obj.get("mimeType")) - type = ExternalToolTextResultForLlmContentAudioType(obj.get("type")) - return ExternalToolTextResultForLlmContentAudio(data, mime_type, type) + return ExternalToolTextResultForLlmContentAudio(data, mime_type) def to_dict(self) -> dict: result: dict = {} result["data"] = from_str(self.data) result["mimeType"] = from_str(self.mime_type) - result["type"] = to_enum(ExternalToolTextResultForLlmContentAudioType, self.type) + result["type"] = self.type return result # Experimental: this type is part of an experimental API and may change or be removed. @@ -6819,7 +6956,7 @@ class ExternalToolTextResultForLlmContentImage: mime_type: str """MIME type of the image (e.g., image/png, image/jpeg)""" - type: ExternalToolTextResultForLlmContentImageType + type: ClassVar[str] = "image" """Content block type discriminator""" @staticmethod @@ -6827,14 +6964,13 @@ def from_dict(obj: Any) -> 'ExternalToolTextResultForLlmContentImage': assert isinstance(obj, dict) data = from_str(obj.get("data")) mime_type = from_str(obj.get("mimeType")) - type = ExternalToolTextResultForLlmContentImageType(obj.get("type")) - return ExternalToolTextResultForLlmContentImage(data, mime_type, type) + return ExternalToolTextResultForLlmContentImage(data, mime_type) def to_dict(self) -> dict: result: dict = {} result["data"] = from_str(self.data) result["mimeType"] = from_str(self.mime_type) - result["type"] = to_enum(ExternalToolTextResultForLlmContentImageType, self.type) + result["type"] = self.type return result # Experimental: this type is part of an experimental API and may change or be removed. @@ -6845,20 +6981,19 @@ class ExternalToolTextResultForLlmContentResource: resource: ExternalToolTextResultForLlmContentResourceDetails """The embedded resource contents, either text or base64-encoded binary""" - type: ExternalToolTextResultForLlmContentResourceType + type: ClassVar[str] = "resource" """Content block type discriminator""" @staticmethod def from_dict(obj: Any) -> 'ExternalToolTextResultForLlmContentResource': assert isinstance(obj, dict) resource = (lambda x: from_union([EmbeddedTextResourceContents.from_dict, EmbeddedBlobResourceContents.from_dict], x))(obj.get("resource")) - type = ExternalToolTextResultForLlmContentResourceType(obj.get("type")) - return ExternalToolTextResultForLlmContentResource(resource, type) + return ExternalToolTextResultForLlmContentResource(resource) def to_dict(self) -> dict: result: dict = {} result["resource"] = from_union([lambda x: to_class(EmbeddedTextResourceContents, x), lambda x: to_class(EmbeddedBlobResourceContents, x)], self.resource) - result["type"] = to_enum(ExternalToolTextResultForLlmContentResourceType, self.type) + result["type"] = self.type return result # Experimental: this type is part of an experimental API and may change or be removed. @@ -6869,7 +7004,7 @@ class ExternalToolTextResultForLlmContentTerminal: text: str """Terminal/shell output text""" - type: ExternalToolTextResultForLlmContentTerminalType + type: ClassVar[str] = "terminal" """Content block type discriminator""" cwd: str | None = None @@ -6882,15 +7017,14 @@ class ExternalToolTextResultForLlmContentTerminal: def from_dict(obj: Any) -> 'ExternalToolTextResultForLlmContentTerminal': assert isinstance(obj, dict) text = from_str(obj.get("text")) - type = ExternalToolTextResultForLlmContentTerminalType(obj.get("type")) cwd = from_union([from_str, from_none], obj.get("cwd")) exit_code = from_union([from_int, from_none], obj.get("exitCode")) - return ExternalToolTextResultForLlmContentTerminal(text, type, cwd, exit_code) + return ExternalToolTextResultForLlmContentTerminal(text, cwd, exit_code) def to_dict(self) -> dict: result: dict = {} result["text"] = from_str(self.text) - result["type"] = to_enum(ExternalToolTextResultForLlmContentTerminalType, self.type) + result["type"] = self.type if self.cwd is not None: result["cwd"] = from_union([from_str, from_none], self.cwd) if self.exit_code is not None: @@ -6905,20 +7039,19 @@ class ExternalToolTextResultForLlmContentText: text: str """The text content""" - type: KindEnum + type: ClassVar[str] = "text" """Content block type discriminator""" @staticmethod def from_dict(obj: Any) -> 'ExternalToolTextResultForLlmContentText': assert isinstance(obj, dict) text = from_str(obj.get("text")) - type = KindEnum(obj.get("type")) - return ExternalToolTextResultForLlmContentText(text, type) + return ExternalToolTextResultForLlmContentText(text) def to_dict(self) -> dict: result: dict = {} result["text"] = from_str(self.text) - result["type"] = to_enum(KindEnum, self.type) + result["type"] = self.type return result # Experimental: this type is part of an experimental API and may change or be removed. @@ -6926,7 +7059,7 @@ def to_dict(self) -> dict: class SlashCommandTextResult: """Schema for the `SlashCommandTextResult` type.""" - kind: KindEnum + kind: ClassVar[str] = "text" """Text result discriminator""" text: str @@ -6946,16 +7079,15 @@ class SlashCommandTextResult: @staticmethod def from_dict(obj: Any) -> 'SlashCommandTextResult': assert isinstance(obj, dict) - kind = KindEnum(obj.get("kind")) text = from_str(obj.get("text")) markdown = from_union([from_bool, from_none], obj.get("markdown")) preserve_ansi = from_union([from_bool, from_none], obj.get("preserveAnsi")) runtime_settings_changed = from_union([from_bool, from_none], obj.get("runtimeSettingsChanged")) - return SlashCommandTextResult(kind, text, markdown, preserve_ansi, runtime_settings_changed) + return SlashCommandTextResult(text, markdown, preserve_ansi, runtime_settings_changed) def to_dict(self) -> dict: result: dict = {} - result["kind"] = to_enum(KindEnum, self.kind) + result["kind"] = self.kind result["text"] = from_str(self.text) if self.markdown is not None: result["markdown"] = from_union([from_bool, from_none], self.markdown) @@ -7977,97 +8109,99 @@ def to_dict(self) -> dict: # Experimental: this type is part of an experimental API and may change or be removed. @dataclass -class PermissionDecisionApproveForLocationApprovalCommands: - """Schema for the `PermissionDecisionApproveForLocationApprovalCommands` type.""" +class PermissionDecisionApproveForLocation: + """Schema for the `PermissionDecisionApproveForLocation` type.""" - command_identifiers: list[str] - """Command identifiers covered by this approval.""" + approval: PermissionDecisionApproveForLocationApproval + """Approval to persist for this location""" - kind: PermissionDecisionApproveForLocationApprovalCommandsKind - """Approval scoped to specific command identifiers.""" + kind: ClassVar[str] = "approve-for-location" + """Approve and persist for this project location""" + + location_key: str + """Location key (git root or cwd) to persist the approval to""" @staticmethod - def from_dict(obj: Any) -> 'PermissionDecisionApproveForLocationApprovalCommands': + def from_dict(obj: Any) -> 'PermissionDecisionApproveForLocation': assert isinstance(obj, dict) - command_identifiers = from_list(from_str, obj.get("commandIdentifiers")) - kind = PermissionDecisionApproveForLocationApprovalCommandsKind(obj.get("kind")) - return PermissionDecisionApproveForLocationApprovalCommands(command_identifiers, kind) + approval = _load_PermissionDecisionApproveForLocationApproval(obj.get("approval")) + location_key = from_str(obj.get("locationKey")) + return PermissionDecisionApproveForLocation(approval, location_key) def to_dict(self) -> dict: result: dict = {} - result["commandIdentifiers"] = from_list(from_str, self.command_identifiers) - result["kind"] = to_enum(PermissionDecisionApproveForLocationApprovalCommandsKind, self.kind) + result["approval"] = (self.approval).to_dict() + result["kind"] = self.kind + result["locationKey"] = from_str(self.location_key) return result # Experimental: this type is part of an experimental API and may change or be removed. @dataclass -class PermissionDecisionApproveForSessionApprovalCommands: - """Schema for the `PermissionDecisionApproveForSessionApprovalCommands` type.""" +class PermissionDecisionApproveForLocationApprovalCommands: + """Schema for the `PermissionDecisionApproveForLocationApprovalCommands` type.""" command_identifiers: list[str] """Command identifiers covered by this approval.""" - kind: PermissionDecisionApproveForLocationApprovalCommandsKind + kind: ClassVar[str] = "commands" """Approval scoped to specific command identifiers.""" @staticmethod - def from_dict(obj: Any) -> 'PermissionDecisionApproveForSessionApprovalCommands': + def from_dict(obj: Any) -> 'PermissionDecisionApproveForLocationApprovalCommands': assert isinstance(obj, dict) command_identifiers = from_list(from_str, obj.get("commandIdentifiers")) - kind = PermissionDecisionApproveForLocationApprovalCommandsKind(obj.get("kind")) - return PermissionDecisionApproveForSessionApprovalCommands(command_identifiers, kind) + return PermissionDecisionApproveForLocationApprovalCommands(command_identifiers) def to_dict(self) -> dict: result: dict = {} result["commandIdentifiers"] = from_list(from_str, self.command_identifiers) - result["kind"] = to_enum(PermissionDecisionApproveForLocationApprovalCommandsKind, self.kind) + result["kind"] = self.kind return result # Experimental: this type is part of an experimental API and may change or be removed. @dataclass -class PermissionsLocationsAddToolApprovalDetailsCommands: - """Schema for the `PermissionsLocationsAddToolApprovalDetailsCommands` type.""" +class PermissionDecisionApproveForSessionApprovalCommands: + """Schema for the `PermissionDecisionApproveForSessionApprovalCommands` type.""" command_identifiers: list[str] """Command identifiers covered by this approval.""" - kind: PermissionDecisionApproveForLocationApprovalCommandsKind + kind: ClassVar[str] = "commands" """Approval scoped to specific command identifiers.""" @staticmethod - def from_dict(obj: Any) -> 'PermissionsLocationsAddToolApprovalDetailsCommands': + def from_dict(obj: Any) -> 'PermissionDecisionApproveForSessionApprovalCommands': assert isinstance(obj, dict) command_identifiers = from_list(from_str, obj.get("commandIdentifiers")) - kind = PermissionDecisionApproveForLocationApprovalCommandsKind(obj.get("kind")) - return PermissionsLocationsAddToolApprovalDetailsCommands(command_identifiers, kind) + return PermissionDecisionApproveForSessionApprovalCommands(command_identifiers) def to_dict(self) -> dict: result: dict = {} result["commandIdentifiers"] = from_list(from_str, self.command_identifiers) - result["kind"] = to_enum(PermissionDecisionApproveForLocationApprovalCommandsKind, self.kind) + result["kind"] = self.kind return result +# Experimental: this type is part of an experimental API and may change or be removed. @dataclass -class UserToolSessionApprovalCommands: - """Schema for the `UserToolSessionApprovalCommands` type.""" +class PermissionsLocationsAddToolApprovalDetailsCommands: + """Schema for the `PermissionsLocationsAddToolApprovalDetailsCommands` type.""" command_identifiers: list[str] - """Command identifiers approved by the user""" + """Command identifiers covered by this approval.""" - kind: PermissionDecisionApproveForLocationApprovalCommandsKind - """Command approval kind""" + kind: ClassVar[str] = "commands" + """Approval scoped to specific command identifiers.""" @staticmethod - def from_dict(obj: Any) -> 'UserToolSessionApprovalCommands': + def from_dict(obj: Any) -> 'PermissionsLocationsAddToolApprovalDetailsCommands': assert isinstance(obj, dict) command_identifiers = from_list(from_str, obj.get("commandIdentifiers")) - kind = PermissionDecisionApproveForLocationApprovalCommandsKind(obj.get("kind")) - return UserToolSessionApprovalCommands(command_identifiers, kind) + return PermissionsLocationsAddToolApprovalDetailsCommands(command_identifiers) def to_dict(self) -> dict: result: dict = {} result["commandIdentifiers"] = from_list(from_str, self.command_identifiers) - result["kind"] = to_enum(PermissionDecisionApproveForLocationApprovalCommandsKind, self.kind) + result["kind"] = self.kind return result # Experimental: this type is part of an experimental API and may change or be removed. @@ -8075,7 +8209,7 @@ def to_dict(self) -> dict: class PermissionDecisionApproveForLocationApprovalCustomTool: """Schema for the `PermissionDecisionApproveForLocationApprovalCustomTool` type.""" - kind: PermissionDecisionApproveForLocationApprovalCustomToolKind + kind: ClassVar[str] = "custom-tool" """Approval covering a custom tool.""" tool_name: str @@ -8084,13 +8218,12 @@ class PermissionDecisionApproveForLocationApprovalCustomTool: @staticmethod def from_dict(obj: Any) -> 'PermissionDecisionApproveForLocationApprovalCustomTool': assert isinstance(obj, dict) - kind = PermissionDecisionApproveForLocationApprovalCustomToolKind(obj.get("kind")) tool_name = from_str(obj.get("toolName")) - return PermissionDecisionApproveForLocationApprovalCustomTool(kind, tool_name) + return PermissionDecisionApproveForLocationApprovalCustomTool(tool_name) def to_dict(self) -> dict: result: dict = {} - result["kind"] = to_enum(PermissionDecisionApproveForLocationApprovalCustomToolKind, self.kind) + result["kind"] = self.kind result["toolName"] = from_str(self.tool_name) return result @@ -8099,7 +8232,7 @@ def to_dict(self) -> dict: class PermissionDecisionApproveForSessionApprovalCustomTool: """Schema for the `PermissionDecisionApproveForSessionApprovalCustomTool` type.""" - kind: PermissionDecisionApproveForLocationApprovalCustomToolKind + kind: ClassVar[str] = "custom-tool" """Approval covering a custom tool.""" tool_name: str @@ -8108,13 +8241,12 @@ class PermissionDecisionApproveForSessionApprovalCustomTool: @staticmethod def from_dict(obj: Any) -> 'PermissionDecisionApproveForSessionApprovalCustomTool': assert isinstance(obj, dict) - kind = PermissionDecisionApproveForLocationApprovalCustomToolKind(obj.get("kind")) tool_name = from_str(obj.get("toolName")) - return PermissionDecisionApproveForSessionApprovalCustomTool(kind, tool_name) + return PermissionDecisionApproveForSessionApprovalCustomTool(tool_name) def to_dict(self) -> dict: result: dict = {} - result["kind"] = to_enum(PermissionDecisionApproveForLocationApprovalCustomToolKind, self.kind) + result["kind"] = self.kind result["toolName"] = from_str(self.tool_name) return result @@ -8123,7 +8255,7 @@ def to_dict(self) -> dict: class PermissionsLocationsAddToolApprovalDetailsCustomTool: """Schema for the `PermissionsLocationsAddToolApprovalDetailsCustomTool` type.""" - kind: PermissionDecisionApproveForLocationApprovalCustomToolKind + kind: ClassVar[str] = "custom-tool" """Approval covering a custom tool.""" tool_name: str @@ -8132,36 +8264,12 @@ class PermissionsLocationsAddToolApprovalDetailsCustomTool: @staticmethod def from_dict(obj: Any) -> 'PermissionsLocationsAddToolApprovalDetailsCustomTool': assert isinstance(obj, dict) - kind = PermissionDecisionApproveForLocationApprovalCustomToolKind(obj.get("kind")) - tool_name = from_str(obj.get("toolName")) - return PermissionsLocationsAddToolApprovalDetailsCustomTool(kind, tool_name) - - def to_dict(self) -> dict: - result: dict = {} - result["kind"] = to_enum(PermissionDecisionApproveForLocationApprovalCustomToolKind, self.kind) - result["toolName"] = from_str(self.tool_name) - return result - -@dataclass -class UserToolSessionApprovalCustomTool: - """Schema for the `UserToolSessionApprovalCustomTool` type.""" - - kind: PermissionDecisionApproveForLocationApprovalCustomToolKind - """Custom tool approval kind""" - - tool_name: str - """Custom tool name""" - - @staticmethod - def from_dict(obj: Any) -> 'UserToolSessionApprovalCustomTool': - assert isinstance(obj, dict) - kind = PermissionDecisionApproveForLocationApprovalCustomToolKind(obj.get("kind")) tool_name = from_str(obj.get("toolName")) - return UserToolSessionApprovalCustomTool(kind, tool_name) + return PermissionsLocationsAddToolApprovalDetailsCustomTool(tool_name) def to_dict(self) -> dict: result: dict = {} - result["kind"] = to_enum(PermissionDecisionApproveForLocationApprovalCustomToolKind, self.kind) + result["kind"] = self.kind result["toolName"] = from_str(self.tool_name) return result @@ -8170,7 +8278,7 @@ def to_dict(self) -> dict: class PermissionDecisionApproveForLocationApprovalExtensionManagement: """Schema for the `PermissionDecisionApproveForLocationApprovalExtensionManagement` type.""" - kind: PermissionDecisionApproveForLocationApprovalExtensionManagementKind + kind: ClassVar[str] = "extension-management" """Approval covering extension lifecycle operations such as enable, disable, or reload.""" operation: str | None = None @@ -8181,13 +8289,12 @@ class PermissionDecisionApproveForLocationApprovalExtensionManagement: @staticmethod def from_dict(obj: Any) -> 'PermissionDecisionApproveForLocationApprovalExtensionManagement': assert isinstance(obj, dict) - kind = PermissionDecisionApproveForLocationApprovalExtensionManagementKind(obj.get("kind")) operation = from_union([from_str, from_none], obj.get("operation")) - return PermissionDecisionApproveForLocationApprovalExtensionManagement(kind, operation) + return PermissionDecisionApproveForLocationApprovalExtensionManagement(operation) def to_dict(self) -> dict: result: dict = {} - result["kind"] = to_enum(PermissionDecisionApproveForLocationApprovalExtensionManagementKind, self.kind) + result["kind"] = self.kind if self.operation is not None: result["operation"] = from_union([from_str, from_none], self.operation) return result @@ -8197,7 +8304,7 @@ def to_dict(self) -> dict: class PermissionDecisionApproveForSessionApprovalExtensionManagement: """Schema for the `PermissionDecisionApproveForSessionApprovalExtensionManagement` type.""" - kind: PermissionDecisionApproveForLocationApprovalExtensionManagementKind + kind: ClassVar[str] = "extension-management" """Approval covering extension lifecycle operations such as enable, disable, or reload.""" operation: str | None = None @@ -8208,13 +8315,12 @@ class PermissionDecisionApproveForSessionApprovalExtensionManagement: @staticmethod def from_dict(obj: Any) -> 'PermissionDecisionApproveForSessionApprovalExtensionManagement': assert isinstance(obj, dict) - kind = PermissionDecisionApproveForLocationApprovalExtensionManagementKind(obj.get("kind")) operation = from_union([from_str, from_none], obj.get("operation")) - return PermissionDecisionApproveForSessionApprovalExtensionManagement(kind, operation) + return PermissionDecisionApproveForSessionApprovalExtensionManagement(operation) def to_dict(self) -> dict: result: dict = {} - result["kind"] = to_enum(PermissionDecisionApproveForLocationApprovalExtensionManagementKind, self.kind) + result["kind"] = self.kind if self.operation is not None: result["operation"] = from_union([from_str, from_none], self.operation) return result @@ -8224,7 +8330,7 @@ def to_dict(self) -> dict: class PermissionsLocationsAddToolApprovalDetailsExtensionManagement: """Schema for the `PermissionsLocationsAddToolApprovalDetailsExtensionManagement` type.""" - kind: PermissionDecisionApproveForLocationApprovalExtensionManagementKind + kind: ClassVar[str] = "extension-management" """Approval covering extension lifecycle operations such as enable, disable, or reload.""" operation: str | None = None @@ -8235,13 +8341,12 @@ class PermissionsLocationsAddToolApprovalDetailsExtensionManagement: @staticmethod def from_dict(obj: Any) -> 'PermissionsLocationsAddToolApprovalDetailsExtensionManagement': assert isinstance(obj, dict) - kind = PermissionDecisionApproveForLocationApprovalExtensionManagementKind(obj.get("kind")) operation = from_union([from_str, from_none], obj.get("operation")) - return PermissionsLocationsAddToolApprovalDetailsExtensionManagement(kind, operation) + return PermissionsLocationsAddToolApprovalDetailsExtensionManagement(operation) def to_dict(self) -> dict: result: dict = {} - result["kind"] = to_enum(PermissionDecisionApproveForLocationApprovalExtensionManagementKind, self.kind) + result["kind"] = self.kind if self.operation is not None: result["operation"] = from_union([from_str, from_none], self.operation) return result @@ -8251,7 +8356,7 @@ def to_dict(self) -> dict: class PermissionDecisionApproveForLocationApprovalMCP: """Schema for the `PermissionDecisionApproveForLocationApprovalMcp` type.""" - kind: PermissionDecisionApproveForLocationApprovalMCPKind + kind: ClassVar[str] = "mcp" """Approval covering an MCP tool.""" server_name: str @@ -8263,14 +8368,13 @@ class PermissionDecisionApproveForLocationApprovalMCP: @staticmethod def from_dict(obj: Any) -> 'PermissionDecisionApproveForLocationApprovalMCP': assert isinstance(obj, dict) - kind = PermissionDecisionApproveForLocationApprovalMCPKind(obj.get("kind")) server_name = from_str(obj.get("serverName")) tool_name = from_union([from_none, from_str], obj.get("toolName")) - return PermissionDecisionApproveForLocationApprovalMCP(kind, server_name, tool_name) + return PermissionDecisionApproveForLocationApprovalMCP(server_name, tool_name) def to_dict(self) -> dict: result: dict = {} - result["kind"] = to_enum(PermissionDecisionApproveForLocationApprovalMCPKind, self.kind) + result["kind"] = self.kind result["serverName"] = from_str(self.server_name) result["toolName"] = from_union([from_none, from_str], self.tool_name) return result @@ -8280,7 +8384,7 @@ def to_dict(self) -> dict: class PermissionDecisionApproveForSessionApprovalMCP: """Schema for the `PermissionDecisionApproveForSessionApprovalMcp` type.""" - kind: PermissionDecisionApproveForLocationApprovalMCPKind + kind: ClassVar[str] = "mcp" """Approval covering an MCP tool.""" server_name: str @@ -8292,14 +8396,13 @@ class PermissionDecisionApproveForSessionApprovalMCP: @staticmethod def from_dict(obj: Any) -> 'PermissionDecisionApproveForSessionApprovalMCP': assert isinstance(obj, dict) - kind = PermissionDecisionApproveForLocationApprovalMCPKind(obj.get("kind")) server_name = from_str(obj.get("serverName")) tool_name = from_union([from_none, from_str], obj.get("toolName")) - return PermissionDecisionApproveForSessionApprovalMCP(kind, server_name, tool_name) + return PermissionDecisionApproveForSessionApprovalMCP(server_name, tool_name) def to_dict(self) -> dict: result: dict = {} - result["kind"] = to_enum(PermissionDecisionApproveForLocationApprovalMCPKind, self.kind) + result["kind"] = self.kind result["serverName"] = from_str(self.server_name) result["toolName"] = from_union([from_none, from_str], self.tool_name) return result @@ -8309,7 +8412,7 @@ def to_dict(self) -> dict: class PermissionsLocationsAddToolApprovalDetailsMCP: """Schema for the `PermissionsLocationsAddToolApprovalDetailsMcp` type.""" - kind: PermissionDecisionApproveForLocationApprovalMCPKind + kind: ClassVar[str] = "mcp" """Approval covering an MCP tool.""" server_name: str @@ -8321,42 +8424,13 @@ class PermissionsLocationsAddToolApprovalDetailsMCP: @staticmethod def from_dict(obj: Any) -> 'PermissionsLocationsAddToolApprovalDetailsMCP': assert isinstance(obj, dict) - kind = PermissionDecisionApproveForLocationApprovalMCPKind(obj.get("kind")) - server_name = from_str(obj.get("serverName")) - tool_name = from_union([from_none, from_str], obj.get("toolName")) - return PermissionsLocationsAddToolApprovalDetailsMCP(kind, server_name, tool_name) - - def to_dict(self) -> dict: - result: dict = {} - result["kind"] = to_enum(PermissionDecisionApproveForLocationApprovalMCPKind, self.kind) - result["serverName"] = from_str(self.server_name) - result["toolName"] = from_union([from_none, from_str], self.tool_name) - return result - -@dataclass -class UserToolSessionApprovalMCP: - """Schema for the `UserToolSessionApprovalMcp` type.""" - - kind: PermissionDecisionApproveForLocationApprovalMCPKind - """MCP tool approval kind""" - - server_name: str - """MCP server name""" - - tool_name: str | None = None - """Optional MCP tool name, or null for all tools on the server""" - - @staticmethod - def from_dict(obj: Any) -> 'UserToolSessionApprovalMCP': - assert isinstance(obj, dict) - kind = PermissionDecisionApproveForLocationApprovalMCPKind(obj.get("kind")) server_name = from_str(obj.get("serverName")) tool_name = from_union([from_none, from_str], obj.get("toolName")) - return UserToolSessionApprovalMCP(kind, server_name, tool_name) + return PermissionsLocationsAddToolApprovalDetailsMCP(server_name, tool_name) def to_dict(self) -> dict: result: dict = {} - result["kind"] = to_enum(PermissionDecisionApproveForLocationApprovalMCPKind, self.kind) + result["kind"] = self.kind result["serverName"] = from_str(self.server_name) result["toolName"] = from_union([from_none, from_str], self.tool_name) return result @@ -8366,7 +8440,7 @@ def to_dict(self) -> dict: class PermissionDecisionApproveForLocationApprovalMCPSampling: """Schema for the `PermissionDecisionApproveForLocationApprovalMcpSampling` type.""" - kind: PermissionDecisionApproveForLocationApprovalMCPSamplingKind + kind: ClassVar[str] = "mcp-sampling" """Approval covering MCP sampling requests for a server.""" server_name: str @@ -8375,13 +8449,12 @@ class PermissionDecisionApproveForLocationApprovalMCPSampling: @staticmethod def from_dict(obj: Any) -> 'PermissionDecisionApproveForLocationApprovalMCPSampling': assert isinstance(obj, dict) - kind = PermissionDecisionApproveForLocationApprovalMCPSamplingKind(obj.get("kind")) server_name = from_str(obj.get("serverName")) - return PermissionDecisionApproveForLocationApprovalMCPSampling(kind, server_name) + return PermissionDecisionApproveForLocationApprovalMCPSampling(server_name) def to_dict(self) -> dict: result: dict = {} - result["kind"] = to_enum(PermissionDecisionApproveForLocationApprovalMCPSamplingKind, self.kind) + result["kind"] = self.kind result["serverName"] = from_str(self.server_name) return result @@ -8390,7 +8463,7 @@ def to_dict(self) -> dict: class PermissionDecisionApproveForSessionApprovalMCPSampling: """Schema for the `PermissionDecisionApproveForSessionApprovalMcpSampling` type.""" - kind: PermissionDecisionApproveForLocationApprovalMCPSamplingKind + kind: ClassVar[str] = "mcp-sampling" """Approval covering MCP sampling requests for a server.""" server_name: str @@ -8399,13 +8472,12 @@ class PermissionDecisionApproveForSessionApprovalMCPSampling: @staticmethod def from_dict(obj: Any) -> 'PermissionDecisionApproveForSessionApprovalMCPSampling': assert isinstance(obj, dict) - kind = PermissionDecisionApproveForLocationApprovalMCPSamplingKind(obj.get("kind")) server_name = from_str(obj.get("serverName")) - return PermissionDecisionApproveForSessionApprovalMCPSampling(kind, server_name) + return PermissionDecisionApproveForSessionApprovalMCPSampling(server_name) def to_dict(self) -> dict: result: dict = {} - result["kind"] = to_enum(PermissionDecisionApproveForLocationApprovalMCPSamplingKind, self.kind) + result["kind"] = self.kind result["serverName"] = from_str(self.server_name) return result @@ -8414,7 +8486,7 @@ def to_dict(self) -> dict: class PermissionsLocationsAddToolApprovalDetailsMCPSampling: """Schema for the `PermissionsLocationsAddToolApprovalDetailsMcpSampling` type.""" - kind: PermissionDecisionApproveForLocationApprovalMCPSamplingKind + kind: ClassVar[str] = "mcp-sampling" """Approval covering MCP sampling requests for a server.""" server_name: str @@ -8423,13 +8495,12 @@ class PermissionsLocationsAddToolApprovalDetailsMCPSampling: @staticmethod def from_dict(obj: Any) -> 'PermissionsLocationsAddToolApprovalDetailsMCPSampling': assert isinstance(obj, dict) - kind = PermissionDecisionApproveForLocationApprovalMCPSamplingKind(obj.get("kind")) server_name = from_str(obj.get("serverName")) - return PermissionsLocationsAddToolApprovalDetailsMCPSampling(kind, server_name) + return PermissionsLocationsAddToolApprovalDetailsMCPSampling(server_name) def to_dict(self) -> dict: result: dict = {} - result["kind"] = to_enum(PermissionDecisionApproveForLocationApprovalMCPSamplingKind, self.kind) + result["kind"] = self.kind result["serverName"] = from_str(self.server_name) return result @@ -8438,18 +8509,17 @@ def to_dict(self) -> dict: class PermissionDecisionApproveForLocationApprovalMemory: """Schema for the `PermissionDecisionApproveForLocationApprovalMemory` type.""" - kind: PermissionDecisionApproveForLocationApprovalMemoryKind + kind: ClassVar[str] = "memory" """Approval covering writes to long-term memory.""" @staticmethod def from_dict(obj: Any) -> 'PermissionDecisionApproveForLocationApprovalMemory': assert isinstance(obj, dict) - kind = PermissionDecisionApproveForLocationApprovalMemoryKind(obj.get("kind")) - return PermissionDecisionApproveForLocationApprovalMemory(kind) + return PermissionDecisionApproveForLocationApprovalMemory() def to_dict(self) -> dict: result: dict = {} - result["kind"] = to_enum(PermissionDecisionApproveForLocationApprovalMemoryKind, self.kind) + result["kind"] = self.kind return result # Experimental: this type is part of an experimental API and may change or be removed. @@ -8457,18 +8527,17 @@ def to_dict(self) -> dict: class PermissionDecisionApproveForSessionApprovalMemory: """Schema for the `PermissionDecisionApproveForSessionApprovalMemory` type.""" - kind: PermissionDecisionApproveForLocationApprovalMemoryKind + kind: ClassVar[str] = "memory" """Approval covering writes to long-term memory.""" @staticmethod def from_dict(obj: Any) -> 'PermissionDecisionApproveForSessionApprovalMemory': assert isinstance(obj, dict) - kind = PermissionDecisionApproveForLocationApprovalMemoryKind(obj.get("kind")) - return PermissionDecisionApproveForSessionApprovalMemory(kind) + return PermissionDecisionApproveForSessionApprovalMemory() def to_dict(self) -> dict: result: dict = {} - result["kind"] = to_enum(PermissionDecisionApproveForLocationApprovalMemoryKind, self.kind) + result["kind"] = self.kind return result # Experimental: this type is part of an experimental API and may change or be removed. @@ -8476,36 +8545,17 @@ def to_dict(self) -> dict: class PermissionsLocationsAddToolApprovalDetailsMemory: """Schema for the `PermissionsLocationsAddToolApprovalDetailsMemory` type.""" - kind: PermissionDecisionApproveForLocationApprovalMemoryKind + kind: ClassVar[str] = "memory" """Approval covering writes to long-term memory.""" @staticmethod def from_dict(obj: Any) -> 'PermissionsLocationsAddToolApprovalDetailsMemory': assert isinstance(obj, dict) - kind = PermissionDecisionApproveForLocationApprovalMemoryKind(obj.get("kind")) - return PermissionsLocationsAddToolApprovalDetailsMemory(kind) - - def to_dict(self) -> dict: - result: dict = {} - result["kind"] = to_enum(PermissionDecisionApproveForLocationApprovalMemoryKind, self.kind) - return result - -@dataclass -class UserToolSessionApprovalMemory: - """Schema for the `UserToolSessionApprovalMemory` type.""" - - kind: PermissionDecisionApproveForLocationApprovalMemoryKind - """Memory approval kind""" - - @staticmethod - def from_dict(obj: Any) -> 'UserToolSessionApprovalMemory': - assert isinstance(obj, dict) - kind = PermissionDecisionApproveForLocationApprovalMemoryKind(obj.get("kind")) - return UserToolSessionApprovalMemory(kind) + return PermissionsLocationsAddToolApprovalDetailsMemory() def to_dict(self) -> dict: result: dict = {} - result["kind"] = to_enum(PermissionDecisionApproveForLocationApprovalMemoryKind, self.kind) + result["kind"] = self.kind return result # Experimental: this type is part of an experimental API and may change or be removed. @@ -8513,18 +8563,17 @@ def to_dict(self) -> dict: class PermissionDecisionApproveForLocationApprovalRead: """Schema for the `PermissionDecisionApproveForLocationApprovalRead` type.""" - kind: PermissionDecisionApproveForLocationApprovalReadKind + kind: ClassVar[str] = "read" """Approval covering read-only filesystem operations.""" @staticmethod def from_dict(obj: Any) -> 'PermissionDecisionApproveForLocationApprovalRead': assert isinstance(obj, dict) - kind = PermissionDecisionApproveForLocationApprovalReadKind(obj.get("kind")) - return PermissionDecisionApproveForLocationApprovalRead(kind) + return PermissionDecisionApproveForLocationApprovalRead() def to_dict(self) -> dict: result: dict = {} - result["kind"] = to_enum(PermissionDecisionApproveForLocationApprovalReadKind, self.kind) + result["kind"] = self.kind return result # Experimental: this type is part of an experimental API and may change or be removed. @@ -8532,18 +8581,17 @@ def to_dict(self) -> dict: class PermissionDecisionApproveForSessionApprovalRead: """Schema for the `PermissionDecisionApproveForSessionApprovalRead` type.""" - kind: PermissionDecisionApproveForLocationApprovalReadKind + kind: ClassVar[str] = "read" """Approval covering read-only filesystem operations.""" @staticmethod def from_dict(obj: Any) -> 'PermissionDecisionApproveForSessionApprovalRead': assert isinstance(obj, dict) - kind = PermissionDecisionApproveForLocationApprovalReadKind(obj.get("kind")) - return PermissionDecisionApproveForSessionApprovalRead(kind) + return PermissionDecisionApproveForSessionApprovalRead() def to_dict(self) -> dict: result: dict = {} - result["kind"] = to_enum(PermissionDecisionApproveForLocationApprovalReadKind, self.kind) + result["kind"] = self.kind return result # Experimental: this type is part of an experimental API and may change or be removed. @@ -8551,36 +8599,17 @@ def to_dict(self) -> dict: class PermissionsLocationsAddToolApprovalDetailsRead: """Schema for the `PermissionsLocationsAddToolApprovalDetailsRead` type.""" - kind: PermissionDecisionApproveForLocationApprovalReadKind + kind: ClassVar[str] = "read" """Approval covering read-only filesystem operations.""" @staticmethod def from_dict(obj: Any) -> 'PermissionsLocationsAddToolApprovalDetailsRead': assert isinstance(obj, dict) - kind = PermissionDecisionApproveForLocationApprovalReadKind(obj.get("kind")) - return PermissionsLocationsAddToolApprovalDetailsRead(kind) - - def to_dict(self) -> dict: - result: dict = {} - result["kind"] = to_enum(PermissionDecisionApproveForLocationApprovalReadKind, self.kind) - return result - -@dataclass -class UserToolSessionApprovalRead: - """Schema for the `UserToolSessionApprovalRead` type.""" - - kind: PermissionDecisionApproveForLocationApprovalReadKind - """Read approval kind""" - - @staticmethod - def from_dict(obj: Any) -> 'UserToolSessionApprovalRead': - assert isinstance(obj, dict) - kind = PermissionDecisionApproveForLocationApprovalReadKind(obj.get("kind")) - return UserToolSessionApprovalRead(kind) + return PermissionsLocationsAddToolApprovalDetailsRead() def to_dict(self) -> dict: result: dict = {} - result["kind"] = to_enum(PermissionDecisionApproveForLocationApprovalReadKind, self.kind) + result["kind"] = self.kind return result # Experimental: this type is part of an experimental API and may change or be removed. @@ -8588,18 +8617,17 @@ def to_dict(self) -> dict: class PermissionDecisionApproveForLocationApprovalWrite: """Schema for the `PermissionDecisionApproveForLocationApprovalWrite` type.""" - kind: PermissionDecisionApproveForLocationApprovalWriteKind + kind: ClassVar[str] = "write" """Approval covering filesystem write operations.""" @staticmethod def from_dict(obj: Any) -> 'PermissionDecisionApproveForLocationApprovalWrite': assert isinstance(obj, dict) - kind = PermissionDecisionApproveForLocationApprovalWriteKind(obj.get("kind")) - return PermissionDecisionApproveForLocationApprovalWrite(kind) + return PermissionDecisionApproveForLocationApprovalWrite() def to_dict(self) -> dict: result: dict = {} - result["kind"] = to_enum(PermissionDecisionApproveForLocationApprovalWriteKind, self.kind) + result["kind"] = self.kind return result # Experimental: this type is part of an experimental API and may change or be removed. @@ -8607,18 +8635,17 @@ def to_dict(self) -> dict: class PermissionDecisionApproveForSessionApprovalWrite: """Schema for the `PermissionDecisionApproveForSessionApprovalWrite` type.""" - kind: PermissionDecisionApproveForLocationApprovalWriteKind + kind: ClassVar[str] = "write" """Approval covering filesystem write operations.""" @staticmethod def from_dict(obj: Any) -> 'PermissionDecisionApproveForSessionApprovalWrite': assert isinstance(obj, dict) - kind = PermissionDecisionApproveForLocationApprovalWriteKind(obj.get("kind")) - return PermissionDecisionApproveForSessionApprovalWrite(kind) + return PermissionDecisionApproveForSessionApprovalWrite() def to_dict(self) -> dict: result: dict = {} - result["kind"] = to_enum(PermissionDecisionApproveForLocationApprovalWriteKind, self.kind) + result["kind"] = self.kind return result # Experimental: this type is part of an experimental API and may change or be removed. @@ -8626,36 +8653,47 @@ def to_dict(self) -> dict: class PermissionsLocationsAddToolApprovalDetailsWrite: """Schema for the `PermissionsLocationsAddToolApprovalDetailsWrite` type.""" - kind: PermissionDecisionApproveForLocationApprovalWriteKind + kind: ClassVar[str] = "write" """Approval covering filesystem write operations.""" @staticmethod def from_dict(obj: Any) -> 'PermissionsLocationsAddToolApprovalDetailsWrite': assert isinstance(obj, dict) - kind = PermissionDecisionApproveForLocationApprovalWriteKind(obj.get("kind")) - return PermissionsLocationsAddToolApprovalDetailsWrite(kind) + return PermissionsLocationsAddToolApprovalDetailsWrite() def to_dict(self) -> dict: result: dict = {} - result["kind"] = to_enum(PermissionDecisionApproveForLocationApprovalWriteKind, self.kind) + result["kind"] = self.kind return result +# Experimental: this type is part of an experimental API and may change or be removed. @dataclass -class UserToolSessionApprovalWrite: - """Schema for the `UserToolSessionApprovalWrite` type.""" +class PermissionDecisionApproveForSession: + """Schema for the `PermissionDecisionApproveForSession` type.""" + + kind: ClassVar[str] = "approve-for-session" + """Approve and remember for the rest of the session""" + + approval: PermissionDecisionApproveForSessionApproval | None = None + """Session-scoped approval to remember (tool prompts only; omitted for path/url prompts)""" - kind: PermissionDecisionApproveForLocationApprovalWriteKind - """Write approval kind""" + domain: str | None = None + """URL domain to approve for the rest of the session (URL prompts only)""" @staticmethod - def from_dict(obj: Any) -> 'UserToolSessionApprovalWrite': + def from_dict(obj: Any) -> 'PermissionDecisionApproveForSession': assert isinstance(obj, dict) - kind = PermissionDecisionApproveForLocationApprovalWriteKind(obj.get("kind")) - return UserToolSessionApprovalWrite(kind) + approval = from_union([_load_PermissionDecisionApproveForSessionApproval, from_none], obj.get("approval")) + domain = from_union([from_str, from_none], obj.get("domain")) + return PermissionDecisionApproveForSession(approval, domain) def to_dict(self) -> dict: result: dict = {} - result["kind"] = to_enum(PermissionDecisionApproveForLocationApprovalWriteKind, self.kind) + result["kind"] = self.kind + if self.approval is not None: + result["approval"] = from_union([lambda x: (x).to_dict(), from_none], self.approval) + if self.domain is not None: + result["domain"] = from_union([from_str, from_none], self.domain) return result # Experimental: this type is part of an experimental API and may change or be removed. @@ -8663,18 +8701,17 @@ def to_dict(self) -> dict: class PermissionDecisionApproveOnce: """Schema for the `PermissionDecisionApproveOnce` type.""" - kind: PermissionDecisionApproveOnceKind + kind: ClassVar[str] = "approve-once" """Approve this single request only""" @staticmethod def from_dict(obj: Any) -> 'PermissionDecisionApproveOnce': assert isinstance(obj, dict) - kind = PermissionDecisionApproveOnceKind(obj.get("kind")) - return PermissionDecisionApproveOnce(kind) + return PermissionDecisionApproveOnce() def to_dict(self) -> dict: result: dict = {} - result["kind"] = to_enum(PermissionDecisionApproveOnceKind, self.kind) + result["kind"] = self.kind return result # Experimental: this type is part of an experimental API and may change or be removed. @@ -8685,20 +8722,19 @@ class PermissionDecisionApprovePermanently: domain: str """URL domain to approve permanently""" - kind: PermissionDecisionApprovePermanentlyKind + kind: ClassVar[str] = "approve-permanently" """Approve and persist across sessions (URL prompts only)""" @staticmethod def from_dict(obj: Any) -> 'PermissionDecisionApprovePermanently': assert isinstance(obj, dict) domain = from_str(obj.get("domain")) - kind = PermissionDecisionApprovePermanentlyKind(obj.get("kind")) - return PermissionDecisionApprovePermanently(domain, kind) + return PermissionDecisionApprovePermanently(domain) def to_dict(self) -> dict: result: dict = {} result["domain"] = from_str(self.domain) - result["kind"] = to_enum(PermissionDecisionApprovePermanentlyKind, self.kind) + result["kind"] = self.kind return result # Experimental: this type is part of an experimental API and may change or be removed. @@ -8706,18 +8742,17 @@ def to_dict(self) -> dict: class PermissionDecisionApproved: """Schema for the `PermissionDecisionApproved` type.""" - kind: PermissionDecisionApprovedKind + kind: ClassVar[str] = "approved" """The permission request was approved""" @staticmethod def from_dict(obj: Any) -> 'PermissionDecisionApproved': assert isinstance(obj, dict) - kind = PermissionDecisionApprovedKind(obj.get("kind")) - return PermissionDecisionApproved(kind) + return PermissionDecisionApproved() def to_dict(self) -> dict: result: dict = {} - result["kind"] = to_enum(PermissionDecisionApprovedKind, self.kind) + result["kind"] = self.kind return result # Experimental: this type is part of an experimental API and may change or be removed. @@ -8728,7 +8763,7 @@ class PermissionDecisionApprovedForLocation: approval: UserToolSessionApproval """The approval to persist for this location""" - kind: PermissionDecisionApprovedForLocationKind + kind: ClassVar[str] = "approved-for-location" """Approved and persisted for this project location""" location_key: str @@ -8738,14 +8773,13 @@ class PermissionDecisionApprovedForLocation: def from_dict(obj: Any) -> 'PermissionDecisionApprovedForLocation': assert isinstance(obj, dict) approval = UserToolSessionApproval.from_dict(obj.get("approval")) - kind = PermissionDecisionApprovedForLocationKind(obj.get("kind")) location_key = from_str(obj.get("locationKey")) - return PermissionDecisionApprovedForLocation(approval, kind, location_key) + return PermissionDecisionApprovedForLocation(approval, location_key) def to_dict(self) -> dict: result: dict = {} result["approval"] = to_class(UserToolSessionApproval, self.approval) - result["kind"] = to_enum(PermissionDecisionApprovedForLocationKind, self.kind) + result["kind"] = self.kind result["locationKey"] = from_str(self.location_key) return result @@ -8757,20 +8791,19 @@ class PermissionDecisionApprovedForSession: approval: UserToolSessionApproval """The approval to add as a session-scoped rule""" - kind: PermissionDecisionApprovedForSessionKind + kind: ClassVar[str] = "approved-for-session" """Approved and remembered for the rest of the session""" @staticmethod def from_dict(obj: Any) -> 'PermissionDecisionApprovedForSession': assert isinstance(obj, dict) approval = UserToolSessionApproval.from_dict(obj.get("approval")) - kind = PermissionDecisionApprovedForSessionKind(obj.get("kind")) - return PermissionDecisionApprovedForSession(approval, kind) + return PermissionDecisionApprovedForSession(approval) def to_dict(self) -> dict: result: dict = {} result["approval"] = to_class(UserToolSessionApproval, self.approval) - result["kind"] = to_enum(PermissionDecisionApprovedForSessionKind, self.kind) + result["kind"] = self.kind return result # Experimental: this type is part of an experimental API and may change or be removed. @@ -8778,7 +8811,7 @@ def to_dict(self) -> dict: class PermissionDecisionCancelled: """Schema for the `PermissionDecisionCancelled` type.""" - kind: PermissionDecisionCancelledKind + kind: ClassVar[str] = "cancelled" """The permission request was cancelled before a response was used""" reason: str | None = None @@ -8787,13 +8820,12 @@ class PermissionDecisionCancelled: @staticmethod def from_dict(obj: Any) -> 'PermissionDecisionCancelled': assert isinstance(obj, dict) - kind = PermissionDecisionCancelledKind(obj.get("kind")) reason = from_union([from_str, from_none], obj.get("reason")) - return PermissionDecisionCancelled(kind, reason) + return PermissionDecisionCancelled(reason) def to_dict(self) -> dict: result: dict = {} - result["kind"] = to_enum(PermissionDecisionCancelledKind, self.kind) + result["kind"] = self.kind if self.reason is not None: result["reason"] = from_union([from_str, from_none], self.reason) return result @@ -8803,7 +8835,7 @@ def to_dict(self) -> dict: class PermissionDecisionDeniedByContentExclusionPolicy: """Schema for the `PermissionDecisionDeniedByContentExclusionPolicy` type.""" - kind: PermissionDecisionDeniedByContentExclusionPolicyKind + kind: ClassVar[str] = "denied-by-content-exclusion-policy" """Denied by the organization's content exclusion policy""" message: str @@ -8815,14 +8847,13 @@ class PermissionDecisionDeniedByContentExclusionPolicy: @staticmethod def from_dict(obj: Any) -> 'PermissionDecisionDeniedByContentExclusionPolicy': assert isinstance(obj, dict) - kind = PermissionDecisionDeniedByContentExclusionPolicyKind(obj.get("kind")) message = from_str(obj.get("message")) path = from_str(obj.get("path")) - return PermissionDecisionDeniedByContentExclusionPolicy(kind, message, path) + return PermissionDecisionDeniedByContentExclusionPolicy(message, path) def to_dict(self) -> dict: result: dict = {} - result["kind"] = to_enum(PermissionDecisionDeniedByContentExclusionPolicyKind, self.kind) + result["kind"] = self.kind result["message"] = from_str(self.message) result["path"] = from_str(self.path) return result @@ -8832,7 +8863,7 @@ def to_dict(self) -> dict: class PermissionDecisionDeniedByPermissionRequestHook: """Schema for the `PermissionDecisionDeniedByPermissionRequestHook` type.""" - kind: PermissionDecisionDeniedByPermissionRequestHookKind + kind: ClassVar[str] = "denied-by-permission-request-hook" """Denied by a permission request hook registered by an extension or plugin""" interrupt: bool | None = None @@ -8844,14 +8875,13 @@ class PermissionDecisionDeniedByPermissionRequestHook: @staticmethod def from_dict(obj: Any) -> 'PermissionDecisionDeniedByPermissionRequestHook': assert isinstance(obj, dict) - kind = PermissionDecisionDeniedByPermissionRequestHookKind(obj.get("kind")) interrupt = from_union([from_bool, from_none], obj.get("interrupt")) message = from_union([from_str, from_none], obj.get("message")) - return PermissionDecisionDeniedByPermissionRequestHook(kind, interrupt, message) + return PermissionDecisionDeniedByPermissionRequestHook(interrupt, message) def to_dict(self) -> dict: result: dict = {} - result["kind"] = to_enum(PermissionDecisionDeniedByPermissionRequestHookKind, self.kind) + result["kind"] = self.kind if self.interrupt is not None: result["interrupt"] = from_union([from_bool, from_none], self.interrupt) if self.message is not None: @@ -8863,7 +8893,7 @@ def to_dict(self) -> dict: class PermissionDecisionDeniedByRules: """Schema for the `PermissionDecisionDeniedByRules` type.""" - kind: PermissionDecisionDeniedByRulesKind + kind: ClassVar[str] = "denied-by-rules" """Denied because approval rules explicitly blocked it""" rules: list[PermissionRule] @@ -8872,13 +8902,12 @@ class PermissionDecisionDeniedByRules: @staticmethod def from_dict(obj: Any) -> 'PermissionDecisionDeniedByRules': assert isinstance(obj, dict) - kind = PermissionDecisionDeniedByRulesKind(obj.get("kind")) rules = from_list(PermissionRule.from_dict, obj.get("rules")) - return PermissionDecisionDeniedByRules(kind, rules) + return PermissionDecisionDeniedByRules(rules) def to_dict(self) -> dict: result: dict = {} - result["kind"] = to_enum(PermissionDecisionDeniedByRulesKind, self.kind) + result["kind"] = self.kind result["rules"] = from_list(lambda x: to_class(PermissionRule, x), self.rules) return result @@ -8887,7 +8916,7 @@ def to_dict(self) -> dict: class PermissionDecisionDeniedInteractivelyByUser: """Schema for the `PermissionDecisionDeniedInteractivelyByUser` type.""" - kind: PermissionDecisionDeniedInteractivelyByUserKind + kind: ClassVar[str] = "denied-interactively-by-user" """Denied by the user during an interactive prompt""" feedback: str | None = None @@ -8899,14 +8928,13 @@ class PermissionDecisionDeniedInteractivelyByUser: @staticmethod def from_dict(obj: Any) -> 'PermissionDecisionDeniedInteractivelyByUser': assert isinstance(obj, dict) - kind = PermissionDecisionDeniedInteractivelyByUserKind(obj.get("kind")) feedback = from_union([from_str, from_none], obj.get("feedback")) force_reject = from_union([from_bool, from_none], obj.get("forceReject")) - return PermissionDecisionDeniedInteractivelyByUser(kind, feedback, force_reject) + return PermissionDecisionDeniedInteractivelyByUser(feedback, force_reject) def to_dict(self) -> dict: result: dict = {} - result["kind"] = to_enum(PermissionDecisionDeniedInteractivelyByUserKind, self.kind) + result["kind"] = self.kind if self.feedback is not None: result["feedback"] = from_union([from_str, from_none], self.feedback) if self.force_reject is not None: @@ -8918,18 +8946,17 @@ def to_dict(self) -> dict: class PermissionDecisionDeniedNoApprovalRuleAndCouldNotRequestFromUser: """Schema for the `PermissionDecisionDeniedNoApprovalRuleAndCouldNotRequestFromUser` type.""" - kind: PermissionDecisionDeniedNoApprovalRuleAndCouldNotRequestFromUserKind + kind: ClassVar[str] = "denied-no-approval-rule-and-could-not-request-from-user" """Denied because no approval rule matched and user confirmation was unavailable""" @staticmethod def from_dict(obj: Any) -> 'PermissionDecisionDeniedNoApprovalRuleAndCouldNotRequestFromUser': assert isinstance(obj, dict) - kind = PermissionDecisionDeniedNoApprovalRuleAndCouldNotRequestFromUserKind(obj.get("kind")) - return PermissionDecisionDeniedNoApprovalRuleAndCouldNotRequestFromUser(kind) + return PermissionDecisionDeniedNoApprovalRuleAndCouldNotRequestFromUser() def to_dict(self) -> dict: result: dict = {} - result["kind"] = to_enum(PermissionDecisionDeniedNoApprovalRuleAndCouldNotRequestFromUserKind, self.kind) + result["kind"] = self.kind return result # Experimental: this type is part of an experimental API and may change or be removed. @@ -8937,7 +8964,7 @@ def to_dict(self) -> dict: class PermissionDecisionReject: """Schema for the `PermissionDecisionReject` type.""" - kind: PermissionDecisionRejectKind + kind: ClassVar[str] = "reject" """Reject the request""" feedback: str | None = None @@ -8946,13 +8973,12 @@ class PermissionDecisionReject: @staticmethod def from_dict(obj: Any) -> 'PermissionDecisionReject': assert isinstance(obj, dict) - kind = PermissionDecisionRejectKind(obj.get("kind")) feedback = from_union([from_str, from_none], obj.get("feedback")) - return PermissionDecisionReject(kind, feedback) + return PermissionDecisionReject(feedback) def to_dict(self) -> dict: result: dict = {} - result["kind"] = to_enum(PermissionDecisionRejectKind, self.kind) + result["kind"] = self.kind if self.feedback is not None: result["feedback"] = from_union([from_str, from_none], self.feedback) return result @@ -8962,18 +8988,17 @@ def to_dict(self) -> dict: class PermissionDecisionUserNotAvailable: """Schema for the `PermissionDecisionUserNotAvailable` type.""" - kind: PermissionDecisionUserNotAvailableKind + kind: ClassVar[str] = "user-not-available" """No user is available to confirm the request""" @staticmethod def from_dict(obj: Any) -> 'PermissionDecisionUserNotAvailable': assert isinstance(obj, dict) - kind = PermissionDecisionUserNotAvailableKind(obj.get("kind")) - return PermissionDecisionUserNotAvailable(kind) + return PermissionDecisionUserNotAvailable() def to_dict(self) -> dict: result: dict = {} - result["kind"] = to_enum(PermissionDecisionUserNotAvailableKind, self.kind) + result["kind"] = self.kind return result # Experimental: this type is part of an experimental API and may change or be removed. @@ -9159,40 +9184,6 @@ def to_dict(self) -> dict: result["kind"] = to_enum(QueuePendingItemsKind, self.kind) return result -# Experimental: this type is part of an experimental API and may change or be removed. -@dataclass -class QueuedCommandResult: - """Result of the queued command execution. - - Schema for the `QueuedCommandHandled` type. - - Schema for the `QueuedCommandNotHandled` type. - """ - handled: bool - """The host actually executed the queued command. - - The host did not execute the queued command. Unblocks the queue without claiming the - command was processed (e.g. when the handler threw before completing). - """ - stop_processing_queue: bool | None = None - """When true, the runtime will not process subsequent queued commands until a new request - comes in. - """ - - @staticmethod - def from_dict(obj: Any) -> 'QueuedCommandResult': - assert isinstance(obj, dict) - handled = from_bool(obj.get("handled")) - stop_processing_queue = from_union([from_bool, from_none], obj.get("stopProcessingQueue")) - return QueuedCommandResult(handled, stop_processing_queue) - - def to_dict(self) -> dict: - result: dict = {} - result["handled"] = from_bool(self.handled) - if self.stop_processing_queue is not None: - result["stopProcessingQueue"] = from_union([from_bool, from_none], self.stop_processing_queue) - return result - # Experimental: this type is part of an experimental API and may change or be removed. @dataclass class RemoteEnableRequest: @@ -9290,7 +9281,7 @@ class SendAttachmentBlob: mime_type: str """MIME type of the inline data""" - type: SendAttachmentBlobType + type: ClassVar[str] = "blob" """Attachment type discriminator""" display_name: str | None = None @@ -9301,15 +9292,14 @@ def from_dict(obj: Any) -> 'SendAttachmentBlob': assert isinstance(obj, dict) data = from_str(obj.get("data")) mime_type = from_str(obj.get("mimeType")) - type = SendAttachmentBlobType(obj.get("type")) display_name = from_union([from_str, from_none], obj.get("displayName")) - return SendAttachmentBlob(data, mime_type, type, display_name) + return SendAttachmentBlob(data, mime_type, display_name) def to_dict(self) -> dict: result: dict = {} result["data"] = from_str(self.data) result["mimeType"] = from_str(self.mime_type) - result["type"] = to_enum(SendAttachmentBlobType, self.type) + result["type"] = self.type if self.display_name is not None: result["displayName"] = from_union([from_str, from_none], self.display_name) return result @@ -9325,7 +9315,7 @@ class SendAttachmentFile: path: str """Absolute file path""" - type: SendAttachmentFileType + type: ClassVar[str] = "file" """Attachment type discriminator""" line_range: SendAttachmentFileLineRange | None = None @@ -9336,15 +9326,14 @@ def from_dict(obj: Any) -> 'SendAttachmentFile': assert isinstance(obj, dict) display_name = from_str(obj.get("displayName")) path = from_str(obj.get("path")) - type = SendAttachmentFileType(obj.get("type")) line_range = from_union([SendAttachmentFileLineRange.from_dict, from_none], obj.get("lineRange")) - return SendAttachmentFile(display_name, path, type, line_range) + return SendAttachmentFile(display_name, path, line_range) def to_dict(self) -> dict: result: dict = {} result["displayName"] = from_str(self.display_name) result["path"] = from_str(self.path) - result["type"] = to_enum(SendAttachmentFileType, self.type) + result["type"] = self.type if self.line_range is not None: result["lineRange"] = from_union([lambda x: to_class(SendAttachmentFileLineRange, x), from_none], self.line_range) return result @@ -9366,7 +9355,7 @@ class SendAttachmentGithubReference: title: str """Title of the referenced item""" - type: SendAttachmentGithubReferenceType + type: ClassVar[str] = "github_reference" """Attachment type discriminator""" url: str @@ -9379,9 +9368,8 @@ def from_dict(obj: Any) -> 'SendAttachmentGithubReference': reference_type = SendAttachmentGithubReferenceTypeEnum(obj.get("referenceType")) state = from_str(obj.get("state")) title = from_str(obj.get("title")) - type = SendAttachmentGithubReferenceType(obj.get("type")) url = from_str(obj.get("url")) - return SendAttachmentGithubReference(number, reference_type, state, title, type, url) + return SendAttachmentGithubReference(number, reference_type, state, title, url) def to_dict(self) -> dict: result: dict = {} @@ -9389,10 +9377,112 @@ def to_dict(self) -> dict: result["referenceType"] = to_enum(SendAttachmentGithubReferenceTypeEnum, self.reference_type) result["state"] = from_str(self.state) result["title"] = from_str(self.title) - result["type"] = to_enum(SendAttachmentGithubReferenceType, self.type) + result["type"] = self.type result["url"] = from_str(self.url) return result +# Experimental: this type is part of an experimental API and may change or be removed. +@dataclass +class SendRequest: + """Parameters for sending a user message to the session""" + + prompt: str + """The user message text""" + + agent_mode: SendAgentMode | None = None + """The UI mode the agent was in when this message was sent. Defaults to the session's + current mode. + """ + attachments: list[SendAttachment] | None = None + """Optional attachments (files, directories, selections, blobs, GitHub references) to + include with the message + """ + billable: bool | None = None + """If false, this message will not trigger a Premium Request Unit charge. User messages + default to billable. + """ + display_prompt: str | None = None + """If provided, this is shown in the timeline instead of `prompt`""" + + mode: SendMode | None = None + """How to deliver the message. `enqueue` (default) appends to the message queue. `immediate` + interjects during an in-progress turn. + """ + prepend: bool | None = None + """If true, adds the message to the front of the queue instead of the end""" + + request_headers: dict[str, str] | None = None + """Custom HTTP headers to include in outbound model requests for this turn. Merged with + session-level provider headers; per-turn headers augment and overwrite session-level + headers with the same key. + """ + required_tool: str | None = None + """If set, the request will fail if the named tool is not available when this message is + among the user messages at the start of the current exchange + """ + source: Any = None + """Optional provenance tag copied to the resulting user.message event. Supported values are + `system`, `command-*`, and `schedule-*`. + """ + traceparent: str | None = None + """W3C Trace Context traceparent header for distributed tracing of this agent turn""" + + tracestate: str | None = None + """W3C Trace Context tracestate header for distributed tracing""" + + wait: bool | None = None + """If true, await completion of the agentic loop for this message before returning. Defaults + to false (fire-and-forget). When true, the result still contains the same `messageId`; + the caller can rely on the agent having processed the message before the call resolves. + """ + + @staticmethod + def from_dict(obj: Any) -> 'SendRequest': + assert isinstance(obj, dict) + prompt = from_str(obj.get("prompt")) + agent_mode = from_union([SendAgentMode, from_none], obj.get("agentMode")) + attachments = from_union([lambda x: from_list(_load_SendAttachment, x), from_none], obj.get("attachments")) + billable = from_union([from_bool, from_none], obj.get("billable")) + display_prompt = from_union([from_str, from_none], obj.get("displayPrompt")) + mode = from_union([SendMode, from_none], obj.get("mode")) + prepend = from_union([from_bool, from_none], obj.get("prepend")) + request_headers = from_union([lambda x: from_dict(from_str, x), from_none], obj.get("requestHeaders")) + required_tool = from_union([from_str, from_none], obj.get("requiredTool")) + source = obj.get("source") + traceparent = from_union([from_str, from_none], obj.get("traceparent")) + tracestate = from_union([from_str, from_none], obj.get("tracestate")) + wait = from_union([from_bool, from_none], obj.get("wait")) + return SendRequest(prompt, agent_mode, attachments, billable, display_prompt, mode, prepend, request_headers, required_tool, source, traceparent, tracestate, wait) + + def to_dict(self) -> dict: + result: dict = {} + result["prompt"] = from_str(self.prompt) + if self.agent_mode is not None: + result["agentMode"] = from_union([lambda x: to_enum(SendAgentMode, x), from_none], self.agent_mode) + if self.attachments is not None: + result["attachments"] = from_union([lambda x: from_list(lambda x: (x).to_dict(), x), from_none], self.attachments) + if self.billable is not None: + result["billable"] = from_union([from_bool, from_none], self.billable) + if self.display_prompt is not None: + result["displayPrompt"] = from_union([from_str, from_none], self.display_prompt) + if self.mode is not None: + result["mode"] = from_union([lambda x: to_enum(SendMode, x), from_none], self.mode) + if self.prepend is not None: + result["prepend"] = from_union([from_bool, from_none], self.prepend) + if self.request_headers is not None: + result["requestHeaders"] = from_union([lambda x: from_dict(from_str, x), from_none], self.request_headers) + if self.required_tool is not None: + result["requiredTool"] = from_union([from_str, from_none], self.required_tool) + if self.source is not None: + result["source"] = self.source + if self.traceparent is not None: + result["traceparent"] = from_union([from_str, from_none], self.traceparent) + if self.tracestate is not None: + result["tracestate"] = from_union([from_str, from_none], self.tracestate) + if self.wait is not None: + result["wait"] = from_union([from_bool, from_none], self.wait) + return result + @dataclass class ServerSkillList: """Skills discovered across global and project sources.""" @@ -9731,7 +9821,7 @@ class SlashCommandAgentPromptResult: display_prompt: str """Prompt text to display to the user""" - kind: SlashCommandAgentPromptResultKind + kind: ClassVar[str] = "agent-prompt" """Agent prompt result discriminator""" prompt: str @@ -9749,16 +9839,15 @@ class SlashCommandAgentPromptResult: def from_dict(obj: Any) -> 'SlashCommandAgentPromptResult': assert isinstance(obj, dict) display_prompt = from_str(obj.get("displayPrompt")) - kind = SlashCommandAgentPromptResultKind(obj.get("kind")) prompt = from_str(obj.get("prompt")) mode = from_union([SessionMode, from_none], obj.get("mode")) runtime_settings_changed = from_union([from_bool, from_none], obj.get("runtimeSettingsChanged")) - return SlashCommandAgentPromptResult(display_prompt, kind, prompt, mode, runtime_settings_changed) + return SlashCommandAgentPromptResult(display_prompt, prompt, mode, runtime_settings_changed) def to_dict(self) -> dict: result: dict = {} result["displayPrompt"] = from_str(self.display_prompt) - result["kind"] = to_enum(SlashCommandAgentPromptResultKind, self.kind) + result["kind"] = self.kind result["prompt"] = from_str(self.prompt) if self.mode is not None: result["mode"] = from_union([lambda x: to_enum(SessionMode, x), from_none], self.mode) @@ -9771,7 +9860,7 @@ def to_dict(self) -> dict: class SlashCommandCompletedResult: """Schema for the `SlashCommandCompletedResult` type.""" - kind: SlashCommandCompletedResultKind + kind: ClassVar[str] = "completed" """Completed result discriminator""" message: str | None = None @@ -9785,14 +9874,13 @@ class SlashCommandCompletedResult: @staticmethod def from_dict(obj: Any) -> 'SlashCommandCompletedResult': assert isinstance(obj, dict) - kind = SlashCommandCompletedResultKind(obj.get("kind")) message = from_union([from_str, from_none], obj.get("message")) runtime_settings_changed = from_union([from_bool, from_none], obj.get("runtimeSettingsChanged")) - return SlashCommandCompletedResult(kind, message, runtime_settings_changed) + return SlashCommandCompletedResult(message, runtime_settings_changed) def to_dict(self) -> dict: result: dict = {} - result["kind"] = to_enum(SlashCommandCompletedResultKind, self.kind) + result["kind"] = self.kind if self.message is not None: result["message"] = from_union([from_str, from_none], self.message) if self.runtime_settings_changed is not None: @@ -9807,7 +9895,7 @@ class SlashCommandSelectSubcommandResult: command: str """Parent command name that requires subcommand selection""" - kind: SlashCommandSelectSubcommandResultKind + kind: ClassVar[str] = "select-subcommand" """Select subcommand result discriminator""" options: list[SlashCommandSelectSubcommandOption] @@ -9825,16 +9913,15 @@ class SlashCommandSelectSubcommandResult: def from_dict(obj: Any) -> 'SlashCommandSelectSubcommandResult': assert isinstance(obj, dict) command = from_str(obj.get("command")) - kind = SlashCommandSelectSubcommandResultKind(obj.get("kind")) options = from_list(SlashCommandSelectSubcommandOption.from_dict, obj.get("options")) title = from_str(obj.get("title")) runtime_settings_changed = from_union([from_bool, from_none], obj.get("runtimeSettingsChanged")) - return SlashCommandSelectSubcommandResult(command, kind, options, title, runtime_settings_changed) + return SlashCommandSelectSubcommandResult(command, options, title, runtime_settings_changed) def to_dict(self) -> dict: result: dict = {} result["command"] = from_str(self.command) - result["kind"] = to_enum(SlashCommandSelectSubcommandResultKind, self.kind) + result["kind"] = self.kind result["options"] = from_list(lambda x: to_class(SlashCommandSelectSubcommandOption, x), self.options) result["title"] = from_str(self.title) if self.runtime_settings_changed is not None: @@ -9895,7 +9982,7 @@ class TaskShellInfo: status: TaskStatus """Current lifecycle status of the task""" - type: TaskShellInfoType + type: ClassVar[str] = "shell" """Task kind""" can_promote_to_background: bool | None = None @@ -9922,13 +10009,12 @@ def from_dict(obj: Any) -> 'TaskShellInfo': id = from_str(obj.get("id")) started_at = from_datetime(obj.get("startedAt")) status = TaskStatus(obj.get("status")) - type = TaskShellInfoType(obj.get("type")) can_promote_to_background = from_union([from_bool, from_none], obj.get("canPromoteToBackground")) completed_at = from_union([from_datetime, from_none], obj.get("completedAt")) execution_mode = from_union([TaskExecutionMode, from_none], obj.get("executionMode")) log_path = from_union([from_str, from_none], obj.get("logPath")) pid = from_union([from_int, from_none], obj.get("pid")) - return TaskShellInfo(attachment_mode, command, description, id, started_at, status, type, can_promote_to_background, completed_at, execution_mode, log_path, pid) + return TaskShellInfo(attachment_mode, command, description, id, started_at, status, can_promote_to_background, completed_at, execution_mode, log_path, pid) def to_dict(self) -> dict: result: dict = {} @@ -9938,7 +10024,7 @@ def to_dict(self) -> dict: result["id"] = from_str(self.id) result["startedAt"] = self.started_at.isoformat() result["status"] = to_enum(TaskStatus, self.status) - result["type"] = to_enum(TaskShellInfoType, self.type) + result["type"] = self.type if self.can_promote_to_background is not None: result["canPromoteToBackground"] = from_union([from_bool, from_none], self.can_promote_to_background) if self.completed_at is not None: @@ -9981,6 +10067,30 @@ def to_dict(self) -> dict: result["pid"] = from_union([from_int, from_none], self.pid) return result +# Experimental: this type is part of an experimental API and may change or be removed. +@dataclass +class PermissionLocationAddToolApprovalParams: + """Location-scoped tool approval to persist.""" + + approval: PermissionsLocationsAddToolApprovalDetails + """Tool approval to persist and apply""" + + location_key: str + """Location key (git root or cwd) to persist the approval to""" + + @staticmethod + def from_dict(obj: Any) -> 'PermissionLocationAddToolApprovalParams': + assert isinstance(obj, dict) + approval = _load_PermissionsLocationsAddToolApprovalDetails(obj.get("approval")) + location_key = from_str(obj.get("locationKey")) + return PermissionLocationAddToolApprovalParams(approval, location_key) + + def to_dict(self) -> dict: + result: dict = {} + result["approval"] = (self.approval).to_dict() + result["locationKey"] = from_str(self.location_key) + return result + @dataclass class ToolList: """Built-in tools available for the requested model, with their parameters and instructions.""" @@ -10711,20 +10821,19 @@ class PermissionDecisionApproveForLocationApprovalExtensionPermissionAccess: extension_name: str """Extension name.""" - kind: PermissionDecisionApproveForLocationApprovalExtensionPermissionAccessKind + kind: ClassVar[str] = "extension-permission-access" """Approval covering an extension's request to access a permission-gated capability.""" @staticmethod def from_dict(obj: Any) -> 'PermissionDecisionApproveForLocationApprovalExtensionPermissionAccess': assert isinstance(obj, dict) extension_name = from_str(obj.get("extensionName")) - kind = PermissionDecisionApproveForLocationApprovalExtensionPermissionAccessKind(obj.get("kind")) - return PermissionDecisionApproveForLocationApprovalExtensionPermissionAccess(extension_name, kind) + return PermissionDecisionApproveForLocationApprovalExtensionPermissionAccess(extension_name) def to_dict(self) -> dict: result: dict = {} result["extensionName"] = from_str(self.extension_name) - result["kind"] = to_enum(PermissionDecisionApproveForLocationApprovalExtensionPermissionAccessKind, self.kind) + result["kind"] = self.kind return result # Experimental: this type is part of an experimental API and may change or be removed. @@ -10736,20 +10845,19 @@ class PermissionDecisionApproveForSessionApprovalExtensionPermissionAccess: extension_name: str """Extension name.""" - kind: PermissionDecisionApproveForLocationApprovalExtensionPermissionAccessKind + kind: ClassVar[str] = "extension-permission-access" """Approval covering an extension's request to access a permission-gated capability.""" @staticmethod def from_dict(obj: Any) -> 'PermissionDecisionApproveForSessionApprovalExtensionPermissionAccess': assert isinstance(obj, dict) extension_name = from_str(obj.get("extensionName")) - kind = PermissionDecisionApproveForLocationApprovalExtensionPermissionAccessKind(obj.get("kind")) - return PermissionDecisionApproveForSessionApprovalExtensionPermissionAccess(extension_name, kind) + return PermissionDecisionApproveForSessionApprovalExtensionPermissionAccess(extension_name) def to_dict(self) -> dict: result: dict = {} result["extensionName"] = from_str(self.extension_name) - result["kind"] = to_enum(PermissionDecisionApproveForLocationApprovalExtensionPermissionAccessKind, self.kind) + result["kind"] = self.kind return result # Experimental: this type is part of an experimental API and may change or be removed. @@ -10760,179 +10868,75 @@ class PermissionsLocationsAddToolApprovalDetailsExtensionPermissionAccess: extension_name: str """Extension name.""" - kind: PermissionDecisionApproveForLocationApprovalExtensionPermissionAccessKind + kind: ClassVar[str] = "extension-permission-access" """Approval covering an extension's request to access a permission-gated capability.""" @staticmethod def from_dict(obj: Any) -> 'PermissionsLocationsAddToolApprovalDetailsExtensionPermissionAccess': assert isinstance(obj, dict) extension_name = from_str(obj.get("extensionName")) - kind = PermissionDecisionApproveForLocationApprovalExtensionPermissionAccessKind(obj.get("kind")) - return PermissionsLocationsAddToolApprovalDetailsExtensionPermissionAccess(extension_name, kind) - - def to_dict(self) -> dict: - result: dict = {} - result["extensionName"] = from_str(self.extension_name) - result["kind"] = to_enum(PermissionDecisionApproveForLocationApprovalExtensionPermissionAccessKind, self.kind) - return result - -@dataclass -class UserToolSessionApprovalExtensionManagement: - """Schema for the `UserToolSessionApprovalExtensionManagement` type.""" - - kind: PermissionDecisionApproveForLocationApprovalExtensionManagementKind - """Extension management approval kind""" - - operation: str | None = None - """Optional operation identifier""" - - @staticmethod - def from_dict(obj: Any) -> 'UserToolSessionApprovalExtensionManagement': - assert isinstance(obj, dict) - kind = PermissionDecisionApproveForLocationApprovalExtensionManagementKind(obj.get("kind")) - operation = from_union([from_str, from_none], obj.get("operation")) - return UserToolSessionApprovalExtensionManagement(kind, operation) - - def to_dict(self) -> dict: - result: dict = {} - result["kind"] = to_enum(PermissionDecisionApproveForLocationApprovalExtensionManagementKind, self.kind) - if self.operation is not None: - result["operation"] = from_union([from_str, from_none], self.operation) - return result - -@dataclass -class UserToolSessionApprovalExtensionPermissionAccess: - """Schema for the `UserToolSessionApprovalExtensionPermissionAccess` type.""" - - extension_name: str - """Extension name""" - - kind: PermissionDecisionApproveForLocationApprovalExtensionPermissionAccessKind - """Extension permission access approval kind""" - - @staticmethod - def from_dict(obj: Any) -> 'UserToolSessionApprovalExtensionPermissionAccess': - assert isinstance(obj, dict) - extension_name = from_str(obj.get("extensionName")) - kind = PermissionDecisionApproveForLocationApprovalExtensionPermissionAccessKind(obj.get("kind")) - return UserToolSessionApprovalExtensionPermissionAccess(extension_name, kind) + return PermissionsLocationsAddToolApprovalDetailsExtensionPermissionAccess(extension_name) def to_dict(self) -> dict: result: dict = {} result["extensionName"] = from_str(self.extension_name) - result["kind"] = to_enum(PermissionDecisionApproveForLocationApprovalExtensionPermissionAccessKind, self.kind) + result["kind"] = self.kind return result # Experimental: this type is part of an experimental API and may change or be removed. @dataclass -class ExternalToolTextResultForLlmContent: - """A content block within a tool result, which may be text, terminal output, image, audio, - or a resource - - Plain text content block - - Terminal/shell output content block with optional exit code and working directory - - Image content block with base64-encoded data - - Audio content block with base64-encoded data - - Resource link content block referencing an external resource - - Embedded resource content block with inline text or binary data - """ - type: ExternalToolTextResultForLlmContentType - """Content block type discriminator""" - - text: str | None = None - """The text content - - Terminal/shell output text - """ - cwd: str | None = None - """Working directory where the command was executed""" +class ExternalToolTextResultForLlm: + """Expanded external tool result payload""" - exit_code: int | None = None - """Process exit code, if the command has completed""" + text_result_for_llm: str + """Text result returned to the model""" - data: str | None = None - """Base64-encoded image data + binary_results_for_llm: list[ExternalToolTextResultForLlmBinaryResultsForLlm] | None = None + """Base64-encoded binary results returned to the model""" - Base64-encoded audio data - """ - mime_type: str | None = None - """MIME type of the image (e.g., image/png, image/jpeg) + contents: list[ExternalToolTextResultForLlmContent] | None = None + """Structured content blocks from the tool""" - MIME type of the audio (e.g., audio/wav, audio/mpeg) + error: str | None = None + """Optional error message for failed executions""" - MIME type of the resource content + result_type: str | None = None + """Execution outcome classification. Optional for back-compat; normalized to 'success' (or + 'failure' when error is present) when missing or unrecognized. """ - description: str | None = None - """Human-readable description of the resource""" - - icons: list[ExternalToolTextResultForLlmContentResourceLinkIcon] | None = None - """Icons associated with this resource""" - - name: str | None = None - """Resource name identifier""" - - size: int | None = None - """Size of the resource in bytes""" - - title: str | None = None - """Human-readable display title for the resource""" - - uri: str | None = None - """URI identifying the resource""" + session_log: str | None = None + """Detailed log content for timeline display""" - resource: ExternalToolTextResultForLlmContentResourceDetails | None = None - """The embedded resource contents, either text or base64-encoded binary""" + tool_telemetry: dict[str, Any] | None = None + """Optional tool-specific telemetry""" @staticmethod - def from_dict(obj: Any) -> 'ExternalToolTextResultForLlmContent': + def from_dict(obj: Any) -> 'ExternalToolTextResultForLlm': assert isinstance(obj, dict) - type = ExternalToolTextResultForLlmContentType(obj.get("type")) - text = from_union([from_str, from_none], obj.get("text")) - cwd = from_union([from_str, from_none], obj.get("cwd")) - exit_code = from_union([from_int, from_none], obj.get("exitCode")) - data = from_union([from_str, from_none], obj.get("data")) - mime_type = from_union([from_str, from_none], obj.get("mimeType")) - description = from_union([from_str, from_none], obj.get("description")) - icons = from_union([lambda x: from_list(ExternalToolTextResultForLlmContentResourceLinkIcon.from_dict, x), from_none], obj.get("icons")) - name = from_union([from_str, from_none], obj.get("name")) - size = from_union([from_int, from_none], obj.get("size")) - title = from_union([from_str, from_none], obj.get("title")) - uri = from_union([from_str, from_none], obj.get("uri")) - resource = from_union([(lambda x: from_union([EmbeddedTextResourceContents.from_dict, EmbeddedBlobResourceContents.from_dict], x)), from_none], obj.get("resource")) - return ExternalToolTextResultForLlmContent(type, text, cwd, exit_code, data, mime_type, description, icons, name, size, title, uri, resource) + text_result_for_llm = from_str(obj.get("textResultForLlm")) + binary_results_for_llm = from_union([lambda x: from_list(ExternalToolTextResultForLlmBinaryResultsForLlm.from_dict, x), from_none], obj.get("binaryResultsForLlm")) + contents = from_union([lambda x: from_list(_load_ExternalToolTextResultForLlmContent, x), from_none], obj.get("contents")) + error = from_union([from_str, from_none], obj.get("error")) + result_type = from_union([from_str, from_none], obj.get("resultType")) + session_log = from_union([from_str, from_none], obj.get("sessionLog")) + tool_telemetry = from_union([lambda x: from_dict(lambda x: x, x), from_none], obj.get("toolTelemetry")) + return ExternalToolTextResultForLlm(text_result_for_llm, binary_results_for_llm, contents, error, result_type, session_log, tool_telemetry) def to_dict(self) -> dict: result: dict = {} - result["type"] = to_enum(ExternalToolTextResultForLlmContentType, self.type) - if self.text is not None: - result["text"] = from_union([from_str, from_none], self.text) - if self.cwd is not None: - result["cwd"] = from_union([from_str, from_none], self.cwd) - if self.exit_code is not None: - result["exitCode"] = from_union([from_int, from_none], self.exit_code) - if self.data is not None: - result["data"] = from_union([from_str, from_none], self.data) - if self.mime_type is not None: - result["mimeType"] = from_union([from_str, from_none], self.mime_type) - if self.description is not None: - result["description"] = from_union([from_str, from_none], self.description) - if self.icons is not None: - result["icons"] = from_union([lambda x: from_list(lambda x: to_class(ExternalToolTextResultForLlmContentResourceLinkIcon, x), x), from_none], self.icons) - if self.name is not None: - result["name"] = from_union([from_str, from_none], self.name) - if self.size is not None: - result["size"] = from_union([from_int, from_none], self.size) - if self.title is not None: - result["title"] = from_union([from_str, from_none], self.title) - if self.uri is not None: - result["uri"] = from_union([from_str, from_none], self.uri) - if self.resource is not None: - result["resource"] = from_union([lambda x: from_union([lambda x: to_class(EmbeddedTextResourceContents, x), lambda x: to_class(EmbeddedBlobResourceContents, x)], x), from_none], self.resource) + result["textResultForLlm"] = from_str(self.text_result_for_llm) + if self.binary_results_for_llm is not None: + result["binaryResultsForLlm"] = from_union([lambda x: from_list(lambda x: to_class(ExternalToolTextResultForLlmBinaryResultsForLlm, x), x), from_none], self.binary_results_for_llm) + if self.contents is not None: + result["contents"] = from_union([lambda x: from_list(lambda x: (x).to_dict(), x), from_none], self.contents) + if self.error is not None: + result["error"] = from_union([from_str, from_none], self.error) + if self.result_type is not None: + result["resultType"] = from_union([from_str, from_none], self.result_type) + if self.session_log is not None: + result["sessionLog"] = from_union([from_str, from_none], self.session_log) + if self.tool_telemetry is not None: + result["toolTelemetry"] = from_union([lambda x: from_dict(lambda x: x, x), from_none], self.tool_telemetry) return result # Experimental: this type is part of an experimental API and may change or be removed. @@ -10943,7 +10947,7 @@ class ExternalToolTextResultForLlmContentResourceLink: name: str """Resource name identifier""" - type: ExternalToolTextResultForLlmContentResourceLinkType + type: ClassVar[str] = "resource_link" """Content block type discriminator""" uri: str @@ -10968,19 +10972,18 @@ class ExternalToolTextResultForLlmContentResourceLink: def from_dict(obj: Any) -> 'ExternalToolTextResultForLlmContentResourceLink': assert isinstance(obj, dict) name = from_str(obj.get("name")) - type = ExternalToolTextResultForLlmContentResourceLinkType(obj.get("type")) uri = from_str(obj.get("uri")) description = from_union([from_str, from_none], obj.get("description")) icons = from_union([lambda x: from_list(ExternalToolTextResultForLlmContentResourceLinkIcon.from_dict, x), from_none], obj.get("icons")) mime_type = from_union([from_str, from_none], obj.get("mimeType")) size = from_union([from_int, from_none], obj.get("size")) title = from_union([from_str, from_none], obj.get("title")) - return ExternalToolTextResultForLlmContentResourceLink(name, type, uri, description, icons, mime_type, size, title) + return ExternalToolTextResultForLlmContentResourceLink(name, uri, description, icons, mime_type, size, title) def to_dict(self) -> dict: result: dict = {} result["name"] = from_str(self.name) - result["type"] = to_enum(ExternalToolTextResultForLlmContentResourceLinkType, self.type) + result["type"] = self.type result["uri"] = from_str(self.uri) if self.description is not None: result["description"] = from_union([from_str, from_none], self.description) @@ -11509,144 +11512,8 @@ def to_dict(self) -> dict: # Experimental: this type is part of an experimental API and may change or be removed. @dataclass -class CommandsRespondToQueuedCommandRequest: - """Queued-command request ID and the result indicating whether the host executed it (and - whether to stop processing further queued commands). - """ - request_id: str - """Request ID from the `command.queued` event the host is responding to.""" - - result: QueuedCommandResult - """Result of the queued command execution.""" - - @staticmethod - def from_dict(obj: Any) -> 'CommandsRespondToQueuedCommandRequest': - assert isinstance(obj, dict) - request_id = from_str(obj.get("requestId")) - result = QueuedCommandResult.from_dict(obj.get("result")) - return CommandsRespondToQueuedCommandRequest(request_id, result) - - def to_dict(self) -> dict: - result: dict = {} - result["requestId"] = from_str(self.request_id) - result["result"] = to_class(QueuedCommandResult, self.result) - return result - -# Experimental: this type is part of an experimental API and may change or be removed. -@dataclass -class SendAttachment: - """A user message attachment — a file, directory, code selection, blob, or GitHub reference - - File attachment - - Directory attachment - - Code selection attachment from an editor - - GitHub issue, pull request, or discussion reference - - Blob attachment with inline base64-encoded data - """ - type: SendAttachmentType - """Attachment type discriminator""" - - display_name: str | None = None - """User-facing display name for the attachment - - User-facing display name for the selection - """ - line_range: SendAttachmentFileLineRange | None = None - """Optional line range to scope the attachment to a specific section of the file""" - - path: str | None = None - """Absolute file path - - Absolute directory path - """ - file_path: str | None = None - """Absolute path to the file containing the selection""" - - selection: SendAttachmentSelectionDetails | None = None - """Position range of the selection within the file""" - - text: str | None = None - """The selected text content""" - - number: int | None = None - """Issue, pull request, or discussion number""" - - reference_type: SendAttachmentGithubReferenceTypeEnum | None = None - """Type of GitHub reference""" - - state: str | None = None - """Current state of the referenced item (e.g., open, closed, merged)""" - - title: str | None = None - """Title of the referenced item""" - - url: str | None = None - """URL to the referenced item on GitHub""" - - data: str | None = None - """Base64-encoded content""" - - mime_type: str | None = None - """MIME type of the inline data""" - - @staticmethod - def from_dict(obj: Any) -> 'SendAttachment': - assert isinstance(obj, dict) - type = SendAttachmentType(obj.get("type")) - display_name = from_union([from_str, from_none], obj.get("displayName")) - line_range = from_union([SendAttachmentFileLineRange.from_dict, from_none], obj.get("lineRange")) - path = from_union([from_str, from_none], obj.get("path")) - file_path = from_union([from_str, from_none], obj.get("filePath")) - selection = from_union([SendAttachmentSelectionDetails.from_dict, from_none], obj.get("selection")) - text = from_union([from_str, from_none], obj.get("text")) - number = from_union([from_int, from_none], obj.get("number")) - reference_type = from_union([SendAttachmentGithubReferenceTypeEnum, from_none], obj.get("referenceType")) - state = from_union([from_str, from_none], obj.get("state")) - title = from_union([from_str, from_none], obj.get("title")) - url = from_union([from_str, from_none], obj.get("url")) - data = from_union([from_str, from_none], obj.get("data")) - mime_type = from_union([from_str, from_none], obj.get("mimeType")) - return SendAttachment(type, display_name, line_range, path, file_path, selection, text, number, reference_type, state, title, url, data, mime_type) - - def to_dict(self) -> dict: - result: dict = {} - result["type"] = to_enum(SendAttachmentType, self.type) - if self.display_name is not None: - result["displayName"] = from_union([from_str, from_none], self.display_name) - if self.line_range is not None: - result["lineRange"] = from_union([lambda x: to_class(SendAttachmentFileLineRange, x), from_none], self.line_range) - if self.path is not None: - result["path"] = from_union([from_str, from_none], self.path) - if self.file_path is not None: - result["filePath"] = from_union([from_str, from_none], self.file_path) - if self.selection is not None: - result["selection"] = from_union([lambda x: to_class(SendAttachmentSelectionDetails, x), from_none], self.selection) - if self.text is not None: - result["text"] = from_union([from_str, from_none], self.text) - if self.number is not None: - result["number"] = from_union([from_int, from_none], self.number) - if self.reference_type is not None: - result["referenceType"] = from_union([lambda x: to_enum(SendAttachmentGithubReferenceTypeEnum, x), from_none], self.reference_type) - if self.state is not None: - result["state"] = from_union([from_str, from_none], self.state) - if self.title is not None: - result["title"] = from_union([from_str, from_none], self.title) - if self.url is not None: - result["url"] = from_union([from_str, from_none], self.url) - if self.data is not None: - result["data"] = from_union([from_str, from_none], self.data) - if self.mime_type is not None: - result["mimeType"] = from_union([from_str, from_none], self.mime_type) - return result - -# Experimental: this type is part of an experimental API and may change or be removed. -@dataclass -class SendAttachmentSelection: - """Code selection attachment from an editor""" +class SendAttachmentSelection: + """Code selection attachment from an editor""" display_name: str """User-facing display name for the selection""" @@ -11660,7 +11527,7 @@ class SendAttachmentSelection: text: str """The selected text content""" - type: SendAttachmentSelectionType + type: ClassVar[str] = "selection" """Attachment type discriminator""" @staticmethod @@ -11670,8 +11537,7 @@ def from_dict(obj: Any) -> 'SendAttachmentSelection': file_path = from_str(obj.get("filePath")) selection = SendAttachmentSelectionDetails.from_dict(obj.get("selection")) text = from_str(obj.get("text")) - type = SendAttachmentSelectionType(obj.get("type")) - return SendAttachmentSelection(display_name, file_path, selection, text, type) + return SendAttachmentSelection(display_name, file_path, selection, text) def to_dict(self) -> dict: result: dict = {} @@ -11679,7 +11545,7 @@ def to_dict(self) -> dict: result["filePath"] = from_str(self.file_path) result["selection"] = to_class(SendAttachmentSelectionDetails, self.selection) result["text"] = from_str(self.text) - result["type"] = to_enum(SendAttachmentSelectionType, self.type) + result["type"] = self.type return result @dataclass @@ -11917,107 +11783,6 @@ def to_dict(self) -> dict: result["agent"] = to_class(AgentInfo, self.agent) return result -# Experimental: this type is part of an experimental API and may change or be removed. -@dataclass -class SlashCommandInvocationResult: - """Result of invoking the slash command (text output, prompt to send to the agent, or - completion). - - Schema for the `SlashCommandTextResult` type. - - Schema for the `SlashCommandAgentPromptResult` type. - - Schema for the `SlashCommandCompletedResult` type. - - Schema for the `SlashCommandSelectSubcommandResult` type. - """ - kind: SlashCommandInvocationResultKind - """Text result discriminator - - Agent prompt result discriminator - - Completed result discriminator - - Select subcommand result discriminator - """ - markdown: bool | None = None - """Whether text contains Markdown""" - - preserve_ansi: bool | None = None - """Whether ANSI sequences should be preserved""" - - runtime_settings_changed: bool | None = None - """True when the invocation mutated user runtime settings; consumers caching settings should - refresh - """ - text: str | None = None - """Text output for the client to render""" - - display_prompt: str | None = None - """Prompt text to display to the user""" - - mode: SessionMode | None = None - """Optional target session mode for the agent prompt""" - - prompt: str | None = None - """Prompt to submit to the agent""" - - message: str | None = None - """Optional user-facing message describing the completed command""" - - command: str | None = None - """Parent command name that requires subcommand selection""" - - options: list[SlashCommandSelectSubcommandOption] | None = None - """Available subcommand options for the client to present""" - - title: str | None = None - """Human-readable title for the selection UI""" - - @staticmethod - def from_dict(obj: Any) -> 'SlashCommandInvocationResult': - assert isinstance(obj, dict) - kind = SlashCommandInvocationResultKind(obj.get("kind")) - markdown = from_union([from_bool, from_none], obj.get("markdown")) - preserve_ansi = from_union([from_bool, from_none], obj.get("preserveAnsi")) - runtime_settings_changed = from_union([from_bool, from_none], obj.get("runtimeSettingsChanged")) - text = from_union([from_str, from_none], obj.get("text")) - display_prompt = from_union([from_str, from_none], obj.get("displayPrompt")) - mode = from_union([SessionMode, from_none], obj.get("mode")) - prompt = from_union([from_str, from_none], obj.get("prompt")) - message = from_union([from_str, from_none], obj.get("message")) - command = from_union([from_str, from_none], obj.get("command")) - options = from_union([lambda x: from_list(SlashCommandSelectSubcommandOption.from_dict, x), from_none], obj.get("options")) - title = from_union([from_str, from_none], obj.get("title")) - return SlashCommandInvocationResult(kind, markdown, preserve_ansi, runtime_settings_changed, text, display_prompt, mode, prompt, message, command, options, title) - - def to_dict(self) -> dict: - result: dict = {} - result["kind"] = to_enum(SlashCommandInvocationResultKind, self.kind) - if self.markdown is not None: - result["markdown"] = from_union([from_bool, from_none], self.markdown) - if self.preserve_ansi is not None: - result["preserveAnsi"] = from_union([from_bool, from_none], self.preserve_ansi) - if self.runtime_settings_changed is not None: - result["runtimeSettingsChanged"] = from_union([from_bool, from_none], self.runtime_settings_changed) - if self.text is not None: - result["text"] = from_union([from_str, from_none], self.text) - if self.display_prompt is not None: - result["displayPrompt"] = from_union([from_str, from_none], self.display_prompt) - if self.mode is not None: - result["mode"] = from_union([lambda x: to_enum(SessionMode, x), from_none], self.mode) - if self.prompt is not None: - result["prompt"] = from_union([from_str, from_none], self.prompt) - if self.message is not None: - result["message"] = from_union([from_str, from_none], self.message) - if self.command is not None: - result["command"] = from_union([from_str, from_none], self.command) - if self.options is not None: - result["options"] = from_union([lambda x: from_list(lambda x: to_class(SlashCommandSelectSubcommandOption, x), x), from_none], self.options) - if self.title is not None: - result["title"] = from_union([from_str, from_none], self.title) - return result - # Experimental: this type is part of an experimental API and may change or be removed. @dataclass class TaskProgress: @@ -12451,7 +12216,7 @@ class APIKeyAuthInfo: host: str """Authentication host.""" - type: APIKeyAuthInfoType + type: ClassVar[str] = "api-key" """API-key authentication for non-GitHub LLM providers (e.g. when running BYOM-style).""" copilot_user: CopilotUserResponse | None = None @@ -12465,15 +12230,14 @@ def from_dict(obj: Any) -> 'APIKeyAuthInfo': assert isinstance(obj, dict) api_key = from_str(obj.get("apiKey")) host = from_str(obj.get("host")) - type = APIKeyAuthInfoType(obj.get("type")) copilot_user = from_union([CopilotUserResponse.from_dict, from_none], obj.get("copilotUser")) - return APIKeyAuthInfo(api_key, host, type, copilot_user) + return APIKeyAuthInfo(api_key, host, copilot_user) def to_dict(self) -> dict: result: dict = {} result["apiKey"] = from_str(self.api_key) result["host"] = from_str(self.host) - result["type"] = to_enum(APIKeyAuthInfoType, self.type) + result["type"] = self.type if self.copilot_user is not None: result["copilotUser"] = from_union([lambda x: to_class(CopilotUserResponse, x), from_none], self.copilot_user) return result @@ -12486,7 +12250,7 @@ class CopilotAPITokenAuthInfo: host: Host """Authentication host (always the public GitHub host).""" - type: CopilotAPITokenAuthInfoType + type: ClassVar[str] = "copilot-api-token" """Direct Copilot API authentication via the `GITHUB_COPILOT_API_TOKEN` + `COPILOT_API_URL` environment-variable pair. The token itself is read from the environment by the runtime, not carried in this struct. @@ -12501,14 +12265,13 @@ class CopilotAPITokenAuthInfo: def from_dict(obj: Any) -> 'CopilotAPITokenAuthInfo': assert isinstance(obj, dict) host = Host(obj.get("host")) - type = CopilotAPITokenAuthInfoType(obj.get("type")) copilot_user = from_union([CopilotUserResponse.from_dict, from_none], obj.get("copilotUser")) - return CopilotAPITokenAuthInfo(host, type, copilot_user) + return CopilotAPITokenAuthInfo(host, copilot_user) def to_dict(self) -> dict: result: dict = {} result["host"] = to_enum(Host, self.host) - result["type"] = to_enum(CopilotAPITokenAuthInfoType, self.type) + result["type"] = self.type if self.copilot_user is not None: result["copilotUser"] = from_union([lambda x: to_class(CopilotUserResponse, x), from_none], self.copilot_user) return result @@ -12527,7 +12290,7 @@ class EnvAuthInfo: token: str """The token value itself. Treat as a secret.""" - type: EnvAuthInfoType + type: ClassVar[str] = "env" """Personal access token (PAT) or server-to-server token sourced from an environment variable. """ @@ -12547,17 +12310,16 @@ def from_dict(obj: Any) -> 'EnvAuthInfo': env_var = from_str(obj.get("envVar")) host = from_str(obj.get("host")) token = from_str(obj.get("token")) - type = EnvAuthInfoType(obj.get("type")) copilot_user = from_union([CopilotUserResponse.from_dict, from_none], obj.get("copilotUser")) login = from_union([from_str, from_none], obj.get("login")) - return EnvAuthInfo(env_var, host, token, type, copilot_user, login) + return EnvAuthInfo(env_var, host, token, copilot_user, login) def to_dict(self) -> dict: result: dict = {} result["envVar"] = from_str(self.env_var) result["host"] = from_str(self.host) result["token"] = from_str(self.token) - result["type"] = to_enum(EnvAuthInfoType, self.type) + result["type"] = self.type if self.copilot_user is not None: result["copilotUser"] = from_union([lambda x: to_class(CopilotUserResponse, x), from_none], self.copilot_user) if self.login is not None: @@ -12578,7 +12340,7 @@ class GhCLIAuthInfo: token: str """The token returned by `gh auth token`. Treat as a secret.""" - type: GhCLIAuthInfoType + type: ClassVar[str] = "gh-cli" """Authentication via the `gh` CLI's saved credentials.""" copilot_user: CopilotUserResponse | None = None @@ -12593,16 +12355,15 @@ def from_dict(obj: Any) -> 'GhCLIAuthInfo': host = from_str(obj.get("host")) login = from_str(obj.get("login")) token = from_str(obj.get("token")) - type = GhCLIAuthInfoType(obj.get("type")) copilot_user = from_union([CopilotUserResponse.from_dict, from_none], obj.get("copilotUser")) - return GhCLIAuthInfo(host, login, token, type, copilot_user) + return GhCLIAuthInfo(host, login, token, copilot_user) def to_dict(self) -> dict: result: dict = {} result["host"] = from_str(self.host) result["login"] = from_str(self.login) result["token"] = from_str(self.token) - result["type"] = to_enum(GhCLIAuthInfoType, self.type) + result["type"] = self.type if self.copilot_user is not None: result["copilotUser"] = from_union([lambda x: to_class(CopilotUserResponse, x), from_none], self.copilot_user) return result @@ -12618,7 +12379,7 @@ class HMACAuthInfo: host: Host """Authentication host. HMAC auth always targets the public GitHub host.""" - type: HMACAuthInfoType + type: ClassVar[str] = "hmac" """HMAC-based authentication used by GitHub-internal services.""" copilot_user: CopilotUserResponse | None = None @@ -12632,15 +12393,14 @@ def from_dict(obj: Any) -> 'HMACAuthInfo': assert isinstance(obj, dict) hmac = from_str(obj.get("hmac")) host = Host(obj.get("host")) - type = HMACAuthInfoType(obj.get("type")) copilot_user = from_union([CopilotUserResponse.from_dict, from_none], obj.get("copilotUser")) - return HMACAuthInfo(hmac, host, type, copilot_user) + return HMACAuthInfo(hmac, host, copilot_user) def to_dict(self) -> dict: result: dict = {} result["hmac"] = from_str(self.hmac) result["host"] = to_enum(Host, self.host) - result["type"] = to_enum(HMACAuthInfoType, self.type) + result["type"] = self.type if self.copilot_user is not None: result["copilotUser"] = from_union([lambda x: to_class(CopilotUserResponse, x), from_none], self.copilot_user) return result @@ -12656,7 +12416,7 @@ class TokenAuthInfo: token: str """The token value itself. Treat as a secret.""" - type: TokenAuthInfoType + type: ClassVar[str] = "token" """SDK-side token authentication; the host configured the token directly via the SDK.""" copilot_user: CopilotUserResponse | None = None @@ -12670,15 +12430,14 @@ def from_dict(obj: Any) -> 'TokenAuthInfo': assert isinstance(obj, dict) host = from_str(obj.get("host")) token = from_str(obj.get("token")) - type = TokenAuthInfoType(obj.get("type")) copilot_user = from_union([CopilotUserResponse.from_dict, from_none], obj.get("copilotUser")) - return TokenAuthInfo(host, token, type, copilot_user) + return TokenAuthInfo(host, token, copilot_user) def to_dict(self) -> dict: result: dict = {} result["host"] = from_str(self.host) result["token"] = from_str(self.token) - result["type"] = to_enum(TokenAuthInfoType, self.type) + result["type"] = self.type if self.copilot_user is not None: result["copilotUser"] = from_union([lambda x: to_class(CopilotUserResponse, x), from_none], self.copilot_user) return result @@ -12694,7 +12453,7 @@ class UserAuthInfo: login: str """OAuth user login.""" - type: UserAuthInfoType + type: ClassVar[str] = "user" """OAuth user authentication. The token itself is held in the runtime's secret token store (keyed by host+login) and is NOT carried in this struct. """ @@ -12709,106 +12468,18 @@ def from_dict(obj: Any) -> 'UserAuthInfo': assert isinstance(obj, dict) host = from_str(obj.get("host")) login = from_str(obj.get("login")) - type = UserAuthInfoType(obj.get("type")) copilot_user = from_union([CopilotUserResponse.from_dict, from_none], obj.get("copilotUser")) - return UserAuthInfo(host, login, type, copilot_user) + return UserAuthInfo(host, login, copilot_user) def to_dict(self) -> dict: result: dict = {} result["host"] = from_str(self.host) result["login"] = from_str(self.login) - result["type"] = to_enum(UserAuthInfoType, self.type) + result["type"] = self.type if self.copilot_user is not None: result["copilotUser"] = from_union([lambda x: to_class(CopilotUserResponse, x), from_none], self.copilot_user) return result -# Experimental: this type is part of an experimental API and may change or be removed. -@dataclass -class PermissionDecisionApproveForLocationApproval: - """Approval to persist for this location - - Schema for the `PermissionDecisionApproveForLocationApprovalCommands` type. - - Schema for the `PermissionDecisionApproveForLocationApprovalRead` type. - - Schema for the `PermissionDecisionApproveForLocationApprovalWrite` type. - - Schema for the `PermissionDecisionApproveForLocationApprovalMcp` type. - - Schema for the `PermissionDecisionApproveForLocationApprovalMcpSampling` type. - - Schema for the `PermissionDecisionApproveForLocationApprovalMemory` type. - - Schema for the `PermissionDecisionApproveForLocationApprovalCustomTool` type. - - Schema for the `PermissionDecisionApproveForLocationApprovalExtensionManagement` type. - - Schema for the `PermissionDecisionApproveForLocationApprovalExtensionPermissionAccess` - type. - """ - kind: ApprovalKind - """Approval scoped to specific command identifiers. - - Approval covering read-only filesystem operations. - - Approval covering filesystem write operations. - - Approval covering an MCP tool. - - Approval covering MCP sampling requests for a server. - - Approval covering writes to long-term memory. - - Approval covering a custom tool. - - Approval covering extension lifecycle operations such as enable, disable, or reload. - - Approval covering an extension's request to access a permission-gated capability. - """ - command_identifiers: list[str] | None = None - """Command identifiers covered by this approval.""" - - server_name: str | None = None - """MCP server name.""" - - tool_name: str | None = None - """MCP tool name, or null to cover every tool on the server. - - Custom tool name. - """ - operation: str | None = None - """Optional operation identifier; when omitted, the approval covers all extension management - operations. - """ - extension_name: str | None = None - """Extension name.""" - - @staticmethod - def from_dict(obj: Any) -> 'PermissionDecisionApproveForLocationApproval': - assert isinstance(obj, dict) - kind = ApprovalKind(obj.get("kind")) - command_identifiers = from_union([lambda x: from_list(from_str, x), from_none], obj.get("commandIdentifiers")) - server_name = from_union([from_str, from_none], obj.get("serverName")) - tool_name = from_union([from_none, from_str], obj.get("toolName")) - operation = from_union([from_str, from_none], obj.get("operation")) - extension_name = from_union([from_str, from_none], obj.get("extensionName")) - return PermissionDecisionApproveForLocationApproval(kind, command_identifiers, server_name, tool_name, operation, extension_name) - - def to_dict(self) -> dict: - result: dict = {} - result["kind"] = to_enum(ApprovalKind, self.kind) - if self.command_identifiers is not None: - result["commandIdentifiers"] = from_union([lambda x: from_list(from_str, x), from_none], self.command_identifiers) - if self.server_name is not None: - result["serverName"] = from_union([from_str, from_none], self.server_name) - if self.tool_name is not None: - result["toolName"] = from_union([from_none, from_str], self.tool_name) - if self.operation is not None: - result["operation"] = from_union([from_str, from_none], self.operation) - if self.extension_name is not None: - result["extensionName"] = from_union([from_str, from_none], self.extension_name) - return result - @dataclass class PermissionDecisionApproveForIonApproval: """Session-scoped approval to remember (tool prompts only; omitted for path/url prompts) @@ -12928,231 +12599,34 @@ def to_dict(self) -> dict: # Experimental: this type is part of an experimental API and may change or be removed. @dataclass -class PermissionDecisionApproveForSessionApproval: - """Session-scoped approval to remember (tool prompts only; omitted for path/url prompts) - - Schema for the `PermissionDecisionApproveForSessionApprovalCommands` type. - - Schema for the `PermissionDecisionApproveForSessionApprovalRead` type. - - Schema for the `PermissionDecisionApproveForSessionApprovalWrite` type. - - Schema for the `PermissionDecisionApproveForSessionApprovalMcp` type. - - Schema for the `PermissionDecisionApproveForSessionApprovalMcpSampling` type. - - Schema for the `PermissionDecisionApproveForSessionApprovalMemory` type. - - Schema for the `PermissionDecisionApproveForSessionApprovalCustomTool` type. - - Schema for the `PermissionDecisionApproveForSessionApprovalExtensionManagement` type. - - Schema for the `PermissionDecisionApproveForSessionApprovalExtensionPermissionAccess` - type. - """ - kind: ApprovalKind - """Approval scoped to specific command identifiers. - - Approval covering read-only filesystem operations. - - Approval covering filesystem write operations. - - Approval covering an MCP tool. - - Approval covering MCP sampling requests for a server. - - Approval covering writes to long-term memory. - - Approval covering a custom tool. - - Approval covering extension lifecycle operations such as enable, disable, or reload. - - Approval covering an extension's request to access a permission-gated capability. - """ - command_identifiers: list[str] | None = None - """Command identifiers covered by this approval.""" - - server_name: str | None = None - """MCP server name.""" - - tool_name: str | None = None - """MCP tool name, or null to cover every tool on the server. - - Custom tool name. - """ - operation: str | None = None - """Optional operation identifier; when omitted, the approval covers all extension management - operations. - """ - extension_name: str | None = None - """Extension name.""" - - @staticmethod - def from_dict(obj: Any) -> 'PermissionDecisionApproveForSessionApproval': - assert isinstance(obj, dict) - kind = ApprovalKind(obj.get("kind")) - command_identifiers = from_union([lambda x: from_list(from_str, x), from_none], obj.get("commandIdentifiers")) - server_name = from_union([from_str, from_none], obj.get("serverName")) - tool_name = from_union([from_none, from_str], obj.get("toolName")) - operation = from_union([from_str, from_none], obj.get("operation")) - extension_name = from_union([from_str, from_none], obj.get("extensionName")) - return PermissionDecisionApproveForSessionApproval(kind, command_identifiers, server_name, tool_name, operation, extension_name) - - def to_dict(self) -> dict: - result: dict = {} - result["kind"] = to_enum(ApprovalKind, self.kind) - if self.command_identifiers is not None: - result["commandIdentifiers"] = from_union([lambda x: from_list(from_str, x), from_none], self.command_identifiers) - if self.server_name is not None: - result["serverName"] = from_union([from_str, from_none], self.server_name) - if self.tool_name is not None: - result["toolName"] = from_union([from_none, from_str], self.tool_name) - if self.operation is not None: - result["operation"] = from_union([from_str, from_none], self.operation) - if self.extension_name is not None: - result["extensionName"] = from_union([from_str, from_none], self.extension_name) - return result - -# Experimental: this type is part of an experimental API and may change or be removed. -@dataclass -class PermissionsLocationsAddToolApprovalDetails: - """Tool approval to persist and apply - - Schema for the `PermissionsLocationsAddToolApprovalDetailsCommands` type. - - Schema for the `PermissionsLocationsAddToolApprovalDetailsRead` type. - - Schema for the `PermissionsLocationsAddToolApprovalDetailsWrite` type. - - Schema for the `PermissionsLocationsAddToolApprovalDetailsMcp` type. - - Schema for the `PermissionsLocationsAddToolApprovalDetailsMcpSampling` type. - - Schema for the `PermissionsLocationsAddToolApprovalDetailsMemory` type. - - Schema for the `PermissionsLocationsAddToolApprovalDetailsCustomTool` type. - - Schema for the `PermissionsLocationsAddToolApprovalDetailsExtensionManagement` type. - - Schema for the `PermissionsLocationsAddToolApprovalDetailsExtensionPermissionAccess` type. - """ - kind: ApprovalKind - """Approval scoped to specific command identifiers. - - Approval covering read-only filesystem operations. - - Approval covering filesystem write operations. - - Approval covering an MCP tool. - - Approval covering MCP sampling requests for a server. - - Approval covering writes to long-term memory. - - Approval covering a custom tool. - - Approval covering extension lifecycle operations such as enable, disable, or reload. - - Approval covering an extension's request to access a permission-gated capability. - """ - command_identifiers: list[str] | None = None - """Command identifiers covered by this approval.""" - - server_name: str | None = None - """MCP server name.""" - - tool_name: str | None = None - """MCP tool name, or null to cover every tool on the server. - - Custom tool name. - """ - operation: str | None = None - """Optional operation identifier; when omitted, the approval covers all extension management - operations. - """ - extension_name: str | None = None - """Extension name.""" - - @staticmethod - def from_dict(obj: Any) -> 'PermissionsLocationsAddToolApprovalDetails': - assert isinstance(obj, dict) - kind = ApprovalKind(obj.get("kind")) - command_identifiers = from_union([lambda x: from_list(from_str, x), from_none], obj.get("commandIdentifiers")) - server_name = from_union([from_str, from_none], obj.get("serverName")) - tool_name = from_union([from_none, from_str], obj.get("toolName")) - operation = from_union([from_str, from_none], obj.get("operation")) - extension_name = from_union([from_str, from_none], obj.get("extensionName")) - return PermissionsLocationsAddToolApprovalDetails(kind, command_identifiers, server_name, tool_name, operation, extension_name) - - def to_dict(self) -> dict: - result: dict = {} - result["kind"] = to_enum(ApprovalKind, self.kind) - if self.command_identifiers is not None: - result["commandIdentifiers"] = from_union([lambda x: from_list(from_str, x), from_none], self.command_identifiers) - if self.server_name is not None: - result["serverName"] = from_union([from_str, from_none], self.server_name) - if self.tool_name is not None: - result["toolName"] = from_union([from_none, from_str], self.tool_name) - if self.operation is not None: - result["operation"] = from_union([from_str, from_none], self.operation) - if self.extension_name is not None: - result["extensionName"] = from_union([from_str, from_none], self.extension_name) - return result - -# Experimental: this type is part of an experimental API and may change or be removed. -@dataclass -class ExternalToolTextResultForLlm: - """Expanded external tool result payload""" - - text_result_for_llm: str - """Text result returned to the model""" - - binary_results_for_llm: list[ExternalToolTextResultForLlmBinaryResultsForLlm] | None = None - """Base64-encoded binary results returned to the model""" - - contents: list[ExternalToolTextResultForLlmContent] | None = None - """Structured content blocks from the tool""" +class HandlePendingToolCallRequest: + """Pending external tool call request ID, with the tool result or an error describing why it + failed. + """ + request_id: str + """Request ID of the pending tool call""" error: str | None = None - """Optional error message for failed executions""" - - result_type: str | None = None - """Execution outcome classification. Optional for back-compat; normalized to 'success' (or - 'failure' when error is present) when missing or unrecognized. - """ - session_log: str | None = None - """Detailed log content for timeline display""" + """Error message if the tool call failed""" - tool_telemetry: dict[str, Any] | None = None - """Optional tool-specific telemetry""" + result: ExternalToolTextResultForLlm | str | None = None + """Tool call result (string or expanded result object)""" @staticmethod - def from_dict(obj: Any) -> 'ExternalToolTextResultForLlm': + def from_dict(obj: Any) -> 'HandlePendingToolCallRequest': assert isinstance(obj, dict) - text_result_for_llm = from_str(obj.get("textResultForLlm")) - binary_results_for_llm = from_union([lambda x: from_list(ExternalToolTextResultForLlmBinaryResultsForLlm.from_dict, x), from_none], obj.get("binaryResultsForLlm")) - contents = from_union([lambda x: from_list(ExternalToolTextResultForLlmContent.from_dict, x), from_none], obj.get("contents")) + request_id = from_str(obj.get("requestId")) error = from_union([from_str, from_none], obj.get("error")) - result_type = from_union([from_str, from_none], obj.get("resultType")) - session_log = from_union([from_str, from_none], obj.get("sessionLog")) - tool_telemetry = from_union([lambda x: from_dict(lambda x: x, x), from_none], obj.get("toolTelemetry")) - return ExternalToolTextResultForLlm(text_result_for_llm, binary_results_for_llm, contents, error, result_type, session_log, tool_telemetry) + result = from_union([ExternalToolTextResultForLlm.from_dict, from_str, from_none], obj.get("result")) + return HandlePendingToolCallRequest(request_id, error, result) def to_dict(self) -> dict: result: dict = {} - result["textResultForLlm"] = from_str(self.text_result_for_llm) - if self.binary_results_for_llm is not None: - result["binaryResultsForLlm"] = from_union([lambda x: from_list(lambda x: to_class(ExternalToolTextResultForLlmBinaryResultsForLlm, x), x), from_none], self.binary_results_for_llm) - if self.contents is not None: - result["contents"] = from_union([lambda x: from_list(lambda x: to_class(ExternalToolTextResultForLlmContent, x), x), from_none], self.contents) + result["requestId"] = from_str(self.request_id) if self.error is not None: result["error"] = from_union([from_str, from_none], self.error) - if self.result_type is not None: - result["resultType"] = from_union([from_str, from_none], self.result_type) - if self.session_log is not None: - result["sessionLog"] = from_union([from_str, from_none], self.session_log) - if self.tool_telemetry is not None: - result["toolTelemetry"] = from_union([lambda x: from_dict(lambda x: x, x), from_none], self.tool_telemetry) + if self.result is not None: + result["result"] = from_union([lambda x: to_class(ExternalToolTextResultForLlm, x), from_str, from_none], self.result) return result # Experimental: this type is part of an experimental API and may change or be removed. @@ -13476,118 +12950,16 @@ def to_dict(self) -> dict: # Experimental: this type is part of an experimental API and may change or be removed. @dataclass -class SendRequest: - """Parameters for sending a user message to the session""" - - prompt: str - """The user message text""" - - agent_mode: SendAgentMode | None = None - """The UI mode the agent was in when this message was sent. Defaults to the session's - current mode. - """ - attachments: list[SendAttachment] | None = None - """Optional attachments (files, directories, selections, blobs, GitHub references) to - include with the message - """ - billable: bool | None = None - """If false, this message will not trigger a Premium Request Unit charge. User messages - default to billable. - """ - display_prompt: str | None = None - """If provided, this is shown in the timeline instead of `prompt`""" - - mode: SendMode | None = None - """How to deliver the message. `enqueue` (default) appends to the message queue. `immediate` - interjects during an in-progress turn. - """ - prepend: bool | None = None - """If true, adds the message to the front of the queue instead of the end""" - - request_headers: dict[str, str] | None = None - """Custom HTTP headers to include in outbound model requests for this turn. Merged with - session-level provider headers; per-turn headers augment and overwrite session-level - headers with the same key. - """ - required_tool: str | None = None - """If set, the request will fail if the named tool is not available when this message is - among the user messages at the start of the current exchange - """ - source: Any = None - """Optional provenance tag copied to the resulting user.message event. Supported values are - `system`, `command-*`, and `schedule-*`. - """ - traceparent: str | None = None - """W3C Trace Context traceparent header for distributed tracing of this agent turn""" - - tracestate: str | None = None - """W3C Trace Context tracestate header for distributed tracing""" +class TasksGetProgressResult: + """Progress information for the task, or null when no task with that ID is tracked.""" - wait: bool | None = None - """If true, await completion of the agentic loop for this message before returning. Defaults - to false (fire-and-forget). When true, the result still contains the same `messageId`; - the caller can rely on the agent having processed the message before the call resolves. + progress: TaskProgress | None = None + """Progress information for the task, discriminated by type. Returns null when no task with + this ID is currently tracked. """ @staticmethod - def from_dict(obj: Any) -> 'SendRequest': - assert isinstance(obj, dict) - prompt = from_str(obj.get("prompt")) - agent_mode = from_union([SendAgentMode, from_none], obj.get("agentMode")) - attachments = from_union([lambda x: from_list(SendAttachment.from_dict, x), from_none], obj.get("attachments")) - billable = from_union([from_bool, from_none], obj.get("billable")) - display_prompt = from_union([from_str, from_none], obj.get("displayPrompt")) - mode = from_union([SendMode, from_none], obj.get("mode")) - prepend = from_union([from_bool, from_none], obj.get("prepend")) - request_headers = from_union([lambda x: from_dict(from_str, x), from_none], obj.get("requestHeaders")) - required_tool = from_union([from_str, from_none], obj.get("requiredTool")) - source = obj.get("source") - traceparent = from_union([from_str, from_none], obj.get("traceparent")) - tracestate = from_union([from_str, from_none], obj.get("tracestate")) - wait = from_union([from_bool, from_none], obj.get("wait")) - return SendRequest(prompt, agent_mode, attachments, billable, display_prompt, mode, prepend, request_headers, required_tool, source, traceparent, tracestate, wait) - - def to_dict(self) -> dict: - result: dict = {} - result["prompt"] = from_str(self.prompt) - if self.agent_mode is not None: - result["agentMode"] = from_union([lambda x: to_enum(SendAgentMode, x), from_none], self.agent_mode) - if self.attachments is not None: - result["attachments"] = from_union([lambda x: from_list(lambda x: to_class(SendAttachment, x), x), from_none], self.attachments) - if self.billable is not None: - result["billable"] = from_union([from_bool, from_none], self.billable) - if self.display_prompt is not None: - result["displayPrompt"] = from_union([from_str, from_none], self.display_prompt) - if self.mode is not None: - result["mode"] = from_union([lambda x: to_enum(SendMode, x), from_none], self.mode) - if self.prepend is not None: - result["prepend"] = from_union([from_bool, from_none], self.prepend) - if self.request_headers is not None: - result["requestHeaders"] = from_union([lambda x: from_dict(from_str, x), from_none], self.request_headers) - if self.required_tool is not None: - result["requiredTool"] = from_union([from_str, from_none], self.required_tool) - if self.source is not None: - result["source"] = self.source - if self.traceparent is not None: - result["traceparent"] = from_union([from_str, from_none], self.traceparent) - if self.tracestate is not None: - result["tracestate"] = from_union([from_str, from_none], self.tracestate) - if self.wait is not None: - result["wait"] = from_union([from_bool, from_none], self.wait) - return result - -# Experimental: this type is part of an experimental API and may change or be removed. -@dataclass -class TasksGetProgressResult: - """Progress information for the task, or null when no task with that ID is tracked.""" - - progress: TaskProgress | None = None - """Progress information for the task, discriminated by type. Returns null when no task with - this ID is currently tracked. - """ - - @staticmethod - def from_dict(obj: Any) -> 'TasksGetProgressResult': + def from_dict(obj: Any) -> 'TasksGetProgressResult': assert isinstance(obj, dict) progress = from_union([TaskProgress.from_dict, from_none], obj.get("progress")) return TasksGetProgressResult(progress) @@ -13628,231 +13000,6 @@ def to_dict(self) -> dict: result["required"] = from_union([lambda x: from_list(from_str, x), from_none], self.required) return result -# Experimental: this type is part of an experimental API and may change or be removed. -@dataclass -class AuthInfo: - """The new auth credentials to install on the session. When omitted or `undefined`, the call - is a no-op and the session's existing credentials are preserved. The runtime stores the - value verbatim and uses it for outbound model/API requests; it does NOT re-validate or - re-fetch the associated Copilot user response. Several variants carry secret material; - treat this method's params as containing secrets at rest and in transit. - - Schema for the `HMACAuthInfo` type. - - Schema for the `EnvAuthInfo` type. - - Schema for the `TokenAuthInfo` type. - - Schema for the `CopilotApiTokenAuthInfo` type. - - Schema for the `UserAuthInfo` type. - - Schema for the `GhCliAuthInfo` type. - - Schema for the `ApiKeyAuthInfo` type. - """ - host: str - """Authentication host. HMAC auth always targets the public GitHub host. - - Authentication host (e.g. https://github.com or a GHES host). - - Authentication host. - - Authentication host (always the public GitHub host). - """ - type: AuthInfoType - """HMAC-based authentication used by GitHub-internal services. - - Personal access token (PAT) or server-to-server token sourced from an environment - variable. - - SDK-side token authentication; the host configured the token directly via the SDK. - - Direct Copilot API authentication via the `GITHUB_COPILOT_API_TOKEN` + `COPILOT_API_URL` - environment-variable pair. The token itself is read from the environment by the runtime, - not carried in this struct. - - OAuth user authentication. The token itself is held in the runtime's secret token store - (keyed by host+login) and is NOT carried in this struct. - - Authentication via the `gh` CLI's saved credentials. - - API-key authentication for non-GitHub LLM providers (e.g. when running BYOM-style). - """ - copilot_user: CopilotUserResponse | None = None - """Snapshot of the authenticated user's Copilot subscription info, if known. Mirrors the - GitHub API `/copilot_internal/v2/token` user response shape — the runtime trusts this - verbatim and does not re-fetch when set. - """ - hmac: str | None = None - """HMAC secret used to sign requests.""" - - env_var: str | None = None - """Name of the environment variable the token was sourced from.""" - - login: str | None = None - """User login associated with the token. Undefined for server-to-server tokens (those - starting with `ghs_`). - - OAuth user login. - - User login as reported by `gh auth status`. - """ - token: str | None = None - """The token value itself. Treat as a secret. - - The token returned by `gh auth token`. Treat as a secret. - """ - api_key: str | None = None - """The API key. Treat as a secret.""" - - @staticmethod - def from_dict(obj: Any) -> 'AuthInfo': - assert isinstance(obj, dict) - host = from_str(obj.get("host")) - type = AuthInfoType(obj.get("type")) - copilot_user = from_union([CopilotUserResponse.from_dict, from_none], obj.get("copilotUser")) - hmac = from_union([from_str, from_none], obj.get("hmac")) - env_var = from_union([from_str, from_none], obj.get("envVar")) - login = from_union([from_str, from_none], obj.get("login")) - token = from_union([from_str, from_none], obj.get("token")) - api_key = from_union([from_str, from_none], obj.get("apiKey")) - return AuthInfo(host, type, copilot_user, hmac, env_var, login, token, api_key) - - def to_dict(self) -> dict: - result: dict = {} - result["host"] = from_str(self.host) - result["type"] = to_enum(AuthInfoType, self.type) - if self.copilot_user is not None: - result["copilotUser"] = from_union([lambda x: to_class(CopilotUserResponse, x), from_none], self.copilot_user) - if self.hmac is not None: - result["hmac"] = from_union([from_str, from_none], self.hmac) - if self.env_var is not None: - result["envVar"] = from_union([from_str, from_none], self.env_var) - if self.login is not None: - result["login"] = from_union([from_str, from_none], self.login) - if self.token is not None: - result["token"] = from_union([from_str, from_none], self.token) - if self.api_key is not None: - result["apiKey"] = from_union([from_str, from_none], self.api_key) - return result - -# Experimental: this type is part of an experimental API and may change or be removed. -@dataclass -class PermissionDecisionApproveForLocation: - """Schema for the `PermissionDecisionApproveForLocation` type.""" - - approval: PermissionDecisionApproveForLocationApproval - """Approval to persist for this location""" - - kind: PermissionDecisionApproveForLocationKind - """Approve and persist for this project location""" - - location_key: str - """Location key (git root or cwd) to persist the approval to""" - - @staticmethod - def from_dict(obj: Any) -> 'PermissionDecisionApproveForLocation': - assert isinstance(obj, dict) - approval = PermissionDecisionApproveForLocationApproval.from_dict(obj.get("approval")) - kind = PermissionDecisionApproveForLocationKind(obj.get("kind")) - location_key = from_str(obj.get("locationKey")) - return PermissionDecisionApproveForLocation(approval, kind, location_key) - - def to_dict(self) -> dict: - result: dict = {} - result["approval"] = to_class(PermissionDecisionApproveForLocationApproval, self.approval) - result["kind"] = to_enum(PermissionDecisionApproveForLocationKind, self.kind) - result["locationKey"] = from_str(self.location_key) - return result - -# Experimental: this type is part of an experimental API and may change or be removed. -@dataclass -class PermissionDecisionApproveForSession: - """Schema for the `PermissionDecisionApproveForSession` type.""" - - kind: PermissionDecisionApproveForSessionKind - """Approve and remember for the rest of the session""" - - approval: PermissionDecisionApproveForSessionApproval | None = None - """Session-scoped approval to remember (tool prompts only; omitted for path/url prompts)""" - - domain: str | None = None - """URL domain to approve for the rest of the session (URL prompts only)""" - - @staticmethod - def from_dict(obj: Any) -> 'PermissionDecisionApproveForSession': - assert isinstance(obj, dict) - kind = PermissionDecisionApproveForSessionKind(obj.get("kind")) - approval = from_union([PermissionDecisionApproveForSessionApproval.from_dict, from_none], obj.get("approval")) - domain = from_union([from_str, from_none], obj.get("domain")) - return PermissionDecisionApproveForSession(kind, approval, domain) - - def to_dict(self) -> dict: - result: dict = {} - result["kind"] = to_enum(PermissionDecisionApproveForSessionKind, self.kind) - if self.approval is not None: - result["approval"] = from_union([lambda x: to_class(PermissionDecisionApproveForSessionApproval, x), from_none], self.approval) - if self.domain is not None: - result["domain"] = from_union([from_str, from_none], self.domain) - return result - -# Experimental: this type is part of an experimental API and may change or be removed. -@dataclass -class PermissionLocationAddToolApprovalParams: - """Location-scoped tool approval to persist.""" - - approval: PermissionsLocationsAddToolApprovalDetails - """Tool approval to persist and apply""" - - location_key: str - """Location key (git root or cwd) to persist the approval to""" - - @staticmethod - def from_dict(obj: Any) -> 'PermissionLocationAddToolApprovalParams': - assert isinstance(obj, dict) - approval = PermissionsLocationsAddToolApprovalDetails.from_dict(obj.get("approval")) - location_key = from_str(obj.get("locationKey")) - return PermissionLocationAddToolApprovalParams(approval, location_key) - - def to_dict(self) -> dict: - result: dict = {} - result["approval"] = to_class(PermissionsLocationsAddToolApprovalDetails, self.approval) - result["locationKey"] = from_str(self.location_key) - return result - -# Experimental: this type is part of an experimental API and may change or be removed. -@dataclass -class HandlePendingToolCallRequest: - """Pending external tool call request ID, with the tool result or an error describing why it - failed. - """ - request_id: str - """Request ID of the pending tool call""" - - error: str | None = None - """Error message if the tool call failed""" - - result: ExternalToolTextResultForLlm | str | None = None - """Tool call result (string or expanded result object)""" - - @staticmethod - def from_dict(obj: Any) -> 'HandlePendingToolCallRequest': - assert isinstance(obj, dict) - request_id = from_str(obj.get("requestId")) - error = from_union([from_str, from_none], obj.get("error")) - result = from_union([ExternalToolTextResultForLlm.from_dict, from_str, from_none], obj.get("result")) - return HandlePendingToolCallRequest(request_id, error, result) - - def to_dict(self) -> dict: - result: dict = {} - result["requestId"] = from_str(self.request_id) - if self.error is not None: - result["error"] = from_union([from_str, from_none], self.error) - if self.result is not None: - result["result"] = from_union([lambda x: to_class(ExternalToolTextResultForLlm, x), from_str, from_none], self.result) - return result - # Experimental: this type is part of an experimental API and may change or be removed. @dataclass class SessionsSetAdditionalPluginsRequest: @@ -14078,263 +13225,63 @@ def to_dict(self) -> dict: if self.feature_flags is not None: result["featureFlags"] = from_union([lambda x: from_dict(from_bool, x), from_none], self.feature_flags) if self.installed_plugins is not None: - result["installedPlugins"] = from_union([lambda x: from_list(lambda x: to_class(SessionInstalledPlugin, x), x), from_none], self.installed_plugins) - if self.integration_id is not None: - result["integrationId"] = from_union([from_str, from_none], self.integration_id) - if self.is_experimental_mode is not None: - result["isExperimentalMode"] = from_union([from_bool, from_none], self.is_experimental_mode) - if self.log_interactive_shells is not None: - result["logInteractiveShells"] = from_union([from_bool, from_none], self.log_interactive_shells) - if self.lsp_client_name is not None: - result["lspClientName"] = from_union([from_str, from_none], self.lsp_client_name) - if self.manage_schedule_enabled is not None: - result["manageScheduleEnabled"] = from_union([from_bool, from_none], self.manage_schedule_enabled) - if self.model is not None: - result["model"] = from_union([from_str, from_none], self.model) - if self.provider is not None: - result["provider"] = self.provider - if self.reasoning_effort is not None: - result["reasoningEffort"] = from_union([from_str, from_none], self.reasoning_effort) - if self.running_in_interactive_mode is not None: - result["runningInInteractiveMode"] = from_union([from_bool, from_none], self.running_in_interactive_mode) - if self.sandbox_config is not None: - result["sandboxConfig"] = self.sandbox_config - if self.shell_init_profile is not None: - result["shellInitProfile"] = from_union([from_str, from_none], self.shell_init_profile) - if self.shell_process_flags is not None: - result["shellProcessFlags"] = from_union([lambda x: from_list(from_str, x), from_none], self.shell_process_flags) - if self.skill_directories is not None: - result["skillDirectories"] = from_union([lambda x: from_list(from_str, x), from_none], self.skill_directories) - if self.skip_custom_instructions is not None: - result["skipCustomInstructions"] = from_union([from_bool, from_none], self.skip_custom_instructions) - if self.trajectory_file is not None: - result["trajectoryFile"] = from_union([from_str, from_none], self.trajectory_file) - if self.working_directory is not None: - result["workingDirectory"] = from_union([from_str, from_none], self.working_directory) - return result - -# Experimental: this type is part of an experimental API and may change or be removed. -@dataclass -class UIElicitationRequest: - """Prompt message and JSON schema describing the form fields to elicit from the user.""" - - message: str - """Message describing what information is needed from the user""" - - requested_schema: UIElicitationSchema - """JSON Schema describing the form fields to present to the user""" - - @staticmethod - def from_dict(obj: Any) -> 'UIElicitationRequest': - assert isinstance(obj, dict) - message = from_str(obj.get("message")) - requested_schema = UIElicitationSchema.from_dict(obj.get("requestedSchema")) - return UIElicitationRequest(message, requested_schema) - - def to_dict(self) -> dict: - result: dict = {} - result["message"] = from_str(self.message) - result["requestedSchema"] = to_class(UIElicitationSchema, self.requested_schema) - return result - -# Experimental: this type is part of an experimental API and may change or be removed. -@dataclass -class SessionSetCredentialsParams: - """New auth credentials to install on the session. Omit to leave credentials unchanged.""" - - credentials: AuthInfo | None = None - """The new auth credentials to install on the session. When omitted or `undefined`, the call - is a no-op and the session's existing credentials are preserved. The runtime stores the - value verbatim and uses it for outbound model/API requests; it does NOT re-validate or - re-fetch the associated Copilot user response. Several variants carry secret material; - treat this method's params as containing secrets at rest and in transit. - """ - - @staticmethod - def from_dict(obj: Any) -> 'SessionSetCredentialsParams': - assert isinstance(obj, dict) - credentials = from_union([AuthInfo.from_dict, from_none], obj.get("credentials")) - return SessionSetCredentialsParams(credentials) - - def to_dict(self) -> dict: - result: dict = {} - if self.credentials is not None: - result["credentials"] = from_union([lambda x: to_class(AuthInfo, x), from_none], self.credentials) - return result - -# Experimental: this type is part of an experimental API and may change or be removed. -@dataclass -class PermissionDecision: - """The client's response to the pending permission prompt - - Schema for the `PermissionDecisionApproveOnce` type. - - Schema for the `PermissionDecisionApproveForSession` type. - - Schema for the `PermissionDecisionApproveForLocation` type. - - Schema for the `PermissionDecisionApprovePermanently` type. - - Schema for the `PermissionDecisionReject` type. - - Schema for the `PermissionDecisionUserNotAvailable` type. - - Schema for the `PermissionDecisionApproved` type. - - Schema for the `PermissionDecisionApprovedForSession` type. - - Schema for the `PermissionDecisionApprovedForLocation` type. - - Schema for the `PermissionDecisionCancelled` type. - - Schema for the `PermissionDecisionDeniedByRules` type. - - Schema for the `PermissionDecisionDeniedNoApprovalRuleAndCouldNotRequestFromUser` type. - - Schema for the `PermissionDecisionDeniedInteractivelyByUser` type. - - Schema for the `PermissionDecisionDeniedByContentExclusionPolicy` type. - - Schema for the `PermissionDecisionDeniedByPermissionRequestHook` type. - """ - kind: PermissionDecisionKind - """Approve this single request only - - Approve and remember for the rest of the session - - Approve and persist for this project location - - Approve and persist across sessions (URL prompts only) - - Reject the request - - No user is available to confirm the request - - The permission request was approved - - Approved and remembered for the rest of the session - - Approved and persisted for this project location - - The permission request was cancelled before a response was used - - Denied because approval rules explicitly blocked it - - Denied because no approval rule matched and user confirmation was unavailable - - Denied by the user during an interactive prompt - - Denied by the organization's content exclusion policy - - Denied by a permission request hook registered by an extension or plugin - """ - approval: PermissionDecisionApproveForIonApproval | None = None - """Session-scoped approval to remember (tool prompts only; omitted for path/url prompts) - - Approval to persist for this location - - The approval to add as a session-scoped rule - - The approval to persist for this location - """ - domain: str | None = None - """URL domain to approve for the rest of the session (URL prompts only) - - URL domain to approve permanently - """ - location_key: str | None = None - """Location key (git root or cwd) to persist the approval to - - The location key (git root or cwd) to persist the approval to - """ - feedback: str | None = None - """Optional feedback explaining the rejection - - Optional feedback from the user explaining the denial - """ - reason: str | None = None - """Optional explanation of why the request was cancelled""" - - rules: list[PermissionRule] | None = None - """Rules that denied the request""" - - force_reject: bool | None = None - """Whether to force-reject the current agent turn""" - - message: str | None = None - """Human-readable explanation of why the path was excluded - - Optional message from the hook explaining the denial - """ - path: str | None = None - """File path that triggered the exclusion""" - - interrupt: bool | None = None - """Whether to interrupt the current agent turn""" - - @staticmethod - def from_dict(obj: Any) -> 'PermissionDecision': - assert isinstance(obj, dict) - kind = PermissionDecisionKind(obj.get("kind")) - approval = from_union([PermissionDecisionApproveForIonApproval.from_dict, from_none], obj.get("approval")) - domain = from_union([from_str, from_none], obj.get("domain")) - location_key = from_union([from_str, from_none], obj.get("locationKey")) - feedback = from_union([from_str, from_none], obj.get("feedback")) - reason = from_union([from_str, from_none], obj.get("reason")) - rules = from_union([lambda x: from_list(PermissionRule.from_dict, x), from_none], obj.get("rules")) - force_reject = from_union([from_bool, from_none], obj.get("forceReject")) - message = from_union([from_str, from_none], obj.get("message")) - path = from_union([from_str, from_none], obj.get("path")) - interrupt = from_union([from_bool, from_none], obj.get("interrupt")) - return PermissionDecision(kind, approval, domain, location_key, feedback, reason, rules, force_reject, message, path, interrupt) - - def to_dict(self) -> dict: - result: dict = {} - result["kind"] = to_enum(PermissionDecisionKind, self.kind) - if self.approval is not None: - result["approval"] = from_union([lambda x: to_class(PermissionDecisionApproveForIonApproval, x), from_none], self.approval) - if self.domain is not None: - result["domain"] = from_union([from_str, from_none], self.domain) - if self.location_key is not None: - result["locationKey"] = from_union([from_str, from_none], self.location_key) - if self.feedback is not None: - result["feedback"] = from_union([from_str, from_none], self.feedback) - if self.reason is not None: - result["reason"] = from_union([from_str, from_none], self.reason) - if self.rules is not None: - result["rules"] = from_union([lambda x: from_list(lambda x: to_class(PermissionRule, x), x), from_none], self.rules) - if self.force_reject is not None: - result["forceReject"] = from_union([from_bool, from_none], self.force_reject) - if self.message is not None: - result["message"] = from_union([from_str, from_none], self.message) - if self.path is not None: - result["path"] = from_union([from_str, from_none], self.path) - if self.interrupt is not None: - result["interrupt"] = from_union([from_bool, from_none], self.interrupt) + result["installedPlugins"] = from_union([lambda x: from_list(lambda x: to_class(SessionInstalledPlugin, x), x), from_none], self.installed_plugins) + if self.integration_id is not None: + result["integrationId"] = from_union([from_str, from_none], self.integration_id) + if self.is_experimental_mode is not None: + result["isExperimentalMode"] = from_union([from_bool, from_none], self.is_experimental_mode) + if self.log_interactive_shells is not None: + result["logInteractiveShells"] = from_union([from_bool, from_none], self.log_interactive_shells) + if self.lsp_client_name is not None: + result["lspClientName"] = from_union([from_str, from_none], self.lsp_client_name) + if self.manage_schedule_enabled is not None: + result["manageScheduleEnabled"] = from_union([from_bool, from_none], self.manage_schedule_enabled) + if self.model is not None: + result["model"] = from_union([from_str, from_none], self.model) + if self.provider is not None: + result["provider"] = self.provider + if self.reasoning_effort is not None: + result["reasoningEffort"] = from_union([from_str, from_none], self.reasoning_effort) + if self.running_in_interactive_mode is not None: + result["runningInInteractiveMode"] = from_union([from_bool, from_none], self.running_in_interactive_mode) + if self.sandbox_config is not None: + result["sandboxConfig"] = self.sandbox_config + if self.shell_init_profile is not None: + result["shellInitProfile"] = from_union([from_str, from_none], self.shell_init_profile) + if self.shell_process_flags is not None: + result["shellProcessFlags"] = from_union([lambda x: from_list(from_str, x), from_none], self.shell_process_flags) + if self.skill_directories is not None: + result["skillDirectories"] = from_union([lambda x: from_list(from_str, x), from_none], self.skill_directories) + if self.skip_custom_instructions is not None: + result["skipCustomInstructions"] = from_union([from_bool, from_none], self.skip_custom_instructions) + if self.trajectory_file is not None: + result["trajectoryFile"] = from_union([from_str, from_none], self.trajectory_file) + if self.working_directory is not None: + result["workingDirectory"] = from_union([from_str, from_none], self.working_directory) return result # Experimental: this type is part of an experimental API and may change or be removed. @dataclass -class PermissionDecisionRequest: - """Pending permission request ID and the decision to apply (approve/reject and scope).""" +class UIElicitationRequest: + """Prompt message and JSON schema describing the form fields to elicit from the user.""" - request_id: str - """Request ID of the pending permission request""" + message: str + """Message describing what information is needed from the user""" - result: PermissionDecision - """The client's response to the pending permission prompt""" + requested_schema: UIElicitationSchema + """JSON Schema describing the form fields to present to the user""" @staticmethod - def from_dict(obj: Any) -> 'PermissionDecisionRequest': + def from_dict(obj: Any) -> 'UIElicitationRequest': assert isinstance(obj, dict) - request_id = from_str(obj.get("requestId")) - result = PermissionDecision.from_dict(obj.get("result")) - return PermissionDecisionRequest(request_id, result) + message = from_str(obj.get("message")) + requested_schema = UIElicitationSchema.from_dict(obj.get("requestedSchema")) + return UIElicitationRequest(message, requested_schema) def to_dict(self) -> dict: result: dict = {} - result["requestId"] = from_str(self.request_id) - result["result"] = to_class(PermissionDecision, self.result) + result["message"] = from_str(self.message) + result["requestedSchema"] = to_class(UIElicitationSchema, self.requested_schema) return result # Experimental: this type is part of an experimental API and may change or be removed. @@ -14642,7 +13589,7 @@ class TaskAgentInfo: tool_call_id: str """Tool call ID associated with this agent task""" - type: TaskAgentInfoType + type: ClassVar[str] = "agent" """Task kind""" active_started_at: datetime | None = None @@ -14687,7 +13634,6 @@ def from_dict(obj: Any) -> 'TaskAgentInfo': started_at = from_datetime(obj.get("startedAt")) status = TaskStatus(obj.get("status")) tool_call_id = from_str(obj.get("toolCallId")) - type = TaskAgentInfoType(obj.get("type")) active_started_at = from_union([from_datetime, from_none], obj.get("activeStartedAt")) active_time_ms = from_union([from_int, from_none], obj.get("activeTimeMs")) can_promote_to_background = from_union([from_bool, from_none], obj.get("canPromoteToBackground")) @@ -14698,7 +13644,7 @@ def from_dict(obj: Any) -> 'TaskAgentInfo': latest_response = from_union([from_str, from_none], obj.get("latestResponse")) model = from_union([from_str, from_none], obj.get("model")) result = from_union([from_str, from_none], obj.get("result")) - return TaskAgentInfo(agent_type, description, id, prompt, started_at, status, tool_call_id, type, active_started_at, active_time_ms, can_promote_to_background, completed_at, error, execution_mode, idle_since, latest_response, model, result) + return TaskAgentInfo(agent_type, description, id, prompt, started_at, status, tool_call_id, active_started_at, active_time_ms, can_promote_to_background, completed_at, error, execution_mode, idle_since, latest_response, model, result) def to_dict(self) -> dict: result: dict = {} @@ -14709,157 +13655,11 @@ def to_dict(self) -> dict: result["startedAt"] = self.started_at.isoformat() result["status"] = to_enum(TaskStatus, self.status) result["toolCallId"] = from_str(self.tool_call_id) - result["type"] = to_enum(TaskAgentInfoType, self.type) - if self.active_started_at is not None: - result["activeStartedAt"] = from_union([lambda x: x.isoformat(), from_none], self.active_started_at) - if self.active_time_ms is not None: - result["activeTimeMs"] = from_union([from_int, from_none], self.active_time_ms) - if self.can_promote_to_background is not None: - result["canPromoteToBackground"] = from_union([from_bool, from_none], self.can_promote_to_background) - if self.completed_at is not None: - result["completedAt"] = from_union([lambda x: x.isoformat(), from_none], self.completed_at) - if self.error is not None: - result["error"] = from_union([from_str, from_none], self.error) - if self.execution_mode is not None: - result["executionMode"] = from_union([lambda x: to_enum(TaskExecutionMode, x), from_none], self.execution_mode) - if self.idle_since is not None: - result["idleSince"] = from_union([lambda x: x.isoformat(), from_none], self.idle_since) - if self.latest_response is not None: - result["latestResponse"] = from_union([from_str, from_none], self.latest_response) - if self.model is not None: - result["model"] = from_union([from_str, from_none], self.model) - if self.result is not None: - result["result"] = from_union([from_str, from_none], self.result) - return result - -# Experimental: this type is part of an experimental API and may change or be removed. -@dataclass -class TaskInfo: - """Schema for the `TaskInfo` type. - - The first sync-waiting task (agent first, then shell) that can currently be promoted to - background mode. Omitted if no such task exists. The returned task is guaranteed to have - executionMode='sync' and canPromoteToBackground=true at the time of the call. - - The promoted task as it now exists in background mode, omitted if no promotable task was - waiting. Atomic operation: avoids the race window of getCurrentPromotable + - promoteToBackground. - - Schema for the `TaskAgentInfo` type. - - Schema for the `TaskShellInfo` type. - """ - description: str - """Short description of the task""" - - id: str - """Unique task identifier""" - - started_at: datetime - """ISO 8601 timestamp when the task was started""" - - status: TaskStatus - """Current lifecycle status of the task""" - - type: TaskInfoType - """Task kind""" - - active_started_at: datetime | None = None - """ISO 8601 timestamp when the current active period began""" - - active_time_ms: int | None = None - """Accumulated active execution time in milliseconds""" - - agent_type: str | None = None - """Type of agent running this task""" - - can_promote_to_background: bool | None = None - """Whether the task is currently in the original sync wait and can be moved to background - mode. False once it is already backgrounded, idle, finished, or no longer has a - promotable sync waiter. - - Whether this shell task can be promoted to background mode - """ - completed_at: datetime | None = None - """ISO 8601 timestamp when the task finished""" - - error: str | None = None - """Error message when the task failed""" - - execution_mode: TaskExecutionMode | None = None - """Whether task execution is synchronously awaited or managed in the background""" - - idle_since: datetime | None = None - """ISO 8601 timestamp when the agent entered idle state""" - - latest_response: str | None = None - """Most recent response text from the agent""" - - model: str | None = None - """Model used for the task when specified""" - - prompt: str | None = None - """Prompt passed to the agent""" - - result: str | None = None - """Result text from the task when available""" - - tool_call_id: str | None = None - """Tool call ID associated with this agent task""" - - attachment_mode: TaskShellInfoAttachmentMode | None = None - """Whether the shell runs inside a managed PTY session or as an independent background - process - """ - command: str | None = None - """Command being executed""" - - log_path: str | None = None - """Path to the detached shell log, when available""" - - pid: int | None = None - """Process ID when available""" - - @staticmethod - def from_dict(obj: Any) -> 'TaskInfo': - assert isinstance(obj, dict) - description = from_str(obj.get("description")) - id = from_str(obj.get("id")) - started_at = from_datetime(obj.get("startedAt")) - status = TaskStatus(obj.get("status")) - type = TaskInfoType(obj.get("type")) - active_started_at = from_union([from_datetime, from_none], obj.get("activeStartedAt")) - active_time_ms = from_union([from_int, from_none], obj.get("activeTimeMs")) - agent_type = from_union([from_str, from_none], obj.get("agentType")) - can_promote_to_background = from_union([from_bool, from_none], obj.get("canPromoteToBackground")) - completed_at = from_union([from_datetime, from_none], obj.get("completedAt")) - error = from_union([from_str, from_none], obj.get("error")) - execution_mode = from_union([TaskExecutionMode, from_none], obj.get("executionMode")) - idle_since = from_union([from_datetime, from_none], obj.get("idleSince")) - latest_response = from_union([from_str, from_none], obj.get("latestResponse")) - model = from_union([from_str, from_none], obj.get("model")) - prompt = from_union([from_str, from_none], obj.get("prompt")) - result = from_union([from_str, from_none], obj.get("result")) - tool_call_id = from_union([from_str, from_none], obj.get("toolCallId")) - attachment_mode = from_union([TaskShellInfoAttachmentMode, from_none], obj.get("attachmentMode")) - command = from_union([from_str, from_none], obj.get("command")) - log_path = from_union([from_str, from_none], obj.get("logPath")) - pid = from_union([from_int, from_none], obj.get("pid")) - return TaskInfo(description, id, started_at, status, type, active_started_at, active_time_ms, agent_type, can_promote_to_background, completed_at, error, execution_mode, idle_since, latest_response, model, prompt, result, tool_call_id, attachment_mode, command, log_path, pid) - - def to_dict(self) -> dict: - result: dict = {} - result["description"] = from_str(self.description) - result["id"] = from_str(self.id) - result["startedAt"] = self.started_at.isoformat() - result["status"] = to_enum(TaskStatus, self.status) - result["type"] = to_enum(TaskInfoType, self.type) + result["type"] = self.type if self.active_started_at is not None: result["activeStartedAt"] = from_union([lambda x: x.isoformat(), from_none], self.active_started_at) if self.active_time_ms is not None: result["activeTimeMs"] = from_union([from_int, from_none], self.active_time_ms) - if self.agent_type is not None: - result["agentType"] = from_union([from_str, from_none], self.agent_type) if self.can_promote_to_background is not None: result["canPromoteToBackground"] = from_union([from_bool, from_none], self.can_promote_to_background) if self.completed_at is not None: @@ -14874,86 +13674,8 @@ def to_dict(self) -> dict: result["latestResponse"] = from_union([from_str, from_none], self.latest_response) if self.model is not None: result["model"] = from_union([from_str, from_none], self.model) - if self.prompt is not None: - result["prompt"] = from_union([from_str, from_none], self.prompt) if self.result is not None: result["result"] = from_union([from_str, from_none], self.result) - if self.tool_call_id is not None: - result["toolCallId"] = from_union([from_str, from_none], self.tool_call_id) - if self.attachment_mode is not None: - result["attachmentMode"] = from_union([lambda x: to_enum(TaskShellInfoAttachmentMode, x), from_none], self.attachment_mode) - if self.command is not None: - result["command"] = from_union([from_str, from_none], self.command) - if self.log_path is not None: - result["logPath"] = from_union([from_str, from_none], self.log_path) - if self.pid is not None: - result["pid"] = from_union([from_int, from_none], self.pid) - return result - -# Experimental: this type is part of an experimental API and may change or be removed. -@dataclass -class TaskList: - """Background tasks currently tracked by the session.""" - - tasks: list[TaskInfo] - """Currently tracked tasks""" - - @staticmethod - def from_dict(obj: Any) -> 'TaskList': - assert isinstance(obj, dict) - tasks = from_list(TaskInfo.from_dict, obj.get("tasks")) - return TaskList(tasks) - - def to_dict(self) -> dict: - result: dict = {} - result["tasks"] = from_list(lambda x: to_class(TaskInfo, x), self.tasks) - return result - -# Experimental: this type is part of an experimental API and may change or be removed. -@dataclass -class TasksGetCurrentPromotableResult: - """The first sync-waiting task that can currently be promoted to background mode.""" - - task: TaskInfo | None = None - """The first sync-waiting task (agent first, then shell) that can currently be promoted to - background mode. Omitted if no such task exists. The returned task is guaranteed to have - executionMode='sync' and canPromoteToBackground=true at the time of the call. - """ - - @staticmethod - def from_dict(obj: Any) -> 'TasksGetCurrentPromotableResult': - assert isinstance(obj, dict) - task = from_union([TaskInfo.from_dict, from_none], obj.get("task")) - return TasksGetCurrentPromotableResult(task) - - def to_dict(self) -> dict: - result: dict = {} - if self.task is not None: - result["task"] = from_union([lambda x: to_class(TaskInfo, x), from_none], self.task) - return result - -# Experimental: this type is part of an experimental API and may change or be removed. -@dataclass -class TasksPromoteCurrentToBackgroundResult: - """The promoted task as it now exists in background mode, omitted if no promotable task was - waiting. - """ - task: TaskInfo | None = None - """The promoted task as it now exists in background mode, omitted if no promotable task was - waiting. Atomic operation: avoids the race window of getCurrentPromotable + - promoteToBackground. - """ - - @staticmethod - def from_dict(obj: Any) -> 'TasksPromoteCurrentToBackgroundResult': - assert isinstance(obj, dict) - task = from_union([TaskInfo.from_dict, from_none], obj.get("task")) - return TasksPromoteCurrentToBackgroundResult(task) - - def to_dict(self) -> dict: - result: dict = {} - if self.task is not None: - result["task"] = from_union([lambda x: to_class(TaskInfo, x), from_none], self.task) return result @dataclass @@ -15449,14 +14171,6 @@ class RPC: usage_metrics_model_metric_usage: UsageMetricsModelMetricUsage usage_metrics_token_detail: UsageMetricsTokenDetail user_auth_info: UserAuthInfo - user_tool_session_approval_commands: UserToolSessionApprovalCommands - user_tool_session_approval_custom_tool: UserToolSessionApprovalCustomTool - user_tool_session_approval_extension_management: UserToolSessionApprovalExtensionManagement - user_tool_session_approval_extension_permission_access: UserToolSessionApprovalExtensionPermissionAccess - user_tool_session_approval_mcp: UserToolSessionApprovalMCP - user_tool_session_approval_memory: UserToolSessionApprovalMemory - user_tool_session_approval_read: UserToolSessionApprovalRead - user_tool_session_approval_write: UserToolSessionApprovalWrite workspaces_checkpoints: WorkspacesCheckpoints workspaces_create_file_request: WorkspacesCreateFileRequest workspaces_get_workspace_result: WorkspacesGetWorkspaceResult @@ -15490,7 +14204,7 @@ def from_dict(obj: Any) -> 'RPC': agent_select_request = AgentSelectRequest.from_dict(obj.get("AgentSelectRequest")) agent_select_result = AgentSelectResult.from_dict(obj.get("AgentSelectResult")) api_key_auth_info = APIKeyAuthInfo.from_dict(obj.get("ApiKeyAuthInfo")) - auth_info = AuthInfo.from_dict(obj.get("AuthInfo")) + auth_info = _load_AuthInfo(obj.get("AuthInfo")) auth_info_type = AuthInfoType(obj.get("AuthInfoType")) command_list = CommandList.from_dict(obj.get("CommandList")) commands_handle_pending_command_request = CommandsHandlePendingCommandRequest.from_dict(obj.get("CommandsHandlePendingCommandRequest")) @@ -15538,7 +14252,7 @@ def from_dict(obj: Any) -> 'RPC': external_tool_text_result_for_llm = ExternalToolTextResultForLlm.from_dict(obj.get("ExternalToolTextResultForLlm")) external_tool_text_result_for_llm_binary_results_for_llm = ExternalToolTextResultForLlmBinaryResultsForLlm.from_dict(obj.get("ExternalToolTextResultForLlmBinaryResultsForLlm")) external_tool_text_result_for_llm_binary_results_for_llm_type = ExternalToolTextResultForLlmBinaryResultsForLlmType(obj.get("ExternalToolTextResultForLlmBinaryResultsForLlmType")) - external_tool_text_result_for_llm_content = ExternalToolTextResultForLlmContent.from_dict(obj.get("ExternalToolTextResultForLlmContent")) + external_tool_text_result_for_llm_content = _load_ExternalToolTextResultForLlmContent(obj.get("ExternalToolTextResultForLlmContent")) external_tool_text_result_for_llm_content_audio = ExternalToolTextResultForLlmContentAudio.from_dict(obj.get("ExternalToolTextResultForLlmContentAudio")) external_tool_text_result_for_llm_content_image = ExternalToolTextResultForLlmContentImage.from_dict(obj.get("ExternalToolTextResultForLlmContentImage")) external_tool_text_result_for_llm_content_resource = ExternalToolTextResultForLlmContentResource.from_dict(obj.get("ExternalToolTextResultForLlmContentResource")) @@ -15651,12 +14365,12 @@ def from_dict(obj: Any) -> 'RPC': options_update_env_value_mode = MCPSetEnvValueModeDetails(obj.get("OptionsUpdateEnvValueMode")) pending_permission_request = PendingPermissionRequest.from_dict(obj.get("PendingPermissionRequest")) pending_permission_request_list = PendingPermissionRequestList.from_dict(obj.get("PendingPermissionRequestList")) - permission_decision = PermissionDecision.from_dict(obj.get("PermissionDecision")) + permission_decision = _load_PermissionDecision(obj.get("PermissionDecision")) permission_decision_approved = PermissionDecisionApproved.from_dict(obj.get("PermissionDecisionApproved")) permission_decision_approved_for_location = PermissionDecisionApprovedForLocation.from_dict(obj.get("PermissionDecisionApprovedForLocation")) permission_decision_approved_for_session = PermissionDecisionApprovedForSession.from_dict(obj.get("PermissionDecisionApprovedForSession")) permission_decision_approve_for_location = PermissionDecisionApproveForLocation.from_dict(obj.get("PermissionDecisionApproveForLocation")) - permission_decision_approve_for_location_approval = PermissionDecisionApproveForLocationApproval.from_dict(obj.get("PermissionDecisionApproveForLocationApproval")) + permission_decision_approve_for_location_approval = _load_PermissionDecisionApproveForLocationApproval(obj.get("PermissionDecisionApproveForLocationApproval")) permission_decision_approve_for_location_approval_commands = PermissionDecisionApproveForLocationApprovalCommands.from_dict(obj.get("PermissionDecisionApproveForLocationApprovalCommands")) permission_decision_approve_for_location_approval_custom_tool = PermissionDecisionApproveForLocationApprovalCustomTool.from_dict(obj.get("PermissionDecisionApproveForLocationApprovalCustomTool")) permission_decision_approve_for_location_approval_extension_management = PermissionDecisionApproveForLocationApprovalExtensionManagement.from_dict(obj.get("PermissionDecisionApproveForLocationApprovalExtensionManagement")) @@ -15667,7 +14381,7 @@ def from_dict(obj: Any) -> 'RPC': permission_decision_approve_for_location_approval_read = PermissionDecisionApproveForLocationApprovalRead.from_dict(obj.get("PermissionDecisionApproveForLocationApprovalRead")) permission_decision_approve_for_location_approval_write = PermissionDecisionApproveForLocationApprovalWrite.from_dict(obj.get("PermissionDecisionApproveForLocationApprovalWrite")) permission_decision_approve_for_session = PermissionDecisionApproveForSession.from_dict(obj.get("PermissionDecisionApproveForSession")) - permission_decision_approve_for_session_approval = PermissionDecisionApproveForSessionApproval.from_dict(obj.get("PermissionDecisionApproveForSessionApproval")) + permission_decision_approve_for_session_approval = _load_PermissionDecisionApproveForSessionApproval(obj.get("PermissionDecisionApproveForSessionApproval")) permission_decision_approve_for_session_approval_commands = PermissionDecisionApproveForSessionApprovalCommands.from_dict(obj.get("PermissionDecisionApproveForSessionApprovalCommands")) permission_decision_approve_for_session_approval_custom_tool = PermissionDecisionApproveForSessionApprovalCustomTool.from_dict(obj.get("PermissionDecisionApproveForSessionApprovalCustomTool")) permission_decision_approve_for_session_approval_extension_management = PermissionDecisionApproveForSessionApprovalExtensionManagement.from_dict(obj.get("PermissionDecisionApproveForSessionApprovalExtensionManagement")) @@ -15712,7 +14426,7 @@ def from_dict(obj: Any) -> 'RPC': permissions_configure_params = PermissionsConfigureParams.from_dict(obj.get("PermissionsConfigureParams")) permissions_configure_result = PermissionsConfigureResult.from_dict(obj.get("PermissionsConfigureResult")) permissions_folder_trust_add_trusted_result = PermissionsFolderTrustAddTrustedResult.from_dict(obj.get("PermissionsFolderTrustAddTrustedResult")) - permissions_locations_add_tool_approval_details = PermissionsLocationsAddToolApprovalDetails.from_dict(obj.get("PermissionsLocationsAddToolApprovalDetails")) + permissions_locations_add_tool_approval_details = _load_PermissionsLocationsAddToolApprovalDetails(obj.get("PermissionsLocationsAddToolApprovalDetails")) permissions_locations_add_tool_approval_details_commands = PermissionsLocationsAddToolApprovalDetailsCommands.from_dict(obj.get("PermissionsLocationsAddToolApprovalDetailsCommands")) permissions_locations_add_tool_approval_details_custom_tool = PermissionsLocationsAddToolApprovalDetailsCustomTool.from_dict(obj.get("PermissionsLocationsAddToolApprovalDetailsCustomTool")) permissions_locations_add_tool_approval_details_extension_management = PermissionsLocationsAddToolApprovalDetailsExtensionManagement.from_dict(obj.get("PermissionsLocationsAddToolApprovalDetailsExtensionManagement")) @@ -15749,7 +14463,7 @@ def from_dict(obj: Any) -> 'RPC': plugin_list = PluginList.from_dict(obj.get("PluginList")) queued_command_handled = QueuedCommandHandled.from_dict(obj.get("QueuedCommandHandled")) queued_command_not_handled = QueuedCommandNotHandled.from_dict(obj.get("QueuedCommandNotHandled")) - queued_command_result = QueuedCommandResult.from_dict(obj.get("QueuedCommandResult")) + queued_command_result = _load_QueuedCommandResult(obj.get("QueuedCommandResult")) queue_pending_items = QueuePendingItems.from_dict(obj.get("QueuePendingItems")) queue_pending_items_kind = QueuePendingItemsKind(obj.get("QueuePendingItemsKind")) queue_pending_items_result = QueuePendingItemsResult.from_dict(obj.get("QueuePendingItemsResult")) @@ -15770,7 +14484,7 @@ def from_dict(obj: Any) -> 'RPC': secrets_add_filter_values_request = SecretsAddFilterValuesRequest.from_dict(obj.get("SecretsAddFilterValuesRequest")) secrets_add_filter_values_result = SecretsAddFilterValuesResult.from_dict(obj.get("SecretsAddFilterValuesResult")) send_agent_mode = SendAgentMode(obj.get("SendAgentMode")) - send_attachment = SendAttachment.from_dict(obj.get("SendAttachment")) + send_attachment = _load_SendAttachment(obj.get("SendAttachment")) send_attachment_blob = SendAttachmentBlob.from_dict(obj.get("SendAttachmentBlob")) send_attachment_directory = SendAttachmentDirectory.from_dict(obj.get("SendAttachmentDirectory")) send_attachment_file = SendAttachmentFile.from_dict(obj.get("SendAttachmentFile")) @@ -15888,7 +14602,7 @@ def from_dict(obj: Any) -> 'RPC': slash_command_info = SlashCommandInfo.from_dict(obj.get("SlashCommandInfo")) slash_command_input = SlashCommandInput.from_dict(obj.get("SlashCommandInput")) slash_command_input_completion = SlashCommandInputCompletion(obj.get("SlashCommandInputCompletion")) - slash_command_invocation_result = SlashCommandInvocationResult.from_dict(obj.get("SlashCommandInvocationResult")) + slash_command_invocation_result = _load_SlashCommandInvocationResult(obj.get("SlashCommandInvocationResult")) slash_command_kind = SlashCommandKind(obj.get("SlashCommandKind")) slash_command_select_subcommand_option = SlashCommandSelectSubcommandOption.from_dict(obj.get("SlashCommandSelectSubcommandOption")) slash_command_select_subcommand_result = SlashCommandSelectSubcommandResult.from_dict(obj.get("SlashCommandSelectSubcommandResult")) @@ -15896,7 +14610,7 @@ def from_dict(obj: Any) -> 'RPC': task_agent_info = TaskAgentInfo.from_dict(obj.get("TaskAgentInfo")) task_agent_progress = TaskAgentProgress.from_dict(obj.get("TaskAgentProgress")) task_execution_mode = TaskExecutionMode(obj.get("TaskExecutionMode")) - task_info = TaskInfo.from_dict(obj.get("TaskInfo")) + task_info = _load_TaskInfo(obj.get("TaskInfo")) task_list = TaskList.from_dict(obj.get("TaskList")) task_progress_line = TaskProgressLine.from_dict(obj.get("TaskProgressLine")) tasks_cancel_request = TasksCancelRequest.from_dict(obj.get("TasksCancelRequest")) @@ -15968,14 +14682,6 @@ def from_dict(obj: Any) -> 'RPC': usage_metrics_model_metric_usage = UsageMetricsModelMetricUsage.from_dict(obj.get("UsageMetricsModelMetricUsage")) usage_metrics_token_detail = UsageMetricsTokenDetail.from_dict(obj.get("UsageMetricsTokenDetail")) user_auth_info = UserAuthInfo.from_dict(obj.get("UserAuthInfo")) - user_tool_session_approval_commands = UserToolSessionApprovalCommands.from_dict(obj.get("UserToolSessionApprovalCommands")) - user_tool_session_approval_custom_tool = UserToolSessionApprovalCustomTool.from_dict(obj.get("UserToolSessionApprovalCustomTool")) - user_tool_session_approval_extension_management = UserToolSessionApprovalExtensionManagement.from_dict(obj.get("UserToolSessionApprovalExtensionManagement")) - user_tool_session_approval_extension_permission_access = UserToolSessionApprovalExtensionPermissionAccess.from_dict(obj.get("UserToolSessionApprovalExtensionPermissionAccess")) - user_tool_session_approval_mcp = UserToolSessionApprovalMCP.from_dict(obj.get("UserToolSessionApprovalMcp")) - user_tool_session_approval_memory = UserToolSessionApprovalMemory.from_dict(obj.get("UserToolSessionApprovalMemory")) - user_tool_session_approval_read = UserToolSessionApprovalRead.from_dict(obj.get("UserToolSessionApprovalRead")) - user_tool_session_approval_write = UserToolSessionApprovalWrite.from_dict(obj.get("UserToolSessionApprovalWrite")) workspaces_checkpoints = WorkspacesCheckpoints.from_dict(obj.get("WorkspacesCheckpoints")) workspaces_create_file_request = WorkspacesCreateFileRequest.from_dict(obj.get("WorkspacesCreateFileRequest")) workspaces_get_workspace_result = WorkspacesGetWorkspaceResult.from_dict(obj.get("WorkspacesGetWorkspaceResult")) @@ -15992,7 +14698,7 @@ def from_dict(obj: Any) -> 'RPC': session_context_info = from_union([SessionContextInfo.from_dict, from_none], obj.get("SessionContextInfo")) task_progress = from_union([TaskProgress.from_dict, from_none], obj.get("TaskProgress")) workspace_summary = from_union([WorkspaceSummary.from_dict, from_none], obj.get("WorkspaceSummary")) - return RPC(abort_request, abort_result, account_get_quota_request, account_get_quota_result, account_quota_snapshot, agent_get_current_result, agent_info, agent_info_source, agent_list, agent_reload_result, agent_select_request, agent_select_result, api_key_auth_info, auth_info, auth_info_type, command_list, commands_handle_pending_command_request, commands_handle_pending_command_result, commands_invoke_request, commands_list_request, commands_respond_to_queued_command_request, commands_respond_to_queued_command_result, connected_remote_session_metadata, connected_remote_session_metadata_kind, connected_remote_session_metadata_repository, connect_remote_session_params, connect_request, connect_result, content_filter_mode, copilot_api_token_auth_info, copilot_user_response, copilot_user_response_endpoints, copilot_user_response_quota_snapshots, copilot_user_response_quota_snapshots_chat, copilot_user_response_quota_snapshots_completions, copilot_user_response_quota_snapshots_premium_interactions, current_model, discovered_mcp_server, discovered_mcp_server_type, enqueue_command_params, enqueue_command_result, env_auth_info, event_log_read_request, event_log_release_interest_result, event_log_tail_result, event_log_types, events_agent_scope, events_cursor_status, events_read_result, execute_command_params, execute_command_result, extension, extension_list, extensions_disable_request, extensions_enable_request, extension_source, extension_status, external_tool_result, external_tool_text_result_for_llm, external_tool_text_result_for_llm_binary_results_for_llm, external_tool_text_result_for_llm_binary_results_for_llm_type, external_tool_text_result_for_llm_content, external_tool_text_result_for_llm_content_audio, external_tool_text_result_for_llm_content_image, external_tool_text_result_for_llm_content_resource, external_tool_text_result_for_llm_content_resource_details, external_tool_text_result_for_llm_content_resource_link, external_tool_text_result_for_llm_content_resource_link_icon, external_tool_text_result_for_llm_content_resource_link_icon_theme, external_tool_text_result_for_llm_content_terminal, external_tool_text_result_for_llm_content_text, filter_mapping, fleet_start_request, fleet_start_result, folder_trust_add_params, folder_trust_check_params, folder_trust_check_result, gh_cli_auth_info, handle_pending_tool_call_request, handle_pending_tool_call_result, history_abort_manual_compaction_result, history_cancel_background_compaction_result, history_compact_context_window, history_compact_request, history_compact_result, history_summarize_for_handoff_result, history_truncate_request, history_truncate_result, hmac_auth_info, installed_plugin, installed_plugin_source, installed_plugin_source_github, installed_plugin_source_local, installed_plugin_source_url, instructions_get_sources_result, instructions_sources, instructions_sources_location, instructions_sources_type, log_request, log_result, lsp_initialize_request, mcp_cancel_sampling_execution_params, mcp_cancel_sampling_execution_result, mcp_config_add_request, mcp_config_disable_request, mcp_config_enable_request, mcp_config_list, mcp_config_remove_request, mcp_config_update_request, mcp_disable_request, mcp_discover_request, mcp_discover_result, mcp_enable_request, mcp_execute_sampling_params, mcp_execute_sampling_request, mcp_execute_sampling_result, mcp_oauth_login_request, mcp_oauth_login_result, mcp_remove_git_hub_result, mcp_sampling_execution_action, mcp_sampling_execution_result, mcp_server, mcp_server_config, mcp_server_config_http, mcp_server_config_http_auth, mcp_server_config_http_oauth_grant_type, mcp_server_config_http_type, mcp_server_config_stdio, mcp_server_list, mcp_set_env_value_mode_details, mcp_set_env_value_mode_params, mcp_set_env_value_mode_result, metadata_context_info_request, metadata_context_info_result, metadata_is_processing_result, metadata_recompute_context_tokens_request, metadata_recompute_context_tokens_result, metadata_record_context_change_request, metadata_record_context_change_result, metadata_set_working_directory_request, metadata_set_working_directory_result, metadata_snapshot_current_mode, metadata_snapshot_remote_metadata, metadata_snapshot_remote_metadata_repository, metadata_snapshot_remote_metadata_task_type, model, model_billing, model_billing_token_prices, model_capabilities, model_capabilities_limits, model_capabilities_limits_vision, model_capabilities_override, model_capabilities_override_limits, model_capabilities_override_limits_vision, model_capabilities_override_supports, model_capabilities_supports, model_list, model_picker_category, model_picker_price_category, model_policy, model_policy_state, model_set_reasoning_effort_request, model_set_reasoning_effort_result, models_list_request, model_switch_to_request, model_switch_to_result, mode_set_request, name_get_result, name_set_auto_request, name_set_auto_result, name_set_request, options_update_env_value_mode, pending_permission_request, pending_permission_request_list, permission_decision, permission_decision_approved, permission_decision_approved_for_location, permission_decision_approved_for_session, permission_decision_approve_for_location, permission_decision_approve_for_location_approval, permission_decision_approve_for_location_approval_commands, permission_decision_approve_for_location_approval_custom_tool, permission_decision_approve_for_location_approval_extension_management, permission_decision_approve_for_location_approval_extension_permission_access, permission_decision_approve_for_location_approval_mcp, permission_decision_approve_for_location_approval_mcp_sampling, permission_decision_approve_for_location_approval_memory, permission_decision_approve_for_location_approval_read, permission_decision_approve_for_location_approval_write, permission_decision_approve_for_session, permission_decision_approve_for_session_approval, permission_decision_approve_for_session_approval_commands, permission_decision_approve_for_session_approval_custom_tool, permission_decision_approve_for_session_approval_extension_management, permission_decision_approve_for_session_approval_extension_permission_access, permission_decision_approve_for_session_approval_mcp, permission_decision_approve_for_session_approval_mcp_sampling, permission_decision_approve_for_session_approval_memory, permission_decision_approve_for_session_approval_read, permission_decision_approve_for_session_approval_write, permission_decision_approve_once, permission_decision_approve_permanently, permission_decision_cancelled, permission_decision_denied_by_content_exclusion_policy, permission_decision_denied_by_permission_request_hook, permission_decision_denied_by_rules, permission_decision_denied_interactively_by_user, permission_decision_denied_no_approval_rule_and_could_not_request_from_user, permission_decision_reject, permission_decision_request, permission_decision_user_not_available, permission_location_add_tool_approval_params, permission_location_apply_params, permission_location_apply_result, permission_location_resolve_params, permission_location_resolve_result, permission_location_type, permission_paths_add_params, permission_paths_allowed_check_params, permission_paths_allowed_check_result, permission_paths_config, permission_paths_list, permission_paths_update_primary_params, permission_paths_workspace_check_params, permission_paths_workspace_check_result, permission_prompt_shown_notification, permission_request_result, permission_rules_set, permissions_configure_additional_content_exclusion_policy, permissions_configure_additional_content_exclusion_policy_rule, permissions_configure_additional_content_exclusion_policy_rule_source, permissions_configure_additional_content_exclusion_policy_scope, permissions_configure_params, permissions_configure_result, permissions_folder_trust_add_trusted_result, permissions_locations_add_tool_approval_details, permissions_locations_add_tool_approval_details_commands, permissions_locations_add_tool_approval_details_custom_tool, permissions_locations_add_tool_approval_details_extension_management, permissions_locations_add_tool_approval_details_extension_permission_access, permissions_locations_add_tool_approval_details_mcp, permissions_locations_add_tool_approval_details_mcp_sampling, permissions_locations_add_tool_approval_details_memory, permissions_locations_add_tool_approval_details_read, permissions_locations_add_tool_approval_details_write, permissions_locations_add_tool_approval_result, permissions_modify_rules_params, permissions_modify_rules_result, permissions_modify_rules_scope, permissions_notify_prompt_shown_result, permissions_paths_add_result, permissions_paths_list_request, permissions_paths_update_primary_result, permissions_pending_requests_request, permissions_reset_session_approvals_request, permissions_reset_session_approvals_result, permissions_set_approve_all_request, permissions_set_approve_all_result, permissions_set_approve_all_source, permissions_set_required_request, permissions_set_required_result, permissions_urls_set_unrestricted_mode_result, permission_urls_config, permission_urls_set_unrestricted_mode_params, ping_request, ping_result, plan_read_result, plan_update_request, plugin, plugin_list, queued_command_handled, queued_command_not_handled, queued_command_result, queue_pending_items, queue_pending_items_kind, queue_pending_items_result, queue_remove_most_recent_result, register_event_interest_params, register_event_interest_result, release_event_interest_params, remote_enable_request, remote_enable_result, remote_notify_steerable_changed_request, remote_notify_steerable_changed_result, remote_session_connection_result, remote_session_mode, schedule_entry, schedule_list, schedule_stop_request, schedule_stop_result, secrets_add_filter_values_request, secrets_add_filter_values_result, send_agent_mode, send_attachment, send_attachment_blob, send_attachment_directory, send_attachment_file, send_attachment_file_line_range, send_attachment_github_reference, send_attachment_github_reference_type, send_attachment_selection, send_attachment_selection_details, send_attachment_selection_details_end, send_attachment_selection_details_start, send_mode, send_request, send_result, server_skill, server_skill_list, session_auth_status, session_bulk_delete_result, session_context, session_context_host_type, session_enrich_metadata_result, session_fs_append_file_request, session_fs_error, session_fs_error_code, session_fs_exists_request, session_fs_exists_result, session_fs_mkdir_request, session_fs_readdir_request, session_fs_readdir_result, session_fs_readdir_with_types_entry, session_fs_readdir_with_types_entry_type, session_fs_readdir_with_types_request, session_fs_readdir_with_types_result, session_fs_read_file_request, session_fs_read_file_result, session_fs_rename_request, session_fs_rm_request, session_fs_set_provider_capabilities, session_fs_set_provider_conventions, session_fs_set_provider_request, session_fs_set_provider_result, session_fs_sqlite_exists_request, session_fs_sqlite_exists_result, session_fs_sqlite_query_request, session_fs_sqlite_query_result, session_fs_sqlite_query_type, session_fs_stat_request, session_fs_stat_result, session_fs_write_file_request, session_installed_plugin, session_installed_plugin_source, session_installed_plugin_source_github, session_installed_plugin_source_local, session_installed_plugin_source_url, session_list, session_list_filter, session_load_deferred_repo_hooks_result, session_log_level, session_metadata, session_metadata_snapshot, session_mode, session_prune_result, sessions_bulk_delete_request, sessions_check_in_use_request, sessions_check_in_use_result, sessions_close_request, sessions_close_result, sessions_enrich_metadata_request, session_set_credentials_params, session_set_credentials_result, sessions_find_by_prefix_request, sessions_find_by_prefix_result, sessions_find_by_task_id_request, sessions_find_by_task_id_result, sessions_fork_request, sessions_fork_result, sessions_get_event_file_path_request, sessions_get_event_file_path_result, sessions_get_last_for_context_request, sessions_get_last_for_context_result, sessions_get_persisted_remote_steerable_request, sessions_get_persisted_remote_steerable_result, session_sizes, sessions_list_request, sessions_load_deferred_repo_hooks_request, sessions_prune_old_request, sessions_release_lock_request, sessions_release_lock_result, sessions_reload_plugin_hooks_request, sessions_reload_plugin_hooks_result, sessions_save_request, sessions_save_result, sessions_set_additional_plugins_request, sessions_set_additional_plugins_result, session_update_options_params, session_update_options_result, session_working_directory_context, session_working_directory_context_host_type, shell_exec_request, shell_exec_result, shell_kill_request, shell_kill_result, shell_kill_signal, shutdown_request, skill, skill_list, skills_config_set_disabled_skills_request, skills_disable_request, skills_discover_request, skills_enable_request, skills_get_invoked_result, skills_invoked_skill, skills_load_diagnostics, slash_command_agent_prompt_result, slash_command_completed_result, slash_command_info, slash_command_input, slash_command_input_completion, slash_command_invocation_result, slash_command_kind, slash_command_select_subcommand_option, slash_command_select_subcommand_result, slash_command_text_result, task_agent_info, task_agent_progress, task_execution_mode, task_info, task_list, task_progress_line, tasks_cancel_request, tasks_cancel_result, tasks_get_current_promotable_result, tasks_get_progress_request, tasks_get_progress_result, task_shell_info, task_shell_info_attachment_mode, task_shell_progress, tasks_promote_current_to_background_result, tasks_promote_to_background_request, tasks_promote_to_background_result, tasks_refresh_result, tasks_remove_request, tasks_remove_result, tasks_send_message_request, tasks_send_message_result, tasks_start_agent_request, tasks_start_agent_result, task_status, tasks_wait_for_pending_result, telemetry_set_feature_overrides_request, token_auth_info, tool, tool_list, tools_initialize_and_validate_result, tools_list_request, ui_auto_mode_switch_response, ui_elicitation_array_any_of_field, ui_elicitation_array_any_of_field_items, ui_elicitation_array_any_of_field_items_any_of, ui_elicitation_array_enum_field, ui_elicitation_array_enum_field_items, ui_elicitation_field_value, ui_elicitation_request, ui_elicitation_response, ui_elicitation_response_action, ui_elicitation_response_content, ui_elicitation_result, ui_elicitation_schema, ui_elicitation_schema_property, ui_elicitation_schema_property_boolean, ui_elicitation_schema_property_number, ui_elicitation_schema_property_number_type, ui_elicitation_schema_property_string, ui_elicitation_schema_property_string_format, ui_elicitation_string_enum_field, ui_elicitation_string_one_of_field, ui_elicitation_string_one_of_field_one_of, ui_exit_plan_mode_action, ui_exit_plan_mode_response, ui_handle_pending_auto_mode_switch_request, ui_handle_pending_elicitation_request, ui_handle_pending_exit_plan_mode_request, ui_handle_pending_result, ui_handle_pending_sampling_request, ui_handle_pending_sampling_response, ui_handle_pending_user_input_request, ui_register_direct_auto_mode_switch_handler_result, ui_unregister_direct_auto_mode_switch_handler_request, ui_unregister_direct_auto_mode_switch_handler_result, ui_user_input_response, usage_get_metrics_result, usage_metrics_code_changes, usage_metrics_model_metric, usage_metrics_model_metric_requests, usage_metrics_model_metric_token_detail, usage_metrics_model_metric_usage, usage_metrics_token_detail, user_auth_info, user_tool_session_approval_commands, user_tool_session_approval_custom_tool, user_tool_session_approval_extension_management, user_tool_session_approval_extension_permission_access, user_tool_session_approval_mcp, user_tool_session_approval_memory, user_tool_session_approval_read, user_tool_session_approval_write, workspaces_checkpoints, workspaces_create_file_request, workspaces_get_workspace_result, workspaces_list_checkpoints_result, workspaces_list_files_result, workspaces_read_checkpoint_request, workspaces_read_checkpoint_result, workspaces_read_file_request, workspaces_read_file_result, workspaces_save_large_paste_request, workspaces_save_large_paste_result, workspace_summary_host_type, workspaces_workspace_details_host_type, session_context_info, task_progress, workspace_summary) + return RPC(abort_request, abort_result, account_get_quota_request, account_get_quota_result, account_quota_snapshot, agent_get_current_result, agent_info, agent_info_source, agent_list, agent_reload_result, agent_select_request, agent_select_result, api_key_auth_info, auth_info, auth_info_type, command_list, commands_handle_pending_command_request, commands_handle_pending_command_result, commands_invoke_request, commands_list_request, commands_respond_to_queued_command_request, commands_respond_to_queued_command_result, connected_remote_session_metadata, connected_remote_session_metadata_kind, connected_remote_session_metadata_repository, connect_remote_session_params, connect_request, connect_result, content_filter_mode, copilot_api_token_auth_info, copilot_user_response, copilot_user_response_endpoints, copilot_user_response_quota_snapshots, copilot_user_response_quota_snapshots_chat, copilot_user_response_quota_snapshots_completions, copilot_user_response_quota_snapshots_premium_interactions, current_model, discovered_mcp_server, discovered_mcp_server_type, enqueue_command_params, enqueue_command_result, env_auth_info, event_log_read_request, event_log_release_interest_result, event_log_tail_result, event_log_types, events_agent_scope, events_cursor_status, events_read_result, execute_command_params, execute_command_result, extension, extension_list, extensions_disable_request, extensions_enable_request, extension_source, extension_status, external_tool_result, external_tool_text_result_for_llm, external_tool_text_result_for_llm_binary_results_for_llm, external_tool_text_result_for_llm_binary_results_for_llm_type, external_tool_text_result_for_llm_content, external_tool_text_result_for_llm_content_audio, external_tool_text_result_for_llm_content_image, external_tool_text_result_for_llm_content_resource, external_tool_text_result_for_llm_content_resource_details, external_tool_text_result_for_llm_content_resource_link, external_tool_text_result_for_llm_content_resource_link_icon, external_tool_text_result_for_llm_content_resource_link_icon_theme, external_tool_text_result_for_llm_content_terminal, external_tool_text_result_for_llm_content_text, filter_mapping, fleet_start_request, fleet_start_result, folder_trust_add_params, folder_trust_check_params, folder_trust_check_result, gh_cli_auth_info, handle_pending_tool_call_request, handle_pending_tool_call_result, history_abort_manual_compaction_result, history_cancel_background_compaction_result, history_compact_context_window, history_compact_request, history_compact_result, history_summarize_for_handoff_result, history_truncate_request, history_truncate_result, hmac_auth_info, installed_plugin, installed_plugin_source, installed_plugin_source_github, installed_plugin_source_local, installed_plugin_source_url, instructions_get_sources_result, instructions_sources, instructions_sources_location, instructions_sources_type, log_request, log_result, lsp_initialize_request, mcp_cancel_sampling_execution_params, mcp_cancel_sampling_execution_result, mcp_config_add_request, mcp_config_disable_request, mcp_config_enable_request, mcp_config_list, mcp_config_remove_request, mcp_config_update_request, mcp_disable_request, mcp_discover_request, mcp_discover_result, mcp_enable_request, mcp_execute_sampling_params, mcp_execute_sampling_request, mcp_execute_sampling_result, mcp_oauth_login_request, mcp_oauth_login_result, mcp_remove_git_hub_result, mcp_sampling_execution_action, mcp_sampling_execution_result, mcp_server, mcp_server_config, mcp_server_config_http, mcp_server_config_http_auth, mcp_server_config_http_oauth_grant_type, mcp_server_config_http_type, mcp_server_config_stdio, mcp_server_list, mcp_set_env_value_mode_details, mcp_set_env_value_mode_params, mcp_set_env_value_mode_result, metadata_context_info_request, metadata_context_info_result, metadata_is_processing_result, metadata_recompute_context_tokens_request, metadata_recompute_context_tokens_result, metadata_record_context_change_request, metadata_record_context_change_result, metadata_set_working_directory_request, metadata_set_working_directory_result, metadata_snapshot_current_mode, metadata_snapshot_remote_metadata, metadata_snapshot_remote_metadata_repository, metadata_snapshot_remote_metadata_task_type, model, model_billing, model_billing_token_prices, model_capabilities, model_capabilities_limits, model_capabilities_limits_vision, model_capabilities_override, model_capabilities_override_limits, model_capabilities_override_limits_vision, model_capabilities_override_supports, model_capabilities_supports, model_list, model_picker_category, model_picker_price_category, model_policy, model_policy_state, model_set_reasoning_effort_request, model_set_reasoning_effort_result, models_list_request, model_switch_to_request, model_switch_to_result, mode_set_request, name_get_result, name_set_auto_request, name_set_auto_result, name_set_request, options_update_env_value_mode, pending_permission_request, pending_permission_request_list, permission_decision, permission_decision_approved, permission_decision_approved_for_location, permission_decision_approved_for_session, permission_decision_approve_for_location, permission_decision_approve_for_location_approval, permission_decision_approve_for_location_approval_commands, permission_decision_approve_for_location_approval_custom_tool, permission_decision_approve_for_location_approval_extension_management, permission_decision_approve_for_location_approval_extension_permission_access, permission_decision_approve_for_location_approval_mcp, permission_decision_approve_for_location_approval_mcp_sampling, permission_decision_approve_for_location_approval_memory, permission_decision_approve_for_location_approval_read, permission_decision_approve_for_location_approval_write, permission_decision_approve_for_session, permission_decision_approve_for_session_approval, permission_decision_approve_for_session_approval_commands, permission_decision_approve_for_session_approval_custom_tool, permission_decision_approve_for_session_approval_extension_management, permission_decision_approve_for_session_approval_extension_permission_access, permission_decision_approve_for_session_approval_mcp, permission_decision_approve_for_session_approval_mcp_sampling, permission_decision_approve_for_session_approval_memory, permission_decision_approve_for_session_approval_read, permission_decision_approve_for_session_approval_write, permission_decision_approve_once, permission_decision_approve_permanently, permission_decision_cancelled, permission_decision_denied_by_content_exclusion_policy, permission_decision_denied_by_permission_request_hook, permission_decision_denied_by_rules, permission_decision_denied_interactively_by_user, permission_decision_denied_no_approval_rule_and_could_not_request_from_user, permission_decision_reject, permission_decision_request, permission_decision_user_not_available, permission_location_add_tool_approval_params, permission_location_apply_params, permission_location_apply_result, permission_location_resolve_params, permission_location_resolve_result, permission_location_type, permission_paths_add_params, permission_paths_allowed_check_params, permission_paths_allowed_check_result, permission_paths_config, permission_paths_list, permission_paths_update_primary_params, permission_paths_workspace_check_params, permission_paths_workspace_check_result, permission_prompt_shown_notification, permission_request_result, permission_rules_set, permissions_configure_additional_content_exclusion_policy, permissions_configure_additional_content_exclusion_policy_rule, permissions_configure_additional_content_exclusion_policy_rule_source, permissions_configure_additional_content_exclusion_policy_scope, permissions_configure_params, permissions_configure_result, permissions_folder_trust_add_trusted_result, permissions_locations_add_tool_approval_details, permissions_locations_add_tool_approval_details_commands, permissions_locations_add_tool_approval_details_custom_tool, permissions_locations_add_tool_approval_details_extension_management, permissions_locations_add_tool_approval_details_extension_permission_access, permissions_locations_add_tool_approval_details_mcp, permissions_locations_add_tool_approval_details_mcp_sampling, permissions_locations_add_tool_approval_details_memory, permissions_locations_add_tool_approval_details_read, permissions_locations_add_tool_approval_details_write, permissions_locations_add_tool_approval_result, permissions_modify_rules_params, permissions_modify_rules_result, permissions_modify_rules_scope, permissions_notify_prompt_shown_result, permissions_paths_add_result, permissions_paths_list_request, permissions_paths_update_primary_result, permissions_pending_requests_request, permissions_reset_session_approvals_request, permissions_reset_session_approvals_result, permissions_set_approve_all_request, permissions_set_approve_all_result, permissions_set_approve_all_source, permissions_set_required_request, permissions_set_required_result, permissions_urls_set_unrestricted_mode_result, permission_urls_config, permission_urls_set_unrestricted_mode_params, ping_request, ping_result, plan_read_result, plan_update_request, plugin, plugin_list, queued_command_handled, queued_command_not_handled, queued_command_result, queue_pending_items, queue_pending_items_kind, queue_pending_items_result, queue_remove_most_recent_result, register_event_interest_params, register_event_interest_result, release_event_interest_params, remote_enable_request, remote_enable_result, remote_notify_steerable_changed_request, remote_notify_steerable_changed_result, remote_session_connection_result, remote_session_mode, schedule_entry, schedule_list, schedule_stop_request, schedule_stop_result, secrets_add_filter_values_request, secrets_add_filter_values_result, send_agent_mode, send_attachment, send_attachment_blob, send_attachment_directory, send_attachment_file, send_attachment_file_line_range, send_attachment_github_reference, send_attachment_github_reference_type, send_attachment_selection, send_attachment_selection_details, send_attachment_selection_details_end, send_attachment_selection_details_start, send_mode, send_request, send_result, server_skill, server_skill_list, session_auth_status, session_bulk_delete_result, session_context, session_context_host_type, session_enrich_metadata_result, session_fs_append_file_request, session_fs_error, session_fs_error_code, session_fs_exists_request, session_fs_exists_result, session_fs_mkdir_request, session_fs_readdir_request, session_fs_readdir_result, session_fs_readdir_with_types_entry, session_fs_readdir_with_types_entry_type, session_fs_readdir_with_types_request, session_fs_readdir_with_types_result, session_fs_read_file_request, session_fs_read_file_result, session_fs_rename_request, session_fs_rm_request, session_fs_set_provider_capabilities, session_fs_set_provider_conventions, session_fs_set_provider_request, session_fs_set_provider_result, session_fs_sqlite_exists_request, session_fs_sqlite_exists_result, session_fs_sqlite_query_request, session_fs_sqlite_query_result, session_fs_sqlite_query_type, session_fs_stat_request, session_fs_stat_result, session_fs_write_file_request, session_installed_plugin, session_installed_plugin_source, session_installed_plugin_source_github, session_installed_plugin_source_local, session_installed_plugin_source_url, session_list, session_list_filter, session_load_deferred_repo_hooks_result, session_log_level, session_metadata, session_metadata_snapshot, session_mode, session_prune_result, sessions_bulk_delete_request, sessions_check_in_use_request, sessions_check_in_use_result, sessions_close_request, sessions_close_result, sessions_enrich_metadata_request, session_set_credentials_params, session_set_credentials_result, sessions_find_by_prefix_request, sessions_find_by_prefix_result, sessions_find_by_task_id_request, sessions_find_by_task_id_result, sessions_fork_request, sessions_fork_result, sessions_get_event_file_path_request, sessions_get_event_file_path_result, sessions_get_last_for_context_request, sessions_get_last_for_context_result, sessions_get_persisted_remote_steerable_request, sessions_get_persisted_remote_steerable_result, session_sizes, sessions_list_request, sessions_load_deferred_repo_hooks_request, sessions_prune_old_request, sessions_release_lock_request, sessions_release_lock_result, sessions_reload_plugin_hooks_request, sessions_reload_plugin_hooks_result, sessions_save_request, sessions_save_result, sessions_set_additional_plugins_request, sessions_set_additional_plugins_result, session_update_options_params, session_update_options_result, session_working_directory_context, session_working_directory_context_host_type, shell_exec_request, shell_exec_result, shell_kill_request, shell_kill_result, shell_kill_signal, shutdown_request, skill, skill_list, skills_config_set_disabled_skills_request, skills_disable_request, skills_discover_request, skills_enable_request, skills_get_invoked_result, skills_invoked_skill, skills_load_diagnostics, slash_command_agent_prompt_result, slash_command_completed_result, slash_command_info, slash_command_input, slash_command_input_completion, slash_command_invocation_result, slash_command_kind, slash_command_select_subcommand_option, slash_command_select_subcommand_result, slash_command_text_result, task_agent_info, task_agent_progress, task_execution_mode, task_info, task_list, task_progress_line, tasks_cancel_request, tasks_cancel_result, tasks_get_current_promotable_result, tasks_get_progress_request, tasks_get_progress_result, task_shell_info, task_shell_info_attachment_mode, task_shell_progress, tasks_promote_current_to_background_result, tasks_promote_to_background_request, tasks_promote_to_background_result, tasks_refresh_result, tasks_remove_request, tasks_remove_result, tasks_send_message_request, tasks_send_message_result, tasks_start_agent_request, tasks_start_agent_result, task_status, tasks_wait_for_pending_result, telemetry_set_feature_overrides_request, token_auth_info, tool, tool_list, tools_initialize_and_validate_result, tools_list_request, ui_auto_mode_switch_response, ui_elicitation_array_any_of_field, ui_elicitation_array_any_of_field_items, ui_elicitation_array_any_of_field_items_any_of, ui_elicitation_array_enum_field, ui_elicitation_array_enum_field_items, ui_elicitation_field_value, ui_elicitation_request, ui_elicitation_response, ui_elicitation_response_action, ui_elicitation_response_content, ui_elicitation_result, ui_elicitation_schema, ui_elicitation_schema_property, ui_elicitation_schema_property_boolean, ui_elicitation_schema_property_number, ui_elicitation_schema_property_number_type, ui_elicitation_schema_property_string, ui_elicitation_schema_property_string_format, ui_elicitation_string_enum_field, ui_elicitation_string_one_of_field, ui_elicitation_string_one_of_field_one_of, ui_exit_plan_mode_action, ui_exit_plan_mode_response, ui_handle_pending_auto_mode_switch_request, ui_handle_pending_elicitation_request, ui_handle_pending_exit_plan_mode_request, ui_handle_pending_result, ui_handle_pending_sampling_request, ui_handle_pending_sampling_response, ui_handle_pending_user_input_request, ui_register_direct_auto_mode_switch_handler_result, ui_unregister_direct_auto_mode_switch_handler_request, ui_unregister_direct_auto_mode_switch_handler_result, ui_user_input_response, usage_get_metrics_result, usage_metrics_code_changes, usage_metrics_model_metric, usage_metrics_model_metric_requests, usage_metrics_model_metric_token_detail, usage_metrics_model_metric_usage, usage_metrics_token_detail, user_auth_info, workspaces_checkpoints, workspaces_create_file_request, workspaces_get_workspace_result, workspaces_list_checkpoints_result, workspaces_list_files_result, workspaces_read_checkpoint_request, workspaces_read_checkpoint_result, workspaces_read_file_request, workspaces_read_file_result, workspaces_save_large_paste_request, workspaces_save_large_paste_result, workspace_summary_host_type, workspaces_workspace_details_host_type, session_context_info, task_progress, workspace_summary) def to_dict(self) -> dict: result: dict = {} @@ -16009,7 +14715,7 @@ def to_dict(self) -> dict: result["AgentSelectRequest"] = to_class(AgentSelectRequest, self.agent_select_request) result["AgentSelectResult"] = to_class(AgentSelectResult, self.agent_select_result) result["ApiKeyAuthInfo"] = to_class(APIKeyAuthInfo, self.api_key_auth_info) - result["AuthInfo"] = to_class(AuthInfo, self.auth_info) + result["AuthInfo"] = (self.auth_info).to_dict() result["AuthInfoType"] = to_enum(AuthInfoType, self.auth_info_type) result["CommandList"] = to_class(CommandList, self.command_list) result["CommandsHandlePendingCommandRequest"] = to_class(CommandsHandlePendingCommandRequest, self.commands_handle_pending_command_request) @@ -16057,7 +14763,7 @@ def to_dict(self) -> dict: result["ExternalToolTextResultForLlm"] = to_class(ExternalToolTextResultForLlm, self.external_tool_text_result_for_llm) result["ExternalToolTextResultForLlmBinaryResultsForLlm"] = to_class(ExternalToolTextResultForLlmBinaryResultsForLlm, self.external_tool_text_result_for_llm_binary_results_for_llm) result["ExternalToolTextResultForLlmBinaryResultsForLlmType"] = to_enum(ExternalToolTextResultForLlmBinaryResultsForLlmType, self.external_tool_text_result_for_llm_binary_results_for_llm_type) - result["ExternalToolTextResultForLlmContent"] = to_class(ExternalToolTextResultForLlmContent, self.external_tool_text_result_for_llm_content) + result["ExternalToolTextResultForLlmContent"] = (self.external_tool_text_result_for_llm_content).to_dict() result["ExternalToolTextResultForLlmContentAudio"] = to_class(ExternalToolTextResultForLlmContentAudio, self.external_tool_text_result_for_llm_content_audio) result["ExternalToolTextResultForLlmContentImage"] = to_class(ExternalToolTextResultForLlmContentImage, self.external_tool_text_result_for_llm_content_image) result["ExternalToolTextResultForLlmContentResource"] = to_class(ExternalToolTextResultForLlmContentResource, self.external_tool_text_result_for_llm_content_resource) @@ -16170,12 +14876,12 @@ def to_dict(self) -> dict: result["OptionsUpdateEnvValueMode"] = to_enum(MCPSetEnvValueModeDetails, self.options_update_env_value_mode) result["PendingPermissionRequest"] = to_class(PendingPermissionRequest, self.pending_permission_request) result["PendingPermissionRequestList"] = to_class(PendingPermissionRequestList, self.pending_permission_request_list) - result["PermissionDecision"] = to_class(PermissionDecision, self.permission_decision) + result["PermissionDecision"] = (self.permission_decision).to_dict() result["PermissionDecisionApproved"] = to_class(PermissionDecisionApproved, self.permission_decision_approved) result["PermissionDecisionApprovedForLocation"] = to_class(PermissionDecisionApprovedForLocation, self.permission_decision_approved_for_location) result["PermissionDecisionApprovedForSession"] = to_class(PermissionDecisionApprovedForSession, self.permission_decision_approved_for_session) result["PermissionDecisionApproveForLocation"] = to_class(PermissionDecisionApproveForLocation, self.permission_decision_approve_for_location) - result["PermissionDecisionApproveForLocationApproval"] = to_class(PermissionDecisionApproveForLocationApproval, self.permission_decision_approve_for_location_approval) + result["PermissionDecisionApproveForLocationApproval"] = (self.permission_decision_approve_for_location_approval).to_dict() result["PermissionDecisionApproveForLocationApprovalCommands"] = to_class(PermissionDecisionApproveForLocationApprovalCommands, self.permission_decision_approve_for_location_approval_commands) result["PermissionDecisionApproveForLocationApprovalCustomTool"] = to_class(PermissionDecisionApproveForLocationApprovalCustomTool, self.permission_decision_approve_for_location_approval_custom_tool) result["PermissionDecisionApproveForLocationApprovalExtensionManagement"] = to_class(PermissionDecisionApproveForLocationApprovalExtensionManagement, self.permission_decision_approve_for_location_approval_extension_management) @@ -16186,7 +14892,7 @@ def to_dict(self) -> dict: result["PermissionDecisionApproveForLocationApprovalRead"] = to_class(PermissionDecisionApproveForLocationApprovalRead, self.permission_decision_approve_for_location_approval_read) result["PermissionDecisionApproveForLocationApprovalWrite"] = to_class(PermissionDecisionApproveForLocationApprovalWrite, self.permission_decision_approve_for_location_approval_write) result["PermissionDecisionApproveForSession"] = to_class(PermissionDecisionApproveForSession, self.permission_decision_approve_for_session) - result["PermissionDecisionApproveForSessionApproval"] = to_class(PermissionDecisionApproveForSessionApproval, self.permission_decision_approve_for_session_approval) + result["PermissionDecisionApproveForSessionApproval"] = (self.permission_decision_approve_for_session_approval).to_dict() result["PermissionDecisionApproveForSessionApprovalCommands"] = to_class(PermissionDecisionApproveForSessionApprovalCommands, self.permission_decision_approve_for_session_approval_commands) result["PermissionDecisionApproveForSessionApprovalCustomTool"] = to_class(PermissionDecisionApproveForSessionApprovalCustomTool, self.permission_decision_approve_for_session_approval_custom_tool) result["PermissionDecisionApproveForSessionApprovalExtensionManagement"] = to_class(PermissionDecisionApproveForSessionApprovalExtensionManagement, self.permission_decision_approve_for_session_approval_extension_management) @@ -16231,7 +14937,7 @@ def to_dict(self) -> dict: result["PermissionsConfigureParams"] = to_class(PermissionsConfigureParams, self.permissions_configure_params) result["PermissionsConfigureResult"] = to_class(PermissionsConfigureResult, self.permissions_configure_result) result["PermissionsFolderTrustAddTrustedResult"] = to_class(PermissionsFolderTrustAddTrustedResult, self.permissions_folder_trust_add_trusted_result) - result["PermissionsLocationsAddToolApprovalDetails"] = to_class(PermissionsLocationsAddToolApprovalDetails, self.permissions_locations_add_tool_approval_details) + result["PermissionsLocationsAddToolApprovalDetails"] = (self.permissions_locations_add_tool_approval_details).to_dict() result["PermissionsLocationsAddToolApprovalDetailsCommands"] = to_class(PermissionsLocationsAddToolApprovalDetailsCommands, self.permissions_locations_add_tool_approval_details_commands) result["PermissionsLocationsAddToolApprovalDetailsCustomTool"] = to_class(PermissionsLocationsAddToolApprovalDetailsCustomTool, self.permissions_locations_add_tool_approval_details_custom_tool) result["PermissionsLocationsAddToolApprovalDetailsExtensionManagement"] = to_class(PermissionsLocationsAddToolApprovalDetailsExtensionManagement, self.permissions_locations_add_tool_approval_details_extension_management) @@ -16268,7 +14974,7 @@ def to_dict(self) -> dict: result["PluginList"] = to_class(PluginList, self.plugin_list) result["QueuedCommandHandled"] = to_class(QueuedCommandHandled, self.queued_command_handled) result["QueuedCommandNotHandled"] = to_class(QueuedCommandNotHandled, self.queued_command_not_handled) - result["QueuedCommandResult"] = to_class(QueuedCommandResult, self.queued_command_result) + result["QueuedCommandResult"] = (self.queued_command_result).to_dict() result["QueuePendingItems"] = to_class(QueuePendingItems, self.queue_pending_items) result["QueuePendingItemsKind"] = to_enum(QueuePendingItemsKind, self.queue_pending_items_kind) result["QueuePendingItemsResult"] = to_class(QueuePendingItemsResult, self.queue_pending_items_result) @@ -16289,7 +14995,7 @@ def to_dict(self) -> dict: result["SecretsAddFilterValuesRequest"] = to_class(SecretsAddFilterValuesRequest, self.secrets_add_filter_values_request) result["SecretsAddFilterValuesResult"] = to_class(SecretsAddFilterValuesResult, self.secrets_add_filter_values_result) result["SendAgentMode"] = to_enum(SendAgentMode, self.send_agent_mode) - result["SendAttachment"] = to_class(SendAttachment, self.send_attachment) + result["SendAttachment"] = (self.send_attachment).to_dict() result["SendAttachmentBlob"] = to_class(SendAttachmentBlob, self.send_attachment_blob) result["SendAttachmentDirectory"] = to_class(SendAttachmentDirectory, self.send_attachment_directory) result["SendAttachmentFile"] = to_class(SendAttachmentFile, self.send_attachment_file) @@ -16407,7 +15113,7 @@ def to_dict(self) -> dict: result["SlashCommandInfo"] = to_class(SlashCommandInfo, self.slash_command_info) result["SlashCommandInput"] = to_class(SlashCommandInput, self.slash_command_input) result["SlashCommandInputCompletion"] = to_enum(SlashCommandInputCompletion, self.slash_command_input_completion) - result["SlashCommandInvocationResult"] = to_class(SlashCommandInvocationResult, self.slash_command_invocation_result) + result["SlashCommandInvocationResult"] = (self.slash_command_invocation_result).to_dict() result["SlashCommandKind"] = to_enum(SlashCommandKind, self.slash_command_kind) result["SlashCommandSelectSubcommandOption"] = to_class(SlashCommandSelectSubcommandOption, self.slash_command_select_subcommand_option) result["SlashCommandSelectSubcommandResult"] = to_class(SlashCommandSelectSubcommandResult, self.slash_command_select_subcommand_result) @@ -16415,7 +15121,7 @@ def to_dict(self) -> dict: result["TaskAgentInfo"] = to_class(TaskAgentInfo, self.task_agent_info) result["TaskAgentProgress"] = to_class(TaskAgentProgress, self.task_agent_progress) result["TaskExecutionMode"] = to_enum(TaskExecutionMode, self.task_execution_mode) - result["TaskInfo"] = to_class(TaskInfo, self.task_info) + result["TaskInfo"] = (self.task_info).to_dict() result["TaskList"] = to_class(TaskList, self.task_list) result["TaskProgressLine"] = to_class(TaskProgressLine, self.task_progress_line) result["TasksCancelRequest"] = to_class(TasksCancelRequest, self.tasks_cancel_request) @@ -16487,14 +15193,6 @@ def to_dict(self) -> dict: result["UsageMetricsModelMetricUsage"] = to_class(UsageMetricsModelMetricUsage, self.usage_metrics_model_metric_usage) result["UsageMetricsTokenDetail"] = to_class(UsageMetricsTokenDetail, self.usage_metrics_token_detail) result["UserAuthInfo"] = to_class(UserAuthInfo, self.user_auth_info) - result["UserToolSessionApprovalCommands"] = to_class(UserToolSessionApprovalCommands, self.user_tool_session_approval_commands) - result["UserToolSessionApprovalCustomTool"] = to_class(UserToolSessionApprovalCustomTool, self.user_tool_session_approval_custom_tool) - result["UserToolSessionApprovalExtensionManagement"] = to_class(UserToolSessionApprovalExtensionManagement, self.user_tool_session_approval_extension_management) - result["UserToolSessionApprovalExtensionPermissionAccess"] = to_class(UserToolSessionApprovalExtensionPermissionAccess, self.user_tool_session_approval_extension_permission_access) - result["UserToolSessionApprovalMcp"] = to_class(UserToolSessionApprovalMCP, self.user_tool_session_approval_mcp) - result["UserToolSessionApprovalMemory"] = to_class(UserToolSessionApprovalMemory, self.user_tool_session_approval_memory) - result["UserToolSessionApprovalRead"] = to_class(UserToolSessionApprovalRead, self.user_tool_session_approval_read) - result["UserToolSessionApprovalWrite"] = to_class(UserToolSessionApprovalWrite, self.user_tool_session_approval_write) result["WorkspacesCheckpoints"] = to_class(WorkspacesCheckpoints, self.workspaces_checkpoints) result["WorkspacesCreateFileRequest"] = to_class(WorkspacesCreateFileRequest, self.workspaces_create_file_request) result["WorkspacesGetWorkspaceResult"] = to_class(WorkspacesGetWorkspaceResult, self.workspaces_get_workspace_result) @@ -16519,6 +15217,164 @@ def rpc_from_dict(s: Any) -> RPC: def rpc_to_dict(x: RPC) -> Any: return to_class(RPC, x) +# The new auth credentials to install on the session. When omitted or `undefined`, the call is a no-op and the session's existing credentials are preserved. The runtime stores the value verbatim and uses it for outbound model/API requests; it does NOT re-validate or re-fetch the associated Copilot user response. Several variants carry secret material; treat this method's params as containing secrets at rest and in transit. +AuthInfo = HMACAuthInfo | EnvAuthInfo | TokenAuthInfo | CopilotAPITokenAuthInfo | UserAuthInfo | GhCLIAuthInfo | APIKeyAuthInfo + +def _load_AuthInfo(obj: Any) -> "AuthInfo": + assert isinstance(obj, dict) + kind = obj.get("type") + match kind: + case "hmac": return HMACAuthInfo.from_dict(obj) + case "env": return EnvAuthInfo.from_dict(obj) + case "token": return TokenAuthInfo.from_dict(obj) + case "copilot-api-token": return CopilotAPITokenAuthInfo.from_dict(obj) + case "user": return UserAuthInfo.from_dict(obj) + case "gh-cli": return GhCLIAuthInfo.from_dict(obj) + case "api-key": return APIKeyAuthInfo.from_dict(obj) + case _: raise ValueError(f"Unknown AuthInfo type: {kind!r}") + +# A content block within a tool result, which may be text, terminal output, image, audio, or a resource +ExternalToolTextResultForLlmContent = ExternalToolTextResultForLlmContentText | ExternalToolTextResultForLlmContentTerminal | ExternalToolTextResultForLlmContentImage | ExternalToolTextResultForLlmContentAudio | ExternalToolTextResultForLlmContentResourceLink | ExternalToolTextResultForLlmContentResource + +def _load_ExternalToolTextResultForLlmContent(obj: Any) -> "ExternalToolTextResultForLlmContent": + assert isinstance(obj, dict) + kind = obj.get("type") + match kind: + case "text": return ExternalToolTextResultForLlmContentText.from_dict(obj) + case "terminal": return ExternalToolTextResultForLlmContentTerminal.from_dict(obj) + case "image": return ExternalToolTextResultForLlmContentImage.from_dict(obj) + case "audio": return ExternalToolTextResultForLlmContentAudio.from_dict(obj) + case "resource_link": return ExternalToolTextResultForLlmContentResourceLink.from_dict(obj) + case "resource": return ExternalToolTextResultForLlmContentResource.from_dict(obj) + case _: raise ValueError(f"Unknown ExternalToolTextResultForLlmContent type: {kind!r}") + +# The client's response to the pending permission prompt +PermissionDecision = PermissionDecisionApproveOnce | PermissionDecisionApproveForSession | PermissionDecisionApproveForLocation | PermissionDecisionApprovePermanently | PermissionDecisionReject | PermissionDecisionUserNotAvailable | PermissionDecisionApproved | PermissionDecisionApprovedForSession | PermissionDecisionApprovedForLocation | PermissionDecisionCancelled | PermissionDecisionDeniedByRules | PermissionDecisionDeniedNoApprovalRuleAndCouldNotRequestFromUser | PermissionDecisionDeniedInteractivelyByUser | PermissionDecisionDeniedByContentExclusionPolicy | PermissionDecisionDeniedByPermissionRequestHook + +def _load_PermissionDecision(obj: Any) -> "PermissionDecision": + assert isinstance(obj, dict) + kind = obj.get("kind") + match kind: + case "approve-once": return PermissionDecisionApproveOnce.from_dict(obj) + case "approve-for-session": return PermissionDecisionApproveForSession.from_dict(obj) + case "approve-for-location": return PermissionDecisionApproveForLocation.from_dict(obj) + case "approve-permanently": return PermissionDecisionApprovePermanently.from_dict(obj) + case "reject": return PermissionDecisionReject.from_dict(obj) + case "user-not-available": return PermissionDecisionUserNotAvailable.from_dict(obj) + case "approved": return PermissionDecisionApproved.from_dict(obj) + case "approved-for-session": return PermissionDecisionApprovedForSession.from_dict(obj) + case "approved-for-location": return PermissionDecisionApprovedForLocation.from_dict(obj) + case "cancelled": return PermissionDecisionCancelled.from_dict(obj) + case "denied-by-rules": return PermissionDecisionDeniedByRules.from_dict(obj) + case "denied-no-approval-rule-and-could-not-request-from-user": return PermissionDecisionDeniedNoApprovalRuleAndCouldNotRequestFromUser.from_dict(obj) + case "denied-interactively-by-user": return PermissionDecisionDeniedInteractivelyByUser.from_dict(obj) + case "denied-by-content-exclusion-policy": return PermissionDecisionDeniedByContentExclusionPolicy.from_dict(obj) + case "denied-by-permission-request-hook": return PermissionDecisionDeniedByPermissionRequestHook.from_dict(obj) + case _: raise ValueError(f"Unknown PermissionDecision kind: {kind!r}") + +# Approval to persist for this location +PermissionDecisionApproveForLocationApproval = PermissionDecisionApproveForLocationApprovalCommands | PermissionDecisionApproveForLocationApprovalRead | PermissionDecisionApproveForLocationApprovalWrite | PermissionDecisionApproveForLocationApprovalMCP | PermissionDecisionApproveForLocationApprovalMCPSampling | PermissionDecisionApproveForLocationApprovalMemory | PermissionDecisionApproveForLocationApprovalCustomTool | PermissionDecisionApproveForLocationApprovalExtensionManagement | PermissionDecisionApproveForLocationApprovalExtensionPermissionAccess + +def _load_PermissionDecisionApproveForLocationApproval(obj: Any) -> "PermissionDecisionApproveForLocationApproval": + assert isinstance(obj, dict) + kind = obj.get("kind") + match kind: + case "commands": return PermissionDecisionApproveForLocationApprovalCommands.from_dict(obj) + case "read": return PermissionDecisionApproveForLocationApprovalRead.from_dict(obj) + case "write": return PermissionDecisionApproveForLocationApprovalWrite.from_dict(obj) + case "mcp": return PermissionDecisionApproveForLocationApprovalMCP.from_dict(obj) + case "mcp-sampling": return PermissionDecisionApproveForLocationApprovalMCPSampling.from_dict(obj) + case "memory": return PermissionDecisionApproveForLocationApprovalMemory.from_dict(obj) + case "custom-tool": return PermissionDecisionApproveForLocationApprovalCustomTool.from_dict(obj) + case "extension-management": return PermissionDecisionApproveForLocationApprovalExtensionManagement.from_dict(obj) + case "extension-permission-access": return PermissionDecisionApproveForLocationApprovalExtensionPermissionAccess.from_dict(obj) + case _: raise ValueError(f"Unknown PermissionDecisionApproveForLocationApproval kind: {kind!r}") + +# Session-scoped approval to remember (tool prompts only; omitted for path/url prompts) +PermissionDecisionApproveForSessionApproval = PermissionDecisionApproveForSessionApprovalCommands | PermissionDecisionApproveForSessionApprovalRead | PermissionDecisionApproveForSessionApprovalWrite | PermissionDecisionApproveForSessionApprovalMCP | PermissionDecisionApproveForSessionApprovalMCPSampling | PermissionDecisionApproveForSessionApprovalMemory | PermissionDecisionApproveForSessionApprovalCustomTool | PermissionDecisionApproveForSessionApprovalExtensionManagement | PermissionDecisionApproveForSessionApprovalExtensionPermissionAccess + +def _load_PermissionDecisionApproveForSessionApproval(obj: Any) -> "PermissionDecisionApproveForSessionApproval": + assert isinstance(obj, dict) + kind = obj.get("kind") + match kind: + case "commands": return PermissionDecisionApproveForSessionApprovalCommands.from_dict(obj) + case "read": return PermissionDecisionApproveForSessionApprovalRead.from_dict(obj) + case "write": return PermissionDecisionApproveForSessionApprovalWrite.from_dict(obj) + case "mcp": return PermissionDecisionApproveForSessionApprovalMCP.from_dict(obj) + case "mcp-sampling": return PermissionDecisionApproveForSessionApprovalMCPSampling.from_dict(obj) + case "memory": return PermissionDecisionApproveForSessionApprovalMemory.from_dict(obj) + case "custom-tool": return PermissionDecisionApproveForSessionApprovalCustomTool.from_dict(obj) + case "extension-management": return PermissionDecisionApproveForSessionApprovalExtensionManagement.from_dict(obj) + case "extension-permission-access": return PermissionDecisionApproveForSessionApprovalExtensionPermissionAccess.from_dict(obj) + case _: raise ValueError(f"Unknown PermissionDecisionApproveForSessionApproval kind: {kind!r}") + +# Tool approval to persist and apply +PermissionsLocationsAddToolApprovalDetails = PermissionsLocationsAddToolApprovalDetailsCommands | PermissionsLocationsAddToolApprovalDetailsRead | PermissionsLocationsAddToolApprovalDetailsWrite | PermissionsLocationsAddToolApprovalDetailsMCP | PermissionsLocationsAddToolApprovalDetailsMCPSampling | PermissionsLocationsAddToolApprovalDetailsMemory | PermissionsLocationsAddToolApprovalDetailsCustomTool | PermissionsLocationsAddToolApprovalDetailsExtensionManagement | PermissionsLocationsAddToolApprovalDetailsExtensionPermissionAccess + +def _load_PermissionsLocationsAddToolApprovalDetails(obj: Any) -> "PermissionsLocationsAddToolApprovalDetails": + assert isinstance(obj, dict) + kind = obj.get("kind") + match kind: + case "commands": return PermissionsLocationsAddToolApprovalDetailsCommands.from_dict(obj) + case "read": return PermissionsLocationsAddToolApprovalDetailsRead.from_dict(obj) + case "write": return PermissionsLocationsAddToolApprovalDetailsWrite.from_dict(obj) + case "mcp": return PermissionsLocationsAddToolApprovalDetailsMCP.from_dict(obj) + case "mcp-sampling": return PermissionsLocationsAddToolApprovalDetailsMCPSampling.from_dict(obj) + case "memory": return PermissionsLocationsAddToolApprovalDetailsMemory.from_dict(obj) + case "custom-tool": return PermissionsLocationsAddToolApprovalDetailsCustomTool.from_dict(obj) + case "extension-management": return PermissionsLocationsAddToolApprovalDetailsExtensionManagement.from_dict(obj) + case "extension-permission-access": return PermissionsLocationsAddToolApprovalDetailsExtensionPermissionAccess.from_dict(obj) + case _: raise ValueError(f"Unknown PermissionsLocationsAddToolApprovalDetails kind: {kind!r}") + +# Result of the queued command execution. +QueuedCommandResult = QueuedCommandHandled | QueuedCommandNotHandled + +def _load_QueuedCommandResult(obj: Any) -> "QueuedCommandResult": + assert isinstance(obj, dict) + kind = obj.get("handled") + match kind: + case "true": return QueuedCommandHandled.from_dict(obj) + case "false": return QueuedCommandNotHandled.from_dict(obj) + case _: raise ValueError(f"Unknown QueuedCommandResult handled: {kind!r}") + +# A user message attachment — a file, directory, code selection, blob, or GitHub reference +SendAttachment = SendAttachmentFile | SendAttachmentDirectory | SendAttachmentSelection | SendAttachmentGithubReference | SendAttachmentBlob + +def _load_SendAttachment(obj: Any) -> "SendAttachment": + assert isinstance(obj, dict) + kind = obj.get("type") + match kind: + case "file": return SendAttachmentFile.from_dict(obj) + case "directory": return SendAttachmentDirectory.from_dict(obj) + case "selection": return SendAttachmentSelection.from_dict(obj) + case "github_reference": return SendAttachmentGithubReference.from_dict(obj) + case "blob": return SendAttachmentBlob.from_dict(obj) + case _: raise ValueError(f"Unknown SendAttachment type: {kind!r}") + +# Result of invoking the slash command (text output, prompt to send to the agent, or completion). +SlashCommandInvocationResult = SlashCommandTextResult | SlashCommandAgentPromptResult | SlashCommandCompletedResult | SlashCommandSelectSubcommandResult + +def _load_SlashCommandInvocationResult(obj: Any) -> "SlashCommandInvocationResult": + assert isinstance(obj, dict) + kind = obj.get("kind") + match kind: + case "text": return SlashCommandTextResult.from_dict(obj) + case "agent-prompt": return SlashCommandAgentPromptResult.from_dict(obj) + case "completed": return SlashCommandCompletedResult.from_dict(obj) + case "select-subcommand": return SlashCommandSelectSubcommandResult.from_dict(obj) + case _: raise ValueError(f"Unknown SlashCommandInvocationResult kind: {kind!r}") + +# Schema for the `TaskInfo` type. +TaskInfo = TaskAgentInfo | TaskShellInfo + +def _load_TaskInfo(obj: Any) -> "TaskInfo": + assert isinstance(obj, dict) + kind = obj.get("type") + match kind: + case "agent": return TaskAgentInfo.from_dict(obj) + case "shell": return TaskShellInfo.from_dict(obj) + case _: raise ValueError(f"Unknown TaskInfo type: {kind!r}") + ExternalToolResult = ExternalToolTextResultForLlm FilterMapping = dict @@ -17261,7 +16117,7 @@ async def invoke(self, params: CommandsInvokeRequest, *, timeout: float | None = "Invokes a slash command in the session.\n\nArgs:\n params: Slash command name and optional raw input string to invoke.\n\nReturns:\n Result of invoking the slash command (text output, prompt to send to the agent, or completion)." params_dict: dict[str, Any] = {k: v for k, v in params.to_dict().items() if v is not None} params_dict["sessionId"] = self._session_id - return SlashCommandInvocationResult.from_dict(await self._client.request("session.commands.invoke", params_dict, **_timeout_kwargs(timeout))) + return _load_SlashCommandInvocationResult(await self._client.request("session.commands.invoke", params_dict, **_timeout_kwargs(timeout))) async def handle_pending_command(self, params: CommandsHandlePendingCommandRequest, *, timeout: float | None = None) -> CommandsHandlePendingCommandResult: "Reports completion of a pending client-handled slash command.\n\nArgs:\n params: Pending command request ID and an optional error if the client handler failed.\n\nReturns:\n Indicates whether the pending client-handled command was completed successfully." diff --git a/python/copilot/generated/session_events.py b/python/copilot/generated/session_events.py index 515f9c317..4b72621df 100644 --- a/python/copilot/generated/session_events.py +++ b/python/copilot/generated/session_events.py @@ -9,7 +9,7 @@ from dataclasses import dataclass from datetime import datetime, timedelta from enum import Enum -from typing import Any, TypeVar, cast +from typing import Any, ClassVar, TypeVar, cast from uuid import UUID import dateutil.parser @@ -1812,6 +1812,91 @@ def to_dict(self) -> dict: return {} +@dataclass +class PermissionApproved: + "Schema for the `PermissionApproved` type." + kind: ClassVar[str] = "approved" + + @staticmethod + def from_dict(obj: Any) -> "PermissionApproved": + assert isinstance(obj, dict) + return PermissionApproved( + ) + + def to_dict(self) -> dict: + result: dict = {} + result["kind"] = self.kind + return result + + +@dataclass +class PermissionApprovedForLocation: + "Schema for the `PermissionApprovedForLocation` type." + approval: UserToolSessionApproval + kind: ClassVar[str] = "approved-for-location" + location_key: str + + @staticmethod + def from_dict(obj: Any) -> "PermissionApprovedForLocation": + assert isinstance(obj, dict) + approval = _load_UserToolSessionApproval(obj.get("approval")) + location_key = from_str(obj.get("locationKey")) + return PermissionApprovedForLocation( + approval=approval, + location_key=location_key, + ) + + def to_dict(self) -> dict: + result: dict = {} + result["approval"] = self.approval.to_dict() + result["kind"] = self.kind + result["locationKey"] = from_str(self.location_key) + return result + + +@dataclass +class PermissionApprovedForSession: + "Schema for the `PermissionApprovedForSession` type." + approval: UserToolSessionApproval + kind: ClassVar[str] = "approved-for-session" + + @staticmethod + def from_dict(obj: Any) -> "PermissionApprovedForSession": + assert isinstance(obj, dict) + approval = _load_UserToolSessionApproval(obj.get("approval")) + return PermissionApprovedForSession( + approval=approval, + ) + + def to_dict(self) -> dict: + result: dict = {} + result["approval"] = self.approval.to_dict() + result["kind"] = self.kind + return result + + +@dataclass +class PermissionCancelled: + "Schema for the `PermissionCancelled` type." + kind: ClassVar[str] = "cancelled" + reason: str | None = None + + @staticmethod + def from_dict(obj: Any) -> "PermissionCancelled": + assert isinstance(obj, dict) + reason = from_union([from_none, from_str], obj.get("reason")) + return PermissionCancelled( + reason=reason, + ) + + def to_dict(self) -> dict: + result: dict = {} + result["kind"] = self.kind + if self.reason is not None: + result["reason"] = from_union([from_none, from_str], self.reason) + return result + + @dataclass class PermissionCompletedData: "Permission request completion notification signaling UI dismissal" @@ -1822,357 +1907,845 @@ class PermissionCompletedData: @staticmethod def from_dict(obj: Any) -> "PermissionCompletedData": assert isinstance(obj, dict) - request_id = from_str(obj.get("requestId")) - result = PermissionResult.from_dict(obj.get("result")) + request_id = from_str(obj.get("requestId")) + result = _load_PermissionResult(obj.get("result")) + tool_call_id = from_union([from_none, from_str], obj.get("toolCallId")) + return PermissionCompletedData( + request_id=request_id, + result=result, + tool_call_id=tool_call_id, + ) + + def to_dict(self) -> dict: + result: dict = {} + result["requestId"] = from_str(self.request_id) + result["result"] = self.result.to_dict() + if self.tool_call_id is not None: + result["toolCallId"] = from_union([from_none, from_str], self.tool_call_id) + return result + + +@dataclass +class PermissionDeniedByContentExclusionPolicy: + "Schema for the `PermissionDeniedByContentExclusionPolicy` type." + kind: ClassVar[str] = "denied-by-content-exclusion-policy" + message: str + path: str + + @staticmethod + def from_dict(obj: Any) -> "PermissionDeniedByContentExclusionPolicy": + assert isinstance(obj, dict) + message = from_str(obj.get("message")) + path = from_str(obj.get("path")) + return PermissionDeniedByContentExclusionPolicy( + message=message, + path=path, + ) + + def to_dict(self) -> dict: + result: dict = {} + result["kind"] = self.kind + result["message"] = from_str(self.message) + result["path"] = from_str(self.path) + return result + + +@dataclass +class PermissionDeniedByPermissionRequestHook: + "Schema for the `PermissionDeniedByPermissionRequestHook` type." + kind: ClassVar[str] = "denied-by-permission-request-hook" + interrupt: bool | None = None + message: str | None = None + + @staticmethod + def from_dict(obj: Any) -> "PermissionDeniedByPermissionRequestHook": + assert isinstance(obj, dict) + interrupt = from_union([from_none, from_bool], obj.get("interrupt")) + message = from_union([from_none, from_str], obj.get("message")) + return PermissionDeniedByPermissionRequestHook( + interrupt=interrupt, + message=message, + ) + + def to_dict(self) -> dict: + result: dict = {} + result["kind"] = self.kind + if self.interrupt is not None: + result["interrupt"] = from_union([from_none, from_bool], self.interrupt) + if self.message is not None: + result["message"] = from_union([from_none, from_str], self.message) + return result + + +@dataclass +class PermissionDeniedByRules: + "Schema for the `PermissionDeniedByRules` type." + kind: ClassVar[str] = "denied-by-rules" + rules: list[PermissionRule] + + @staticmethod + def from_dict(obj: Any) -> "PermissionDeniedByRules": + assert isinstance(obj, dict) + rules = from_list(PermissionRule.from_dict, obj.get("rules")) + return PermissionDeniedByRules( + rules=rules, + ) + + def to_dict(self) -> dict: + result: dict = {} + result["kind"] = self.kind + result["rules"] = from_list(lambda x: to_class(PermissionRule, x), self.rules) + return result + + +@dataclass +class PermissionDeniedInteractivelyByUser: + "Schema for the `PermissionDeniedInteractivelyByUser` type." + kind: ClassVar[str] = "denied-interactively-by-user" + feedback: str | None = None + force_reject: bool | None = None + + @staticmethod + def from_dict(obj: Any) -> "PermissionDeniedInteractivelyByUser": + assert isinstance(obj, dict) + feedback = from_union([from_none, from_str], obj.get("feedback")) + force_reject = from_union([from_none, from_bool], obj.get("forceReject")) + return PermissionDeniedInteractivelyByUser( + feedback=feedback, + force_reject=force_reject, + ) + + def to_dict(self) -> dict: + result: dict = {} + result["kind"] = self.kind + if self.feedback is not None: + result["feedback"] = from_union([from_none, from_str], self.feedback) + if self.force_reject is not None: + result["forceReject"] = from_union([from_none, from_bool], self.force_reject) + return result + + +@dataclass +class PermissionDeniedNoApprovalRuleAndCouldNotRequestFromUser: + "Schema for the `PermissionDeniedNoApprovalRuleAndCouldNotRequestFromUser` type." + kind: ClassVar[str] = "denied-no-approval-rule-and-could-not-request-from-user" + + @staticmethod + def from_dict(obj: Any) -> "PermissionDeniedNoApprovalRuleAndCouldNotRequestFromUser": + assert isinstance(obj, dict) + return PermissionDeniedNoApprovalRuleAndCouldNotRequestFromUser( + ) + + def to_dict(self) -> dict: + result: dict = {} + result["kind"] = self.kind + return result + + +@dataclass +class PermissionPromptRequestCommands: + "Shell command permission prompt" + can_offer_session_approval: bool + command_identifiers: list[str] + full_command_text: str + intention: str + kind: ClassVar[str] = "commands" + tool_call_id: str | None = None + warning: str | None = None + + @staticmethod + def from_dict(obj: Any) -> "PermissionPromptRequestCommands": + assert isinstance(obj, dict) + can_offer_session_approval = from_bool(obj.get("canOfferSessionApproval")) + command_identifiers = from_list(from_str, obj.get("commandIdentifiers")) + full_command_text = from_str(obj.get("fullCommandText")) + intention = from_str(obj.get("intention")) + tool_call_id = from_union([from_none, from_str], obj.get("toolCallId")) + warning = from_union([from_none, from_str], obj.get("warning")) + return PermissionPromptRequestCommands( + can_offer_session_approval=can_offer_session_approval, + command_identifiers=command_identifiers, + full_command_text=full_command_text, + intention=intention, + tool_call_id=tool_call_id, + warning=warning, + ) + + def to_dict(self) -> dict: + result: dict = {} + result["canOfferSessionApproval"] = from_bool(self.can_offer_session_approval) + result["commandIdentifiers"] = from_list(from_str, self.command_identifiers) + result["fullCommandText"] = from_str(self.full_command_text) + result["intention"] = from_str(self.intention) + result["kind"] = self.kind + if self.tool_call_id is not None: + result["toolCallId"] = from_union([from_none, from_str], self.tool_call_id) + if self.warning is not None: + result["warning"] = from_union([from_none, from_str], self.warning) + return result + + +@dataclass +class PermissionPromptRequestCustomTool: + "Custom tool invocation permission prompt" + kind: ClassVar[str] = "custom-tool" + tool_description: str + tool_name: str + args: Any = None + tool_call_id: str | None = None + + @staticmethod + def from_dict(obj: Any) -> "PermissionPromptRequestCustomTool": + assert isinstance(obj, dict) + tool_description = from_str(obj.get("toolDescription")) + tool_name = from_str(obj.get("toolName")) + args = obj.get("args") + tool_call_id = from_union([from_none, from_str], obj.get("toolCallId")) + return PermissionPromptRequestCustomTool( + tool_description=tool_description, + tool_name=tool_name, + args=args, + tool_call_id=tool_call_id, + ) + + def to_dict(self) -> dict: + result: dict = {} + result["kind"] = self.kind + result["toolDescription"] = from_str(self.tool_description) + result["toolName"] = from_str(self.tool_name) + if self.args is not None: + result["args"] = self.args + if self.tool_call_id is not None: + result["toolCallId"] = from_union([from_none, from_str], self.tool_call_id) + return result + + +@dataclass +class PermissionPromptRequestExtensionManagement: + "Extension management permission prompt" + kind: ClassVar[str] = "extension-management" + operation: str + extension_name: str | None = None + tool_call_id: str | None = None + + @staticmethod + def from_dict(obj: Any) -> "PermissionPromptRequestExtensionManagement": + assert isinstance(obj, dict) + operation = from_str(obj.get("operation")) + extension_name = from_union([from_none, from_str], obj.get("extensionName")) + tool_call_id = from_union([from_none, from_str], obj.get("toolCallId")) + return PermissionPromptRequestExtensionManagement( + operation=operation, + extension_name=extension_name, + tool_call_id=tool_call_id, + ) + + def to_dict(self) -> dict: + result: dict = {} + result["kind"] = self.kind + result["operation"] = from_str(self.operation) + if self.extension_name is not None: + result["extensionName"] = from_union([from_none, from_str], self.extension_name) + if self.tool_call_id is not None: + result["toolCallId"] = from_union([from_none, from_str], self.tool_call_id) + return result + + +@dataclass +class PermissionPromptRequestExtensionPermissionAccess: + "Extension permission access prompt" + capabilities: list[str] + extension_name: str + kind: ClassVar[str] = "extension-permission-access" + tool_call_id: str | None = None + + @staticmethod + def from_dict(obj: Any) -> "PermissionPromptRequestExtensionPermissionAccess": + assert isinstance(obj, dict) + capabilities = from_list(from_str, obj.get("capabilities")) + extension_name = from_str(obj.get("extensionName")) + tool_call_id = from_union([from_none, from_str], obj.get("toolCallId")) + return PermissionPromptRequestExtensionPermissionAccess( + capabilities=capabilities, + extension_name=extension_name, + tool_call_id=tool_call_id, + ) + + def to_dict(self) -> dict: + result: dict = {} + result["capabilities"] = from_list(from_str, self.capabilities) + result["extensionName"] = from_str(self.extension_name) + result["kind"] = self.kind + if self.tool_call_id is not None: + result["toolCallId"] = from_union([from_none, from_str], self.tool_call_id) + return result + + +@dataclass +class PermissionPromptRequestHook: + "Hook confirmation permission prompt" + kind: ClassVar[str] = "hook" + tool_name: str + hook_message: str | None = None + tool_args: Any = None + tool_call_id: str | None = None + + @staticmethod + def from_dict(obj: Any) -> "PermissionPromptRequestHook": + assert isinstance(obj, dict) + tool_name = from_str(obj.get("toolName")) + hook_message = from_union([from_none, from_str], obj.get("hookMessage")) + tool_args = obj.get("toolArgs") + tool_call_id = from_union([from_none, from_str], obj.get("toolCallId")) + return PermissionPromptRequestHook( + tool_name=tool_name, + hook_message=hook_message, + tool_args=tool_args, + tool_call_id=tool_call_id, + ) + + def to_dict(self) -> dict: + result: dict = {} + result["kind"] = self.kind + result["toolName"] = from_str(self.tool_name) + if self.hook_message is not None: + result["hookMessage"] = from_union([from_none, from_str], self.hook_message) + if self.tool_args is not None: + result["toolArgs"] = self.tool_args + if self.tool_call_id is not None: + result["toolCallId"] = from_union([from_none, from_str], self.tool_call_id) + return result + + +@dataclass +class PermissionPromptRequestMcp: + "MCP tool invocation permission prompt" + kind: ClassVar[str] = "mcp" + server_name: str + tool_name: str + tool_title: str + args: Any | None = None + tool_call_id: str | None = None + + @staticmethod + def from_dict(obj: Any) -> "PermissionPromptRequestMcp": + assert isinstance(obj, dict) + server_name = from_str(obj.get("serverName")) + tool_name = from_str(obj.get("toolName")) + tool_title = from_str(obj.get("toolTitle")) + args = from_union([from_none, lambda x: x], obj.get("args")) + tool_call_id = from_union([from_none, from_str], obj.get("toolCallId")) + return PermissionPromptRequestMcp( + server_name=server_name, + tool_name=tool_name, + tool_title=tool_title, + args=args, + tool_call_id=tool_call_id, + ) + + def to_dict(self) -> dict: + result: dict = {} + result["kind"] = self.kind + result["serverName"] = from_str(self.server_name) + result["toolName"] = from_str(self.tool_name) + result["toolTitle"] = from_str(self.tool_title) + if self.args is not None: + result["args"] = from_union([from_none, lambda x: x], self.args) + if self.tool_call_id is not None: + result["toolCallId"] = from_union([from_none, from_str], self.tool_call_id) + return result + + +@dataclass +class PermissionPromptRequestMemory: + "Memory operation permission prompt" + fact: str + kind: ClassVar[str] = "memory" + action: PermissionRequestMemoryAction | None = None + citations: str | None = None + direction: PermissionRequestMemoryDirection | None = None + reason: str | None = None + subject: str | None = None + tool_call_id: str | None = None + + @staticmethod + def from_dict(obj: Any) -> "PermissionPromptRequestMemory": + assert isinstance(obj, dict) + fact = from_str(obj.get("fact")) + action = from_union([from_none, lambda x: parse_enum(PermissionRequestMemoryAction, x)], obj.get("action")) + citations = from_union([from_none, from_str], obj.get("citations")) + direction = from_union([from_none, lambda x: parse_enum(PermissionRequestMemoryDirection, x)], obj.get("direction")) + reason = from_union([from_none, from_str], obj.get("reason")) + subject = from_union([from_none, from_str], obj.get("subject")) + tool_call_id = from_union([from_none, from_str], obj.get("toolCallId")) + return PermissionPromptRequestMemory( + fact=fact, + action=action, + citations=citations, + direction=direction, + reason=reason, + subject=subject, + tool_call_id=tool_call_id, + ) + + def to_dict(self) -> dict: + result: dict = {} + result["fact"] = from_str(self.fact) + result["kind"] = self.kind + if self.action is not None: + result["action"] = from_union([from_none, lambda x: to_enum(PermissionRequestMemoryAction, x)], self.action) + if self.citations is not None: + result["citations"] = from_union([from_none, from_str], self.citations) + if self.direction is not None: + result["direction"] = from_union([from_none, lambda x: to_enum(PermissionRequestMemoryDirection, x)], self.direction) + if self.reason is not None: + result["reason"] = from_union([from_none, from_str], self.reason) + if self.subject is not None: + result["subject"] = from_union([from_none, from_str], self.subject) + if self.tool_call_id is not None: + result["toolCallId"] = from_union([from_none, from_str], self.tool_call_id) + return result + + +@dataclass +class PermissionPromptRequestPath: + "Path access permission prompt" + access_kind: PermissionPromptRequestPathAccessKind + kind: ClassVar[str] = "path" + paths: list[str] + tool_call_id: str | None = None + + @staticmethod + def from_dict(obj: Any) -> "PermissionPromptRequestPath": + assert isinstance(obj, dict) + access_kind = parse_enum(PermissionPromptRequestPathAccessKind, obj.get("accessKind")) + paths = from_list(from_str, obj.get("paths")) + tool_call_id = from_union([from_none, from_str], obj.get("toolCallId")) + return PermissionPromptRequestPath( + access_kind=access_kind, + paths=paths, + tool_call_id=tool_call_id, + ) + + def to_dict(self) -> dict: + result: dict = {} + result["accessKind"] = to_enum(PermissionPromptRequestPathAccessKind, self.access_kind) + result["kind"] = self.kind + result["paths"] = from_list(from_str, self.paths) + if self.tool_call_id is not None: + result["toolCallId"] = from_union([from_none, from_str], self.tool_call_id) + return result + + +@dataclass +class PermissionPromptRequestRead: + "File read permission prompt" + intention: str + kind: ClassVar[str] = "read" + path: str + tool_call_id: str | None = None + + @staticmethod + def from_dict(obj: Any) -> "PermissionPromptRequestRead": + assert isinstance(obj, dict) + intention = from_str(obj.get("intention")) + path = from_str(obj.get("path")) + tool_call_id = from_union([from_none, from_str], obj.get("toolCallId")) + return PermissionPromptRequestRead( + intention=intention, + path=path, + tool_call_id=tool_call_id, + ) + + def to_dict(self) -> dict: + result: dict = {} + result["intention"] = from_str(self.intention) + result["kind"] = self.kind + result["path"] = from_str(self.path) + if self.tool_call_id is not None: + result["toolCallId"] = from_union([from_none, from_str], self.tool_call_id) + return result + + +@dataclass +class PermissionPromptRequestUrl: + "URL access permission prompt" + intention: str + kind: ClassVar[str] = "url" + url: str + tool_call_id: str | None = None + + @staticmethod + def from_dict(obj: Any) -> "PermissionPromptRequestUrl": + assert isinstance(obj, dict) + intention = from_str(obj.get("intention")) + url = from_str(obj.get("url")) + tool_call_id = from_union([from_none, from_str], obj.get("toolCallId")) + return PermissionPromptRequestUrl( + intention=intention, + url=url, + tool_call_id=tool_call_id, + ) + + def to_dict(self) -> dict: + result: dict = {} + result["intention"] = from_str(self.intention) + result["kind"] = self.kind + result["url"] = from_str(self.url) + if self.tool_call_id is not None: + result["toolCallId"] = from_union([from_none, from_str], self.tool_call_id) + return result + + +@dataclass +class PermissionPromptRequestWrite: + "File write permission prompt" + can_offer_session_approval: bool + diff: str + file_name: str + intention: str + kind: ClassVar[str] = "write" + new_file_contents: str | None = None + tool_call_id: str | None = None + + @staticmethod + def from_dict(obj: Any) -> "PermissionPromptRequestWrite": + assert isinstance(obj, dict) + can_offer_session_approval = from_bool(obj.get("canOfferSessionApproval")) + diff = from_str(obj.get("diff")) + file_name = from_str(obj.get("fileName")) + intention = from_str(obj.get("intention")) + new_file_contents = from_union([from_none, from_str], obj.get("newFileContents")) + tool_call_id = from_union([from_none, from_str], obj.get("toolCallId")) + return PermissionPromptRequestWrite( + can_offer_session_approval=can_offer_session_approval, + diff=diff, + file_name=file_name, + intention=intention, + new_file_contents=new_file_contents, + tool_call_id=tool_call_id, + ) + + def to_dict(self) -> dict: + result: dict = {} + result["canOfferSessionApproval"] = from_bool(self.can_offer_session_approval) + result["diff"] = from_str(self.diff) + result["fileName"] = from_str(self.file_name) + result["intention"] = from_str(self.intention) + result["kind"] = self.kind + if self.new_file_contents is not None: + result["newFileContents"] = from_union([from_none, from_str], self.new_file_contents) + if self.tool_call_id is not None: + result["toolCallId"] = from_union([from_none, from_str], self.tool_call_id) + return result + + +@dataclass +class PermissionRequestCustomTool: + "Custom tool invocation permission request" + kind: ClassVar[str] = "custom-tool" + tool_description: str + tool_name: str + args: Any = None + tool_call_id: str | None = None + + @staticmethod + def from_dict(obj: Any) -> "PermissionRequestCustomTool": + assert isinstance(obj, dict) + tool_description = from_str(obj.get("toolDescription")) + tool_name = from_str(obj.get("toolName")) + args = obj.get("args") + tool_call_id = from_union([from_none, from_str], obj.get("toolCallId")) + return PermissionRequestCustomTool( + tool_description=tool_description, + tool_name=tool_name, + args=args, + tool_call_id=tool_call_id, + ) + + def to_dict(self) -> dict: + result: dict = {} + result["kind"] = self.kind + result["toolDescription"] = from_str(self.tool_description) + result["toolName"] = from_str(self.tool_name) + if self.args is not None: + result["args"] = self.args + if self.tool_call_id is not None: + result["toolCallId"] = from_union([from_none, from_str], self.tool_call_id) + return result + + +@dataclass +class PermissionRequestExtensionManagement: + "Extension management permission request" + kind: ClassVar[str] = "extension-management" + operation: str + extension_name: str | None = None + tool_call_id: str | None = None + + @staticmethod + def from_dict(obj: Any) -> "PermissionRequestExtensionManagement": + assert isinstance(obj, dict) + operation = from_str(obj.get("operation")) + extension_name = from_union([from_none, from_str], obj.get("extensionName")) + tool_call_id = from_union([from_none, from_str], obj.get("toolCallId")) + return PermissionRequestExtensionManagement( + operation=operation, + extension_name=extension_name, + tool_call_id=tool_call_id, + ) + + def to_dict(self) -> dict: + result: dict = {} + result["kind"] = self.kind + result["operation"] = from_str(self.operation) + if self.extension_name is not None: + result["extensionName"] = from_union([from_none, from_str], self.extension_name) + if self.tool_call_id is not None: + result["toolCallId"] = from_union([from_none, from_str], self.tool_call_id) + return result + + +@dataclass +class PermissionRequestExtensionPermissionAccess: + "Extension permission access request" + capabilities: list[str] + extension_name: str + kind: ClassVar[str] = "extension-permission-access" + tool_call_id: str | None = None + + @staticmethod + def from_dict(obj: Any) -> "PermissionRequestExtensionPermissionAccess": + assert isinstance(obj, dict) + capabilities = from_list(from_str, obj.get("capabilities")) + extension_name = from_str(obj.get("extensionName")) + tool_call_id = from_union([from_none, from_str], obj.get("toolCallId")) + return PermissionRequestExtensionPermissionAccess( + capabilities=capabilities, + extension_name=extension_name, + tool_call_id=tool_call_id, + ) + + def to_dict(self) -> dict: + result: dict = {} + result["capabilities"] = from_list(from_str, self.capabilities) + result["extensionName"] = from_str(self.extension_name) + result["kind"] = self.kind + if self.tool_call_id is not None: + result["toolCallId"] = from_union([from_none, from_str], self.tool_call_id) + return result + + +@dataclass +class PermissionRequestHook: + "Hook confirmation permission request" + kind: ClassVar[str] = "hook" + tool_name: str + hook_message: str | None = None + tool_args: Any = None + tool_call_id: str | None = None + + @staticmethod + def from_dict(obj: Any) -> "PermissionRequestHook": + assert isinstance(obj, dict) + tool_name = from_str(obj.get("toolName")) + hook_message = from_union([from_none, from_str], obj.get("hookMessage")) + tool_args = obj.get("toolArgs") + tool_call_id = from_union([from_none, from_str], obj.get("toolCallId")) + return PermissionRequestHook( + tool_name=tool_name, + hook_message=hook_message, + tool_args=tool_args, + tool_call_id=tool_call_id, + ) + + def to_dict(self) -> dict: + result: dict = {} + result["kind"] = self.kind + result["toolName"] = from_str(self.tool_name) + if self.hook_message is not None: + result["hookMessage"] = from_union([from_none, from_str], self.hook_message) + if self.tool_args is not None: + result["toolArgs"] = self.tool_args + if self.tool_call_id is not None: + result["toolCallId"] = from_union([from_none, from_str], self.tool_call_id) + return result + + +@dataclass +class PermissionRequestMcp: + "MCP tool invocation permission request" + kind: ClassVar[str] = "mcp" + read_only: bool + server_name: str + tool_name: str + tool_title: str + args: Any = None + tool_call_id: str | None = None + + @staticmethod + def from_dict(obj: Any) -> "PermissionRequestMcp": + assert isinstance(obj, dict) + read_only = from_bool(obj.get("readOnly")) + server_name = from_str(obj.get("serverName")) + tool_name = from_str(obj.get("toolName")) + tool_title = from_str(obj.get("toolTitle")) + args = obj.get("args") tool_call_id = from_union([from_none, from_str], obj.get("toolCallId")) - return PermissionCompletedData( - request_id=request_id, - result=result, + return PermissionRequestMcp( + read_only=read_only, + server_name=server_name, + tool_name=tool_name, + tool_title=tool_title, + args=args, tool_call_id=tool_call_id, ) def to_dict(self) -> dict: result: dict = {} - result["requestId"] = from_str(self.request_id) - result["result"] = to_class(PermissionResult, self.result) + result["kind"] = self.kind + result["readOnly"] = from_bool(self.read_only) + result["serverName"] = from_str(self.server_name) + result["toolName"] = from_str(self.tool_name) + result["toolTitle"] = from_str(self.tool_title) + if self.args is not None: + result["args"] = self.args if self.tool_call_id is not None: result["toolCallId"] = from_union([from_none, from_str], self.tool_call_id) return result @dataclass -class PermissionPromptRequest: - "Derived user-facing permission prompt details for UI consumers" - kind: PermissionPromptRequestKind - access_kind: PermissionPromptRequestPathAccessKind | None = None +class PermissionRequestMemory: + "Memory operation permission request" + fact: str + kind: ClassVar[str] = "memory" action: PermissionRequestMemoryAction | None = None - args: Any | None = None - can_offer_session_approval: bool | None = None - capabilities: list[str] | None = None citations: str | None = None - command_identifiers: list[str] | None = None - diff: str | None = None direction: PermissionRequestMemoryDirection | None = None - extension_name: str | None = None - fact: str | None = None - file_name: str | None = None - full_command_text: str | None = None - hook_message: str | None = None - intention: str | None = None - new_file_contents: str | None = None - operation: str | None = None - path: str | None = None - paths: list[str] | None = None reason: str | None = None - server_name: str | None = None subject: str | None = None - tool_args: Any = None tool_call_id: str | None = None - tool_description: str | None = None - tool_name: str | None = None - tool_title: str | None = None - url: str | None = None - warning: str | None = None @staticmethod - def from_dict(obj: Any) -> "PermissionPromptRequest": + def from_dict(obj: Any) -> "PermissionRequestMemory": assert isinstance(obj, dict) - kind = parse_enum(PermissionPromptRequestKind, obj.get("kind")) - access_kind = from_union([from_none, lambda x: parse_enum(PermissionPromptRequestPathAccessKind, x)], obj.get("accessKind")) + fact = from_str(obj.get("fact")) action = from_union([from_none, lambda x: parse_enum(PermissionRequestMemoryAction, x)], obj.get("action")) - args = from_union([from_none, lambda x: x], obj.get("args")) - can_offer_session_approval = from_union([from_none, from_bool], obj.get("canOfferSessionApproval")) - capabilities = from_union([from_none, lambda x: from_list(from_str, x)], obj.get("capabilities")) citations = from_union([from_none, from_str], obj.get("citations")) - command_identifiers = from_union([from_none, lambda x: from_list(from_str, x)], obj.get("commandIdentifiers")) - diff = from_union([from_none, from_str], obj.get("diff")) direction = from_union([from_none, lambda x: parse_enum(PermissionRequestMemoryDirection, x)], obj.get("direction")) - extension_name = from_union([from_none, from_str], obj.get("extensionName")) - fact = from_union([from_none, from_str], obj.get("fact")) - file_name = from_union([from_none, from_str], obj.get("fileName")) - full_command_text = from_union([from_none, from_str], obj.get("fullCommandText")) - hook_message = from_union([from_none, from_str], obj.get("hookMessage")) - intention = from_union([from_none, from_str], obj.get("intention")) - new_file_contents = from_union([from_none, from_str], obj.get("newFileContents")) - operation = from_union([from_none, from_str], obj.get("operation")) - path = from_union([from_none, from_str], obj.get("path")) - paths = from_union([from_none, lambda x: from_list(from_str, x)], obj.get("paths")) reason = from_union([from_none, from_str], obj.get("reason")) - server_name = from_union([from_none, from_str], obj.get("serverName")) subject = from_union([from_none, from_str], obj.get("subject")) - tool_args = obj.get("toolArgs") tool_call_id = from_union([from_none, from_str], obj.get("toolCallId")) - tool_description = from_union([from_none, from_str], obj.get("toolDescription")) - tool_name = from_union([from_none, from_str], obj.get("toolName")) - tool_title = from_union([from_none, from_str], obj.get("toolTitle")) - url = from_union([from_none, from_str], obj.get("url")) - warning = from_union([from_none, from_str], obj.get("warning")) - return PermissionPromptRequest( - kind=kind, - access_kind=access_kind, + return PermissionRequestMemory( + fact=fact, action=action, - args=args, - can_offer_session_approval=can_offer_session_approval, - capabilities=capabilities, citations=citations, - command_identifiers=command_identifiers, - diff=diff, direction=direction, - extension_name=extension_name, - fact=fact, - file_name=file_name, - full_command_text=full_command_text, - hook_message=hook_message, - intention=intention, - new_file_contents=new_file_contents, - operation=operation, - path=path, - paths=paths, reason=reason, - server_name=server_name, subject=subject, - tool_args=tool_args, tool_call_id=tool_call_id, - tool_description=tool_description, - tool_name=tool_name, - tool_title=tool_title, - url=url, - warning=warning, ) def to_dict(self) -> dict: result: dict = {} - result["kind"] = to_enum(PermissionPromptRequestKind, self.kind) - if self.access_kind is not None: - result["accessKind"] = from_union([from_none, lambda x: to_enum(PermissionPromptRequestPathAccessKind, x)], self.access_kind) + result["fact"] = from_str(self.fact) + result["kind"] = self.kind if self.action is not None: result["action"] = from_union([from_none, lambda x: to_enum(PermissionRequestMemoryAction, x)], self.action) - if self.args is not None: - result["args"] = from_union([from_none, lambda x: x], self.args) - if self.can_offer_session_approval is not None: - result["canOfferSessionApproval"] = from_union([from_none, from_bool], self.can_offer_session_approval) - if self.capabilities is not None: - result["capabilities"] = from_union([from_none, lambda x: from_list(from_str, x)], self.capabilities) if self.citations is not None: result["citations"] = from_union([from_none, from_str], self.citations) - if self.command_identifiers is not None: - result["commandIdentifiers"] = from_union([from_none, lambda x: from_list(from_str, x)], self.command_identifiers) - if self.diff is not None: - result["diff"] = from_union([from_none, from_str], self.diff) if self.direction is not None: result["direction"] = from_union([from_none, lambda x: to_enum(PermissionRequestMemoryDirection, x)], self.direction) - if self.extension_name is not None: - result["extensionName"] = from_union([from_none, from_str], self.extension_name) - if self.fact is not None: - result["fact"] = from_union([from_none, from_str], self.fact) - if self.file_name is not None: - result["fileName"] = from_union([from_none, from_str], self.file_name) - if self.full_command_text is not None: - result["fullCommandText"] = from_union([from_none, from_str], self.full_command_text) - if self.hook_message is not None: - result["hookMessage"] = from_union([from_none, from_str], self.hook_message) - if self.intention is not None: - result["intention"] = from_union([from_none, from_str], self.intention) - if self.new_file_contents is not None: - result["newFileContents"] = from_union([from_none, from_str], self.new_file_contents) - if self.operation is not None: - result["operation"] = from_union([from_none, from_str], self.operation) - if self.path is not None: - result["path"] = from_union([from_none, from_str], self.path) - if self.paths is not None: - result["paths"] = from_union([from_none, lambda x: from_list(from_str, x)], self.paths) if self.reason is not None: result["reason"] = from_union([from_none, from_str], self.reason) - if self.server_name is not None: - result["serverName"] = from_union([from_none, from_str], self.server_name) if self.subject is not None: result["subject"] = from_union([from_none, from_str], self.subject) - if self.tool_args is not None: - result["toolArgs"] = self.tool_args if self.tool_call_id is not None: result["toolCallId"] = from_union([from_none, from_str], self.tool_call_id) - if self.tool_description is not None: - result["toolDescription"] = from_union([from_none, from_str], self.tool_description) - if self.tool_name is not None: - result["toolName"] = from_union([from_none, from_str], self.tool_name) - if self.tool_title is not None: - result["toolTitle"] = from_union([from_none, from_str], self.tool_title) - if self.url is not None: - result["url"] = from_union([from_none, from_str], self.url) - if self.warning is not None: - result["warning"] = from_union([from_none, from_str], self.warning) return result @dataclass -class PermissionRequest: - "Details of the permission being requested" - kind: PermissionRequestKind - action: PermissionRequestMemoryAction | None = None - args: Any = None - can_offer_session_approval: bool | None = None - capabilities: list[str] | None = None - citations: str | None = None - commands: list[PermissionRequestShellCommand] | None = None - diff: str | None = None - direction: PermissionRequestMemoryDirection | None = None - extension_name: str | None = None - fact: str | None = None - file_name: str | None = None - full_command_text: str | None = None - has_write_file_redirection: bool | None = None - hook_message: str | None = None - intention: str | None = None - new_file_contents: str | None = None - operation: str | None = None - path: str | None = None - possible_paths: list[str] | None = None - possible_urls: list[PermissionRequestShellPossibleUrl] | None = None - read_only: bool | None = None - reason: str | None = None - server_name: str | None = None - subject: str | None = None - tool_args: Any = None +class PermissionRequestRead: + "File or directory read permission request" + intention: str + kind: ClassVar[str] = "read" + path: str + tool_call_id: str | None = None + + @staticmethod + def from_dict(obj: Any) -> "PermissionRequestRead": + assert isinstance(obj, dict) + intention = from_str(obj.get("intention")) + path = from_str(obj.get("path")) + tool_call_id = from_union([from_none, from_str], obj.get("toolCallId")) + return PermissionRequestRead( + intention=intention, + path=path, + tool_call_id=tool_call_id, + ) + + def to_dict(self) -> dict: + result: dict = {} + result["intention"] = from_str(self.intention) + result["kind"] = self.kind + result["path"] = from_str(self.path) + if self.tool_call_id is not None: + result["toolCallId"] = from_union([from_none, from_str], self.tool_call_id) + return result + + +@dataclass +class PermissionRequestShell: + "Shell command permission request" + can_offer_session_approval: bool + commands: list[PermissionRequestShellCommand] + full_command_text: str + has_write_file_redirection: bool + intention: str + kind: ClassVar[str] = "shell" + possible_paths: list[str] + possible_urls: list[PermissionRequestShellPossibleUrl] tool_call_id: str | None = None - tool_description: str | None = None - tool_name: str | None = None - tool_title: str | None = None - url: str | None = None warning: str | None = None @staticmethod - def from_dict(obj: Any) -> "PermissionRequest": + def from_dict(obj: Any) -> "PermissionRequestShell": assert isinstance(obj, dict) - kind = parse_enum(PermissionRequestKind, obj.get("kind")) - action = from_union([from_none, lambda x: parse_enum(PermissionRequestMemoryAction, x)], obj.get("action")) - args = obj.get("args") - can_offer_session_approval = from_union([from_none, from_bool], obj.get("canOfferSessionApproval")) - capabilities = from_union([from_none, lambda x: from_list(from_str, x)], obj.get("capabilities")) - citations = from_union([from_none, from_str], obj.get("citations")) - commands = from_union([from_none, lambda x: from_list(PermissionRequestShellCommand.from_dict, x)], obj.get("commands")) - diff = from_union([from_none, from_str], obj.get("diff")) - direction = from_union([from_none, lambda x: parse_enum(PermissionRequestMemoryDirection, x)], obj.get("direction")) - extension_name = from_union([from_none, from_str], obj.get("extensionName")) - fact = from_union([from_none, from_str], obj.get("fact")) - file_name = from_union([from_none, from_str], obj.get("fileName")) - full_command_text = from_union([from_none, from_str], obj.get("fullCommandText")) - has_write_file_redirection = from_union([from_none, from_bool], obj.get("hasWriteFileRedirection")) - hook_message = from_union([from_none, from_str], obj.get("hookMessage")) - intention = from_union([from_none, from_str], obj.get("intention")) - new_file_contents = from_union([from_none, from_str], obj.get("newFileContents")) - operation = from_union([from_none, from_str], obj.get("operation")) - path = from_union([from_none, from_str], obj.get("path")) - possible_paths = from_union([from_none, lambda x: from_list(from_str, x)], obj.get("possiblePaths")) - possible_urls = from_union([from_none, lambda x: from_list(PermissionRequestShellPossibleUrl.from_dict, x)], obj.get("possibleUrls")) - read_only = from_union([from_none, from_bool], obj.get("readOnly")) - reason = from_union([from_none, from_str], obj.get("reason")) - server_name = from_union([from_none, from_str], obj.get("serverName")) - subject = from_union([from_none, from_str], obj.get("subject")) - tool_args = obj.get("toolArgs") + can_offer_session_approval = from_bool(obj.get("canOfferSessionApproval")) + commands = from_list(PermissionRequestShellCommand.from_dict, obj.get("commands")) + full_command_text = from_str(obj.get("fullCommandText")) + has_write_file_redirection = from_bool(obj.get("hasWriteFileRedirection")) + intention = from_str(obj.get("intention")) + possible_paths = from_list(from_str, obj.get("possiblePaths")) + possible_urls = from_list(PermissionRequestShellPossibleUrl.from_dict, obj.get("possibleUrls")) tool_call_id = from_union([from_none, from_str], obj.get("toolCallId")) - tool_description = from_union([from_none, from_str], obj.get("toolDescription")) - tool_name = from_union([from_none, from_str], obj.get("toolName")) - tool_title = from_union([from_none, from_str], obj.get("toolTitle")) - url = from_union([from_none, from_str], obj.get("url")) warning = from_union([from_none, from_str], obj.get("warning")) - return PermissionRequest( - kind=kind, - action=action, - args=args, + return PermissionRequestShell( can_offer_session_approval=can_offer_session_approval, - capabilities=capabilities, - citations=citations, commands=commands, - diff=diff, - direction=direction, - extension_name=extension_name, - fact=fact, - file_name=file_name, full_command_text=full_command_text, has_write_file_redirection=has_write_file_redirection, - hook_message=hook_message, intention=intention, - new_file_contents=new_file_contents, - operation=operation, - path=path, possible_paths=possible_paths, possible_urls=possible_urls, - read_only=read_only, - reason=reason, - server_name=server_name, - subject=subject, - tool_args=tool_args, tool_call_id=tool_call_id, - tool_description=tool_description, - tool_name=tool_name, - tool_title=tool_title, - url=url, warning=warning, ) def to_dict(self) -> dict: result: dict = {} - result["kind"] = to_enum(PermissionRequestKind, self.kind) - if self.action is not None: - result["action"] = from_union([from_none, lambda x: to_enum(PermissionRequestMemoryAction, x)], self.action) - if self.args is not None: - result["args"] = self.args - if self.can_offer_session_approval is not None: - result["canOfferSessionApproval"] = from_union([from_none, from_bool], self.can_offer_session_approval) - if self.capabilities is not None: - result["capabilities"] = from_union([from_none, lambda x: from_list(from_str, x)], self.capabilities) - if self.citations is not None: - result["citations"] = from_union([from_none, from_str], self.citations) - if self.commands is not None: - result["commands"] = from_union([from_none, lambda x: from_list(lambda x: to_class(PermissionRequestShellCommand, x), x)], self.commands) - if self.diff is not None: - result["diff"] = from_union([from_none, from_str], self.diff) - if self.direction is not None: - result["direction"] = from_union([from_none, lambda x: to_enum(PermissionRequestMemoryDirection, x)], self.direction) - if self.extension_name is not None: - result["extensionName"] = from_union([from_none, from_str], self.extension_name) - if self.fact is not None: - result["fact"] = from_union([from_none, from_str], self.fact) - if self.file_name is not None: - result["fileName"] = from_union([from_none, from_str], self.file_name) - if self.full_command_text is not None: - result["fullCommandText"] = from_union([from_none, from_str], self.full_command_text) - if self.has_write_file_redirection is not None: - result["hasWriteFileRedirection"] = from_union([from_none, from_bool], self.has_write_file_redirection) - if self.hook_message is not None: - result["hookMessage"] = from_union([from_none, from_str], self.hook_message) - if self.intention is not None: - result["intention"] = from_union([from_none, from_str], self.intention) - if self.new_file_contents is not None: - result["newFileContents"] = from_union([from_none, from_str], self.new_file_contents) - if self.operation is not None: - result["operation"] = from_union([from_none, from_str], self.operation) - if self.path is not None: - result["path"] = from_union([from_none, from_str], self.path) - if self.possible_paths is not None: - result["possiblePaths"] = from_union([from_none, lambda x: from_list(from_str, x)], self.possible_paths) - if self.possible_urls is not None: - result["possibleUrls"] = from_union([from_none, lambda x: from_list(lambda x: to_class(PermissionRequestShellPossibleUrl, x), x)], self.possible_urls) - if self.read_only is not None: - result["readOnly"] = from_union([from_none, from_bool], self.read_only) - if self.reason is not None: - result["reason"] = from_union([from_none, from_str], self.reason) - if self.server_name is not None: - result["serverName"] = from_union([from_none, from_str], self.server_name) - if self.subject is not None: - result["subject"] = from_union([from_none, from_str], self.subject) - if self.tool_args is not None: - result["toolArgs"] = self.tool_args + result["canOfferSessionApproval"] = from_bool(self.can_offer_session_approval) + result["commands"] = from_list(lambda x: to_class(PermissionRequestShellCommand, x), self.commands) + result["fullCommandText"] = from_str(self.full_command_text) + result["hasWriteFileRedirection"] = from_bool(self.has_write_file_redirection) + result["intention"] = from_str(self.intention) + result["kind"] = self.kind + result["possiblePaths"] = from_list(from_str, self.possible_paths) + result["possibleUrls"] = from_list(lambda x: to_class(PermissionRequestShellPossibleUrl, x), self.possible_urls) if self.tool_call_id is not None: result["toolCallId"] = from_union([from_none, from_str], self.tool_call_id) - if self.tool_description is not None: - result["toolDescription"] = from_union([from_none, from_str], self.tool_description) - if self.tool_name is not None: - result["toolName"] = from_union([from_none, from_str], self.tool_name) - if self.tool_title is not None: - result["toolTitle"] = from_union([from_none, from_str], self.tool_title) - if self.url is not None: - result["url"] = from_union([from_none, from_str], self.url) if self.warning is not None: result["warning"] = from_union([from_none, from_str], self.warning) return result @@ -2221,99 +2794,108 @@ def to_dict(self) -> dict: @dataclass -class PermissionRequestedData: - "Permission request notification requiring client approval with request details" - permission_request: PermissionRequest - request_id: str - prompt_request: PermissionPromptRequest | None = None - resolved_by_hook: bool | None = None +class PermissionRequestUrl: + "URL access permission request" + intention: str + kind: ClassVar[str] = "url" + url: str + tool_call_id: str | None = None @staticmethod - def from_dict(obj: Any) -> "PermissionRequestedData": + def from_dict(obj: Any) -> "PermissionRequestUrl": assert isinstance(obj, dict) - permission_request = PermissionRequest.from_dict(obj.get("permissionRequest")) - request_id = from_str(obj.get("requestId")) - prompt_request = from_union([from_none, PermissionPromptRequest.from_dict], obj.get("promptRequest")) - resolved_by_hook = from_union([from_none, from_bool], obj.get("resolvedByHook")) - return PermissionRequestedData( - permission_request=permission_request, - request_id=request_id, - prompt_request=prompt_request, - resolved_by_hook=resolved_by_hook, + intention = from_str(obj.get("intention")) + url = from_str(obj.get("url")) + tool_call_id = from_union([from_none, from_str], obj.get("toolCallId")) + return PermissionRequestUrl( + intention=intention, + url=url, + tool_call_id=tool_call_id, ) def to_dict(self) -> dict: result: dict = {} - result["permissionRequest"] = to_class(PermissionRequest, self.permission_request) - result["requestId"] = from_str(self.request_id) - if self.prompt_request is not None: - result["promptRequest"] = from_union([from_none, lambda x: to_class(PermissionPromptRequest, x)], self.prompt_request) - if self.resolved_by_hook is not None: - result["resolvedByHook"] = from_union([from_none, from_bool], self.resolved_by_hook) + result["intention"] = from_str(self.intention) + result["kind"] = self.kind + result["url"] = from_str(self.url) + if self.tool_call_id is not None: + result["toolCallId"] = from_union([from_none, from_str], self.tool_call_id) return result @dataclass -class PermissionResult: - "The result of the permission request" - kind: PermissionResultKind - approval: UserToolSessionApproval | None = None - feedback: str | None = None - force_reject: bool | None = None - interrupt: bool | None = None - location_key: str | None = None - message: str | None = None - path: str | None = None - reason: str | None = None - rules: list[PermissionRule] | None = None +class PermissionRequestWrite: + "File write permission request" + can_offer_session_approval: bool + diff: str + file_name: str + intention: str + kind: ClassVar[str] = "write" + new_file_contents: str | None = None + tool_call_id: str | None = None @staticmethod - def from_dict(obj: Any) -> "PermissionResult": + def from_dict(obj: Any) -> "PermissionRequestWrite": assert isinstance(obj, dict) - kind = parse_enum(PermissionResultKind, obj.get("kind")) - approval = from_union([from_none, UserToolSessionApproval.from_dict], obj.get("approval")) - feedback = from_union([from_none, from_str], obj.get("feedback")) - force_reject = from_union([from_none, from_bool], obj.get("forceReject")) - interrupt = from_union([from_none, from_bool], obj.get("interrupt")) - location_key = from_union([from_none, from_str], obj.get("locationKey")) - message = from_union([from_none, from_str], obj.get("message")) - path = from_union([from_none, from_str], obj.get("path")) - reason = from_union([from_none, from_str], obj.get("reason")) - rules = from_union([from_none, lambda x: from_list(PermissionRule.from_dict, x)], obj.get("rules")) - return PermissionResult( - kind=kind, - approval=approval, - feedback=feedback, - force_reject=force_reject, - interrupt=interrupt, - location_key=location_key, - message=message, - path=path, - reason=reason, - rules=rules, + can_offer_session_approval = from_bool(obj.get("canOfferSessionApproval")) + diff = from_str(obj.get("diff")) + file_name = from_str(obj.get("fileName")) + intention = from_str(obj.get("intention")) + new_file_contents = from_union([from_none, from_str], obj.get("newFileContents")) + tool_call_id = from_union([from_none, from_str], obj.get("toolCallId")) + return PermissionRequestWrite( + can_offer_session_approval=can_offer_session_approval, + diff=diff, + file_name=file_name, + intention=intention, + new_file_contents=new_file_contents, + tool_call_id=tool_call_id, + ) + + def to_dict(self) -> dict: + result: dict = {} + result["canOfferSessionApproval"] = from_bool(self.can_offer_session_approval) + result["diff"] = from_str(self.diff) + result["fileName"] = from_str(self.file_name) + result["intention"] = from_str(self.intention) + result["kind"] = self.kind + if self.new_file_contents is not None: + result["newFileContents"] = from_union([from_none, from_str], self.new_file_contents) + if self.tool_call_id is not None: + result["toolCallId"] = from_union([from_none, from_str], self.tool_call_id) + return result + + +@dataclass +class PermissionRequestedData: + "Permission request notification requiring client approval with request details" + permission_request: PermissionRequest + request_id: str + prompt_request: PermissionPromptRequest | None = None + resolved_by_hook: bool | None = None + + @staticmethod + def from_dict(obj: Any) -> "PermissionRequestedData": + assert isinstance(obj, dict) + permission_request = _load_PermissionRequest(obj.get("permissionRequest")) + request_id = from_str(obj.get("requestId")) + prompt_request = from_union([from_none, _load_PermissionPromptRequest], obj.get("promptRequest")) + resolved_by_hook = from_union([from_none, from_bool], obj.get("resolvedByHook")) + return PermissionRequestedData( + permission_request=permission_request, + request_id=request_id, + prompt_request=prompt_request, + resolved_by_hook=resolved_by_hook, ) def to_dict(self) -> dict: result: dict = {} - result["kind"] = to_enum(PermissionResultKind, self.kind) - if self.approval is not None: - result["approval"] = from_union([from_none, lambda x: to_class(UserToolSessionApproval, x)], self.approval) - if self.feedback is not None: - result["feedback"] = from_union([from_none, from_str], self.feedback) - if self.force_reject is not None: - result["forceReject"] = from_union([from_none, from_bool], self.force_reject) - if self.interrupt is not None: - result["interrupt"] = from_union([from_none, from_bool], self.interrupt) - if self.location_key is not None: - result["locationKey"] = from_union([from_none, from_str], self.location_key) - if self.message is not None: - result["message"] = from_union([from_none, from_str], self.message) - if self.path is not None: - result["path"] = from_union([from_none, from_str], self.path) - if self.reason is not None: - result["reason"] = from_union([from_none, from_str], self.reason) - if self.rules is not None: - result["rules"] = from_union([from_none, lambda x: from_list(lambda x: to_class(PermissionRule, x), x)], self.rules) + result["permissionRequest"] = self.permission_request.to_dict() + result["requestId"] = from_str(self.request_id) + if self.prompt_request is not None: + result["promptRequest"] = from_union([from_none, lambda x: x.to_dict()], self.prompt_request) + if self.resolved_by_hook is not None: + result["resolvedByHook"] = from_union([from_none, from_bool], self.resolved_by_hook) return result @@ -3962,91 +4544,71 @@ def to_dict(self) -> dict: @dataclass -class SystemNotification: - "Structured metadata identifying what triggered this notification" - type: SystemNotificationType - agent_id: str | None = None - agent_type: str | None = None +class SystemNotificationAgentCompleted: + "Schema for the `SystemNotificationAgentCompleted` type." + agent_id: str + agent_type: str + status: SystemNotificationAgentCompletedStatus + type: ClassVar[str] = "agent_completed" description: str | None = None - entry_id: str | None = None - exit_code: int | None = None prompt: str | None = None - sender_name: str | None = None - sender_type: str | None = None - shell_id: str | None = None - source_path: str | None = None - status: SystemNotificationAgentCompletedStatus | None = None - summary: str | None = None - trigger_file: str | None = None - trigger_tool: str | None = None @staticmethod - def from_dict(obj: Any) -> "SystemNotification": + def from_dict(obj: Any) -> "SystemNotificationAgentCompleted": assert isinstance(obj, dict) - type = parse_enum(SystemNotificationType, obj.get("type")) - agent_id = from_union([from_none, from_str], obj.get("agentId")) - agent_type = from_union([from_none, from_str], obj.get("agentType")) + agent_id = from_str(obj.get("agentId")) + agent_type = from_str(obj.get("agentType")) + status = parse_enum(SystemNotificationAgentCompletedStatus, obj.get("status")) description = from_union([from_none, from_str], obj.get("description")) - entry_id = from_union([from_none, from_str], obj.get("entryId")) - exit_code = from_union([from_none, from_int], obj.get("exitCode")) prompt = from_union([from_none, from_str], obj.get("prompt")) - sender_name = from_union([from_none, from_str], obj.get("senderName")) - sender_type = from_union([from_none, from_str], obj.get("senderType")) - shell_id = from_union([from_none, from_str], obj.get("shellId")) - source_path = from_union([from_none, from_str], obj.get("sourcePath")) - status = from_union([from_none, lambda x: parse_enum(SystemNotificationAgentCompletedStatus, x)], obj.get("status")) - summary = from_union([from_none, from_str], obj.get("summary")) - trigger_file = from_union([from_none, from_str], obj.get("triggerFile")) - trigger_tool = from_union([from_none, from_str], obj.get("triggerTool")) - return SystemNotification( - type=type, + return SystemNotificationAgentCompleted( agent_id=agent_id, agent_type=agent_type, + status=status, description=description, - entry_id=entry_id, - exit_code=exit_code, prompt=prompt, - sender_name=sender_name, - sender_type=sender_type, - shell_id=shell_id, - source_path=source_path, - status=status, - summary=summary, - trigger_file=trigger_file, - trigger_tool=trigger_tool, ) def to_dict(self) -> dict: result: dict = {} - result["type"] = to_enum(SystemNotificationType, self.type) - if self.agent_id is not None: - result["agentId"] = from_union([from_none, from_str], self.agent_id) - if self.agent_type is not None: - result["agentType"] = from_union([from_none, from_str], self.agent_type) + result["agentId"] = from_str(self.agent_id) + result["agentType"] = from_str(self.agent_type) + result["status"] = to_enum(SystemNotificationAgentCompletedStatus, self.status) + result["type"] = self.type if self.description is not None: result["description"] = from_union([from_none, from_str], self.description) - if self.entry_id is not None: - result["entryId"] = from_union([from_none, from_str], self.entry_id) - if self.exit_code is not None: - result["exitCode"] = from_union([from_none, to_int], self.exit_code) if self.prompt is not None: result["prompt"] = from_union([from_none, from_str], self.prompt) - if self.sender_name is not None: - result["senderName"] = from_union([from_none, from_str], self.sender_name) - if self.sender_type is not None: - result["senderType"] = from_union([from_none, from_str], self.sender_type) - if self.shell_id is not None: - result["shellId"] = from_union([from_none, from_str], self.shell_id) - if self.source_path is not None: - result["sourcePath"] = from_union([from_none, from_str], self.source_path) - if self.status is not None: - result["status"] = from_union([from_none, lambda x: to_enum(SystemNotificationAgentCompletedStatus, x)], self.status) - if self.summary is not None: - result["summary"] = from_union([from_none, from_str], self.summary) - if self.trigger_file is not None: - result["triggerFile"] = from_union([from_none, from_str], self.trigger_file) - if self.trigger_tool is not None: - result["triggerTool"] = from_union([from_none, from_str], self.trigger_tool) + return result + + +@dataclass +class SystemNotificationAgentIdle: + "Schema for the `SystemNotificationAgentIdle` type." + agent_id: str + agent_type: str + type: ClassVar[str] = "agent_idle" + description: str | None = None + + @staticmethod + def from_dict(obj: Any) -> "SystemNotificationAgentIdle": + assert isinstance(obj, dict) + agent_id = from_str(obj.get("agentId")) + agent_type = from_str(obj.get("agentType")) + description = from_union([from_none, from_str], obj.get("description")) + return SystemNotificationAgentIdle( + agent_id=agent_id, + agent_type=agent_type, + description=description, + ) + + def to_dict(self) -> dict: + result: dict = {} + result["agentId"] = from_str(self.agent_id) + result["agentType"] = from_str(self.agent_type) + result["type"] = self.type + if self.description is not None: + result["description"] = from_union([from_none, from_str], self.description) return result @@ -4060,7 +4622,7 @@ class SystemNotificationData: def from_dict(obj: Any) -> "SystemNotificationData": assert isinstance(obj, dict) content = from_str(obj.get("content")) - kind = SystemNotification.from_dict(obj.get("kind")) + kind = _load_SystemNotification(obj.get("kind")) return SystemNotificationData( content=content, kind=kind, @@ -4069,86 +4631,252 @@ def from_dict(obj: Any) -> "SystemNotificationData": def to_dict(self) -> dict: result: dict = {} result["content"] = from_str(self.content) - result["kind"] = to_class(SystemNotification, self.kind) + result["kind"] = self.kind.to_dict() return result @dataclass -class ToolExecutionCompleteContent: - "A content block within a tool result, which may be text, terminal output, image, audio, or a resource" - type: ToolExecutionCompleteContentType - cwd: str | None = None - data: str | None = None +class SystemNotificationInstructionDiscovered: + "Schema for the `SystemNotificationInstructionDiscovered` type." + source_path: str + trigger_file: str + trigger_tool: str + type: ClassVar[str] = "instruction_discovered" + description: str | None = None + + @staticmethod + def from_dict(obj: Any) -> "SystemNotificationInstructionDiscovered": + assert isinstance(obj, dict) + source_path = from_str(obj.get("sourcePath")) + trigger_file = from_str(obj.get("triggerFile")) + trigger_tool = from_str(obj.get("triggerTool")) + description = from_union([from_none, from_str], obj.get("description")) + return SystemNotificationInstructionDiscovered( + source_path=source_path, + trigger_file=trigger_file, + trigger_tool=trigger_tool, + description=description, + ) + + def to_dict(self) -> dict: + result: dict = {} + result["sourcePath"] = from_str(self.source_path) + result["triggerFile"] = from_str(self.trigger_file) + result["triggerTool"] = from_str(self.trigger_tool) + result["type"] = self.type + if self.description is not None: + result["description"] = from_union([from_none, from_str], self.description) + return result + + +@dataclass +class SystemNotificationNewInboxMessage: + "Schema for the `SystemNotificationNewInboxMessage` type." + entry_id: str + sender_name: str + sender_type: str + summary: str + type: ClassVar[str] = "new_inbox_message" + + @staticmethod + def from_dict(obj: Any) -> "SystemNotificationNewInboxMessage": + assert isinstance(obj, dict) + entry_id = from_str(obj.get("entryId")) + sender_name = from_str(obj.get("senderName")) + sender_type = from_str(obj.get("senderType")) + summary = from_str(obj.get("summary")) + return SystemNotificationNewInboxMessage( + entry_id=entry_id, + sender_name=sender_name, + sender_type=sender_type, + summary=summary, + ) + + def to_dict(self) -> dict: + result: dict = {} + result["entryId"] = from_str(self.entry_id) + result["senderName"] = from_str(self.sender_name) + result["senderType"] = from_str(self.sender_type) + result["summary"] = from_str(self.summary) + result["type"] = self.type + return result + + +@dataclass +class SystemNotificationShellCompleted: + "Schema for the `SystemNotificationShellCompleted` type." + shell_id: str + type: ClassVar[str] = "shell_completed" description: str | None = None exit_code: int | None = None + + @staticmethod + def from_dict(obj: Any) -> "SystemNotificationShellCompleted": + assert isinstance(obj, dict) + shell_id = from_str(obj.get("shellId")) + description = from_union([from_none, from_str], obj.get("description")) + exit_code = from_union([from_none, from_int], obj.get("exitCode")) + return SystemNotificationShellCompleted( + shell_id=shell_id, + description=description, + exit_code=exit_code, + ) + + def to_dict(self) -> dict: + result: dict = {} + result["shellId"] = from_str(self.shell_id) + result["type"] = self.type + if self.description is not None: + result["description"] = from_union([from_none, from_str], self.description) + if self.exit_code is not None: + result["exitCode"] = from_union([from_none, to_int], self.exit_code) + return result + + +@dataclass +class SystemNotificationShellDetachedCompleted: + "Schema for the `SystemNotificationShellDetachedCompleted` type." + shell_id: str + type: ClassVar[str] = "shell_detached_completed" + description: str | None = None + + @staticmethod + def from_dict(obj: Any) -> "SystemNotificationShellDetachedCompleted": + assert isinstance(obj, dict) + shell_id = from_str(obj.get("shellId")) + description = from_union([from_none, from_str], obj.get("description")) + return SystemNotificationShellDetachedCompleted( + shell_id=shell_id, + description=description, + ) + + def to_dict(self) -> dict: + result: dict = {} + result["shellId"] = from_str(self.shell_id) + result["type"] = self.type + if self.description is not None: + result["description"] = from_union([from_none, from_str], self.description) + return result + + +@dataclass +class ToolExecutionCompleteContentAudio: + "Audio content block with base64-encoded data" + data: str + mime_type: str + type: ClassVar[str] = "audio" + + @staticmethod + def from_dict(obj: Any) -> "ToolExecutionCompleteContentAudio": + assert isinstance(obj, dict) + data = from_str(obj.get("data")) + mime_type = from_str(obj.get("mimeType")) + return ToolExecutionCompleteContentAudio( + data=data, + mime_type=mime_type, + ) + + def to_dict(self) -> dict: + result: dict = {} + result["data"] = from_str(self.data) + result["mimeType"] = from_str(self.mime_type) + result["type"] = self.type + return result + + +@dataclass +class ToolExecutionCompleteContentImage: + "Image content block with base64-encoded data" + data: str + mime_type: str + type: ClassVar[str] = "image" + + @staticmethod + def from_dict(obj: Any) -> "ToolExecutionCompleteContentImage": + assert isinstance(obj, dict) + data = from_str(obj.get("data")) + mime_type = from_str(obj.get("mimeType")) + return ToolExecutionCompleteContentImage( + data=data, + mime_type=mime_type, + ) + + def to_dict(self) -> dict: + result: dict = {} + result["data"] = from_str(self.data) + result["mimeType"] = from_str(self.mime_type) + result["type"] = self.type + return result + + +@dataclass +class ToolExecutionCompleteContentResource: + "Embedded resource content block with inline text or binary data" + resource: ToolExecutionCompleteContentResourceDetails + type: ClassVar[str] = "resource" + + @staticmethod + def from_dict(obj: Any) -> "ToolExecutionCompleteContentResource": + assert isinstance(obj, dict) + resource = from_union([EmbeddedTextResourceContents.from_dict, EmbeddedBlobResourceContents.from_dict], obj.get("resource")) + return ToolExecutionCompleteContentResource( + resource=resource, + ) + + def to_dict(self) -> dict: + result: dict = {} + result["resource"] = from_union([lambda x: to_class(EmbeddedTextResourceContents, x), lambda x: to_class(EmbeddedBlobResourceContents, x)], self.resource) + result["type"] = self.type + return result + + +@dataclass +class ToolExecutionCompleteContentResourceLink: + "Resource link content block referencing an external resource" + name: str + type: ClassVar[str] = "resource_link" + uri: str + description: str | None = None icons: list[ToolExecutionCompleteContentResourceLinkIcon] | None = None mime_type: str | None = None - name: str | None = None - resource: ToolExecutionCompleteContentResourceDetails | None = None size: int | None = None - text: str | None = None title: str | None = None - uri: str | None = None @staticmethod - def from_dict(obj: Any) -> "ToolExecutionCompleteContent": + def from_dict(obj: Any) -> "ToolExecutionCompleteContentResourceLink": assert isinstance(obj, dict) - type = parse_enum(ToolExecutionCompleteContentType, obj.get("type")) - cwd = from_union([from_none, from_str], obj.get("cwd")) - data = from_union([from_none, from_str], obj.get("data")) + name = from_str(obj.get("name")) + uri = from_str(obj.get("uri")) description = from_union([from_none, from_str], obj.get("description")) - exit_code = from_union([from_none, from_int], obj.get("exitCode")) icons = from_union([from_none, lambda x: from_list(ToolExecutionCompleteContentResourceLinkIcon.from_dict, x)], obj.get("icons")) mime_type = from_union([from_none, from_str], obj.get("mimeType")) - name = from_union([from_none, from_str], obj.get("name")) - resource = from_union([from_none, lambda x: from_union([EmbeddedTextResourceContents.from_dict, EmbeddedBlobResourceContents.from_dict], x)], obj.get("resource")) size = from_union([from_none, from_int], obj.get("size")) - text = from_union([from_none, from_str], obj.get("text")) title = from_union([from_none, from_str], obj.get("title")) - uri = from_union([from_none, from_str], obj.get("uri")) - return ToolExecutionCompleteContent( - type=type, - cwd=cwd, - data=data, + return ToolExecutionCompleteContentResourceLink( + name=name, + uri=uri, description=description, - exit_code=exit_code, icons=icons, mime_type=mime_type, - name=name, - resource=resource, size=size, - text=text, title=title, - uri=uri, ) def to_dict(self) -> dict: result: dict = {} - result["type"] = to_enum(ToolExecutionCompleteContentType, self.type) - if self.cwd is not None: - result["cwd"] = from_union([from_none, from_str], self.cwd) - if self.data is not None: - result["data"] = from_union([from_none, from_str], self.data) + result["name"] = from_str(self.name) + result["type"] = self.type + result["uri"] = from_str(self.uri) if self.description is not None: result["description"] = from_union([from_none, from_str], self.description) - if self.exit_code is not None: - result["exitCode"] = from_union([from_none, to_int], self.exit_code) if self.icons is not None: result["icons"] = from_union([from_none, lambda x: from_list(lambda x: to_class(ToolExecutionCompleteContentResourceLinkIcon, x), x)], self.icons) if self.mime_type is not None: result["mimeType"] = from_union([from_none, from_str], self.mime_type) - if self.name is not None: - result["name"] = from_union([from_none, from_str], self.name) - if self.resource is not None: - result["resource"] = from_union([from_none, lambda x: from_union([lambda x: to_class(EmbeddedTextResourceContents, x), lambda x: to_class(EmbeddedBlobResourceContents, x)], x)], self.resource) if self.size is not None: result["size"] = from_union([from_none, to_int], self.size) - if self.text is not None: - result["text"] = from_union([from_none, from_str], self.text) if self.title is not None: result["title"] = from_union([from_none, from_str], self.title) - if self.uri is not None: - result["uri"] = from_union([from_none, from_str], self.uri) return result @@ -4176,13 +4904,65 @@ def from_dict(obj: Any) -> "ToolExecutionCompleteContentResourceLinkIcon": def to_dict(self) -> dict: result: dict = {} - result["src"] = from_str(self.src) - if self.mime_type is not None: - result["mimeType"] = from_union([from_none, from_str], self.mime_type) - if self.sizes is not None: - result["sizes"] = from_union([from_none, lambda x: from_list(from_str, x)], self.sizes) - if self.theme is not None: - result["theme"] = from_union([from_none, lambda x: to_enum(ToolExecutionCompleteContentResourceLinkIconTheme, x)], self.theme) + result["src"] = from_str(self.src) + if self.mime_type is not None: + result["mimeType"] = from_union([from_none, from_str], self.mime_type) + if self.sizes is not None: + result["sizes"] = from_union([from_none, lambda x: from_list(from_str, x)], self.sizes) + if self.theme is not None: + result["theme"] = from_union([from_none, lambda x: to_enum(ToolExecutionCompleteContentResourceLinkIconTheme, x)], self.theme) + return result + + +@dataclass +class ToolExecutionCompleteContentTerminal: + "Terminal/shell output content block with optional exit code and working directory" + text: str + type: ClassVar[str] = "terminal" + cwd: str | None = None + exit_code: int | None = None + + @staticmethod + def from_dict(obj: Any) -> "ToolExecutionCompleteContentTerminal": + assert isinstance(obj, dict) + text = from_str(obj.get("text")) + cwd = from_union([from_none, from_str], obj.get("cwd")) + exit_code = from_union([from_none, from_int], obj.get("exitCode")) + return ToolExecutionCompleteContentTerminal( + text=text, + cwd=cwd, + exit_code=exit_code, + ) + + def to_dict(self) -> dict: + result: dict = {} + result["text"] = from_str(self.text) + result["type"] = self.type + if self.cwd is not None: + result["cwd"] = from_union([from_none, from_str], self.cwd) + if self.exit_code is not None: + result["exitCode"] = from_union([from_none, to_int], self.exit_code) + return result + + +@dataclass +class ToolExecutionCompleteContentText: + "Plain text content block" + text: str + type: ClassVar[str] = "text" + + @staticmethod + def from_dict(obj: Any) -> "ToolExecutionCompleteContentText": + assert isinstance(obj, dict) + text = from_str(obj.get("text")) + return ToolExecutionCompleteContentText( + text=text, + ) + + def to_dict(self) -> dict: + result: dict = {} + result["text"] = from_str(self.text) + result["type"] = self.type return result @@ -4290,7 +5070,7 @@ class ToolExecutionCompleteResult: def from_dict(obj: Any) -> "ToolExecutionCompleteResult": assert isinstance(obj, dict) content = from_str(obj.get("content")) - contents = from_union([from_none, lambda x: from_list(ToolExecutionCompleteContent.from_dict, x)], obj.get("contents")) + contents = from_union([from_none, lambda x: from_list(_load_ToolExecutionCompleteContent, x)], obj.get("contents")) detailed_content = from_union([from_none, from_str], obj.get("detailedContent")) return ToolExecutionCompleteResult( content=content, @@ -4302,7 +5082,7 @@ def to_dict(self) -> dict: result: dict = {} result["content"] = from_str(self.content) if self.contents is not None: - result["contents"] = from_union([from_none, lambda x: from_list(lambda x: to_class(ToolExecutionCompleteContent, x), x)], self.contents) + result["contents"] = from_union([from_none, lambda x: from_list(lambda x: x.to_dict(), x)], self.contents) if self.detailed_content is not None: result["detailedContent"] = from_union([from_none, from_str], self.detailed_content) return result @@ -4499,86 +5279,87 @@ def to_dict(self) -> dict: @dataclass -class UserMessageAttachment: - "A user message attachment — a file, directory, code selection, blob, or GitHub reference" - type: UserMessageAttachmentType - data: str | None = None +class UserMessageAttachmentBlob: + "Blob attachment with inline base64-encoded data" + data: str + mime_type: str + type: ClassVar[str] = "blob" display_name: str | None = None - file_path: str | None = None - line_range: UserMessageAttachmentFileLineRange | None = None - mime_type: str | None = None - number: int | None = None - path: str | None = None - reference_type: UserMessageAttachmentGithubReferenceType | None = None - selection: UserMessageAttachmentSelectionDetails | None = None - state: str | None = None - text: str | None = None - title: str | None = None - url: str | None = None @staticmethod - def from_dict(obj: Any) -> "UserMessageAttachment": + def from_dict(obj: Any) -> "UserMessageAttachmentBlob": assert isinstance(obj, dict) - type = parse_enum(UserMessageAttachmentType, obj.get("type")) - data = from_union([from_none, from_str], obj.get("data")) + data = from_str(obj.get("data")) + mime_type = from_str(obj.get("mimeType")) display_name = from_union([from_none, from_str], obj.get("displayName")) - file_path = from_union([from_none, from_str], obj.get("filePath")) - line_range = from_union([from_none, UserMessageAttachmentFileLineRange.from_dict], obj.get("lineRange")) - mime_type = from_union([from_none, from_str], obj.get("mimeType")) - number = from_union([from_none, from_int], obj.get("number")) - path = from_union([from_none, from_str], obj.get("path")) - reference_type = from_union([from_none, lambda x: parse_enum(UserMessageAttachmentGithubReferenceType, x)], obj.get("referenceType")) - selection = from_union([from_none, UserMessageAttachmentSelectionDetails.from_dict], obj.get("selection")) - state = from_union([from_none, from_str], obj.get("state")) - text = from_union([from_none, from_str], obj.get("text")) - title = from_union([from_none, from_str], obj.get("title")) - url = from_union([from_none, from_str], obj.get("url")) - return UserMessageAttachment( - type=type, + return UserMessageAttachmentBlob( data=data, - display_name=display_name, - file_path=file_path, - line_range=line_range, mime_type=mime_type, - number=number, - path=path, - reference_type=reference_type, - selection=selection, - state=state, - text=text, - title=title, - url=url, + display_name=display_name, ) def to_dict(self) -> dict: result: dict = {} - result["type"] = to_enum(UserMessageAttachmentType, self.type) - if self.data is not None: - result["data"] = from_union([from_none, from_str], self.data) + result["data"] = from_str(self.data) + result["mimeType"] = from_str(self.mime_type) + result["type"] = self.type if self.display_name is not None: result["displayName"] = from_union([from_none, from_str], self.display_name) - if self.file_path is not None: - result["filePath"] = from_union([from_none, from_str], self.file_path) + return result + + +@dataclass +class UserMessageAttachmentDirectory: + "Directory attachment" + display_name: str + path: str + type: ClassVar[str] = "directory" + + @staticmethod + def from_dict(obj: Any) -> "UserMessageAttachmentDirectory": + assert isinstance(obj, dict) + display_name = from_str(obj.get("displayName")) + path = from_str(obj.get("path")) + return UserMessageAttachmentDirectory( + display_name=display_name, + path=path, + ) + + def to_dict(self) -> dict: + result: dict = {} + result["displayName"] = from_str(self.display_name) + result["path"] = from_str(self.path) + result["type"] = self.type + return result + + +@dataclass +class UserMessageAttachmentFile: + "File attachment" + display_name: str + path: str + type: ClassVar[str] = "file" + line_range: UserMessageAttachmentFileLineRange | None = None + + @staticmethod + def from_dict(obj: Any) -> "UserMessageAttachmentFile": + assert isinstance(obj, dict) + display_name = from_str(obj.get("displayName")) + path = from_str(obj.get("path")) + line_range = from_union([from_none, UserMessageAttachmentFileLineRange.from_dict], obj.get("lineRange")) + return UserMessageAttachmentFile( + display_name=display_name, + path=path, + line_range=line_range, + ) + + def to_dict(self) -> dict: + result: dict = {} + result["displayName"] = from_str(self.display_name) + result["path"] = from_str(self.path) + result["type"] = self.type if self.line_range is not None: result["lineRange"] = from_union([from_none, lambda x: to_class(UserMessageAttachmentFileLineRange, x)], self.line_range) - if self.mime_type is not None: - result["mimeType"] = from_union([from_none, from_str], self.mime_type) - if self.number is not None: - result["number"] = from_union([from_none, to_int], self.number) - if self.path is not None: - result["path"] = from_union([from_none, from_str], self.path) - if self.reference_type is not None: - result["referenceType"] = from_union([from_none, lambda x: to_enum(UserMessageAttachmentGithubReferenceType, x)], self.reference_type) - if self.selection is not None: - result["selection"] = from_union([from_none, lambda x: to_class(UserMessageAttachmentSelectionDetails, x)], self.selection) - if self.state is not None: - result["state"] = from_union([from_none, from_str], self.state) - if self.text is not None: - result["text"] = from_union([from_none, from_str], self.text) - if self.title is not None: - result["title"] = from_union([from_none, from_str], self.title) - if self.url is not None: - result["url"] = from_union([from_none, from_str], self.url) return result @@ -4605,6 +5386,76 @@ def to_dict(self) -> dict: return result +@dataclass +class UserMessageAttachmentGithubReference: + "GitHub issue, pull request, or discussion reference" + number: int + reference_type: UserMessageAttachmentGithubReferenceType + state: str + title: str + type: ClassVar[str] = "github_reference" + url: str + + @staticmethod + def from_dict(obj: Any) -> "UserMessageAttachmentGithubReference": + assert isinstance(obj, dict) + number = from_int(obj.get("number")) + reference_type = parse_enum(UserMessageAttachmentGithubReferenceType, obj.get("referenceType")) + state = from_str(obj.get("state")) + title = from_str(obj.get("title")) + url = from_str(obj.get("url")) + return UserMessageAttachmentGithubReference( + number=number, + reference_type=reference_type, + state=state, + title=title, + url=url, + ) + + def to_dict(self) -> dict: + result: dict = {} + result["number"] = to_int(self.number) + result["referenceType"] = to_enum(UserMessageAttachmentGithubReferenceType, self.reference_type) + result["state"] = from_str(self.state) + result["title"] = from_str(self.title) + result["type"] = self.type + result["url"] = from_str(self.url) + return result + + +@dataclass +class UserMessageAttachmentSelection: + "Code selection attachment from an editor" + display_name: str + file_path: str + selection: UserMessageAttachmentSelectionDetails + text: str + type: ClassVar[str] = "selection" + + @staticmethod + def from_dict(obj: Any) -> "UserMessageAttachmentSelection": + assert isinstance(obj, dict) + display_name = from_str(obj.get("displayName")) + file_path = from_str(obj.get("filePath")) + selection = UserMessageAttachmentSelectionDetails.from_dict(obj.get("selection")) + text = from_str(obj.get("text")) + return UserMessageAttachmentSelection( + display_name=display_name, + file_path=file_path, + selection=selection, + text=text, + ) + + def to_dict(self) -> dict: + result: dict = {} + result["displayName"] = from_str(self.display_name) + result["filePath"] = from_str(self.file_path) + result["selection"] = to_class(UserMessageAttachmentSelectionDetails, self.selection) + result["text"] = from_str(self.text) + result["type"] = self.type + return result + + @dataclass class UserMessageAttachmentSelectionDetails: "Position range of the selection within the file" @@ -4693,7 +5544,7 @@ def from_dict(obj: Any) -> "UserMessageData": assert isinstance(obj, dict) content = from_str(obj.get("content")) agent_mode = from_union([from_none, lambda x: parse_enum(UserMessageAgentMode, x)], obj.get("agentMode")) - attachments = from_union([from_none, lambda x: from_list(UserMessageAttachment.from_dict, x)], obj.get("attachments")) + attachments = from_union([from_none, lambda x: from_list(_load_UserMessageAttachment, x)], obj.get("attachments")) interaction_id = from_union([from_none, from_str], obj.get("interactionId")) is_autopilot_continuation = from_union([from_none, from_bool], obj.get("isAutopilotContinuation")) native_document_path_fallback_paths = from_union([from_none, lambda x: from_list(from_str, x)], obj.get("nativeDocumentPathFallbackPaths")) @@ -4720,7 +5571,7 @@ def to_dict(self) -> dict: if self.agent_mode is not None: result["agentMode"] = from_union([from_none, lambda x: to_enum(UserMessageAgentMode, x)], self.agent_mode) if self.attachments is not None: - result["attachments"] = from_union([from_none, lambda x: from_list(lambda x: to_class(UserMessageAttachment, x), x)], self.attachments) + result["attachments"] = from_union([from_none, lambda x: from_list(lambda x: x.to_dict(), x)], self.attachments) if self.interaction_id is not None: result["interactionId"] = from_union([from_none, from_str], self.interaction_id) if self.is_autopilot_continuation is not None: @@ -4739,46 +5590,163 @@ def to_dict(self) -> dict: @dataclass -class UserToolSessionApproval: - "The approval to add as a session-scoped rule" - kind: UserToolSessionApprovalKind - command_identifiers: list[str] | None = None - extension_name: str | None = None +class UserToolSessionApprovalCommands: + "Schema for the `UserToolSessionApprovalCommands` type." + command_identifiers: list[str] + kind: ClassVar[str] = "commands" + + @staticmethod + def from_dict(obj: Any) -> "UserToolSessionApprovalCommands": + assert isinstance(obj, dict) + command_identifiers = from_list(from_str, obj.get("commandIdentifiers")) + return UserToolSessionApprovalCommands( + command_identifiers=command_identifiers, + ) + + def to_dict(self) -> dict: + result: dict = {} + result["commandIdentifiers"] = from_list(from_str, self.command_identifiers) + result["kind"] = self.kind + return result + + +@dataclass +class UserToolSessionApprovalCustomTool: + "Schema for the `UserToolSessionApprovalCustomTool` type." + kind: ClassVar[str] = "custom-tool" + tool_name: str + + @staticmethod + def from_dict(obj: Any) -> "UserToolSessionApprovalCustomTool": + assert isinstance(obj, dict) + tool_name = from_str(obj.get("toolName")) + return UserToolSessionApprovalCustomTool( + tool_name=tool_name, + ) + + def to_dict(self) -> dict: + result: dict = {} + result["kind"] = self.kind + result["toolName"] = from_str(self.tool_name) + return result + + +@dataclass +class UserToolSessionApprovalExtensionManagement: + "Schema for the `UserToolSessionApprovalExtensionManagement` type." + kind: ClassVar[str] = "extension-management" operation: str | None = None - server_name: str | None = None - tool_name: str | None = None @staticmethod - def from_dict(obj: Any) -> "UserToolSessionApproval": + def from_dict(obj: Any) -> "UserToolSessionApprovalExtensionManagement": assert isinstance(obj, dict) - kind = parse_enum(UserToolSessionApprovalKind, obj.get("kind")) - command_identifiers = from_union([from_none, lambda x: from_list(from_str, x)], obj.get("commandIdentifiers")) - extension_name = from_union([from_none, from_str], obj.get("extensionName")) operation = from_union([from_none, from_str], obj.get("operation")) - server_name = from_union([from_none, from_str], obj.get("serverName")) - tool_name = from_union([from_none, from_str], obj.get("toolName")) - return UserToolSessionApproval( - kind=kind, - command_identifiers=command_identifiers, - extension_name=extension_name, + return UserToolSessionApprovalExtensionManagement( operation=operation, - server_name=server_name, - tool_name=tool_name, ) def to_dict(self) -> dict: result: dict = {} - result["kind"] = to_enum(UserToolSessionApprovalKind, self.kind) - if self.command_identifiers is not None: - result["commandIdentifiers"] = from_union([from_none, lambda x: from_list(from_str, x)], self.command_identifiers) - if self.extension_name is not None: - result["extensionName"] = from_union([from_none, from_str], self.extension_name) + result["kind"] = self.kind if self.operation is not None: result["operation"] = from_union([from_none, from_str], self.operation) - if self.server_name is not None: - result["serverName"] = from_union([from_none, from_str], self.server_name) - if self.tool_name is not None: - result["toolName"] = from_union([from_none, from_str], self.tool_name) + return result + + +@dataclass +class UserToolSessionApprovalExtensionPermissionAccess: + "Schema for the `UserToolSessionApprovalExtensionPermissionAccess` type." + extension_name: str + kind: ClassVar[str] = "extension-permission-access" + + @staticmethod + def from_dict(obj: Any) -> "UserToolSessionApprovalExtensionPermissionAccess": + assert isinstance(obj, dict) + extension_name = from_str(obj.get("extensionName")) + return UserToolSessionApprovalExtensionPermissionAccess( + extension_name=extension_name, + ) + + def to_dict(self) -> dict: + result: dict = {} + result["extensionName"] = from_str(self.extension_name) + result["kind"] = self.kind + return result + + +@dataclass +class UserToolSessionApprovalMcp: + "Schema for the `UserToolSessionApprovalMcp` type." + kind: ClassVar[str] = "mcp" + server_name: str + tool_name: str | None + + @staticmethod + def from_dict(obj: Any) -> "UserToolSessionApprovalMcp": + assert isinstance(obj, dict) + server_name = from_str(obj.get("serverName")) + tool_name = from_union([from_none, from_str], obj.get("toolName")) + return UserToolSessionApprovalMcp( + server_name=server_name, + tool_name=tool_name, + ) + + def to_dict(self) -> dict: + result: dict = {} + result["kind"] = self.kind + result["serverName"] = from_str(self.server_name) + result["toolName"] = from_union([from_none, from_str], self.tool_name) + return result + + +@dataclass +class UserToolSessionApprovalMemory: + "Schema for the `UserToolSessionApprovalMemory` type." + kind: ClassVar[str] = "memory" + + @staticmethod + def from_dict(obj: Any) -> "UserToolSessionApprovalMemory": + assert isinstance(obj, dict) + return UserToolSessionApprovalMemory( + ) + + def to_dict(self) -> dict: + result: dict = {} + result["kind"] = self.kind + return result + + +@dataclass +class UserToolSessionApprovalRead: + "Schema for the `UserToolSessionApprovalRead` type." + kind: ClassVar[str] = "read" + + @staticmethod + def from_dict(obj: Any) -> "UserToolSessionApprovalRead": + assert isinstance(obj, dict) + return UserToolSessionApprovalRead( + ) + + def to_dict(self) -> dict: + result: dict = {} + result["kind"] = self.kind + return result + + +@dataclass +class UserToolSessionApprovalWrite: + "Schema for the `UserToolSessionApprovalWrite` type." + kind: ClassVar[str] = "write" + + @staticmethod + def from_dict(obj: Any) -> "UserToolSessionApprovalWrite": + assert isinstance(obj, dict) + return UserToolSessionApprovalWrite( + ) + + def to_dict(self) -> dict: + result: dict = {} + result["kind"] = self.kind return result @@ -4836,10 +5804,142 @@ def to_dict(self) -> dict: return result +def _load_PermissionPromptRequest(obj: Any) -> "PermissionPromptRequest": + assert isinstance(obj, dict) + kind = obj.get("kind") + match kind: + case "commands": return PermissionPromptRequestCommands.from_dict(obj) + case "write": return PermissionPromptRequestWrite.from_dict(obj) + case "read": return PermissionPromptRequestRead.from_dict(obj) + case "mcp": return PermissionPromptRequestMcp.from_dict(obj) + case "url": return PermissionPromptRequestUrl.from_dict(obj) + case "memory": return PermissionPromptRequestMemory.from_dict(obj) + case "custom-tool": return PermissionPromptRequestCustomTool.from_dict(obj) + case "path": return PermissionPromptRequestPath.from_dict(obj) + case "hook": return PermissionPromptRequestHook.from_dict(obj) + case "extension-management": return PermissionPromptRequestExtensionManagement.from_dict(obj) + case "extension-permission-access": return PermissionPromptRequestExtensionPermissionAccess.from_dict(obj) + case _: raise ValueError(f"Unknown PermissionPromptRequest kind: {kind!r}") + + +def _load_PermissionRequest(obj: Any) -> "PermissionRequest": + assert isinstance(obj, dict) + kind = obj.get("kind") + match kind: + case "shell": return PermissionRequestShell.from_dict(obj) + case "write": return PermissionRequestWrite.from_dict(obj) + case "read": return PermissionRequestRead.from_dict(obj) + case "mcp": return PermissionRequestMcp.from_dict(obj) + case "url": return PermissionRequestUrl.from_dict(obj) + case "memory": return PermissionRequestMemory.from_dict(obj) + case "custom-tool": return PermissionRequestCustomTool.from_dict(obj) + case "hook": return PermissionRequestHook.from_dict(obj) + case "extension-management": return PermissionRequestExtensionManagement.from_dict(obj) + case "extension-permission-access": return PermissionRequestExtensionPermissionAccess.from_dict(obj) + case _: raise ValueError(f"Unknown PermissionRequest kind: {kind!r}") + + +def _load_PermissionResult(obj: Any) -> "PermissionResult": + assert isinstance(obj, dict) + kind = obj.get("kind") + match kind: + case "approved": return PermissionApproved.from_dict(obj) + case "approved-for-session": return PermissionApprovedForSession.from_dict(obj) + case "approved-for-location": return PermissionApprovedForLocation.from_dict(obj) + case "cancelled": return PermissionCancelled.from_dict(obj) + case "denied-by-rules": return PermissionDeniedByRules.from_dict(obj) + case "denied-no-approval-rule-and-could-not-request-from-user": return PermissionDeniedNoApprovalRuleAndCouldNotRequestFromUser.from_dict(obj) + case "denied-interactively-by-user": return PermissionDeniedInteractivelyByUser.from_dict(obj) + case "denied-by-content-exclusion-policy": return PermissionDeniedByContentExclusionPolicy.from_dict(obj) + case "denied-by-permission-request-hook": return PermissionDeniedByPermissionRequestHook.from_dict(obj) + case _: raise ValueError(f"Unknown PermissionResult kind: {kind!r}") + + +def _load_SystemNotification(obj: Any) -> "SystemNotification": + assert isinstance(obj, dict) + kind = obj.get("type") + match kind: + case "agent_completed": return SystemNotificationAgentCompleted.from_dict(obj) + case "agent_idle": return SystemNotificationAgentIdle.from_dict(obj) + case "new_inbox_message": return SystemNotificationNewInboxMessage.from_dict(obj) + case "shell_completed": return SystemNotificationShellCompleted.from_dict(obj) + case "shell_detached_completed": return SystemNotificationShellDetachedCompleted.from_dict(obj) + case "instruction_discovered": return SystemNotificationInstructionDiscovered.from_dict(obj) + case _: raise ValueError(f"Unknown SystemNotification type: {kind!r}") + + +def _load_ToolExecutionCompleteContent(obj: Any) -> "ToolExecutionCompleteContent": + assert isinstance(obj, dict) + kind = obj.get("type") + match kind: + case "text": return ToolExecutionCompleteContentText.from_dict(obj) + case "terminal": return ToolExecutionCompleteContentTerminal.from_dict(obj) + case "image": return ToolExecutionCompleteContentImage.from_dict(obj) + case "audio": return ToolExecutionCompleteContentAudio.from_dict(obj) + case "resource_link": return ToolExecutionCompleteContentResourceLink.from_dict(obj) + case "resource": return ToolExecutionCompleteContentResource.from_dict(obj) + case _: raise ValueError(f"Unknown ToolExecutionCompleteContent type: {kind!r}") + + +def _load_UserMessageAttachment(obj: Any) -> "UserMessageAttachment": + assert isinstance(obj, dict) + kind = obj.get("type") + match kind: + case "file": return UserMessageAttachmentFile.from_dict(obj) + case "directory": return UserMessageAttachmentDirectory.from_dict(obj) + case "selection": return UserMessageAttachmentSelection.from_dict(obj) + case "github_reference": return UserMessageAttachmentGithubReference.from_dict(obj) + case "blob": return UserMessageAttachmentBlob.from_dict(obj) + case _: raise ValueError(f"Unknown UserMessageAttachment type: {kind!r}") + + +def _load_UserToolSessionApproval(obj: Any) -> "UserToolSessionApproval": + assert isinstance(obj, dict) + kind = obj.get("kind") + match kind: + case "commands": return UserToolSessionApprovalCommands.from_dict(obj) + case "read": return UserToolSessionApprovalRead.from_dict(obj) + case "write": return UserToolSessionApprovalWrite.from_dict(obj) + case "mcp": return UserToolSessionApprovalMcp.from_dict(obj) + case "memory": return UserToolSessionApprovalMemory.from_dict(obj) + case "custom-tool": return UserToolSessionApprovalCustomTool.from_dict(obj) + case "extension-management": return UserToolSessionApprovalExtensionManagement.from_dict(obj) + case "extension-permission-access": return UserToolSessionApprovalExtensionPermissionAccess.from_dict(obj) + case _: raise ValueError(f"Unknown UserToolSessionApproval kind: {kind!r}") + + +# A content block within a tool result, which may be text, terminal output, image, audio, or a resource +ToolExecutionCompleteContent = ToolExecutionCompleteContentText | ToolExecutionCompleteContentTerminal | ToolExecutionCompleteContentImage | ToolExecutionCompleteContentAudio | ToolExecutionCompleteContentResourceLink | ToolExecutionCompleteContentResource + + +# A user message attachment — a file, directory, code selection, blob, or GitHub reference +UserMessageAttachment = UserMessageAttachmentFile | UserMessageAttachmentDirectory | UserMessageAttachmentSelection | UserMessageAttachmentGithubReference | UserMessageAttachmentBlob + + +# Derived user-facing permission prompt details for UI consumers +PermissionPromptRequest = PermissionPromptRequestCommands | PermissionPromptRequestWrite | PermissionPromptRequestRead | PermissionPromptRequestMcp | PermissionPromptRequestUrl | PermissionPromptRequestMemory | PermissionPromptRequestCustomTool | PermissionPromptRequestPath | PermissionPromptRequestHook | PermissionPromptRequestExtensionManagement | PermissionPromptRequestExtensionPermissionAccess + + +# Details of the permission being requested +PermissionRequest = PermissionRequestShell | PermissionRequestWrite | PermissionRequestRead | PermissionRequestMcp | PermissionRequestUrl | PermissionRequestMemory | PermissionRequestCustomTool | PermissionRequestHook | PermissionRequestExtensionManagement | PermissionRequestExtensionPermissionAccess + + +# Structured metadata identifying what triggered this notification +SystemNotification = SystemNotificationAgentCompleted | SystemNotificationAgentIdle | SystemNotificationNewInboxMessage | SystemNotificationShellCompleted | SystemNotificationShellDetachedCompleted | SystemNotificationInstructionDiscovered + + +# The approval to add as a session-scoped rule +UserToolSessionApproval = UserToolSessionApprovalCommands | UserToolSessionApprovalRead | UserToolSessionApprovalWrite | UserToolSessionApprovalMcp | UserToolSessionApprovalMemory | UserToolSessionApprovalCustomTool | UserToolSessionApprovalExtensionManagement | UserToolSessionApprovalExtensionPermissionAccess + + # The embedded resource contents, either text or base64-encoded binary ToolExecutionCompleteContentResourceDetails = EmbeddedTextResourceContents | EmbeddedBlobResourceContents +# The result of the permission request +PermissionResult = PermissionApproved | PermissionApprovedForSession | PermissionApprovedForLocation | PermissionCancelled | PermissionDeniedByRules | PermissionDeniedNoApprovalRuleAndCouldNotRequestFromUser | PermissionDeniedInteractivelyByUser | PermissionDeniedByContentExclusionPolicy | PermissionDeniedByPermissionRequestHook + + class AbortReason(Enum): "Finite reason code describing why the current turn was aborted" # The local user requested the abort, for example by pressing Ctrl+C in the CLI. @@ -4976,21 +6076,6 @@ class ModelCallFailureSource(Enum): MCP_SAMPLING = "mcp_sampling" -class PermissionPromptRequestKind(Enum): - "Derived user-facing permission prompt details for UI consumers discriminator" - COMMANDS = "commands" - WRITE = "write" - READ = "read" - MCP = "mcp" - URL = "url" - MEMORY = "memory" - CUSTOM_TOOL = "custom-tool" - PATH = "path" - HOOK = "hook" - EXTENSION_MANAGEMENT = "extension-management" - EXTENSION_PERMISSION_ACCESS = "extension-permission-access" - - class PermissionPromptRequestPathAccessKind(Enum): "Underlying permission kind that needs path approval" # Read access to a filesystem path. @@ -5001,20 +6086,6 @@ class PermissionPromptRequestPathAccessKind(Enum): WRITE = "write" -class PermissionRequestKind(Enum): - "Details of the permission being requested discriminator" - SHELL = "shell" - WRITE = "write" - READ = "read" - MCP = "mcp" - URL = "url" - MEMORY = "memory" - CUSTOM_TOOL = "custom-tool" - HOOK = "hook" - EXTENSION_MANAGEMENT = "extension-management" - EXTENSION_PERMISSION_ACCESS = "extension-permission-access" - - class PermissionRequestMemoryAction(Enum): "Whether this is a store or vote memory operation" # Store a new memory. @@ -5031,19 +6102,6 @@ class PermissionRequestMemoryDirection(Enum): DOWNVOTE = "downvote" -class PermissionResultKind(Enum): - "The result of the permission request discriminator" - APPROVED = "approved" - APPROVED_FOR_SESSION = "approved-for-session" - APPROVED_FOR_LOCATION = "approved-for-location" - CANCELLED = "cancelled" - DENIED_BY_RULES = "denied-by-rules" - DENIED_NO_APPROVAL_RULE_AND_COULD_NOT_REQUEST_FROM_USER = "denied-no-approval-rule-and-could-not-request-from-user" - DENIED_INTERACTIVELY_BY_USER = "denied-interactively-by-user" - DENIED_BY_CONTENT_EXCLUSION_POLICY = "denied-by-content-exclusion-policy" - DENIED_BY_PERMISSION_REQUEST_HOOK = "denied-by-permission-request-hook" - - class PlanChangedOperation(Enum): "The type of operation performed on the plan file" # The plan file was created. @@ -5116,16 +6174,6 @@ class SystemNotificationAgentCompletedStatus(Enum): FAILED = "failed" -class SystemNotificationType(Enum): - "Structured metadata identifying what triggered this notification discriminator" - AGENT_COMPLETED = "agent_completed" - AGENT_IDLE = "agent_idle" - NEW_INBOX_MESSAGE = "new_inbox_message" - SHELL_COMPLETED = "shell_completed" - SHELL_DETACHED_COMPLETED = "shell_detached_completed" - INSTRUCTION_DISCOVERED = "instruction_discovered" - - class ToolExecutionCompleteContentResourceLinkIconTheme(Enum): "Theme variant this icon is intended for" # Icon intended for light themes. @@ -5134,16 +6182,6 @@ class ToolExecutionCompleteContentResourceLinkIconTheme(Enum): DARK = "dark" -class ToolExecutionCompleteContentType(Enum): - "A content block within a tool result, which may be text, terminal output, image, audio, or a resource discriminator" - TEXT = "text" - TERMINAL = "terminal" - IMAGE = "image" - AUDIO = "audio" - RESOURCE_LINK = "resource_link" - RESOURCE = "resource" - - class UserMessageAgentMode(Enum): "The agent mode that was active when this message was sent" # The agent is responding interactively to the user. @@ -5166,27 +6204,6 @@ class UserMessageAttachmentGithubReferenceType(Enum): DISCUSSION = "discussion" -class UserMessageAttachmentType(Enum): - "A user message attachment — a file, directory, code selection, blob, or GitHub reference discriminator" - FILE = "file" - DIRECTORY = "directory" - SELECTION = "selection" - GITHUB_REFERENCE = "github_reference" - BLOB = "blob" - - -class UserToolSessionApprovalKind(Enum): - "The approval to add as a session-scoped rule discriminator" - COMMANDS = "commands" - READ = "read" - WRITE = "write" - MCP = "mcp" - MEMORY = "memory" - CUSTOM_TOOL = "custom-tool" - EXTENSION_MANAGEMENT = "extension-management" - EXTENSION_PERMISSION_ACCESS = "extension-permission-access" - - class WorkingDirectoryContextHostType(Enum): "Hosting platform type of the repository (github or ado)" # Repository is hosted on GitHub. diff --git a/python/copilot/session.py b/python/copilot/session.py index caf2e3020..4ca33784c 100644 --- a/python/copilot/session.py +++ b/python/copilot/session.py @@ -32,8 +32,9 @@ LogRequest, ModelSwitchToRequest, PermissionDecision, - PermissionDecisionKind, + PermissionDecisionApproveOnce, PermissionDecisionRequest, + PermissionDecisionUserNotAvailable, SessionLogLevel, SessionRpc, UIElicitationRequest, @@ -231,19 +232,26 @@ class SystemMessageCustomizeConfig(TypedDict, total=False): # Permission Types # ============================================================================ -PermissionRequestResultKind = Literal[ - "approve-once", - "reject", - "user-not-available", - "no-result", -] - @dataclass -class PermissionRequestResult: - """Result of a permission request.""" +class PermissionNoResult: + """Sentinel returned by a permission handler to leave the request unanswered. + + Only meaningful against protocol-v1 servers. v2 servers reject ``no-result`` + responses; the SDK raises :class:`ValueError` if a v2 server receives one. + Mirrors the ``{kind: "no-result"}`` extension TS adds to its ``PermissionDecision`` + union (see ``nodejs/src/types.ts:883``). + """ + + kind: Literal["no-result"] = "no-result" - kind: PermissionRequestResultKind = "user-not-available" + +# The decision returned by a permission handler. Identical shape to the wire +# ``PermissionDecision`` discriminated union, plus a :class:`PermissionNoResult` +# sentinel for v1 servers. Construct via the generated variant classes: +# ``PermissionDecisionApproveOnce(kind=...)``, ``PermissionDecisionReject(kind=..., +# feedback=...)``, etc. +PermissionRequestResult = PermissionDecision | PermissionNoResult _PermissionHandlerFn = Callable[ @@ -257,7 +265,7 @@ class PermissionHandler: def approve_all( request: PermissionRequest, invocation: dict[str, str] ) -> PermissionRequestResult: - return PermissionRequestResult(kind="approve-once") + return PermissionDecisionApproveOnce() # ============================================================================ @@ -1703,18 +1711,14 @@ async def _execute_permission_and_respond( ) result = cast(PermissionRequestResult, result) - if result.kind == "no-result": + if isinstance(result, PermissionNoResult): return - perm_result = PermissionDecision( - kind=PermissionDecisionKind(result.kind), - ) - rpc_start = time.perf_counter() await self.rpc.permissions.handle_pending_permission_request( PermissionDecisionRequest( request_id=request_id, - result=perm_result, + result=result, ) ) log_timing( @@ -1730,9 +1734,7 @@ async def _execute_permission_and_respond( await self.rpc.permissions.handle_pending_permission_request( PermissionDecisionRequest( request_id=request_id, - result=PermissionDecision( - kind=PermissionDecisionKind.USER_NOT_AVAILABLE, - ), + result=PermissionDecisionUserNotAvailable(), ) ) except (JsonRpcError, ProcessExitedError, OSError): @@ -1995,8 +1997,8 @@ async def _handle_permission_request( handler = self._permission_handler if not handler: - # No handler registered, deny permission - return PermissionRequestResult() + # No handler registered, deny permission. + return PermissionDecisionUserNotAvailable() try: handler_start = time.perf_counter() @@ -2012,13 +2014,13 @@ async def _handle_permission_request( ) return cast(PermissionRequestResult, result) except Exception: # pylint: disable=broad-except - # Handler failed, deny permission + # Handler failed, deny permission. logger.debug( "Error handling permission request", extra={"session_id": self.session_id}, exc_info=True, ) - return PermissionRequestResult() + return PermissionDecisionUserNotAvailable() def _register_user_input_handler(self, handler: UserInputHandler | None) -> None: """ diff --git a/python/e2e/test_multi_client_e2e.py b/python/e2e/test_multi_client_e2e.py index 06f671e94..560be31d0 100644 --- a/python/e2e/test_multi_client_e2e.py +++ b/python/e2e/test_multi_client_e2e.py @@ -16,7 +16,8 @@ from copilot import CopilotClient, define_tool from copilot.client import ExternalServerConfig, SubprocessConfig -from copilot.session import PermissionHandler, PermissionRequestResult +from copilot.generated.rpc import PermissionDecisionApproveOnce, PermissionDecisionReject +from copilot.session import PermissionHandler, PermissionNoResult from copilot.tools import ToolInvocation from .testharness import get_final_assistant_message @@ -242,16 +243,14 @@ async def test_one_client_approves_permission_and_both_see_the_result( # Client 1 creates a session and manually approves permission requests session1 = await mctx.client1.create_session( on_permission_request=lambda request, invocation: ( - permission_requests.append(request) or PermissionRequestResult(kind="approve-once") + permission_requests.append(request) or PermissionDecisionApproveOnce() ), ) # Client 2 observes the permission request but leaves the decision to client 1. session2 = await mctx.client2.resume_session( session1.session_id, - on_permission_request=lambda request, invocation: PermissionRequestResult( - kind="no-result" - ), + on_permission_request=lambda request, invocation: PermissionNoResult(), ) client1_events = [] @@ -289,17 +288,13 @@ async def test_one_client_rejects_permission_and_both_see_the_result( """One client rejects a permission request and both see the result.""" # Client 1 creates a session and denies all permission requests session1 = await mctx.client1.create_session( - on_permission_request=lambda request, invocation: PermissionRequestResult( - kind="reject" - ), + on_permission_request=lambda request, invocation: PermissionDecisionReject(), ) # Client 2 observes the permission request but leaves the decision to client 1. session2 = await mctx.client2.resume_session( session1.session_id, - on_permission_request=lambda request, invocation: PermissionRequestResult( - kind="no-result" - ), + on_permission_request=lambda request, invocation: PermissionNoResult(), ) client1_events = [] diff --git a/python/e2e/test_pending_work_resume_e2e.py b/python/e2e/test_pending_work_resume_e2e.py index be0e4feec..f2f31835a 100644 --- a/python/e2e/test_pending_work_resume_e2e.py +++ b/python/e2e/test_pending_work_resume_e2e.py @@ -18,8 +18,12 @@ from copilot import CopilotClient from copilot.client import ExternalServerConfig, SubprocessConfig -from copilot.generated.rpc import HandlePendingToolCallRequest, PermissionDecisionRequest -from copilot.session import PermissionHandler, PermissionRequestResult +from copilot.generated.rpc import ( + HandlePendingToolCallRequest, + PermissionDecisionRequest, + PermissionDecisionUserNotAvailable, +) +from copilot.session import PermissionHandler from copilot.tools import Tool, ToolInvocation, ToolResult from .testharness import E2ETestContext, get_final_assistant_message @@ -182,9 +186,7 @@ def resumed_tool_handler(args): try: session2 = await resumed_client.resume_session( session_id, - on_permission_request=lambda req, inv: PermissionRequestResult( - kind="user-not-available" - ), + on_permission_request=lambda req, inv: PermissionDecisionUserNotAvailable(), continue_pending_work=True, tools=[_make_pending_tool("resume_permission_tool", resumed_tool_handler)], ) @@ -212,7 +214,7 @@ def resumed_tool_handler(args): await _safe_force_stop(resumed_client) finally: if not release_original.done(): - release_original.set_result(PermissionRequestResult(kind="user-not-available")) + release_original.set_result(PermissionDecisionUserNotAvailable()) finally: await _safe_force_stop(server) diff --git a/python/e2e/test_permissions_e2e.py b/python/e2e/test_permissions_e2e.py index 46cf2f3d4..b6dcb2d71 100644 --- a/python/e2e/test_permissions_e2e.py +++ b/python/e2e/test_permissions_e2e.py @@ -6,12 +6,17 @@ import pytest +from copilot.generated.rpc import ( + PermissionDecisionApproveOnce, + PermissionDecisionReject, + PermissionDecisionUserNotAvailable, +) from copilot.generated.session_events import ( PermissionRequest, SessionIdleData, ToolExecutionCompleteData, ) -from copilot.session import PermissionHandler, PermissionRequestResult +from copilot.session import PermissionHandler, PermissionNoResult, PermissionRequestResult from .testharness import E2ETestContext from .testharness.helper import read_file, write_file @@ -29,7 +34,7 @@ def on_permission_request( ) -> PermissionRequestResult: permission_requests.append(request) assert invocation["session_id"] == session.session_id - return PermissionRequestResult(kind="approve-once") + return PermissionDecisionApproveOnce() session = await ctx.client.create_session(on_permission_request=on_permission_request) @@ -52,7 +57,7 @@ async def test_should_deny_permission_when_handler_returns_denied(self, ctx: E2E def on_permission_request( request: PermissionRequest, invocation: dict ) -> PermissionRequestResult: - return PermissionRequestResult(kind="reject") + return PermissionDecisionReject() session = await ctx.client.create_session(on_permission_request=on_permission_request) @@ -97,7 +102,7 @@ async def test_should_deny_tool_operations_when_handler_explicitly_denies( """Test that tool operations are denied when handler explicitly denies""" def deny_all(request, invocation): - return PermissionRequestResult() + return PermissionDecisionUserNotAvailable() session = await ctx.client.create_session(on_permission_request=deny_all) @@ -138,7 +143,7 @@ async def test_should_deny_tool_operations_when_handler_explicitly_denies_after_ await session1.send_and_wait("What is 1+1?") def deny_all(request, invocation): - return PermissionRequestResult() + return PermissionDecisionUserNotAvailable() session2 = await ctx.client.resume_session(session_id, on_permission_request=deny_all) @@ -190,7 +195,7 @@ async def on_permission_request( ) -> PermissionRequestResult: permission_requests.append(request) await asyncio.sleep(0) - return PermissionRequestResult(kind="approve-once") + return PermissionDecisionApproveOnce() session = await ctx.client.create_session(on_permission_request=on_permission_request) @@ -216,7 +221,7 @@ def on_permission_request( request: PermissionRequest, invocation: dict ) -> PermissionRequestResult: permission_requests.append(request) - return PermissionRequestResult(kind="approve-once") + return PermissionDecisionApproveOnce() session2 = await ctx.client.resume_session( session_id, on_permission_request=on_permission_request @@ -260,7 +265,7 @@ def on_permission_request( received_tool_call_id = True assert isinstance(request.tool_call_id, str) assert len(request.tool_call_id) > 0 - return PermissionRequestResult(kind="approve-once") + return PermissionDecisionApproveOnce() session = await ctx.client.create_session(on_permission_request=on_permission_request) @@ -289,7 +294,7 @@ async def slow_permission(request: PermissionRequest, invocation: dict): handler_entered.set_result(True) await asyncio.wait_for(release_handler, timeout=30.0) add_event("permission-complete", tool_call_id) - return PermissionRequestResult(kind="approve-once") + return PermissionDecisionApproveOnce() session = await ctx.client.create_session(on_permission_request=slow_permission) @@ -376,7 +381,7 @@ async def test_should_deny_permission_with_noresult_kind(self, ctx: E2ETestConte def deny_noresult(request: PermissionRequest, invocation: dict) -> PermissionRequestResult: if not permission_called.done(): permission_called.set_result(True) - return PermissionRequestResult(kind="no-result") + return PermissionNoResult() session = await ctx.client.create_session(on_permission_request=deny_noresult) try: @@ -399,7 +404,7 @@ def counting_handler( ) -> PermissionRequestResult: nonlocal handler_call_count handler_call_count += 1 - return PermissionRequestResult(kind="approve-once") + return PermissionDecisionApproveOnce() session = await ctx.client.create_session(on_permission_request=counting_handler) try: @@ -458,7 +463,7 @@ async def concurrent_permission(request: PermissionRequest, invocation: dict): if permission_request_count >= 2 and not both_started.done(): both_started.set_result(True) await asyncio.wait_for(both_started, timeout=30.0) - return PermissionRequestResult(kind="approve-once") + return PermissionDecisionApproveOnce() def first_tool_handler(invocation: ToolInvocation) -> ToolResult: nonlocal first_tool_called diff --git a/python/e2e/test_suspend_e2e.py b/python/e2e/test_suspend_e2e.py index ec34bfc37..956ee86c2 100644 --- a/python/e2e/test_suspend_e2e.py +++ b/python/e2e/test_suspend_e2e.py @@ -16,7 +16,8 @@ from copilot import CopilotClient from copilot.client import ExternalServerConfig, SubprocessConfig -from copilot.session import PermissionHandler, PermissionRequestResult +from copilot.generated.rpc import PermissionDecisionUserNotAvailable +from copilot.session import PermissionHandler from copilot.tools import Tool, ToolInvocation, ToolResult from .testharness import E2ETestContext @@ -172,9 +173,7 @@ def tool_handler(args): assert not tool_invoked finally: if not release_permission_handler.done(): - release_permission_handler.set_result( - PermissionRequestResult(kind="user-not-available") - ) + release_permission_handler.set_result(PermissionDecisionUserNotAvailable()) await _safe_disconnect(session) async def test_should_reject_pending_external_tool_when_suspending(self, ctx: E2ETestContext): diff --git a/python/e2e/test_tools_e2e.py b/python/e2e/test_tools_e2e.py index 4800d97c4..20c202f7f 100644 --- a/python/e2e/test_tools_e2e.py +++ b/python/e2e/test_tools_e2e.py @@ -6,7 +6,8 @@ from pydantic import BaseModel, Field from copilot import define_tool -from copilot.session import PermissionHandler, PermissionRequestResult +from copilot.generated.rpc import PermissionDecisionApproveOnce, PermissionDecisionReject +from copilot.session import PermissionHandler, PermissionNoResult from copilot.tools import Tool, ToolInvocation, ToolResult from .testharness import E2ETestContext, get_final_assistant_message @@ -148,7 +149,7 @@ def safe_lookup(params: LookupParams, invocation: ToolInvocation) -> str: def tracking_handler(request, invocation): nonlocal did_run_permission_request did_run_permission_request = True - return PermissionRequestResult(kind="no-result") + return PermissionNoResult() session = await ctx.client.create_session( on_permission_request=tracking_handler, tools=[safe_lookup] @@ -191,7 +192,7 @@ def encrypt_string(params: EncryptParams, invocation: ToolInvocation) -> str: def on_permission_request(request, invocation): permission_requests.append(request) - return PermissionRequestResult(kind="approve-once") + return PermissionDecisionApproveOnce() session = await ctx.client.create_session( on_permission_request=on_permission_request, tools=[encrypt_string] @@ -219,7 +220,7 @@ def encrypt_string(params: EncryptParams, invocation: ToolInvocation) -> str: return params.input.upper() def on_permission_request(request, invocation): - return PermissionRequestResult(kind="reject") + return PermissionDecisionReject() session = await ctx.client.create_session( on_permission_request=on_permission_request, tools=[encrypt_string] diff --git a/python/test_client.py b/python/test_client.py index 8add6975b..a3975620d 100644 --- a/python/test_client.py +++ b/python/test_client.py @@ -19,7 +19,7 @@ ModelSupports, SubprocessConfig, ) -from copilot.session import PermissionHandler, PermissionRequestResult +from copilot.session import PermissionHandler, PermissionNoResult from e2e.testharness import CLI_PATH @@ -50,15 +50,19 @@ async def test_v2_permission_adapter_rejects_no_result(self): await client.start() try: session = await client.create_session( - on_permission_request=lambda request, invocation: PermissionRequestResult( - kind="no-result" - ) + on_permission_request=lambda request, invocation: PermissionNoResult() ) with pytest.raises(ValueError, match="protocol v2 server"): await client._handle_permission_request_v2( { "sessionId": session.session_id, - "permissionRequest": {"kind": "write"}, + "permissionRequest": { + "kind": "write", + "canOfferSessionApproval": True, + "diff": "", + "fileName": "test.txt", + "intention": "test", + }, } ) finally: diff --git a/python/test_event_forward_compatibility.py b/python/test_event_forward_compatibility.py index 25b5cc2bc..8950f839b 100644 --- a/python/test_event_forward_compatibility.py +++ b/python/test_event_forward_compatibility.py @@ -17,10 +17,8 @@ ElicitationCompletedAction, ElicitationRequestedMode, ElicitationRequestedSchema, - PermissionPromptRequest, - PermissionPromptRequestKind, - PermissionRequest, - PermissionRequestKind, + PermissionPromptRequestMemory, + PermissionRequestMemory, PermissionRequestMemoryAction, SessionEventType, SessionTaskCompleteData, @@ -149,13 +147,20 @@ def test_missing_optional_fields_remain_none_after_parsing(self): the schema default instead of ``None`` and broke ``from_dict(to_dict(x))`` round-trips for instances where the field was ``None``. """ + from copilot.generated.session_events import ( + _load_PermissionPromptRequest, + _load_PermissionRequest, + ) + # #1141: PermissionRequest.action defaults to None when missing. - request = PermissionRequest.from_dict({"kind": "memory", "fact": "remember this"}) + request = _load_PermissionRequest({"kind": "memory", "fact": "remember this"}) + assert isinstance(request, PermissionRequestMemory) assert request.action is None assert PermissionRequestMemoryAction.STORE.value == "store" # sanity # #1140: PermissionPromptRequest.action defaults to None when missing. - prompt_request = PermissionPromptRequest.from_dict({"kind": "memory"}) + prompt_request = _load_PermissionPromptRequest({"kind": "memory", "fact": "remember this"}) + assert isinstance(prompt_request, PermissionPromptRequestMemory) assert prompt_request.action is None # #1139: SessionTaskCompleteData.summary defaults to None when missing. @@ -175,14 +180,28 @@ def test_optional_fields_round_trip_none(self): task = SessionTaskCompleteData(success=None, summary=None) assert SessionTaskCompleteData.from_dict(task.to_dict()) == task - # #1140: PermissionPromptRequest round-trip with action=None. - prompt = PermissionPromptRequest(kind=PermissionPromptRequestKind.MEMORY) + # #1140: PermissionPromptRequestMemory round-trip with action=None. + prompt = PermissionPromptRequestMemory(fact="test-fact") assert prompt.action is None assert "action" not in prompt.to_dict() - assert PermissionPromptRequest.from_dict(prompt.to_dict()) == prompt + assert PermissionPromptRequestMemory.from_dict(prompt.to_dict()) == prompt - # #1141: PermissionRequest round-trip with action=None. - permission = PermissionRequest(kind=PermissionRequestKind.MEMORY) + # #1141: PermissionRequestMemory round-trip with action=None. + permission = PermissionRequestMemory(fact="test-fact") assert permission.action is None assert "action" not in permission.to_dict() - assert PermissionRequest.from_dict(permission.to_dict()) == permission + assert PermissionRequestMemory.from_dict(permission.to_dict()) == permission + + # PermissionRequest is now a discriminated union; the dispatch loader + # should round-trip via the correct variant class. + from copilot.generated.session_events import _load_PermissionRequest + + round_tripped = _load_PermissionRequest(permission.to_dict()) + assert isinstance(round_tripped, PermissionRequestMemory) + assert round_tripped == permission + # PermissionPromptRequest likewise. + from copilot.generated.session_events import _load_PermissionPromptRequest + + round_tripped_prompt = _load_PermissionPromptRequest(prompt.to_dict()) + assert isinstance(round_tripped_prompt, PermissionPromptRequestMemory) + assert round_tripped_prompt == prompt diff --git a/python/test_rpc_generated.py b/python/test_rpc_generated.py index 5f484add0..5d003da42 100644 --- a/python/test_rpc_generated.py +++ b/python/test_rpc_generated.py @@ -7,7 +7,7 @@ from copilot.generated.rpc import ( CommandsApi, CommandsInvokeRequest, - SlashCommandInvocationResultKind, + SlashCommandTextResult, ) @@ -19,6 +19,6 @@ async def test_commands_invoke_deserializes_slash_command_result(): result = await api.invoke(CommandsInvokeRequest(name="help")) - assert result.kind is SlashCommandInvocationResultKind.TEXT + assert isinstance(result, SlashCommandTextResult) assert result.text == "hello" assert result.markdown is True diff --git a/scripts/codegen/python.ts b/scripts/codegen/python.ts index 52b11ed59..90054cd44 100644 --- a/scripts/codegen/python.ts +++ b/scripts/codegen/python.ts @@ -263,6 +263,412 @@ function postProcessExternalUnionAliasesForPython(code: string, aliases: Map; +} +function postProcessRefBasedDiscriminatedUnionsForPython( + code: string, + definitions: Record, + definitionCollections: DefinitionCollections +): { code: string; unions: ResolvedRefBasedUnion[] } { + interface UnionInfo { + aliasName: string; + variantNames: string[]; + discriminatorProp: string; + dispatch: Array<{ value: string; typeName: string }>; + description: string | undefined; + } + const unions: UnionInfo[] = []; + + for (const [defName, definition] of Object.entries(definitions)) { + const variants = (definition.anyOf ?? definition.oneOf) as JSONSchema7[] | undefined; + if (!Array.isArray(variants) || variants.length < 2) continue; + if (!variants.every((v) => typeof v === "object" && v !== null && typeof v.$ref === "string")) { + continue; + } + + const variantRefNames = variants.map((v) => refTypeName(v.$ref as string, definitionCollections)); + const resolvedVariants = variants.map( + (v) => + resolveObjectSchema(v, definitionCollections) ?? + resolveSchema(v, definitionCollections) ?? + v + ); + if (resolvedVariants.some((rv) => !rv || rv.properties === undefined)) continue; + + const discriminator = findPyDiscriminator(resolvedVariants as JSONSchema7[]); + if (!discriminator) continue; + + const aliasName = toPascalCase(defName); + const dispatch = variants.map((_, i) => { + const discProp = (resolvedVariants[i].properties as Record)[ + discriminator.property + ]; + return { + value: String(discProp.const), + typeName: toPascalCase(variantRefNames[i]), + }; + }); + + unions.push({ + aliasName, + variantNames: variantRefNames.map(toPascalCase), + discriminatorProp: discriminator.property, + dispatch, + description: typeof definition.description === "string" ? definition.description : undefined, + }); + } + + const resolved: ResolvedRefBasedUnion[] = []; + if (unions.length === 0) return { code, unions: resolved }; + + const emittedClassNames = new Set(); + for (const match of code.matchAll(/^class (\w+)[:\(]/gm)) { + emittedClassNames.add(match[1]); + } + const acronymCandidates = (name: string): string[] => { + const substitutions: Array<[RegExp, string]> = [ + [/Api/g, "API"], + [/Mcp/g, "MCP"], + [/Url/g, "URL"], + [/Json/g, "JSON"], + [/Http/g, "HTTP"], + [/Hmac/g, "HMAC"], + [/Tcp/g, "TCP"], + [/Sql/g, "SQL"], + [/Id\b/g, "ID"], + [/Llm/g, "LLM"], + [/Cli/g, "CLI"], + ]; + const results = new Set([name]); + for (const [pattern, replacement] of substitutions) { + for (const existing of [...results]) { + results.add(existing.replace(pattern, replacement)); + } + } + return [...results]; + }; + const resolveActualName = (expected: string): string | undefined => { + for (const candidate of acronymCandidates(expected)) { + if (emittedClassNames.has(candidate)) return candidate; + } + return undefined; + }; + + for (const union of unions) { + const actualAliasName = resolveActualName(union.aliasName); + const actualVariantNames: string[] = []; + const actualDispatch: Array<{ value: string; typeName: string }> = []; + let allResolved = true; + for (let i = 0; i < union.variantNames.length; i++) { + const actual = resolveActualName(union.variantNames[i]); + if (!actual) { + allResolved = false; + break; + } + actualVariantNames.push(actual); + actualDispatch.push({ value: union.dispatch[i].value, typeName: actual }); + } + if (!allResolved || !actualAliasName) { + continue; + } + resolved.push({ + aliasName: actualAliasName, + discriminatorProp: union.discriminatorProp, + dispatch: actualDispatch, + }); + + const lines = code.split("\n"); + let classStart = -1; + for (let i = 0; i < lines.length; i++) { + if (lines[i] === `class ${actualAliasName}:` || lines[i].startsWith(`class ${actualAliasName}(`)) { + classStart = i; + break; + } + } + if (classStart >= 0) { + let blockStart = classStart; + while ( + blockStart > 0 && + (lines[blockStart - 1] === "@dataclass" || /^# /.test(lines[blockStart - 1])) + ) { + blockStart--; + } + let blockEnd = classStart + 1; + while (blockEnd < lines.length) { + const ln = lines[blockEnd]; + if ( + /^class \w/.test(ln) || + /^def \w/.test(ln) || + ln === "@dataclass" || + /^# (?:Experimental|Deprecated|Internal):/.test(ln) + ) { + break; + } + blockEnd++; + } + lines.splice(blockStart, blockEnd - blockStart); + code = lines.join("\n"); + } + + const aliasLine = union.description + ? `# ${union.description.replace(/\n/g, " ")}\n${actualAliasName} = ${actualVariantNames.join(" | ")}` + : `${actualAliasName} = ${actualVariantNames.join(" | ")}`; + + const dispatcherLines: string[] = []; + dispatcherLines.push(`def _load_${actualAliasName}(obj: Any) -> "${actualAliasName}":`); + dispatcherLines.push(` assert isinstance(obj, dict)`); + dispatcherLines.push(` kind = obj.get(${JSON.stringify(union.discriminatorProp)})`); + dispatcherLines.push(` match kind:`); + for (const m of actualDispatch) { + dispatcherLines.push(` case ${JSON.stringify(m.value)}: return ${m.typeName}.from_dict(obj)`); + } + dispatcherLines.push( + ` case _: raise ValueError(f"Unknown ${actualAliasName} ${union.discriminatorProp}: {kind!r}")` + ); + + code = `${code.trimEnd()}\n\n\n${aliasLine}\n\n\n${dispatcherLines.join("\n")}\n`; + } + + code = applyUnionRewritesToPython(code, resolved); + return { code, unions: resolved }; +} + +/** + * Rewrite occurrences of `Name.from_dict(...)` to `_load_Name(...)` and + * `to_class(Name, x)` to `(x).to_dict()` for each union the caller passes in. + * Safe to apply repeatedly — re-running on already-rewritten code is a no-op. + */ +function applyUnionRewritesToPython(code: string, unions: ResolvedRefBasedUnion[]): string { + for (const union of unions) { + code = code.replace( + new RegExp(`\\b${union.aliasName}\\.from_dict\\b`, "g"), + `_load_${union.aliasName}` + ); + code = code.replace( + new RegExp(`to_class\\(${union.aliasName},\\s*([^,)]+)\\)`, "g"), + `($1).to_dict()` + ); + } + return code; +} + +/** + * For each discriminated-union variant class, replace the dataclass-level + * discriminator field (e.g. ``kind: PermissionDecisionApproveOnceKind``) with + * a class-level constant (e.g. ``kind: ClassVar[str] = "approve-once"``). + * This lets users construct variants without supplying the discriminator + * value (``PermissionDecisionApproveOnce()`` instead of + * ``PermissionDecisionApproveOnce(kind=PermissionDecisionApproveOnceKind.APPROVE_ONCE)``), + * matching the TS / Rust / .NET / Go ergonomics for the same schema. + * + * Also rewrites the generated ``from_dict`` to skip parsing the discriminator + * (the dispatcher routed based on it; the variant class identity carries it) + * and ``to_dict`` to emit the constant directly. + */ +function postProcessDiscriminatorDefaultsForPython( + code: string, + unions: ResolvedRefBasedUnion[] +): string { + // Build variant lookup: variant class name → { prop, value }. + const variantInfo = new Map(); + for (const union of unions) { + for (const d of union.dispatch) { + // First-wins; multiple unions referencing the same variant share a + // discriminator/value pair anyway. + if (!variantInfo.has(d.typeName)) { + variantInfo.set(d.typeName, { prop: union.discriminatorProp, value: d.value }); + } + } + } + if (variantInfo.size === 0) return code; + + const lines = code.split("\n"); + const out: string[] = []; + let usedClassVar = false; + + let i = 0; + while (i < lines.length) { + const line = lines[i]; + const classMatch = line.match(/^class (\w+)[:\(]/); + if (!classMatch) { + out.push(line); + i++; + continue; + } + const className = classMatch[1]; + const info = variantInfo.get(className); + if (!info) { + out.push(line); + i++; + continue; + } + + // Find the bounds of this class block: everything indented under it. + const classStart = i; + let classEnd = i + 1; + while (classEnd < lines.length) { + const ln = lines[classEnd]; + if ( + /^class \w/.test(ln) || + /^def \w/.test(ln) || + ln === "@dataclass" || + /^# (?:Experimental|Deprecated|Internal):/.test(ln) || + ln.startsWith("@dataclass(") + ) { + break; + } + classEnd++; + } + const block = lines.slice(classStart, classEnd); + + // Locate the discriminator field declaration. Quicktype emits + // ` kind: PermissionDecisionApproveOnceKind` while the + // session-events codegen emits ` kind: str` — both match the + // simple `: ` shape (no default value, since the + // field is required in the schema). + const fieldPattern = new RegExp(`^(\\s+)${info.prop}: [\\w\\[\\], ]+$`); + let fieldIdx = -1; + for (let j = 1; j < block.length; j++) { + if (fieldPattern.test(block[j])) { + fieldIdx = j; + break; + } + } + if (fieldIdx < 0) { + // Variant class without an explicit discriminator field — leave alone. + out.push(...block); + i = classEnd; + continue; + } + const fieldIndent = (block[fieldIdx].match(/^(\s+)/) ?? ["", ""])[1]; + const literal = JSON.stringify(info.value); + // Replace the field with a class-level constant. + block[fieldIdx] = `${fieldIndent}${info.prop}: ClassVar[str] = ${literal}`; + usedClassVar = true; + + // Drop any field-trailing docstring lines that immediately followed the + // original field. Quicktype emits """..."""-style block strings; the + // session-events codegen does not emit per-field docstrings. We only + // touch the line at fieldIdx+1 if it's a docstring or blank. + // (Conservative: leave additional lines in place; they don't reference + // the dropped enum.) + + // Rewrite from_dict / to_dict bodies. + for (let j = fieldIdx + 1; j < block.length; j++) { + const ln = block[j]; + + // Drop ` = ...(obj.get(""))` parse line in from_dict. + const propAssignPattern = new RegExp( + `^\\s+${info.prop} = .+\\(obj\\.get\\(${JSON.stringify(info.prop)}\\)\\)` + ); + if (propAssignPattern.test(ln)) { + block[j] = "<<>>"; + continue; + } + + // Drop multi-line constructor kwarg of the form ` kind=kind,` — + // emitted by the session-events codegen when the constructor call + // is broken across lines. + const multilineKwargPattern = new RegExp( + `^\\s+${info.prop}=${info.prop},?\\s*$` + ); + if (multilineKwargPattern.test(ln)) { + block[j] = "<<>>"; + continue; + } + + // Convert `return X(a, prop, b)` (single-line positional) to drop + // the prop arg. Quicktype-emitted constructors are single-line. + const ctorMatch = ln.match(new RegExp(`^(\\s+)return ${className}\\((.*)\\)\\s*$`)); + if (ctorMatch) { + const argList = ctorMatch[2]; + const args = splitTopLevelCommasMulti(argList); + const filtered = args + .map((a) => a.trim()) + .filter((a) => { + const kw = a.match(/^([a-zA-Z_]\w*)\s*=/); + const name = kw ? kw[1] : a; + return name !== info.prop; + }); + block[j] = `${ctorMatch[1]}return ${className}(${filtered.join(", ")})`; + continue; + } + + // Rewrite `result[""] = to_enum(, self.)` to + // emit the class-level constant directly. + const toDictPattern = new RegExp( + `^(\\s+)result\\[${JSON.stringify(info.prop)}\\] = .+` + ); + if (toDictPattern.test(ln)) { + const indent = (ln.match(/^(\s+)/) ?? ["", ""])[1]; + block[j] = `${indent}result[${JSON.stringify(info.prop)}] = self.${info.prop}`; + continue; + } + } + + out.push(...block.filter((l) => l !== "<<>>")); + i = classEnd; + } + + let result = out.join("\n"); + if (usedClassVar) { + result = ensureClassVarImport(result); + } + return result; +} + +function splitTopLevelCommasMulti(s: string): string[] { + const parts: string[] = []; + let depth = 0; + let start = 0; + for (let i = 0; i < s.length; i++) { + const c = s[i]; + if (c === "(" || c === "[" || c === "{") depth++; + else if (c === ")" || c === "]" || c === "}") depth--; + else if (c === "," && depth === 0) { + parts.push(s.slice(start, i)); + start = i + 1; + } + } + parts.push(s.slice(start)); + return parts.filter((p) => p.trim().length > 0); +} + +function ensureClassVarImport(code: string): string { + // Already imported? + if (/\bfrom typing import [^\n]*\bClassVar\b/.test(code)) return code; + return code.replace( + /^from typing import (.+)$/m, + (_match, names) => { + const list = names.split(",").map((n: string) => n.trim()).filter(Boolean); + list.push("ClassVar"); + list.sort(); + return `from typing import ${[...new Set(list)].join(", ")}`; + } + ); +} + function pushPyExperimentalComment(lines: string[], subject: PyExperimentalSubject, indent = ""): void { lines.push(pyExperimentalComment(subject, indent)); } @@ -859,6 +1265,7 @@ interface PyCodegenCtx { usesTimedelta: boolean; usesIntegerTimedelta: boolean; definitions: DefinitionCollections; + refBasedUnions: ResolvedRefBasedUnion[]; } function toEnumMemberName(value: string): string { @@ -969,6 +1376,104 @@ function pyDurationResolvedType(ctx: PyCodegenCtx, isInteger: boolean): PyResolv }; } +/** + * Emit a "$ref-based discriminated union" — a Python equivalent of the + * polymorphic hierarchies that TS / Rust / .NET / Go produce for the same + * schema shape. Given a definition like + * + * "PermissionRequest": { "anyOf": [ {"$ref": "#/.../PermissionRequestShell"}, ... ] } + * + * where every variant is a `$ref` to a sibling definition and the variants + * share a `const` discriminator property (e.g. `kind`), emit each variant as a + * standalone `@dataclass`, plus a union alias and a `from_dict` dispatcher. + * + * Returns the resolved type or `undefined` if the schema doesn't match the + * expected shape (caller falls back to other paths). + */ +function tryEmitPyRefBasedDiscriminatedUnion( + aliasName: string, + resolved: JSONSchema7, + ctx: PyCodegenCtx +): PyResolvedType | undefined { + const variants = (resolved.anyOf ?? resolved.oneOf) as JSONSchema7[] | undefined; + if (!Array.isArray(variants) || variants.length < 2) return undefined; + + const variantRefNames: string[] = []; + for (const v of variants) { + if (!v || typeof v !== "object") return undefined; + const ref = (v as JSONSchema7).$ref; + if (typeof ref !== "string" || !ref.startsWith("#/definitions/")) { + return undefined; + } + variantRefNames.push(refTypeName(ref, ctx.definitions)); + } + + const resolvedVariants = variants.map( + (v) => + resolveObjectSchema(v, ctx.definitions) ?? + resolveSchema(v, ctx.definitions) ?? + (v as JSONSchema7) + ); + if (resolvedVariants.some((rv) => !rv || rv.properties === undefined)) { + return undefined; + } + const discriminator = findPyDiscriminator(resolvedVariants as JSONSchema7[]); + if (!discriminator) return undefined; + + const variantTypeNames: string[] = []; + const dispatch: Array<{ value: string; typeName: string }> = []; + for (let i = 0; i < variants.length; i++) { + const variantTypeName = toPascalCase(variantRefNames[i]); + const variantSchema = resolveObjectSchema(variants[i], ctx.definitions); + if (variantSchema) { + emitPyClass(variantTypeName, variantSchema, ctx, variantSchema.description); + } + variantTypeNames.push(variantTypeName); + const discProp = resolvedVariants[i].properties?.[discriminator.property] as JSONSchema7; + dispatch.push({ value: String(discProp.const), typeName: variantTypeName }); + } + + if (!ctx.aliasesByName.has(aliasName)) { + const lines: string[] = []; + if (resolved.description) { + lines.push(`# ${resolved.description}`); + } + lines.push(`${aliasName} = ${variantTypeNames.join(" | ")}`); + ctx.aliasesByName.add(aliasName); + ctx.aliases.push(lines.join("\n")); + ctx.refBasedUnions.push({ + aliasName, + discriminatorProp: discriminator.property, + dispatch, + }); + } + + const dispatcherName = `_load_${aliasName}`; + if (!ctx.generatedNames.has(dispatcherName)) { + ctx.generatedNames.add(dispatcherName); + const lines: string[] = []; + lines.push(`def ${dispatcherName}(obj: Any) -> "${aliasName}":`); + lines.push(` assert isinstance(obj, dict)`); + lines.push(` kind = obj.get(${JSON.stringify(discriminator.property)})`); + lines.push(` match kind:`); + for (const m of dispatch) { + lines.push( + ` case ${JSON.stringify(m.value)}: return ${m.typeName}.from_dict(obj)` + ); + } + lines.push( + ` case _: raise ValueError(f"Unknown ${aliasName} ${discriminator.property}: {kind!r}")` + ); + ctx.classes.push(lines.join("\n")); + } + + return { + annotation: aliasName, + fromExpr: (expr) => `${dispatcherName}(${expr})`, + toExpr: (expr) => `${expr}.to_dict()`, + }; +} + function isPyBase64StringSchema(schema: JSONSchema7): boolean { return schema.format === "byte" || (schema as Record).contentEncoding === "base64"; } @@ -1228,6 +1733,17 @@ function resolvePyPropertyType( return isRequired ? enumResolved : pyOptionalResolvedType(enumResolved); } + // Emit "$ref"-based discriminated unions as proper Python unions + // (per-variant dataclasses + alias + dispatcher) rather than flat + // merged dataclasses. Matches the polymorphic hierarchies emitted + // by the TS / Rust / .NET / Go SDKs for the same schema shape. + if (resolved.anyOf || resolved.oneOf) { + const unionResolved = tryEmitPyRefBasedDiscriminatedUnion(typeName, resolved, ctx); + if (unionResolved) { + return isRequired ? unionResolved : pyOptionalResolvedType(unionResolved); + } + } + const resolvedObject = resolveObjectSchema(propSchema, ctx.definitions); if (isNamedPyObjectSchema(resolvedObject)) { emitPyClass(typeName, resolvedObject, ctx, resolvedObject.description); @@ -1275,6 +1791,21 @@ function resolvePyPropertyType( if (nonNull.length > 1) { const discriminator = findPyDiscriminator(nonNull); if (discriminator) { + // Prefer the proper per-variant union shape when every variant + // is a `$ref` to a sibling definition. Same rationale as in the + // top-level $ref branch above: matches TS/Rust/.NET/Go. + if (variantSchemas.every((s) => typeof s.$ref === "string")) { + const unionResolved = tryEmitPyRefBasedDiscriminatedUnion( + nestedName, + propSchema, + ctx + ); + if (unionResolved) { + return hasNull || !isRequired + ? pyOptionalResolvedType(unionResolved) + : unionResolved; + } + } emitPyFlatDiscriminatedUnion( nestedName, discriminator.property, @@ -1753,6 +2284,7 @@ export function generatePythonSessionEventsCode(schema: JSONSchema7): string { usesTimedelta: false, usesIntegerTimedelta: false, definitions: collectDefinitionCollections(schema as Record), + refBasedUnions: [], }; for (const variant of variants) { @@ -2082,7 +2614,9 @@ export function generatePythonSessionEventsCode(schema: JSONSchema7): string { out.push(``); out.push(``); - return postProcessPythonSessionEventCode(out.join("\n")); + let finalCode = postProcessPythonSessionEventCode(out.join("\n")); + finalCode = postProcessDiscriminatorDefaultsForPython(finalCode, ctx.refBasedUnions); + return finalCode; } async function generateSessionEvents(schemaPath?: string): Promise { @@ -2221,6 +2755,12 @@ async function generateRpc(schemaPath?: string, sessionEventsSchema?: JSONSchema typesCode = collapsePlaceholderPythonDataclasses(typesCode, knownDefNames); typesCode = postProcessExternalUnionAliasesForPython(typesCode, externalUnionAliases); typesCode = postProcessExternalRefsForPython(typesCode, externalRefs.placeholderNames, externalEnumNames); + const { code: typesCodeAfterUnions, unions: refBasedUnions } = postProcessRefBasedDiscriminatedUnionsForPython( + typesCode, + allDefinitions, + allDefinitionCollections + ); + typesCode = typesCodeAfterUnions; typesCode = modernizePython(typesCode); // Fix quicktype's Enum-suffix renaming: quicktype sometimes renames "Xyz" to @@ -2370,6 +2910,7 @@ async function generateRpc(schemaPath?: string, sessionEventsSchema?: JSONSchema AUTO-GENERATED FILE - DO NOT EDIT Generated from: api.schema.json """ +from __future__ import annotations from typing import TYPE_CHECKING @@ -2461,6 +3002,11 @@ def _patch_model_capabilities(data: dict) -> dict: /(_patch_model_capabilities\(await self\._client\.request\("models\.list"[^)]*\)[^)]*\))/, "$1)", ); + // Apply union rewrites to the assembled code so RPC method wrappers + // generated after the types section also route Name.from_dict / to_class + // through the discriminator dispatcher. + finalCode = applyUnionRewritesToPython(finalCode, refBasedUnions); + finalCode = postProcessDiscriminatorDefaultsForPython(finalCode, refBasedUnions); finalCode = unwrapRedundantPythonLambdas(finalCode); const outPath = await writeGeneratedFile("python/copilot/generated/rpc.py", finalCode); diff --git a/test/scenarios/callbacks/hooks/python/main.py b/test/scenarios/callbacks/hooks/python/main.py index ba224ef24..f7eaf9603 100644 --- a/test/scenarios/callbacks/hooks/python/main.py +++ b/test/scenarios/callbacks/hooks/python/main.py @@ -2,14 +2,14 @@ import os from copilot import CopilotClient from copilot.client import SubprocessConfig -from copilot.session import PermissionRequestResult +from copilot.generated.rpc import PermissionDecisionApproveOnce hook_log: list[str] = [] async def auto_approve_permission(request, invocation): - return PermissionRequestResult(kind="approve-once") + return PermissionDecisionApproveOnce() async def on_session_start(input_data, invocation): diff --git a/test/scenarios/callbacks/permissions/python/main.py b/test/scenarios/callbacks/permissions/python/main.py index 677ca58d0..5455548ba 100644 --- a/test/scenarios/callbacks/permissions/python/main.py +++ b/test/scenarios/callbacks/permissions/python/main.py @@ -2,7 +2,7 @@ import os from copilot import CopilotClient from copilot.client import SubprocessConfig -from copilot.session import PermissionRequestResult +from copilot.generated.rpc import PermissionDecisionApproveOnce # Track which tools requested permission permission_log: list[str] = [] @@ -10,7 +10,7 @@ async def log_permission(request, invocation): permission_log.append(f"approved:{request.tool_name}") - return PermissionRequestResult(kind="approve-once") + return PermissionDecisionApproveOnce() async def auto_approve_tool(input_data, invocation): diff --git a/test/scenarios/callbacks/user-input/python/main.py b/test/scenarios/callbacks/user-input/python/main.py index 07a7eb40e..9e58d6a80 100644 --- a/test/scenarios/callbacks/user-input/python/main.py +++ b/test/scenarios/callbacks/user-input/python/main.py @@ -2,14 +2,14 @@ import os from copilot import CopilotClient from copilot.client import SubprocessConfig -from copilot.session import PermissionRequestResult +from copilot.generated.rpc import PermissionDecisionApproveOnce input_log: list[str] = [] async def auto_approve_permission(request, invocation): - return PermissionRequestResult(kind="approve-once") + return PermissionDecisionApproveOnce() async def auto_approve_tool(input_data, invocation): diff --git a/test/scenarios/tools/skills/python/main.py b/test/scenarios/tools/skills/python/main.py index a6d6bf2c0..e7a78b8e1 100644 --- a/test/scenarios/tools/skills/python/main.py +++ b/test/scenarios/tools/skills/python/main.py @@ -4,7 +4,7 @@ from copilot import CopilotClient from copilot.client import SubprocessConfig -from copilot.session import PermissionRequestResult +from copilot.generated.rpc import PermissionDecisionApproveOnce async def main(): @@ -17,7 +17,7 @@ async def main(): skills_dir = str(Path(__file__).resolve().parent.parent / "sample-skills") session = await client.create_session( - on_permission_request=lambda _, __: PermissionRequestResult(kind="approve-once"), + on_permission_request=lambda _, __: PermissionDecisionApproveOnce(), model="claude-haiku-4.5", skill_directories=[skills_dir], hooks={ diff --git a/test/scenarios/tools/virtual-filesystem/python/main.py b/test/scenarios/tools/virtual-filesystem/python/main.py index 57b197509..21a92698a 100644 --- a/test/scenarios/tools/virtual-filesystem/python/main.py +++ b/test/scenarios/tools/virtual-filesystem/python/main.py @@ -2,7 +2,7 @@ import os from copilot import CopilotClient, define_tool from copilot.client import SubprocessConfig -from copilot.session import PermissionRequestResult +from copilot.generated.rpc import PermissionDecisionApproveOnce from pydantic import BaseModel, Field # In-memory virtual filesystem @@ -40,7 +40,7 @@ def list_files() -> str: async def auto_approve_permission(request, invocation): - return PermissionRequestResult(kind="approve-once") + return PermissionDecisionApproveOnce() async def auto_approve_tool(input_data, invocation): From 56c52a387f03959a8f287c523703b89b04d4daad Mon Sep 17 00:00:00 2001 From: Steve Sanderson Date: Fri, 22 May 2026 11:24:21 +0100 Subject: [PATCH 02/21] Phase A: SessionConfig/ResumeSessionConfig renames - on_exit_plan_mode -> on_exit_plan_mode_request (handler kwarg + TypedDict field) - on_auto_mode_switch -> on_auto_mode_switch_request (handler kwarg + TypedDict field) - CopilotSession.get_messages() -> get_events() (returns SessionEvent[], not messages) - ProviderConfig.max_input_tokens -> max_prompt_tokens (matches wire key maxPromptTokens) Mirrors PR #1357 Phase A (TypeScript SDK API review). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- python/README.md | 2 +- python/copilot/client.py | 36 +++++++++---------- python/copilot/session.py | 14 ++++---- python/e2e/test_error_resilience_e2e.py | 2 +- python/e2e/test_event_fidelity_e2e.py | 2 +- python/e2e/test_mode_handlers_e2e.py | 8 ++--- python/e2e/test_pending_work_resume_e2e.py | 4 +-- python/e2e/test_rpc_event_side_effects_e2e.py | 6 ++-- python/e2e/test_rpc_session_state_e2e.py | 14 ++++---- python/e2e/test_rpc_shell_and_fleet_e2e.py | 2 +- python/e2e/test_session_config_e2e.py | 2 +- python/e2e/test_session_e2e.py | 22 ++++++------ python/e2e/test_session_fs_e2e.py | 2 +- python/e2e/test_streaming_fidelity_e2e.py | 4 +-- python/e2e/testharness/helper.py | 2 +- python/test_client.py | 4 +-- python/test_commands_and_elicitation.py | 12 +++---- 17 files changed, 69 insertions(+), 69 deletions(-) diff --git a/python/README.md b/python/README.md index 3cee037cc..d2de5ece7 100644 --- a/python/README.md +++ b/python/README.md @@ -103,7 +103,7 @@ asyncio.run(main()) - ✅ Full JSON-RPC protocol support - ✅ stdio and TCP transports - ✅ Real-time streaming events -- ✅ Session history with `get_messages()` +- ✅ Session history with `get_events()` - ✅ Type hints throughout - ✅ Async/await native - ✅ Async context manager support for automatic resource cleanup diff --git a/python/copilot/client.py b/python/copilot/client.py index 6527e3cdc..7b68a9c5c 100644 --- a/python/copilot/client.py +++ b/python/copilot/client.py @@ -1377,8 +1377,8 @@ async def create_session( on_event: Callable[[SessionEvent], None] | None = None, commands: list[CommandDefinition] | None = None, on_elicitation_request: ElicitationHandler | None = None, - on_exit_plan_mode: ExitPlanModeHandler | None = None, - on_auto_mode_switch: AutoModeSwitchHandler | None = None, + on_exit_plan_mode_request: ExitPlanModeHandler | None = None, + on_auto_mode_switch_request: AutoModeSwitchHandler | None = None, create_session_fs_handler: CreateSessionFsHandler | None = None, github_token: str | None = None, remote_session: RemoteSessionMode | None = None, @@ -1520,8 +1520,8 @@ async def create_session( # Enable elicitation request callback if handler provided payload["requestElicitation"] = bool(on_elicitation_request) - payload["requestExitPlanMode"] = bool(on_exit_plan_mode) - payload["requestAutoModeSwitch"] = bool(on_auto_mode_switch) + payload["requestExitPlanMode"] = bool(on_exit_plan_mode_request) + payload["requestAutoModeSwitch"] = bool(on_auto_mode_switch_request) # Serialize commands (name + description only) into payload if commands: @@ -1664,10 +1664,10 @@ async def create_session( session._register_user_input_handler(on_user_input_request) if on_elicitation_request: session._register_elicitation_handler(on_elicitation_request) - if on_exit_plan_mode: - session._register_exit_plan_mode_handler(on_exit_plan_mode) - if on_auto_mode_switch: - session._register_auto_mode_switch_handler(on_auto_mode_switch) + if on_exit_plan_mode_request: + session._register_exit_plan_mode_handler(on_exit_plan_mode_request) + if on_auto_mode_switch_request: + session._register_auto_mode_switch_handler(on_auto_mode_switch_request) if hooks: session._register_hooks(hooks) if transform_callbacks: @@ -1756,8 +1756,8 @@ async def resume_session( on_event: Callable[[SessionEvent], None] | None = None, commands: list[CommandDefinition] | None = None, on_elicitation_request: ElicitationHandler | None = None, - on_exit_plan_mode: ExitPlanModeHandler | None = None, - on_auto_mode_switch: AutoModeSwitchHandler | None = None, + on_exit_plan_mode_request: ExitPlanModeHandler | None = None, + on_auto_mode_switch_request: AutoModeSwitchHandler | None = None, create_session_fs_handler: CreateSessionFsHandler | None = None, github_token: str | None = None, remote_session: RemoteSessionMode | None = None, @@ -1914,8 +1914,8 @@ async def resume_session( # Enable elicitation request callback if handler provided payload["requestElicitation"] = bool(on_elicitation_request) - payload["requestExitPlanMode"] = bool(on_exit_plan_mode) - payload["requestAutoModeSwitch"] = bool(on_auto_mode_switch) + payload["requestExitPlanMode"] = bool(on_exit_plan_mode_request) + payload["requestAutoModeSwitch"] = bool(on_auto_mode_switch_request) # Serialize commands (name + description only) into payload if commands: @@ -2017,10 +2017,10 @@ async def resume_session( session._register_user_input_handler(on_user_input_request) if on_elicitation_request: session._register_elicitation_handler(on_elicitation_request) - if on_exit_plan_mode: - session._register_exit_plan_mode_handler(on_exit_plan_mode) - if on_auto_mode_switch: - session._register_auto_mode_switch_handler(on_auto_mode_switch) + if on_exit_plan_mode_request: + session._register_exit_plan_mode_handler(on_exit_plan_mode_request) + if on_auto_mode_switch_request: + session._register_auto_mode_switch_handler(on_auto_mode_switch_request) if hooks: session._register_hooks(hooks) if transform_callbacks: @@ -2548,8 +2548,8 @@ def _convert_provider_to_wire_format( wire_provider["modelId"] = provider["model_id"] if "wire_model" in provider: wire_provider["wireModel"] = provider["wire_model"] - if "max_input_tokens" in provider: - wire_provider["maxPromptTokens"] = provider["max_input_tokens"] + if "max_prompt_tokens" in provider: + wire_provider["maxPromptTokens"] = provider["max_prompt_tokens"] if "max_output_tokens" in provider: wire_provider["maxOutputTokens"] = provider["max_output_tokens"] if "azure" in provider: diff --git a/python/copilot/session.py b/python/copilot/session.py index 4ca33784c..8ef6a9ce8 100644 --- a/python/copilot/session.py +++ b/python/copilot/session.py @@ -937,7 +937,7 @@ class ProviderConfig(TypedDict, total=False): # triggers conversation compaction before sending a request when the prompt # (system message, history, tool definitions, user message) would exceed # this limit. - max_input_tokens: int + max_prompt_tokens: int # Overrides the resolved model's default max output tokens. When hit, the # model stops generating and returns a truncated response. max_output_tokens: int @@ -1027,9 +1027,9 @@ class SessionConfig(TypedDict, total=False): # When provided, the server calls back to this client for form-based UI dialogs. on_elicitation_request: ElicitationHandler # Handler for exit-plan-mode requests from the server. - on_exit_plan_mode: ExitPlanModeHandler + on_exit_plan_mode_request: ExitPlanModeHandler # Handler for auto-mode-switch requests from the server. - on_auto_mode_switch: AutoModeSwitchHandler + on_auto_mode_switch_request: AutoModeSwitchHandler # Handler factory for session-scoped sessionFs operations. create_session_fs_handler: CreateSessionFsHandler @@ -1117,9 +1117,9 @@ class ResumeSessionConfig(TypedDict, total=False): # Handler for elicitation requests from the server. on_elicitation_request: ElicitationHandler # Handler for exit-plan-mode requests from the server. - on_exit_plan_mode: ExitPlanModeHandler + on_exit_plan_mode_request: ExitPlanModeHandler # Handler for auto-mode-switch requests from the server. - on_auto_mode_switch: AutoModeSwitchHandler + on_auto_mode_switch_request: AutoModeSwitchHandler # Handler factory for session-scoped sessionFs operations. create_session_fs_handler: CreateSessionFsHandler @@ -2252,7 +2252,7 @@ async def _handle_hooks_invoke(self, hook_type: str, input_data: Any) -> Any: ) return None - async def get_messages(self) -> list[SessionEvent]: + async def get_events(self) -> list[SessionEvent]: """ Retrieve all events and messages from this session's history. @@ -2267,7 +2267,7 @@ async def get_messages(self) -> list[SessionEvent]: Example: >>> from copilot.generated.session_events import AssistantMessageData - >>> events = await session.get_messages() + >>> events = await session.get_events() >>> for event in events: ... match event.data: ... case AssistantMessageData() as data: diff --git a/python/e2e/test_error_resilience_e2e.py b/python/e2e/test_error_resilience_e2e.py index 4afb78a6e..ab031842c 100644 --- a/python/e2e/test_error_resilience_e2e.py +++ b/python/e2e/test_error_resilience_e2e.py @@ -30,7 +30,7 @@ async def test_should_throw_when_getting_messages_from_disconnected_session( await session.disconnect() with pytest.raises(Exception): - await session.get_messages() + await session.get_events() async def test_should_handle_double_abort_without_error(self, ctx: E2ETestContext): session = await ctx.client.create_session( diff --git a/python/e2e/test_event_fidelity_e2e.py b/python/e2e/test_event_fidelity_e2e.py index 17193a308..b85609640 100644 --- a/python/e2e/test_event_fidelity_e2e.py +++ b/python/e2e/test_event_fidelity_e2e.py @@ -207,7 +207,7 @@ async def test_should_preserve_message_order_in_getmessages_after_tool_use( try: await session.send_and_wait("Read the file 'order.txt' and tell me what the number is.") - messages = await session.get_messages() + messages = await session.get_events() types = [m.type.value for m in messages] # Verify complete event ordering contract: diff --git a/python/e2e/test_mode_handlers_e2e.py b/python/e2e/test_mode_handlers_e2e.py index c0e19da13..1557e67a9 100644 --- a/python/e2e/test_mode_handlers_e2e.py +++ b/python/e2e/test_mode_handlers_e2e.py @@ -75,7 +75,7 @@ async def test_should_invoke_exit_plan_mode_handler_when_model_uses_tool( ): exit_plan_mode_requests = [] - async def on_exit_plan_mode(request, invocation): + async def on_exit_plan_mode_request(request, invocation): exit_plan_mode_requests.append(request) assert invocation["session_id"] == session.session_id return { @@ -87,7 +87,7 @@ async def on_exit_plan_mode(request, invocation): session = await mode_ctx.client.create_session( github_token=MODE_HANDLER_TOKEN, on_permission_request=PermissionHandler.approve_all, - on_exit_plan_mode=on_exit_plan_mode, + on_exit_plan_mode_request=on_exit_plan_mode_request, ) try: @@ -139,7 +139,7 @@ async def test_should_invoke_auto_mode_switch_handler_when_rate_limited( ): auto_mode_switch_requests = [] - async def on_auto_mode_switch(request, invocation): + async def on_auto_mode_switch_request(request, invocation): auto_mode_switch_requests.append(request) assert invocation["session_id"] == session.session_id return "yes" @@ -147,7 +147,7 @@ async def on_auto_mode_switch(request, invocation): session = await mode_ctx.client.create_session( github_token=MODE_HANDLER_TOKEN, on_permission_request=PermissionHandler.approve_all, - on_auto_mode_switch=on_auto_mode_switch, + on_auto_mode_switch_request=on_auto_mode_switch_request, ) try: diff --git a/python/e2e/test_pending_work_resume_e2e.py b/python/e2e/test_pending_work_resume_e2e.py index f2f31835a..63c658dc6 100644 --- a/python/e2e/test_pending_work_resume_e2e.py +++ b/python/e2e/test_pending_work_resume_e2e.py @@ -481,7 +481,7 @@ async def blocking_external_tool(args): ) # Verify resume event: continue_pending_work=False and session_was_active=True - messages = await session2.get_messages() + messages = await session2.get_events() resume_events = [m for m in messages if isinstance(m.data, SessionResumeData)] assert len(resume_events) == 1, "Expected exactly one session.resume event" resume_event = resume_events[0] @@ -549,7 +549,7 @@ async def test_should_report_continuependingwork_true_in_resume_event( continue_pending_work=True, ) - messages = await resumed_session.get_messages() + messages = await resumed_session.get_events() resume_events = [m for m in messages if isinstance(m.data, SessionResumeData)] assert len(resume_events) == 1, "Expected exactly one session.resume event" resume_event = resume_events[0] diff --git a/python/e2e/test_rpc_event_side_effects_e2e.py b/python/e2e/test_rpc_event_side_effects_e2e.py index b4a5b2790..9725e211a 100644 --- a/python/e2e/test_rpc_event_side_effects_e2e.py +++ b/python/e2e/test_rpc_event_side_effects_e2e.py @@ -215,7 +215,7 @@ async def test_should_emit_snapshot_rewind_event_and_remove_events_on_truncate( try: await session.send_and_wait("Say SNAPSHOT_REWIND_TARGET exactly.", timeout=60.0) - events = await session.get_messages() + events = await session.get_events() user_msgs = [e for e in events if isinstance(e.data, UserMessageData)] assert len(user_msgs) >= 1 first_user_event_id = str(user_msgs[0].id) @@ -236,7 +236,7 @@ def on_event(event): assert evt.data.events_removed >= 1 assert evt.data.up_to_event_id.lower() == first_user_event_id.lower() - messages_after = await session.get_messages() + messages_after = await session.get_events() assert not any(e.id == user_msgs[0].id for e in messages_after) except Exception as exc: if "unhandled method" in str(exc).lower(): @@ -257,7 +257,7 @@ async def test_should_allow_session_use_after_truncate(self, ctx: E2ETestContext try: await session.send_and_wait("Say SNAPSHOT_REWIND_TARGET exactly.", timeout=60.0) - events = await session.get_messages() + events = await session.get_events() user_msgs = [e for e in events if isinstance(e.data, UserMessageData)] assert len(user_msgs) >= 1 first_user_event_id = str(user_msgs[0].id) diff --git a/python/e2e/test_rpc_session_state_e2e.py b/python/e2e/test_rpc_session_state_e2e.py index b7329158c..f5b11f6fa 100644 --- a/python/e2e/test_rpc_session_state_e2e.py +++ b/python/e2e/test_rpc_session_state_e2e.py @@ -171,7 +171,7 @@ async def test_should_fork_session_with_persisted_messages(self, ctx: E2ETestCon assert initial_answer is not None assert "FORK_SOURCE_ALPHA" in (initial_answer.data.content or "") - source_messages = await session.get_messages() + source_messages = await session.get_events() source_conversation = _conversation_messages(source_messages) assert any( role == "user" and content == source_prompt for role, content in source_conversation @@ -192,7 +192,7 @@ async def test_should_fork_session_with_persisted_messages(self, ctx: E2ETestCon on_permission_request=PermissionHandler.approve_all, ) try: - forked_messages = await forked_session.get_messages() + forked_messages = await forked_session.get_events() forked_conversation = _conversation_messages(forked_messages) assert forked_conversation[: len(source_conversation)] == source_conversation @@ -200,10 +200,10 @@ async def test_should_fork_session_with_persisted_messages(self, ctx: E2ETestCon assert fork_answer is not None assert "FORK_CHILD_BETA" in (fork_answer.data.content or "") - source_after_fork = _conversation_messages(await session.get_messages()) + source_after_fork = _conversation_messages(await session.get_events()) assert all(content != fork_prompt for _, content in source_after_fork) - fork_after_prompt = _conversation_messages(await forked_session.get_messages()) + fork_after_prompt = _conversation_messages(await forked_session.get_events()) assert any( role == "user" and content == fork_prompt for role, content in fork_after_prompt ) @@ -241,7 +241,7 @@ async def test_should_handle_forking_session_without_persisted_events( on_permission_request=PermissionHandler.approve_all, ) try: - assert _conversation_messages(await forked_session.get_messages()) == [] + assert _conversation_messages(await forked_session.get_events()) == [] finally: await forked_session.disconnect() finally: @@ -506,7 +506,7 @@ async def test_should_fork_session_to_event_id_excluding_boundary_event( await session.send_and_wait(first_prompt, timeout=60.0) await session.send_and_wait(second_prompt, timeout=60.0) - source_events = await session.get_messages() + source_events = await session.get_events() second_user_event = next( ( e @@ -531,7 +531,7 @@ async def test_should_fork_session_to_event_id_excluding_boundary_event( on_permission_request=PermissionHandler.approve_all, ) try: - forked_events = await forked_session.get_messages() + forked_events = await forked_session.get_events() forked_ids = {str(e.id) for e in forked_events} assert boundary_event_id not in forked_ids, ( "toEventId is exclusive — boundary event must not be in forked session" diff --git a/python/e2e/test_rpc_shell_and_fleet_e2e.py b/python/e2e/test_rpc_shell_and_fleet_e2e.py index c5384825b..32177cbbd 100644 --- a/python/e2e/test_rpc_shell_and_fleet_e2e.py +++ b/python/e2e/test_rpc_shell_and_fleet_e2e.py @@ -128,7 +128,7 @@ def record_fleet_completion(invocation: ToolInvocation) -> ToolResult: async def _wait_for_messages(timeout: float = 120.0): deadline = asyncio.get_event_loop().time() + timeout while asyncio.get_event_loop().time() < deadline: - messages = await session.get_messages() + messages = await session.get_events() if any( isinstance(m.data, AssistantMessageData) and "fleet task" in (m.data.content or "").lower() diff --git a/python/e2e/test_session_config_e2e.py b/python/e2e/test_session_config_e2e.py index 1fd2cd0a2..b018ba6f8 100644 --- a/python/e2e/test_session_config_e2e.py +++ b/python/e2e/test_session_config_e2e.py @@ -171,7 +171,7 @@ async def test_should_use_custom_sessionid(self, ctx: E2ETestContext): ) assert session.session_id == requested_session_id - messages = await session.get_messages() + messages = await session.get_events() assert messages start_event = messages[0] assert isinstance(start_event.data, SessionStartData) diff --git a/python/e2e/test_session_e2e.py b/python/e2e/test_session_e2e.py index d5a0c970e..2e42b8593 100644 --- a/python/e2e/test_session_e2e.py +++ b/python/e2e/test_session_e2e.py @@ -23,7 +23,7 @@ async def test_should_create_and_disconnect_sessions(self, ctx: E2ETestContext): ) assert session.session_id - messages = await session.get_messages() + messages = await session.get_events() assert len(messages) > 0 assert messages[0].type.value == "session.start" assert messages[0].data.session_id == session.session_id @@ -32,7 +32,7 @@ async def test_should_create_and_disconnect_sessions(self, ctx: E2ETestContext): await session.disconnect() with pytest.raises(Exception, match="Session not found"): - await session.get_messages() + await session.get_events() async def test_should_have_stateful_conversation(self, ctx: E2ETestContext): session = await ctx.client.create_session( @@ -194,7 +194,7 @@ async def test_should_handle_multiple_concurrent_sessions(self, ctx: E2ETestCont # All are connected for s in [s1, s2, s3]: - messages = await s.get_messages() + messages = await s.get_events() assert len(messages) > 0 assert messages[0].type.value == "session.start" assert messages[0].data.session_id == s.session_id @@ -203,7 +203,7 @@ async def test_should_handle_multiple_concurrent_sessions(self, ctx: E2ETestCont await asyncio.gather(s1.disconnect(), s2.disconnect(), s3.disconnect()) for s in [s1, s2, s3]: with pytest.raises(Exception, match="Session not found"): - await s.get_messages() + await s.get_events() async def test_should_resume_a_session_using_the_same_client(self, ctx: E2ETestContext): # Create initial session @@ -257,7 +257,7 @@ async def test_should_resume_a_session_using_a_new_client(self, ctx: E2ETestCont ) assert session2.session_id == session_id - messages = await session2.get_messages() + messages = await session2.get_events() message_types = [m.type.value for m in messages] assert "user.message" in message_types assert "session.resume" in message_types @@ -499,7 +499,7 @@ async def test_should_abort_a_session(self, ctx: E2ETestContext): _ = await wait_for_session_idle # The session should still be alive and usable after abort - messages = await session.get_messages() + messages = await session.get_events() assert len(messages) > 0 # Verify an abort event exists in messages @@ -555,7 +555,7 @@ def on_event(event): assert "session.idle" in event_types # Verify the assistant response contains the expected answer. - # session.idle is ephemeral and not in get_messages(), but we already + # session.idle is ephemeral and not in get_events(), but we already # confirmed idle via the live event handler above. assistant_message = await get_final_assistant_message(session, already_idle=True) assert "300" in assistant_message.data.content @@ -696,7 +696,7 @@ async def test_should_send_with_file_attachment(self, ctx: E2ETestContext): ], ) - messages = await session.get_messages() + messages = await session.get_events() user_messages = [m for m in messages if isinstance(m.data, UserMessageData)] assert user_messages attachments = user_messages[-1].data.attachments @@ -734,7 +734,7 @@ async def test_should_send_with_directory_attachment(self, ctx: E2ETestContext): ], ) - messages = await session.get_messages() + messages = await session.get_events() user_messages = [m for m in messages if isinstance(m.data, UserMessageData)] assert user_messages attachments = user_messages[-1].data.attachments @@ -773,7 +773,7 @@ async def test_should_send_with_selection_attachment(self, ctx: E2ETestContext): ], ) - messages = await session.get_messages() + messages = await session.get_events() user_messages = [m for m in messages if isinstance(m.data, UserMessageData)] assert user_messages attachments = user_messages[-1].data.attachments @@ -1085,7 +1085,7 @@ async def test_should_send_with_mode_property(self, ctx: E2ETestContext): mode="plan", # type: ignore[arg-type] ) - messages = await session.get_messages() + messages = await session.get_events() user_messages = [m for m in messages if isinstance(m.data, UserMessageData)] assert user_messages last = user_messages[-1].data diff --git a/python/e2e/test_session_fs_e2e.py b/python/e2e/test_session_fs_e2e.py index 0afb565ef..787f0025b 100644 --- a/python/e2e/test_session_fs_e2e.py +++ b/python/e2e/test_session_fs_e2e.py @@ -171,7 +171,7 @@ def get_big_string() -> str: "Call the get_big_string tool and reply with the word DONE only." ) - messages = await session.get_messages() + messages = await session.get_events() tool_result = find_tool_call_result(messages, "get_big_string") assert tool_result is not None assert f"{SESSION_STATE_PATH}/temp/" in tool_result diff --git a/python/e2e/test_streaming_fidelity_e2e.py b/python/e2e/test_streaming_fidelity_e2e.py index e47fb9911..9b4447bf2 100644 --- a/python/e2e/test_streaming_fidelity_e2e.py +++ b/python/e2e/test_streaming_fidelity_e2e.py @@ -184,8 +184,8 @@ async def test_should_emit_streaming_deltas_with_reasoning_effort_configured( assistant_events = [e for e in events if e.type.value == "assistant.message"] assert len(assistant_events) >= 1, "Expected final assistant.message" - # Check session.start event (from get_messages) has reasoning_effort - all_msgs = await session.get_messages() + # Check session.start event (from get_events) has reasoning_effort + all_msgs = await session.get_events() start_event = next((e for e in all_msgs if isinstance(e.data, SessionStartData)), None) assert start_event is not None, "Expected session.start event" assert start_event.data.reasoning_effort == "high" diff --git a/python/e2e/testharness/helper.py b/python/e2e/testharness/helper.py index c603a8ec5..d64ee00b8 100644 --- a/python/e2e/testharness/helper.py +++ b/python/e2e/testharness/helper.py @@ -65,7 +65,7 @@ def on_event(event): async def _get_existing_final_response(session: CopilotSession, already_idle: bool = False): """Check existing messages for a final response.""" - messages = await session.get_messages() + messages = await session.get_events() # Find last user message final_user_message_index = -1 diff --git a/python/test_client.py b/python/test_client.py index a3975620d..41ba3c664 100644 --- a/python/test_client.py +++ b/python/test_client.py @@ -660,7 +660,7 @@ async def mock_request(method, params): "headers": {"Authorization": "Bearer provider-token"}, "model_id": "gpt-4o", "wire_model": "my-finetune-v3", - "max_input_tokens": 100_000, + "max_prompt_tokens": 100_000, "max_output_tokens": 4096, }, ) @@ -703,7 +703,7 @@ async def mock_request(method, params): "headers": {"Authorization": "Bearer resume-token"}, "model_id": "gpt-4o", "wire_model": "my-finetune-v3", - "max_input_tokens": 100_000, + "max_prompt_tokens": 100_000, "max_output_tokens": 4096, }, ) diff --git a/python/test_commands_and_elicitation.py b/python/test_commands_and_elicitation.py index 470e2f8f3..d031d2967 100644 --- a/python/test_commands_and_elicitation.py +++ b/python/test_commands_and_elicitation.py @@ -494,8 +494,8 @@ def auto_handler( session = await client.create_session( on_permission_request=PermissionHandler.approve_all, - on_exit_plan_mode=exit_handler, - on_auto_mode_switch=auto_handler, + on_exit_plan_mode_request=exit_handler, + on_auto_mode_switch_request=auto_handler, ) assert session is not None @@ -528,8 +528,8 @@ async def mock_request(method, params): await client.resume_session( session.session_id, on_permission_request=PermissionHandler.approve_all, - on_exit_plan_mode=lambda request, invocation: {"approved": True}, - on_auto_mode_switch=lambda request, invocation: "yes", + on_exit_plan_mode_request=lambda request, invocation: {"approved": True}, + on_auto_mode_switch_request=lambda request, invocation: "yes", ) payload = captured["session.resume"] @@ -570,8 +570,8 @@ async def auto_handler( session = await client.create_session( on_permission_request=PermissionHandler.approve_all, - on_exit_plan_mode=exit_handler, - on_auto_mode_switch=auto_handler, + on_exit_plan_mode_request=exit_handler, + on_auto_mode_switch_request=auto_handler, ) exit_result = await client._handle_exit_plan_mode_request( From c252a04686c9cb5d83ae39b54a6b4feede95825a Mon Sep 17 00:00:00 2001 From: Steve Sanderson Date: Fri, 22 May 2026 11:57:16 +0100 Subject: [PATCH 03/21] Phase B: RuntimeConnection discriminated config + CopilotClientOptions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Mirrors PR #1343 Phase 9 (.NET) and PR #1357 Phase I (TypeScript). Removes the flat `SubprocessConfig` / `ExternalServerConfig` split in favour of a `RuntimeConnection` discriminated hierarchy, with process-management options moved to a new `CopilotClientOptions` dataclass. Public API: * `RuntimeConnection` abstract base with static factories `stdio()`, `tcp()`, `uri()`. * `ChildProcessRuntimeConnection` intermediate base carrying `path` and `args` shared by Stdio + Tcp. * `StdioRuntimeConnection`, `TcpRuntimeConnection`, `UriRuntimeConnection` concrete subclasses; pattern-match / `isinstance` on the class to branch on the transport. * `CopilotClientOptions(connection=..., working_directory=..., log_level=..., env=..., github_token=..., base_directory=..., use_logged_in_user=..., telemetry=..., session_fs=..., session_idle_timeout_seconds=..., enable_remote_sessions=...)`. * `CopilotClient(options=CopilotClientOptions(...) | None, *, auto_start=..., on_list_models=...)`. Renames: * `copilot_home` → `base_directory` * `remote` → `enable_remote_sessions` * `CopilotClient.actual_port` → `CopilotClient.runtime_port` * `CopilotClient.on(...)` → `CopilotClient.on_lifecycle(...)` * `tcp_connection_token` moves off `CopilotClientOptions` and onto the variant: `TcpRuntimeConnection.connection_token` / `UriRuntimeConnection.connection_token` * `use_stdio` bool is gone — the variant class IS the discriminator. Migration: * All hand-written code that previously branched on `isinstance(config, ExternalServerConfig)` now branches on the runtime-connection variant via `isinstance(connection, UriRuntimeConnection)` / `StdioRuntimeConnection` etc., giving proper type narrowing in pyright/mypy without relying on Literal-based tagged-union narrowing. * Samples, scenarios, README, and tests updated in lock-step. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- python/README.md | 44 +- python/copilot/__init__.py | 16 +- python/copilot/client.py | 416 ++++--- python/copilot/generated/rpc.py | 1016 +++++++++-------- python/copilot/generated/session_events.py | 368 +++--- python/e2e/test_agent_and_compact_rpc_e2e.py | 27 +- python/e2e/test_client_e2e.py | 59 +- python/e2e/test_client_lifecycle_e2e.py | 23 +- python/e2e/test_client_options_e2e.py | 76 +- python/e2e/test_commands_e2e.py | 21 +- python/e2e/test_connection_token.py | 23 +- python/e2e/test_mode_handlers_e2e.py | 2 +- python/e2e/test_multi_client_e2e.py | 29 +- python/e2e/test_pending_work_resume_e2e.py | 93 +- python/e2e/test_per_session_auth_e2e.py | 8 +- python/e2e/test_rpc_e2e.py | 27 +- python/e2e/test_rpc_server_e2e.py | 9 +- python/e2e/test_session_e2e.py | 7 +- python/e2e/test_session_fs_e2e.py | 24 +- python/e2e/test_session_fs_sqlite_e2e.py | 7 +- python/e2e/test_streaming_fidelity_e2e.py | 11 +- python/e2e/test_subagent_hooks_e2e.py | 6 +- python/e2e/test_suspend_e2e.py | 23 +- python/e2e/test_telemetry_e2e.py | 9 +- .../test_ui_elicitation_multi_client_e2e.py | 29 +- python/e2e/testharness/context.py | 13 +- python/test_client.py | 247 ++-- python/test_commands_and_elicitation.py | 75 +- python/test_telemetry.py | 8 +- .../auth/byok-anthropic/python/main.py | 40 +- test/scenarios/auth/byok-azure/python/main.py | 44 +- .../scenarios/auth/byok-ollama/python/main.py | 39 +- .../scenarios/auth/byok-openai/python/main.py | 30 +- test/scenarios/auth/gh-app/python/main.py | 13 +- .../app-backend-to-server/python/main.py | 9 +- .../bundling/app-direct-server/python/main.py | 18 +- .../bundling/container-proxy/proxy.py | 2 +- .../bundling/container-proxy/python/main.py | 18 +- .../bundling/fully-bundled/python/main.py | 17 +- test/scenarios/callbacks/hooks/python/main.py | 14 +- .../callbacks/permissions/python/main.py | 13 +- .../callbacks/user-input/python/main.py | 14 +- test/scenarios/modes/default/python/main.py | 27 +- test/scenarios/modes/minimal/python/main.py | 37 +- .../prompts/attachments/python/main.py | 13 +- .../prompts/reasoning-effort/python/main.py | 37 +- .../prompts/system-message/python/main.py | 17 +- .../concurrent-sessions/python/main.py | 21 +- .../sessions/infinite-sessions/python/main.py | 41 +- .../sessions/session-resume/python/main.py | 21 +- .../sessions/streaming/python/main.py | 17 +- .../tools/custom-agents/python/main.py | 13 +- .../tools/mcp-servers/python/main.py | 23 +- test/scenarios/tools/no-tools/python/main.py | 17 +- test/scenarios/tools/skills/python/main.py | 12 +- .../tools/tool-filtering/python/main.py | 13 +- .../tools/tool-overrides/python/main.py | 26 +- .../tools/virtual-filesystem/python/main.py | 16 +- .../transport/reconnect/python/main.py | 22 +- test/scenarios/transport/stdio/python/main.py | 17 +- test/scenarios/transport/tcp/python/main.py | 18 +- 61 files changed, 1875 insertions(+), 1520 deletions(-) diff --git a/python/README.md b/python/README.md index d2de5ece7..6d6cb2cba 100644 --- a/python/README.md +++ b/python/README.md @@ -113,7 +113,7 @@ asyncio.run(main()) ### CopilotClient ```python -from copilot import CopilotClient, SubprocessConfig +from copilot import CopilotClient from copilot.session import PermissionHandler async with CopilotClient() as client: @@ -133,40 +133,44 @@ async with CopilotClient() as client: > **Note:** For manual lifecycle management, see [Manual Resource Management](#manual-resource-management) above. ```python -from copilot import CopilotClient, ExternalServerConfig +from copilot import CopilotClient, CopilotClientOptions, RuntimeConnection # Connect to an existing CLI server -client = CopilotClient(ExternalServerConfig(url="localhost:3000")) +client = CopilotClient( + CopilotClientOptions(connection=RuntimeConnection.uri("localhost:3000")) +) ``` **CopilotClient Constructor:** ```python CopilotClient( - config=None, # SubprocessConfig | ExternalServerConfig | None + options=None, # CopilotClientOptions | None *, - auto_start=True, # auto-start server on first use + auto_start=True, # auto-start server on first use on_list_models=None, # custom handler for list_models() ) ``` -**SubprocessConfig** — spawn a local CLI process: +**CopilotClientOptions** — configure the client: -- `cli_path` (str | None): Path to CLI executable (default: `COPILOT_CLI_PATH` env var, or bundled binary) -- `cli_args` (list[str]): Extra arguments for the CLI executable -- `cwd` (str | None): Working directory for CLI process (default: current dir) -- `use_stdio` (bool): Use stdio transport instead of TCP (default: True) -- `port` (int): Server port for TCP mode (default: 0 for random) -- `log_level` (str): Log level (default: "info") -- `env` (dict | None): Environment variables for the CLI process +- `connection` (RuntimeConnection | None): How to reach the runtime. Use + `RuntimeConnection.stdio(...)`, `RuntimeConnection.tcp(...)`, or + `RuntimeConnection.uri(...)`. Defaults to a stdio connection with the bundled binary. +- `working_directory` (str | None): Working directory for the CLI process (default: current dir). +- `log_level` (str): Log level (default: "info"). +- `env` (dict | None): Environment variables for the CLI process. - `github_token` (str | None): GitHub token for authentication. When provided, takes priority over other auth methods. -- `copilot_home` (str | None): Base directory for Copilot data (session state, config, etc.). Sets `COPILOT_HOME` on the spawned CLI process. When `None`, the CLI defaults to `~/.copilot`. Useful in restricted environments where only specific directories are writable. Ignored when using `ExternalServerConfig`. +- `base_directory` (str | None): Base directory for Copilot data (session state, config, etc.). Sets `COPILOT_HOME` on the spawned CLI process. When `None`, the CLI defaults to `~/.copilot`. Useful in restricted environments where only specific directories are writable. Ignored when using a `UriRuntimeConnection`. - `use_logged_in_user` (bool | None): Whether to use logged-in user for authentication (default: True, but False when `github_token` is provided). - `telemetry` (dict | None): OpenTelemetry configuration for the CLI process. Providing this enables telemetry — no separate flag needed. See [Telemetry](#telemetry) below. +- `enable_remote_sessions` (bool): Enable remote/cloud session support (default: False). -**ExternalServerConfig** — connect to an existing CLI server: +**RuntimeConnection variants:** -- `url` (str): Server URL (e.g., `"localhost:8080"`, `"http://127.0.0.1:9000"`, or just `"8080"`). +- `RuntimeConnection.stdio(path=None, args=None)` — spawn a local CLI process and talk over stdio. +- `RuntimeConnection.tcp(port=0, connection_token=None, path=None, args=None)` — spawn a local CLI in TCP mode. +- `RuntimeConnection.uri(url, connection_token=None)` — connect to an existing CLI server (e.g. `"localhost:8080"`). **`CopilotClient.create_session()`:** @@ -197,10 +201,10 @@ await client.set_foreground_session_id("session-123") def on_lifecycle(event): print(f"{event.type}: {event.sessionId}") -unsubscribe = client.on(on_lifecycle) +unsubscribe = client.on_lifecycle(on_lifecycle) # Subscribe to specific event type -unsubscribe = client.on("session.foreground", lambda e: print(f"Foreground: {e.sessionId}")) +unsubscribe = client.on_lifecycle("session.foreground", lambda e: print(f"Foreground: {e.sessionId}")) # Later, to stop receiving events: unsubscribe() @@ -531,9 +535,9 @@ async with await client.create_session( The SDK supports OpenTelemetry for distributed tracing. Provide a `telemetry` config to enable trace export and automatic W3C Trace Context propagation. ```python -from copilot import CopilotClient, SubprocessConfig +from copilot import CopilotClient, CopilotClientOptions -client = CopilotClient(SubprocessConfig( +client = CopilotClient(CopilotClientOptions( telemetry={ "otlp_endpoint": "http://localhost:4318", }, diff --git a/python/copilot/__init__.py b/python/copilot/__init__.py index c7a37ea0b..6ca58b3c3 100644 --- a/python/copilot/__init__.py +++ b/python/copilot/__init__.py @@ -5,16 +5,20 @@ """ from .client import ( + ChildProcessRuntimeConnection, CloudSessionOptions, CloudSessionRepository, CopilotClient, - ExternalServerConfig, + CopilotClientOptions, ModelCapabilitiesOverride, ModelLimitsOverride, ModelSupportsOverride, ModelVisionLimitsOverride, RemoteSessionMode, - SubprocessConfig, + RuntimeConnection, + StdioRuntimeConnection, + TcpRuntimeConnection, + UriRuntimeConnection, ) from .session import ( AutoModeSwitchHandler, @@ -62,10 +66,12 @@ "AutoModeSwitchHandler", "AutoModeSwitchRequest", "AutoModeSwitchResponse", + "ChildProcessRuntimeConnection", "CommandDefinition", "CloudSessionOptions", "CloudSessionRepository", "CopilotClient", + "CopilotClientOptions", "CopilotSession", "CreateSessionFsHandler", "ElicitationHandler", @@ -75,7 +81,6 @@ "ExitPlanModeHandler", "ExitPlanModeRequest", "ExitPlanModeResult", - "ExternalServerConfig", "InputOptions", "ModelCapabilitiesOverride", "ModelLimitsOverride", @@ -83,6 +88,7 @@ "ModelVisionLimitsOverride", "ProviderConfig", "RemoteSessionMode", + "RuntimeConnection", "SessionCapabilities", "SessionFsCapabilities", "SessionFsConfig", @@ -93,11 +99,13 @@ "create_session_fs_adapter", "SessionUiApi", "SessionUiCapabilities", - "SubprocessConfig", + "StdioRuntimeConnection", + "TcpRuntimeConnection", "Tool", "ToolBinaryResult", "ToolInvocation", "ToolResult", + "UriRuntimeConnection", "convert_mcp_call_tool_result", "define_tool", ] diff --git a/python/copilot/client.py b/python/copilot/client.py index 7b68a9c5c..0eca03e55 100644 --- a/python/copilot/client.py +++ b/python/copilot/client.py @@ -25,8 +25,8 @@ import threading import time import uuid -from collections.abc import Awaitable, Callable -from dataclasses import KW_ONLY, dataclass, field +from collections.abc import Awaitable, Callable, Sequence +from dataclasses import dataclass from datetime import UTC, datetime from pathlib import Path from types import TracebackType @@ -156,61 +156,162 @@ class TelemetryConfig(TypedDict, total=False): @dataclass -class SubprocessConfig: - """Config for spawning a local Copilot CLI subprocess. +class RuntimeConnection: + """Discriminated config describing how to reach the Copilot runtime. + + Construct via the static factories :meth:`stdio`, :meth:`tcp`, or + :meth:`uri`. Each factory returns the matching subclass; pattern-match + on the subclass (or :func:`isinstance`) to branch on the transport. Example: - >>> config = SubprocessConfig(github_token="ghp_...") - >>> client = CopilotClient(config) - - >>> # Custom CLI path with TCP transport - >>> config = SubprocessConfig( - ... cli_path="/usr/local/bin/copilot", - ... use_stdio=False, - ... log_level="debug", - ... ) + >>> CopilotClient() # default: stdio with the bundled runtime + >>> CopilotClient(CopilotClientOptions(connection=RuntimeConnection.uri("localhost:3000"))) """ - cli_path: str | None = None - """Path to the Copilot CLI executable. ``None`` uses the bundled binary.""" + @staticmethod + def stdio( + *, + path: str | None = None, + args: Sequence[str] = (), + ) -> StdioRuntimeConnection: + """Spawn a runtime child process and communicate over its stdin/stdout. + + This is the default when no :attr:`CopilotClientOptions.connection` + is supplied. - cli_args: list[str] = field(default_factory=list) - """Extra arguments passed to the CLI executable (inserted before SDK-managed args).""" + Args: + path: Path to the runtime executable. When ``None``, uses the + bundled binary. + args: Extra command-line arguments passed to the runtime process. + """ + return StdioRuntimeConnection(path=path, args=tuple(args)) - _: KW_ONLY + @staticmethod + def tcp( + *, + port: int = 0, + connection_token: str | None = None, + path: str | None = None, + args: Sequence[str] = (), + ) -> TcpRuntimeConnection: + """Spawn a runtime child process listening on a TCP socket. - working_directory: str | None = None - """Working directory for the CLI process. ``None`` uses the current directory.""" + Args: + port: TCP port to listen on. ``0`` (the default) auto-allocates + a free port. If the chosen port is already in use, startup + fails. + connection_token: Optional shared secret the SDK sends to the + spawned runtime to authenticate the TCP connection. When + ``None``, a UUID is generated automatically so the loopback + listener is safe by default. + path: Path to the runtime executable. When ``None``, uses the + bundled binary. + args: Extra command-line arguments passed to the runtime process. + """ + return TcpRuntimeConnection( + path=path, + args=tuple(args), + port=port, + connection_token=connection_token, + ) + + @staticmethod + def uri(url: str, *, connection_token: str | None = None) -> UriRuntimeConnection: + """Connect to an already-running runtime at the given URL. + + Args: + url: URL of the runtime to connect to. Accepts ``"port"``, + ``"host:port"``, or a full URL. + connection_token: Optional shared secret to authenticate the + connection. Required when the server was started with a + token; ignored by legacy servers without ``connect`` support. + """ + return UriRuntimeConnection(url=url, connection_token=connection_token) + + +@dataclass +class ChildProcessRuntimeConnection(RuntimeConnection): + """Base for :class:`RuntimeConnection` variants that spawn a runtime child process. + + Construct via :meth:`RuntimeConnection.stdio` or :meth:`RuntimeConnection.tcp`. + """ + + path: str | None = None + """Path to the runtime executable. ``None`` uses the bundled binary.""" + + args: Sequence[str] = () + """Extra command-line arguments passed to the runtime process.""" + + +@dataclass +class StdioRuntimeConnection(ChildProcessRuntimeConnection): + """Spawns a runtime child process and communicates over its stdin/stdout. + + Construct via :meth:`RuntimeConnection.stdio`. + """ - use_stdio: bool = True - """Use stdio transport (``True``, default) or TCP (``False``).""" - tcp_connection_token: str | None = None - """Connection token for the headless CLI server (TCP only). +@dataclass +class TcpRuntimeConnection(ChildProcessRuntimeConnection): + """Spawns a runtime child process listening on a TCP socket. - Only meaningful when ``use_stdio=False``. When the SDK spawns the CLI in TCP mode and - this is omitted, a UUID is generated automatically so the loopback listener is safe by - default. Combining this with ``use_stdio=True`` raises :class:`ValueError`. + Construct via :meth:`RuntimeConnection.tcp`. """ port: int = 0 - """TCP port for the CLI server (only when ``use_stdio=False``). 0 means random.""" + """TCP port to listen on. ``0`` (the default) auto-allocates a free port.""" + + connection_token: str | None = None + """Shared secret the SDK sends to the spawned runtime. ``None`` auto-generates one.""" + + +@dataclass +class UriRuntimeConnection(RuntimeConnection): + """Connects to an already-running runtime at the specified URL. + + Construct via :meth:`RuntimeConnection.uri`. + """ + + url: str = "" + """URL of the runtime to connect to. Accepts ``"port"``, ``"host:port"``, or a full URL.""" + + connection_token: str | None = None + """Shared secret to authenticate the connection.""" + + +@dataclass +class CopilotClientOptions: + """Configuration options for a :class:`CopilotClient`. + + All process-management options (``working_directory``, ``log_level``, + ``env``, ``github_token``, …) apply only when the SDK spawns the runtime + (stdio / tcp connections). They are ignored when connecting to an + existing runtime via :meth:`RuntimeConnection.uri`. + """ + + connection: RuntimeConnection | None = None + """How to reach the runtime. + + Defaults to :meth:`RuntimeConnection.stdio` with the bundled binary. + """ + + working_directory: str | None = None + """Working directory for the runtime process. ``None`` uses the current directory.""" log_level: LogLevel = "info" - """Log level for the CLI process.""" + """Log level for the runtime process.""" env: dict[str, str] | None = None - """Environment variables for the CLI process. ``None`` inherits the current env.""" + """Environment variables for the runtime process. ``None`` inherits the current env.""" github_token: str | None = None """GitHub token for authentication. Takes priority over other auth methods.""" - copilot_home: str | None = None + base_directory: str | None = None """Base directory for Copilot data (session state, config, etc.). - Sets the ``COPILOT_HOME`` environment variable on the spawned CLI process. - When ``None``, the CLI defaults to ``~/.copilot``. - This option is only used when the SDK spawns the CLI process. + Sets the ``COPILOT_HOME`` environment variable on the spawned runtime. + When ``None``, the runtime defaults to ``~/.copilot``. """ use_logged_in_user: bool | None = None @@ -220,7 +321,7 @@ class SubprocessConfig: """ telemetry: TelemetryConfig | None = None - """OpenTelemetry configuration. Providing this enables telemetry — no separate flag needed.""" + """OpenTelemetry configuration. Providing this enables telemetry.""" session_fs: SessionFsConfig | None = None """Connection-level session filesystem provider configuration.""" @@ -229,40 +330,16 @@ class SubprocessConfig: """Server-wide session idle timeout in seconds. Sessions without activity for this duration are automatically cleaned up. - Set to ``None`` or ``0`` to disable (sessions live indefinitely). - This option is only used when the SDK spawns the CLI process. + Set to ``None`` or ``0`` to disable. """ - remote: bool = False + enable_remote_sessions: bool = False """Enable remote session support (Mission Control integration). When ``True``, sessions in a GitHub repository working directory are accessible from GitHub web and mobile. - This option is only used when the SDK spawns the CLI process. - """ - - -@dataclass -class ExternalServerConfig: - """Config for connecting to an existing Copilot CLI server over TCP. - - Example: - >>> config = ExternalServerConfig(url="localhost:3000") - >>> client = CopilotClient(config) """ - url: str - """Server URL. Supports ``"host:port"``, ``"http://host:port"``, or just ``"port"``.""" - - _: KW_ONLY - - tcp_connection_token: str | None = None - """Connection token sent in the ``connect`` handshake. Required when the server was - started with a token; ignored by legacy servers without ``connect`` support.""" - - session_fs: SessionFsConfig | None = None - """Connection-level session filesystem provider configuration.""" - # ============================================================================ # Response Types @@ -925,12 +1002,14 @@ class CopilotClient: >>> await client.stop() >>> # Or connect to an existing server - >>> client = CopilotClient(ExternalServerConfig(url="localhost:3000")) + >>> client = CopilotClient( + ... CopilotClientOptions(connection=RuntimeConnection.uri("localhost:3000")) + ... ) """ def __init__( self, - config: SubprocessConfig | ExternalServerConfig | None = None, + options: CopilotClientOptions | None = None, *, auto_start: bool = True, on_list_models: Callable[[], list[ModelInfo] | Awaitable[list[ModelInfo]]] | None = None, @@ -939,81 +1018,93 @@ def __init__( Initialize a new CopilotClient. Args: - config: Connection configuration. Pass a :class:`SubprocessConfig` to - spawn a local CLI process, or an :class:`ExternalServerConfig` to - connect to an existing server. Defaults to ``SubprocessConfig()``. + options: Client configuration. Defaults to ``CopilotClientOptions()`` + with a default :meth:`RuntimeConnection.stdio` connection using + the bundled runtime binary. auto_start: Automatically start the connection on first use (default: ``True``). on_list_models: Custom handler for :meth:`list_models`. When provided, - the handler is called instead of querying the CLI server. + the handler is called instead of querying the runtime server. Example: - >>> # Default — spawns CLI server using stdio + >>> # Default — spawns runtime using stdio with the bundled binary >>> client = CopilotClient() >>> - >>> # Connect to an existing server - >>> client = CopilotClient(ExternalServerConfig(url="localhost:3000")) + >>> # Connect to an existing runtime + >>> client = CopilotClient( + ... CopilotClientOptions(connection=RuntimeConnection.uri("localhost:3000")) + ... ) >>> - >>> # Custom CLI path with specific log level + >>> # Custom runtime path with specific log level >>> client = CopilotClient( - ... SubprocessConfig( - ... cli_path="/usr/local/bin/copilot", + ... CopilotClientOptions( + ... connection=RuntimeConnection.stdio(path="/usr/local/bin/copilot"), ... log_level="debug", ... ) ... ) """ - if config is None: - config = SubprocessConfig() + if options is None: + options = CopilotClientOptions() + connection = ( + options.connection if options.connection is not None else RuntimeConnection.stdio() + ) - self._config: SubprocessConfig | ExternalServerConfig = config + self._options: CopilotClientOptions = options + self._connection: RuntimeConnection = connection self._auto_start = auto_start self._on_list_models = on_list_models - # Resolve connection-mode-specific state + # Resolve connection-mode-specific state. self._actual_host: str = "localhost" - self._is_external_server: bool = isinstance(config, ExternalServerConfig) - - if config.tcp_connection_token is not None and len(config.tcp_connection_token) == 0: - raise ValueError("tcp_connection_token must be a non-empty string") - - if isinstance(config, ExternalServerConfig): - self._actual_host, actual_port = self._parse_cli_url(config.url) - self._actual_port: int | None = actual_port - self._effective_connection_token: str | None = config.tcp_connection_token + self._is_external_server: bool = isinstance(connection, UriRuntimeConnection) + + if isinstance(connection, UriRuntimeConnection): + if connection.connection_token is not None and len(connection.connection_token) == 0: + raise ValueError("connection_token must be a non-empty string") + self._actual_host, actual_port = self._parse_cli_url(connection.url) + self._runtime_port: int | None = actual_port + self._effective_connection_token: str | None = connection.connection_token else: - self._actual_port = None - - if config.tcp_connection_token is not None and config.use_stdio: - raise ValueError("tcp_connection_token cannot be used with use_stdio=True") - if config.use_stdio: - self._effective_connection_token = None - elif config.tcp_connection_token is not None: - self._effective_connection_token = config.tcp_connection_token + assert isinstance(connection, ChildProcessRuntimeConnection) + self._runtime_port = None + + if isinstance(connection, TcpRuntimeConnection): + if ( + connection.connection_token is not None + and len(connection.connection_token) == 0 + ): + raise ValueError("connection_token must be a non-empty string") + self._effective_connection_token = ( + connection.connection_token + if connection.connection_token is not None + else str(uuid.uuid4()) + ) else: - self._effective_connection_token = str(uuid.uuid4()) + self._effective_connection_token = None - # Resolve CLI path: explicit > COPILOT_CLI_PATH env var > bundled binary - effective_env = config.env if config.env is not None else os.environ + # Resolve CLI path: explicit > COPILOT_CLI_PATH env var > bundled binary. + effective_env = options.env if options.env is not None else os.environ self._cli_path_source: str | None = "explicit" - if config.cli_path is None: + if connection.path is None: env_cli_path = effective_env.get("COPILOT_CLI_PATH") if env_cli_path: - config.cli_path = env_cli_path + connection.path = env_cli_path self._cli_path_source = "environment" else: bundled_path = _get_bundled_cli_path() if bundled_path: - config.cli_path = bundled_path + connection.path = bundled_path self._cli_path_source = "bundled" else: raise RuntimeError( "Copilot CLI not found. The bundled CLI binary is not available. " - "Ensure you installed a platform-specific wheel, or provide cli_path." + "Ensure you installed a platform-specific wheel, or set " + "RuntimeConnection.stdio(path=...) / RuntimeConnection.tcp(path=...)." ) # Resolve use_logged_in_user default - if config.use_logged_in_user is None: - config.use_logged_in_user = not bool(config.github_token) + if options.use_logged_in_user is None: + options.use_logged_in_user = not bool(options.github_token) self._process: subprocess.Popen | None = None self._client: JsonRpcClient | None = None @@ -1029,9 +1120,9 @@ def __init__( self._lifecycle_handlers_lock = threading.Lock() self._rpc: ServerRpc | None = None self._negotiated_protocol_version: int | None = None - if config.session_fs is not None: - _validate_session_fs_config(config.session_fs) - self._session_fs_config = config.session_fs + if options.session_fs is not None: + _validate_session_fs_config(options.session_fs) + self._session_fs_config = options.session_fs @property def rpc(self) -> ServerRpc: @@ -1041,14 +1132,14 @@ def rpc(self) -> ServerRpc: return self._rpc @property - def actual_port(self) -> int | None: - """The actual TCP port the CLI server is listening on, if using TCP transport. + def runtime_port(self) -> int | None: + """TCP port the runtime is listening on, when using TCP transport. Useful for multi-client scenarios where a second client needs to connect - to the same server. Only available after :meth:`start` completes and + to the same runtime. Only available after :meth:`start` completes and only when not using stdio transport. """ - return self._actual_port + return self._runtime_port def _parse_cli_url(self, url: str) -> tuple[str, int]: """ @@ -1130,7 +1221,7 @@ async def start(self) -> None: """ Start the CLI server and establish a connection. - If connecting to an external server (via :class:`ExternalServerConfig`), + If connecting to an already-running runtime (via :meth:`RuntimeConnection.uri`), only establishes the connection. Otherwise, spawns the CLI server process and then connects. @@ -1287,7 +1378,7 @@ async def stop(self) -> None: self._state = "disconnected" if not self._is_external_server: - self._actual_port = None + self._runtime_port = None if errors: raise ExceptionGroup("errors during CopilotClient.stop()", errors) @@ -1342,7 +1433,7 @@ async def force_stop(self) -> None: self._state = "disconnected" if not self._is_external_server: - self._actual_port = None + self._runtime_port = None async def create_session( self, @@ -2378,14 +2469,14 @@ async def set_foreground_session_id(self, session_id: str) -> None: raise RuntimeError(f"Failed to set foreground session: {error}") @overload - def on(self, handler: SessionLifecycleHandler, /) -> HandlerUnsubcribe: ... + def on_lifecycle(self, handler: SessionLifecycleHandler, /) -> HandlerUnsubcribe: ... @overload - def on( + def on_lifecycle( self, event_type: SessionLifecycleEventType, /, handler: SessionLifecycleHandler ) -> HandlerUnsubcribe: ... - def on( + def on_lifecycle( self, event_type_or_handler: SessionLifecycleEventType | SessionLifecycleHandler, /, @@ -2398,8 +2489,8 @@ def on( or change foreground/background state (in TUI+server mode). Can be called in two ways: - - on(handler): Subscribe to all lifecycle events - - on(event_type, handler): Subscribe to a specific event type + - on_lifecycle(handler): Subscribe to all lifecycle events + - on_lifecycle(event_type, handler): Subscribe to a specific event type Args: event_type_or_handler: Either a specific event type to listen for, @@ -2411,10 +2502,12 @@ def on( Example: >>> # Subscribe to specific event type - >>> unsubscribe = client.on("session.foreground", lambda e: print(e.sessionId)) + >>> unsubscribe = client.on_lifecycle( + ... "session.foreground", lambda e: print(e.sessionId) + ... ) >>> >>> # Subscribe to all events - >>> unsubscribe = client.on(lambda e: print(f"{e.type}: {e.sessionId}")) + >>> unsubscribe = client.on_lifecycle(lambda e: print(f"{e.type}: {e.sessionId}")) >>> >>> # Later, to stop receiving events: >>> unsubscribe() @@ -2446,7 +2539,10 @@ def unsubscribe_typed() -> None: return unsubscribe_typed else: - raise ValueError("Invalid arguments: use on(handler) or on(event_type, handler)") + raise ValueError( + "Invalid arguments: use on_lifecycle(handler) " + "or on_lifecycle(event_type, handler)" + ) def _dispatch_lifecycle_event(self, event: SessionLifecycleEvent) -> None: """Dispatch a lifecycle event to all registered handlers.""" @@ -2608,19 +2704,21 @@ def _convert_default_agent_to_wire_format( return wire async def _start_cli_server(self) -> None: - """ - Start the CLI server process. + """Start the runtime process. - This spawns the CLI server as a subprocess using the configured transport + This spawns the runtime as a subprocess using the configured transport mode (stdio or TCP). Raises: RuntimeError: If the server fails to start or times out. """ - assert isinstance(self._config, SubprocessConfig) - cfg = self._config + assert isinstance(self._connection, ChildProcessRuntimeConnection) + conn = self._connection + opts = self._options + use_stdio = isinstance(conn, StdioRuntimeConnection) + tcp_port = conn.port if isinstance(conn, TcpRuntimeConnection) else 0 - cli_path = cfg.cli_path + cli_path = conn.path assert cli_path is not None # resolved in __init__ # Verify CLI exists @@ -2629,24 +2727,24 @@ async def _start_cli_server(self) -> None: if (cli_path := shutil.which(cli_path)) is None: raise RuntimeError(f"Copilot CLI not found at {original_path}") - # Start with user-provided cli_args, then add SDK-managed args - args = list(cfg.cli_args) + [ + # Start with user-provided args, then add SDK-managed args + args = list(conn.args) + [ "--headless", "--no-auto-update", "--log-level", - cfg.log_level, + opts.log_level, ] # Add auth-related flags - if cfg.github_token: + if opts.github_token: args.extend(["--auth-token-env", "COPILOT_SDK_AUTH_TOKEN"]) - if not cfg.use_logged_in_user: + if not opts.use_logged_in_user: args.append("--no-auto-login") - if cfg.session_idle_timeout_seconds is not None and cfg.session_idle_timeout_seconds > 0: - args.extend(["--session-idle-timeout", str(cfg.session_idle_timeout_seconds)]) + if opts.session_idle_timeout_seconds is not None and opts.session_idle_timeout_seconds > 0: + args.extend(["--session-idle-timeout", str(opts.session_idle_timeout_seconds)]) - if cfg.remote: + if opts.enable_remote_sessions: args.append("--remote") # If cli_path is a .js file, run it with node @@ -2661,28 +2759,28 @@ async def _start_cli_server(self) -> None: "cli_path": cli_path, "executable": args[0], "cli_path_source": self._cli_path_source, - "use_stdio": cfg.use_stdio, - "port": None if cfg.use_stdio else cfg.port, + "use_stdio": use_stdio, + "port": None if use_stdio else tcp_port, }, ) # Get environment variables - if cfg.env is None: + if opts.env is None: env = dict(os.environ) else: - env = dict(cfg.env) + env = dict(opts.env) # Set auth token in environment if provided - if cfg.github_token: - env["COPILOT_SDK_AUTH_TOKEN"] = cfg.github_token + if opts.github_token: + env["COPILOT_SDK_AUTH_TOKEN"] = opts.github_token if self._effective_connection_token: env["COPILOT_CONNECTION_TOKEN"] = self._effective_connection_token - if cfg.copilot_home: - env["COPILOT_HOME"] = cfg.copilot_home + if opts.base_directory: + env["COPILOT_HOME"] = opts.base_directory # Set OpenTelemetry environment variables if telemetry config is provided - telemetry = cfg.telemetry + telemetry = opts.telemetry if telemetry is not None: env["COPILOT_OTEL_ENABLED"] = "true" if "otlp_endpoint" in telemetry: @@ -2701,11 +2799,11 @@ async def _start_cli_server(self) -> None: # On Windows, hide the console window to avoid distracting users in GUI apps creationflags = subprocess.CREATE_NO_WINDOW if sys.platform == "win32" else 0 - cwd = cfg.working_directory or os.getcwd() + cwd = opts.working_directory or os.getcwd() # Choose transport mode spawn_start = time.perf_counter() - if cfg.use_stdio: + if use_stdio: args.append("--stdio") # Use regular Popen with pipes (buffering=0 for unbuffered) self._process = subprocess.Popen( @@ -2719,8 +2817,8 @@ async def _start_cli_server(self) -> None: creationflags=creationflags, ) else: - if cfg.port > 0: - args.extend(["--port", str(cfg.port)]) + if tcp_port > 0: + args.extend(["--port", str(tcp_port)]) self._process = subprocess.Popen( args, stdin=subprocess.DEVNULL, @@ -2738,7 +2836,7 @@ async def _start_cli_server(self) -> None: ) # For stdio mode, we're ready immediately - if cfg.use_stdio: + if use_stdio: return # For TCP mode, wait for port announcement @@ -2757,7 +2855,7 @@ async def read_port(): logger.debug("[CLI] %s", line_str.rstrip()) match = re.search(r"listening on port (\d+)", line_str, re.IGNORECASE) if match: - self._actual_port = int(match.group(1)) + self._runtime_port = int(match.group(1)) return try: @@ -2768,14 +2866,13 @@ async def read_port(): logging.DEBUG, "CopilotClient._start_cli_server TCP port wait complete", port_wait_start, - port=self._actual_port, + port=self._runtime_port, ) except TimeoutError: raise RuntimeError("Timeout waiting for CLI server to start") async def _connect_to_server(self) -> None: - """ - Connect to the CLI server via the configured transport. + """Connect to the runtime via the configured transport. Uses either stdio or TCP based on the client configuration. @@ -2783,8 +2880,7 @@ async def _connect_to_server(self) -> None: RuntimeError: If the connection fails. """ setup_start = time.perf_counter() - use_stdio = isinstance(self._config, SubprocessConfig) and self._config.use_stdio - if use_stdio: + if isinstance(self._connection, StdioRuntimeConnection): await self._connect_via_stdio() else: await self._connect_via_tcp() @@ -2862,7 +2958,7 @@ async def _connect_via_tcp(self) -> None: Raises: RuntimeError: If the server port is not available or connection fails. """ - if not self._actual_port: + if not self._runtime_port: raise RuntimeError("Server port not available") # Create a TCP socket connection with timeout @@ -2878,9 +2974,9 @@ async def _connect_via_tcp(self) -> None: tcp_connect_start = time.perf_counter() logger.info( "CopilotClient._connect_via_tcp connecting to CLI server", - extra={"host": self._actual_host, "port": self._actual_port}, + extra={"host": self._actual_host, "port": self._runtime_port}, ) - sock.connect((self._actual_host, self._actual_port)) + sock.connect((self._actual_host, self._runtime_port)) sock.settimeout(None) # Remove timeout after connection log_timing( logger, @@ -2888,11 +2984,11 @@ async def _connect_via_tcp(self) -> None: "CopilotClient._connect_via_tcp TCP connect complete", tcp_connect_start, host=self._actual_host, - port=self._actual_port, + port=self._runtime_port, ) except OSError as e: raise RuntimeError( - f"Failed to connect to CLI server at {self._actual_host}:{self._actual_port}: {e}" + f"Failed to connect to CLI server at {self._actual_host}:{self._runtime_port}: {e}" ) # Create a file-like wrapper for the socket diff --git a/python/copilot/generated/rpc.py b/python/copilot/generated/rpc.py index 929aa79e6..31c10f66b 100644 --- a/python/copilot/generated/rpc.py +++ b/python/copilot/generated/rpc.py @@ -4,9 +4,23 @@ """ from __future__ import annotations -from typing import ClassVar, TYPE_CHECKING - -from .session_events import AbortReason, EmbeddedBlobResourceContents, EmbeddedTextResourceContents, McpServerSource, McpServerStatus, PermissionPromptRequest, PermissionRule, ReasoningSummary, SessionEvent, SessionMode, ShutdownType, SkillSource, UserToolSessionApproval +from typing import TYPE_CHECKING, ClassVar + +from .session_events import ( + AbortReason, + EmbeddedBlobResourceContents, + EmbeddedTextResourceContents, + McpServerSource, + McpServerStatus, + PermissionPromptRequest, + PermissionRule, + ReasoningSummary, + SessionEvent, + SessionMode, + ShutdownType, + SkillSource, + UserToolSessionApproval, +) if TYPE_CHECKING: from .._jsonrpc import JsonRpcClient @@ -84,7 +98,7 @@ class AbortRequest: """Finite reason code describing why the current turn was aborted""" @staticmethod - def from_dict(obj: Any) -> 'AbortRequest': + def from_dict(obj: Any) -> AbortRequest: assert isinstance(obj, dict) reason = from_union([AbortReason, from_none], obj.get("reason")) return AbortRequest(reason) @@ -107,7 +121,7 @@ class AbortResult: """Error message if the abort failed""" @staticmethod - def from_dict(obj: Any) -> 'AbortResult': + def from_dict(obj: Any) -> AbortResult: assert isinstance(obj, dict) success = from_bool(obj.get("success")) error = from_union([from_str, from_none], obj.get("error")) @@ -128,7 +142,7 @@ class AccountGetQuotaRequest: """ @staticmethod - def from_dict(obj: Any) -> 'AccountGetQuotaRequest': + def from_dict(obj: Any) -> AccountGetQuotaRequest: assert isinstance(obj, dict) git_hub_token = from_union([from_str, from_none], obj.get("gitHubToken")) return AccountGetQuotaRequest(git_hub_token) @@ -168,7 +182,7 @@ class AccountQuotaSnapshot: """Date when the quota resets (ISO 8601 string)""" @staticmethod - def from_dict(obj: Any) -> 'AccountQuotaSnapshot': + def from_dict(obj: Any) -> AccountQuotaSnapshot: assert isinstance(obj, dict) entitlement_requests = from_int(obj.get("entitlementRequests")) is_unlimited_entitlement = from_bool(obj.get("isUnlimitedEntitlement")) @@ -213,7 +227,7 @@ class AgentSelectRequest: """Name of the custom agent to select""" @staticmethod - def from_dict(obj: Any) -> 'AgentSelectRequest': + def from_dict(obj: Any) -> AgentSelectRequest: assert isinstance(obj, dict) name = from_str(obj.get("name")) return AgentSelectRequest(name) @@ -234,7 +248,7 @@ class CopilotUserResponseEndpoints: telemetry: str | None = None @staticmethod - def from_dict(obj: Any) -> 'CopilotUserResponseEndpoints': + def from_dict(obj: Any) -> CopilotUserResponseEndpoints: assert isinstance(obj, dict) api = from_union([from_str, from_none], obj.get("api")) origin_tracker = from_union([from_str, from_none], obj.get("origin-tracker")) @@ -296,7 +310,7 @@ class CommandsHandlePendingCommandRequest: """Error message if the command handler failed""" @staticmethod - def from_dict(obj: Any) -> 'CommandsHandlePendingCommandRequest': + def from_dict(obj: Any) -> CommandsHandlePendingCommandRequest: assert isinstance(obj, dict) request_id = from_str(obj.get("requestId")) error = from_union([from_str, from_none], obj.get("error")) @@ -318,7 +332,7 @@ class CommandsHandlePendingCommandResult: """Whether the command was handled successfully""" @staticmethod - def from_dict(obj: Any) -> 'CommandsHandlePendingCommandResult': + def from_dict(obj: Any) -> CommandsHandlePendingCommandResult: assert isinstance(obj, dict) success = from_bool(obj.get("success")) return CommandsHandlePendingCommandResult(success) @@ -340,7 +354,7 @@ class CommandsInvokeRequest: """Raw input after the command name""" @staticmethod - def from_dict(obj: Any) -> 'CommandsInvokeRequest': + def from_dict(obj: Any) -> CommandsInvokeRequest: assert isinstance(obj, dict) name = from_str(obj.get("name")) input = from_union([from_str, from_none], obj.get("input")) @@ -368,7 +382,7 @@ class CommandsListRequest: """Include enabled user-invocable skills and commands""" @staticmethod - def from_dict(obj: Any) -> 'CommandsListRequest': + def from_dict(obj: Any) -> CommandsListRequest: assert isinstance(obj, dict) include_builtins = from_union([from_bool, from_none], obj.get("includeBuiltins")) include_client_commands = from_union([from_bool, from_none], obj.get("includeClientCommands")) @@ -398,7 +412,7 @@ class CommandsRespondToQueuedCommandRequest: """Result of the queued command execution.""" @staticmethod - def from_dict(obj: Any) -> 'CommandsRespondToQueuedCommandRequest': + def from_dict(obj: Any) -> CommandsRespondToQueuedCommandRequest: assert isinstance(obj, dict) request_id = from_str(obj.get("requestId")) result = _load_QueuedCommandResult(obj.get("result")) @@ -421,7 +435,7 @@ class CommandsRespondToQueuedCommandResult: """ @staticmethod - def from_dict(obj: Any) -> 'CommandsRespondToQueuedCommandResult': + def from_dict(obj: Any) -> CommandsRespondToQueuedCommandResult: assert isinstance(obj, dict) success = from_bool(obj.get("success")) return CommandsRespondToQueuedCommandResult(success) @@ -440,7 +454,7 @@ class ConnectRemoteSessionParams: """Session ID to connect to.""" @staticmethod - def from_dict(obj: Any) -> 'ConnectRemoteSessionParams': + def from_dict(obj: Any) -> ConnectRemoteSessionParams: assert isinstance(obj, dict) session_id = from_str(obj.get("sessionId")) return ConnectRemoteSessionParams(session_id) @@ -459,7 +473,7 @@ class ConnectRequest: """Connection token; required when the server was started with COPILOT_CONNECTION_TOKEN""" @staticmethod - def from_dict(obj: Any) -> 'ConnectRequest': + def from_dict(obj: Any) -> ConnectRequest: assert isinstance(obj, dict) token = from_union([from_str, from_none], obj.get("token")) return ConnectRequest(token) @@ -485,7 +499,7 @@ class ConnectResult: """Server package version""" @staticmethod - def from_dict(obj: Any) -> 'ConnectResult': + def from_dict(obj: Any) -> ConnectResult: assert isinstance(obj, dict) ok = from_bool(obj.get("ok")) protocol_version = from_int(obj.get("protocolVersion")) @@ -521,7 +535,7 @@ class ConnectedRemoteSessionMetadataRepository: """Repository owner or organization login.""" @staticmethod - def from_dict(obj: Any) -> 'ConnectedRemoteSessionMetadataRepository': + def from_dict(obj: Any) -> ConnectedRemoteSessionMetadataRepository: assert isinstance(obj, dict) branch = from_str(obj.get("branch")) name = from_str(obj.get("name")) @@ -569,7 +583,7 @@ class CopilotUserResponseQuotaSnapshotsChat: unlimited: bool | None = None @staticmethod - def from_dict(obj: Any) -> 'CopilotUserResponseQuotaSnapshotsChat': + def from_dict(obj: Any) -> CopilotUserResponseQuotaSnapshotsChat: assert isinstance(obj, dict) entitlement = from_union([from_float, from_none], obj.get("entitlement")) has_quota = from_union([from_bool, from_none], obj.get("has_quota")) @@ -632,7 +646,7 @@ class CopilotUserResponseQuotaSnapshotsCompletions: unlimited: bool | None = None @staticmethod - def from_dict(obj: Any) -> 'CopilotUserResponseQuotaSnapshotsCompletions': + def from_dict(obj: Any) -> CopilotUserResponseQuotaSnapshotsCompletions: assert isinstance(obj, dict) entitlement = from_union([from_float, from_none], obj.get("entitlement")) has_quota = from_union([from_bool, from_none], obj.get("has_quota")) @@ -695,7 +709,7 @@ class CopilotUserResponseQuotaSnapshotsPremiumInteractions: unlimited: bool | None = None @staticmethod - def from_dict(obj: Any) -> 'CopilotUserResponseQuotaSnapshotsPremiumInteractions': + def from_dict(obj: Any) -> CopilotUserResponseQuotaSnapshotsPremiumInteractions: assert isinstance(obj, dict) entitlement = from_union([from_float, from_none], obj.get("entitlement")) has_quota = from_union([from_bool, from_none], obj.get("has_quota")) @@ -754,7 +768,7 @@ class CurrentModel: """ @staticmethod - def from_dict(obj: Any) -> 'CurrentModel': + def from_dict(obj: Any) -> CurrentModel: assert isinstance(obj, dict) model_id = from_union([from_str, from_none], obj.get("modelId")) reasoning_effort = from_union([from_str, from_none], obj.get("reasoningEffort")) @@ -787,7 +801,7 @@ class EnqueueCommandParams: """ @staticmethod - def from_dict(obj: Any) -> 'EnqueueCommandParams': + def from_dict(obj: Any) -> EnqueueCommandParams: assert isinstance(obj, dict) command = from_str(obj.get("command")) return EnqueueCommandParams(command) @@ -808,7 +822,7 @@ class EnqueueCommandResult: """ @staticmethod - def from_dict(obj: Any) -> 'EnqueueCommandResult': + def from_dict(obj: Any) -> EnqueueCommandResult: assert isinstance(obj, dict) queued = from_bool(obj.get("queued")) return EnqueueCommandResult(queued) @@ -844,7 +858,7 @@ class EventLogReleaseInterestResult: """Whether the operation succeeded""" @staticmethod - def from_dict(obj: Any) -> 'EventLogReleaseInterestResult': + def from_dict(obj: Any) -> EventLogReleaseInterestResult: assert isinstance(obj, dict) success = from_bool(obj.get("success")) return EventLogReleaseInterestResult(success) @@ -870,7 +884,7 @@ class EventLogTailResult: """ @staticmethod - def from_dict(obj: Any) -> 'EventLogTailResult': + def from_dict(obj: Any) -> EventLogTailResult: assert isinstance(obj, dict) cursor = from_str(obj.get("cursor")) return EventLogTailResult(cursor) @@ -901,7 +915,7 @@ class ExecuteCommandParams: """Name of the slash command to invoke (without the leading '/').""" @staticmethod - def from_dict(obj: Any) -> 'ExecuteCommandParams': + def from_dict(obj: Any) -> ExecuteCommandParams: assert isinstance(obj, dict) args = from_str(obj.get("args")) command_name = from_str(obj.get("commandName")) @@ -924,7 +938,7 @@ class ExecuteCommandResult: """ @staticmethod - def from_dict(obj: Any) -> 'ExecuteCommandResult': + def from_dict(obj: Any) -> ExecuteCommandResult: assert isinstance(obj, dict) error = from_union([from_str, from_none], obj.get("error")) return ExecuteCommandResult(error) @@ -960,7 +974,7 @@ class ExtensionsDisableRequest: """Source-qualified extension ID to disable""" @staticmethod - def from_dict(obj: Any) -> 'ExtensionsDisableRequest': + def from_dict(obj: Any) -> ExtensionsDisableRequest: assert isinstance(obj, dict) id = from_str(obj.get("id")) return ExtensionsDisableRequest(id) @@ -979,7 +993,7 @@ class ExtensionsEnableRequest: """Source-qualified extension ID to enable""" @staticmethod - def from_dict(obj: Any) -> 'ExtensionsEnableRequest': + def from_dict(obj: Any) -> ExtensionsEnableRequest: assert isinstance(obj, dict) id = from_str(obj.get("id")) return ExtensionsEnableRequest(id) @@ -1039,7 +1053,7 @@ class FleetStartRequest: """Optional user prompt to combine with fleet instructions""" @staticmethod - def from_dict(obj: Any) -> 'FleetStartRequest': + def from_dict(obj: Any) -> FleetStartRequest: assert isinstance(obj, dict) prompt = from_union([from_str, from_none], obj.get("prompt")) return FleetStartRequest(prompt) @@ -1059,7 +1073,7 @@ class FleetStartResult: """Whether fleet mode was successfully activated""" @staticmethod - def from_dict(obj: Any) -> 'FleetStartResult': + def from_dict(obj: Any) -> FleetStartResult: assert isinstance(obj, dict) started = from_bool(obj.get("started")) return FleetStartResult(started) @@ -1078,7 +1092,7 @@ class FolderTrustAddParams: """Folder path to mark as trusted""" @staticmethod - def from_dict(obj: Any) -> 'FolderTrustAddParams': + def from_dict(obj: Any) -> FolderTrustAddParams: assert isinstance(obj, dict) path = from_str(obj.get("path")) return FolderTrustAddParams(path) @@ -1097,7 +1111,7 @@ class FolderTrustCheckParams: """Folder path to check""" @staticmethod - def from_dict(obj: Any) -> 'FolderTrustCheckParams': + def from_dict(obj: Any) -> FolderTrustCheckParams: assert isinstance(obj, dict) path = from_str(obj.get("path")) return FolderTrustCheckParams(path) @@ -1116,7 +1130,7 @@ class FolderTrustCheckResult: """Whether the folder is trusted""" @staticmethod - def from_dict(obj: Any) -> 'FolderTrustCheckResult': + def from_dict(obj: Any) -> FolderTrustCheckResult: assert isinstance(obj, dict) trusted = from_bool(obj.get("trusted")) return FolderTrustCheckResult(trusted) @@ -1138,7 +1152,7 @@ class HandlePendingToolCallResult: """Whether the tool call result was handled successfully""" @staticmethod - def from_dict(obj: Any) -> 'HandlePendingToolCallResult': + def from_dict(obj: Any) -> HandlePendingToolCallResult: assert isinstance(obj, dict) success = from_bool(obj.get("success")) return HandlePendingToolCallResult(success) @@ -1159,7 +1173,7 @@ class HistoryAbortManualCompactionResult: """ @staticmethod - def from_dict(obj: Any) -> 'HistoryAbortManualCompactionResult': + def from_dict(obj: Any) -> HistoryAbortManualCompactionResult: assert isinstance(obj, dict) aborted = from_bool(obj.get("aborted")) return HistoryAbortManualCompactionResult(aborted) @@ -1180,7 +1194,7 @@ class HistoryCancelBackgroundCompactionResult: """ @staticmethod - def from_dict(obj: Any) -> 'HistoryCancelBackgroundCompactionResult': + def from_dict(obj: Any) -> HistoryCancelBackgroundCompactionResult: assert isinstance(obj, dict) cancelled = from_bool(obj.get("cancelled")) return HistoryCancelBackgroundCompactionResult(cancelled) @@ -1214,7 +1228,7 @@ class HistoryCompactContextWindow: """Token count from tool definitions""" @staticmethod - def from_dict(obj: Any) -> 'HistoryCompactContextWindow': + def from_dict(obj: Any) -> HistoryCompactContextWindow: assert isinstance(obj, dict) current_tokens = from_int(obj.get("currentTokens")) messages_length = from_int(obj.get("messagesLength")) @@ -1246,7 +1260,7 @@ class HistoryCompactRequest: """Optional user-provided instructions to focus the compaction summary""" @staticmethod - def from_dict(obj: Any) -> 'HistoryCompactRequest': + def from_dict(obj: Any) -> HistoryCompactRequest: assert isinstance(obj, dict) custom_instructions = from_union([from_str, from_none], obj.get("customInstructions")) return HistoryCompactRequest(custom_instructions) @@ -1268,7 +1282,7 @@ class HistorySummarizeForHandoffResult: """ @staticmethod - def from_dict(obj: Any) -> 'HistorySummarizeForHandoffResult': + def from_dict(obj: Any) -> HistorySummarizeForHandoffResult: assert isinstance(obj, dict) summary = from_str(obj.get("summary")) return HistorySummarizeForHandoffResult(summary) @@ -1287,7 +1301,7 @@ class HistoryTruncateRequest: """Event ID to truncate to. This event and all events after it are removed from the session.""" @staticmethod - def from_dict(obj: Any) -> 'HistoryTruncateRequest': + def from_dict(obj: Any) -> HistoryTruncateRequest: assert isinstance(obj, dict) event_id = from_str(obj.get("eventId")) return HistoryTruncateRequest(event_id) @@ -1306,7 +1320,7 @@ class HistoryTruncateResult: """Number of events that were removed""" @staticmethod - def from_dict(obj: Any) -> 'HistoryTruncateResult': + def from_dict(obj: Any) -> HistoryTruncateResult: assert isinstance(obj, dict) events_removed = from_int(obj.get("eventsRemoved")) return HistoryTruncateResult(events_removed) @@ -1372,7 +1386,7 @@ class LogResult: """The unique identifier of the emitted session event""" @staticmethod - def from_dict(obj: Any) -> 'LogResult': + def from_dict(obj: Any) -> LogResult: assert isinstance(obj, dict) event_id = UUID(obj.get("eventId")) return LogResult(event_id) @@ -1401,7 +1415,7 @@ class LspInitializeRequest: """ @staticmethod - def from_dict(obj: Any) -> 'LspInitializeRequest': + def from_dict(obj: Any) -> LspInitializeRequest: assert isinstance(obj, dict) force = from_union([from_bool, from_none], obj.get("force")) git_root = from_union([from_str, from_none], obj.get("gitRoot")) @@ -1427,7 +1441,7 @@ class MCPCancelSamplingExecutionParams: """The requestId previously passed to executeSampling that should be cancelled""" @staticmethod - def from_dict(obj: Any) -> 'MCPCancelSamplingExecutionParams': + def from_dict(obj: Any) -> MCPCancelSamplingExecutionParams: assert isinstance(obj, dict) request_id = from_str(obj.get("requestId")) return MCPCancelSamplingExecutionParams(request_id) @@ -1450,7 +1464,7 @@ class MCPCancelSamplingExecutionResult: """ @staticmethod - def from_dict(obj: Any) -> 'MCPCancelSamplingExecutionResult': + def from_dict(obj: Any) -> MCPCancelSamplingExecutionResult: assert isinstance(obj, dict) cancelled = from_bool(obj.get("cancelled")) return MCPCancelSamplingExecutionResult(cancelled) @@ -1468,7 +1482,7 @@ class MCPServerConfigHTTPAuth: """Fixed port for the OAuth redirect callback server.""" @staticmethod - def from_dict(obj: Any) -> 'MCPServerConfigHTTPAuth': + def from_dict(obj: Any) -> MCPServerConfigHTTPAuth: assert isinstance(obj, dict) redirect_port = from_union([from_int, from_none], obj.get("redirectPort")) return MCPServerConfigHTTPAuth(redirect_port) @@ -1502,7 +1516,7 @@ class MCPConfigDisableRequest: """ @staticmethod - def from_dict(obj: Any) -> 'MCPConfigDisableRequest': + def from_dict(obj: Any) -> MCPConfigDisableRequest: assert isinstance(obj, dict) names = from_list(from_str, obj.get("names")) return MCPConfigDisableRequest(names) @@ -1522,7 +1536,7 @@ class MCPConfigEnableRequest: """ @staticmethod - def from_dict(obj: Any) -> 'MCPConfigEnableRequest': + def from_dict(obj: Any) -> MCPConfigEnableRequest: assert isinstance(obj, dict) names = from_list(from_str, obj.get("names")) return MCPConfigEnableRequest(names) @@ -1540,7 +1554,7 @@ class MCPConfigRemoveRequest: """Name of the MCP server to remove""" @staticmethod - def from_dict(obj: Any) -> 'MCPConfigRemoveRequest': + def from_dict(obj: Any) -> MCPConfigRemoveRequest: assert isinstance(obj, dict) name = from_str(obj.get("name")) return MCPConfigRemoveRequest(name) @@ -1559,7 +1573,7 @@ class MCPDisableRequest: """Name of the MCP server to disable""" @staticmethod - def from_dict(obj: Any) -> 'MCPDisableRequest': + def from_dict(obj: Any) -> MCPDisableRequest: assert isinstance(obj, dict) server_name = from_str(obj.get("serverName")) return MCPDisableRequest(server_name) @@ -1577,7 +1591,7 @@ class MCPDiscoverRequest: """Working directory used as context for discovery (e.g., plugin resolution)""" @staticmethod - def from_dict(obj: Any) -> 'MCPDiscoverRequest': + def from_dict(obj: Any) -> MCPDiscoverRequest: assert isinstance(obj, dict) working_directory = from_union([from_str, from_none], obj.get("workingDirectory")) return MCPDiscoverRequest(working_directory) @@ -1597,7 +1611,7 @@ class MCPEnableRequest: """Name of the MCP server to enable""" @staticmethod - def from_dict(obj: Any) -> 'MCPEnableRequest': + def from_dict(obj: Any) -> MCPEnableRequest: assert isinstance(obj, dict) server_name = from_str(obj.get("serverName")) return MCPEnableRequest(server_name) @@ -1635,7 +1649,7 @@ class MCPOauthLoginRequest: """ @staticmethod - def from_dict(obj: Any) -> 'MCPOauthLoginRequest': + def from_dict(obj: Any) -> MCPOauthLoginRequest: assert isinstance(obj, dict) server_name = from_str(obj.get("serverName")) callback_success_message = from_union([from_str, from_none], obj.get("callbackSuccessMessage")) @@ -1669,7 +1683,7 @@ class MCPOauthLoginResult: """ @staticmethod - def from_dict(obj: Any) -> 'MCPOauthLoginResult': + def from_dict(obj: Any) -> MCPOauthLoginResult: assert isinstance(obj, dict) authorization_url = from_union([from_str, from_none], obj.get("authorizationUrl")) return MCPOauthLoginResult(authorization_url) @@ -1693,7 +1707,7 @@ class MCPRemoveGitHubResult: """ @staticmethod - def from_dict(obj: Any) -> 'MCPRemoveGitHubResult': + def from_dict(obj: Any) -> MCPRemoveGitHubResult: assert isinstance(obj, dict) removed = from_bool(obj.get("removed")) return MCPRemoveGitHubResult(removed) @@ -1731,7 +1745,7 @@ class MCPServer: """Configuration source: user, workspace, plugin, or builtin""" @staticmethod - def from_dict(obj: Any) -> 'MCPServer': + def from_dict(obj: Any) -> MCPServer: assert isinstance(obj, dict) name = from_str(obj.get("name")) status = McpServerStatus(obj.get("status")) @@ -1800,7 +1814,7 @@ class SessionContextInfo: """Sum of system, conversation and tool-definition tokens""" @staticmethod - def from_dict(obj: Any) -> 'SessionContextInfo': + def from_dict(obj: Any) -> SessionContextInfo: assert isinstance(obj, dict) buffer_tokens = from_int(obj.get("bufferTokens")) compaction_threshold = from_int(obj.get("compactionThreshold")) @@ -1839,7 +1853,7 @@ class MetadataIsProcessingResult: """ @staticmethod - def from_dict(obj: Any) -> 'MetadataIsProcessingResult': + def from_dict(obj: Any) -> MetadataIsProcessingResult: assert isinstance(obj, dict) processing = from_bool(obj.get("processing")) return MetadataIsProcessingResult(processing) @@ -1869,7 +1883,7 @@ class MetadataRecomputeContextTokensResult: """ @staticmethod - def from_dict(obj: Any) -> 'MetadataRecomputeContextTokensResult': + def from_dict(obj: Any) -> MetadataRecomputeContextTokensResult: assert isinstance(obj, dict) messages_token_count = from_int(obj.get("messagesTokenCount")) system_token_count = from_int(obj.get("systemTokenCount")) @@ -1905,7 +1919,7 @@ class MetadataRecordContextChangeResult: session's normal lifecycle (e.g., after a shell command in interactive mode). """ @staticmethod - def from_dict(obj: Any) -> 'MetadataRecordContextChangeResult': + def from_dict(obj: Any) -> MetadataRecordContextChangeResult: assert isinstance(obj, dict) return MetadataRecordContextChangeResult() @@ -1925,7 +1939,7 @@ class MetadataSetWorkingDirectoryRequest: """ @staticmethod - def from_dict(obj: Any) -> 'MetadataSetWorkingDirectoryRequest': + def from_dict(obj: Any) -> MetadataSetWorkingDirectoryRequest: assert isinstance(obj, dict) working_directory = from_str(obj.get("workingDirectory")) return MetadataSetWorkingDirectoryRequest(working_directory) @@ -1947,7 +1961,7 @@ class MetadataSetWorkingDirectoryResult: """Working directory after the update""" @staticmethod - def from_dict(obj: Any) -> 'MetadataSetWorkingDirectoryResult': + def from_dict(obj: Any) -> MetadataSetWorkingDirectoryResult: assert isinstance(obj, dict) working_directory = from_str(obj.get("workingDirectory")) return MetadataSetWorkingDirectoryResult(working_directory) @@ -1980,7 +1994,7 @@ class MetadataSnapshotRemoteMetadataRepository: """The GitHub owner (user or organization) of the target repository.""" @staticmethod - def from_dict(obj: Any) -> 'MetadataSnapshotRemoteMetadataRepository': + def from_dict(obj: Any) -> MetadataSnapshotRemoteMetadataRepository: assert isinstance(obj, dict) branch = from_str(obj.get("branch")) name = from_str(obj.get("name")) @@ -2011,7 +2025,7 @@ class ModeSetRequest: """The session mode the agent is operating in""" @staticmethod - def from_dict(obj: Any) -> 'ModeSetRequest': + def from_dict(obj: Any) -> ModeSetRequest: assert isinstance(obj, dict) mode = SessionMode(obj.get("mode")) return ModeSetRequest(mode) @@ -2042,7 +2056,7 @@ class ModelBillingTokenPrices: """ @staticmethod - def from_dict(obj: Any) -> 'ModelBillingTokenPrices': + def from_dict(obj: Any) -> ModelBillingTokenPrices: assert isinstance(obj, dict) batch_size = from_union([from_int, from_none], obj.get("batchSize")) cache_price = from_union([from_int, from_none], obj.get("cachePrice")) @@ -2076,7 +2090,7 @@ class ModelCapabilitiesLimitsVision: """MIME types the model accepts""" @staticmethod - def from_dict(obj: Any) -> 'ModelCapabilitiesLimitsVision': + def from_dict(obj: Any) -> ModelCapabilitiesLimitsVision: assert isinstance(obj, dict) max_prompt_image_size = from_int(obj.get("max_prompt_image_size")) max_prompt_images = from_int(obj.get("max_prompt_images")) @@ -2101,7 +2115,7 @@ class ModelCapabilitiesSupports: """Whether this model supports vision/image input""" @staticmethod - def from_dict(obj: Any) -> 'ModelCapabilitiesSupports': + def from_dict(obj: Any) -> ModelCapabilitiesSupports: assert isinstance(obj, dict) reasoning_effort = from_union([from_bool, from_none], obj.get("reasoningEffort")) vision = from_union([from_bool, from_none], obj.get("vision")) @@ -2145,7 +2159,7 @@ class ModelCapabilitiesOverrideLimitsVision: """MIME types the model accepts""" @staticmethod - def from_dict(obj: Any) -> 'ModelCapabilitiesOverrideLimitsVision': + def from_dict(obj: Any) -> ModelCapabilitiesOverrideLimitsVision: assert isinstance(obj, dict) max_prompt_image_size = from_union([from_int, from_none], obj.get("max_prompt_image_size")) max_prompt_images = from_union([from_int, from_none], obj.get("max_prompt_images")) @@ -2174,7 +2188,7 @@ class ModelCapabilitiesOverrideSupports: """Whether this model supports vision/image input""" @staticmethod - def from_dict(obj: Any) -> 'ModelCapabilitiesOverrideSupports': + def from_dict(obj: Any) -> ModelCapabilitiesOverrideSupports: assert isinstance(obj, dict) reasoning_effort = from_union([from_bool, from_none], obj.get("reasoningEffort")) vision = from_union([from_bool, from_none], obj.get("vision")) @@ -2199,7 +2213,7 @@ class ModelSetReasoningEffortRequest: """ @staticmethod - def from_dict(obj: Any) -> 'ModelSetReasoningEffortRequest': + def from_dict(obj: Any) -> ModelSetReasoningEffortRequest: assert isinstance(obj, dict) reasoning_effort = from_str(obj.get("reasoningEffort")) return ModelSetReasoningEffortRequest(reasoning_effort) @@ -2220,7 +2234,7 @@ class ModelSetReasoningEffortResult: """Reasoning effort level recorded on the session after the update""" @staticmethod - def from_dict(obj: Any) -> 'ModelSetReasoningEffortResult': + def from_dict(obj: Any) -> ModelSetReasoningEffortResult: assert isinstance(obj, dict) reasoning_effort = from_str(obj.get("reasoningEffort")) return ModelSetReasoningEffortResult(reasoning_effort) @@ -2239,7 +2253,7 @@ class ModelSwitchToResult: """Currently active model identifier after the switch""" @staticmethod - def from_dict(obj: Any) -> 'ModelSwitchToResult': + def from_dict(obj: Any) -> ModelSwitchToResult: assert isinstance(obj, dict) model_id = from_union([from_str, from_none], obj.get("modelId")) return ModelSwitchToResult(model_id) @@ -2258,7 +2272,7 @@ class ModelsListRequest: """ @staticmethod - def from_dict(obj: Any) -> 'ModelsListRequest': + def from_dict(obj: Any) -> ModelsListRequest: assert isinstance(obj, dict) git_hub_token = from_union([from_str, from_none], obj.get("gitHubToken")) return ModelsListRequest(git_hub_token) @@ -2278,7 +2292,7 @@ class NameGetResult: """The session name (user-set or auto-generated), or null if not yet set""" @staticmethod - def from_dict(obj: Any) -> 'NameGetResult': + def from_dict(obj: Any) -> NameGetResult: assert isinstance(obj, dict) name = from_union([from_none, from_str], obj.get("name")) return NameGetResult(name) @@ -2300,7 +2314,7 @@ class NameSetAutoRequest: """ @staticmethod - def from_dict(obj: Any) -> 'NameSetAutoRequest': + def from_dict(obj: Any) -> NameSetAutoRequest: assert isinstance(obj, dict) summary = from_str(obj.get("summary")) return NameSetAutoRequest(summary) @@ -2321,7 +2335,7 @@ class NameSetAutoResult: """ @staticmethod - def from_dict(obj: Any) -> 'NameSetAutoResult': + def from_dict(obj: Any) -> NameSetAutoResult: assert isinstance(obj, dict) applied = from_bool(obj.get("applied")) return NameSetAutoResult(applied) @@ -2340,7 +2354,7 @@ class NameSetRequest: """New session name (1–100 characters, trimmed of leading/trailing whitespace)""" @staticmethod - def from_dict(obj: Any) -> 'NameSetRequest': + def from_dict(obj: Any) -> NameSetRequest: assert isinstance(obj, dict) name = from_str(obj.get("name")) return NameSetRequest(name) @@ -2363,7 +2377,7 @@ class PendingPermissionRequest: """Unique identifier for the pending permission request""" @staticmethod - def from_dict(obj: Any) -> 'PendingPermissionRequest': + def from_dict(obj: Any) -> PendingPermissionRequest: assert isinstance(obj, dict) request = PermissionPromptRequest.from_dict(obj.get("request")) request_id = from_str(obj.get("requestId")) @@ -2484,7 +2498,7 @@ class PermissionDecisionRequest: """The client's response to the pending permission prompt""" @staticmethod - def from_dict(obj: Any) -> 'PermissionDecisionRequest': + def from_dict(obj: Any) -> PermissionDecisionRequest: assert isinstance(obj, dict) request_id = from_str(obj.get("requestId")) result = _load_PermissionDecision(obj.get("result")) @@ -2508,7 +2522,7 @@ class PermissionLocationApplyParams: """Working directory whose persisted location permissions should be applied""" @staticmethod - def from_dict(obj: Any) -> 'PermissionLocationApplyParams': + def from_dict(obj: Any) -> PermissionLocationApplyParams: assert isinstance(obj, dict) working_directory = from_str(obj.get("workingDirectory")) return PermissionLocationApplyParams(working_directory) @@ -2534,7 +2548,7 @@ class PermissionLocationResolveParams: """Working directory whose permission location should be resolved""" @staticmethod - def from_dict(obj: Any) -> 'PermissionLocationResolveParams': + def from_dict(obj: Any) -> PermissionLocationResolveParams: assert isinstance(obj, dict) working_directory = from_str(obj.get("workingDirectory")) return PermissionLocationResolveParams(working_directory) @@ -2555,7 +2569,7 @@ class PermissionPathsAddParams: """ @staticmethod - def from_dict(obj: Any) -> 'PermissionPathsAddParams': + def from_dict(obj: Any) -> PermissionPathsAddParams: assert isinstance(obj, dict) path = from_str(obj.get("path")) return PermissionPathsAddParams(path) @@ -2574,7 +2588,7 @@ class PermissionPathsAllowedCheckParams: """Path to check against the session's allowed directories""" @staticmethod - def from_dict(obj: Any) -> 'PermissionPathsAllowedCheckParams': + def from_dict(obj: Any) -> PermissionPathsAllowedCheckParams: assert isinstance(obj, dict) path = from_str(obj.get("path")) return PermissionPathsAllowedCheckParams(path) @@ -2593,7 +2607,7 @@ class PermissionPathsAllowedCheckResult: """Whether the path is within the session's allowed directories""" @staticmethod - def from_dict(obj: Any) -> 'PermissionPathsAllowedCheckResult': + def from_dict(obj: Any) -> PermissionPathsAllowedCheckResult: assert isinstance(obj, dict) allowed = from_bool(obj.get("allowed")) return PermissionPathsAllowedCheckResult(allowed) @@ -2615,7 +2629,7 @@ class PermissionPathsList: """The primary working directory for this session.""" @staticmethod - def from_dict(obj: Any) -> 'PermissionPathsList': + def from_dict(obj: Any) -> PermissionPathsList: assert isinstance(obj, dict) directories = from_list(from_str, obj.get("directories")) primary = from_str(obj.get("primary")) @@ -2636,7 +2650,7 @@ class PermissionPathsUpdatePrimaryParams: """Directory to set as the new primary working directory for the session's permission policy.""" @staticmethod - def from_dict(obj: Any) -> 'PermissionPathsUpdatePrimaryParams': + def from_dict(obj: Any) -> PermissionPathsUpdatePrimaryParams: assert isinstance(obj, dict) path = from_str(obj.get("path")) return PermissionPathsUpdatePrimaryParams(path) @@ -2655,7 +2669,7 @@ class PermissionPathsWorkspaceCheckParams: """Path to check against the session workspace directory""" @staticmethod - def from_dict(obj: Any) -> 'PermissionPathsWorkspaceCheckParams': + def from_dict(obj: Any) -> PermissionPathsWorkspaceCheckParams: assert isinstance(obj, dict) path = from_str(obj.get("path")) return PermissionPathsWorkspaceCheckParams(path) @@ -2674,7 +2688,7 @@ class PermissionPathsWorkspaceCheckResult: """Whether the path is within the session workspace directory""" @staticmethod - def from_dict(obj: Any) -> 'PermissionPathsWorkspaceCheckResult': + def from_dict(obj: Any) -> PermissionPathsWorkspaceCheckResult: assert isinstance(obj, dict) allowed = from_bool(obj.get("allowed")) return PermissionPathsWorkspaceCheckResult(allowed) @@ -2696,7 +2710,7 @@ class PermissionPromptShownNotification: """ @staticmethod - def from_dict(obj: Any) -> 'PermissionPromptShownNotification': + def from_dict(obj: Any) -> PermissionPromptShownNotification: assert isinstance(obj, dict) message = from_str(obj.get("message")) return PermissionPromptShownNotification(message) @@ -2716,7 +2730,7 @@ class PermissionRequestResult: """Whether the permission request was handled successfully""" @staticmethod - def from_dict(obj: Any) -> 'PermissionRequestResult': + def from_dict(obj: Any) -> PermissionRequestResult: assert isinstance(obj, dict) success = from_bool(obj.get("success")) return PermissionRequestResult(success) @@ -2739,7 +2753,7 @@ class PermissionRulesSet: """Rules that auto-deny matching requests""" @staticmethod - def from_dict(obj: Any) -> 'PermissionRulesSet': + def from_dict(obj: Any) -> PermissionRulesSet: assert isinstance(obj, dict) approved = from_list(PermissionRule.from_dict, obj.get("approved")) denied = from_list(PermissionRule.from_dict, obj.get("denied")) @@ -2768,7 +2782,7 @@ class PermissionUrlsConfig: """ @staticmethod - def from_dict(obj: Any) -> 'PermissionUrlsConfig': + def from_dict(obj: Any) -> PermissionUrlsConfig: assert isinstance(obj, dict) initial_allowed = from_union([lambda x: from_list(from_str, x), from_none], obj.get("initialAllowed")) unrestricted = from_union([from_bool, from_none], obj.get("unrestricted")) @@ -2793,7 +2807,7 @@ class PermissionUrlsSetUnrestrictedModeParams: """ @staticmethod - def from_dict(obj: Any) -> 'PermissionUrlsSetUnrestrictedModeParams': + def from_dict(obj: Any) -> PermissionUrlsSetUnrestrictedModeParams: assert isinstance(obj, dict) enabled = from_bool(obj.get("enabled")) return PermissionUrlsSetUnrestrictedModeParams(enabled) @@ -2812,7 +2826,7 @@ class PermissionsConfigureAdditionalContentExclusionPolicyRuleSource: type: str @staticmethod - def from_dict(obj: Any) -> 'PermissionsConfigureAdditionalContentExclusionPolicyRuleSource': + def from_dict(obj: Any) -> PermissionsConfigureAdditionalContentExclusionPolicyRuleSource: assert isinstance(obj, dict) name = from_str(obj.get("name")) type = from_str(obj.get("type")) @@ -2841,7 +2855,7 @@ class PermissionsConfigureResult: """Whether the operation succeeded""" @staticmethod - def from_dict(obj: Any) -> 'PermissionsConfigureResult': + def from_dict(obj: Any) -> PermissionsConfigureResult: assert isinstance(obj, dict) success = from_bool(obj.get("success")) return PermissionsConfigureResult(success) @@ -2860,7 +2874,7 @@ class PermissionsFolderTrustAddTrustedResult: """Whether the operation succeeded""" @staticmethod - def from_dict(obj: Any) -> 'PermissionsFolderTrustAddTrustedResult': + def from_dict(obj: Any) -> PermissionsFolderTrustAddTrustedResult: assert isinstance(obj, dict) success = from_bool(obj.get("success")) return PermissionsFolderTrustAddTrustedResult(success) @@ -2879,7 +2893,7 @@ class PermissionsLocationsAddToolApprovalResult: """Whether the operation succeeded""" @staticmethod - def from_dict(obj: Any) -> 'PermissionsLocationsAddToolApprovalResult': + def from_dict(obj: Any) -> PermissionsLocationsAddToolApprovalResult: assert isinstance(obj, dict) success = from_bool(obj.get("success")) return PermissionsLocationsAddToolApprovalResult(success) @@ -2906,7 +2920,7 @@ class PermissionsModifyRulesResult: """Whether the operation succeeded""" @staticmethod - def from_dict(obj: Any) -> 'PermissionsModifyRulesResult': + def from_dict(obj: Any) -> PermissionsModifyRulesResult: assert isinstance(obj, dict) success = from_bool(obj.get("success")) return PermissionsModifyRulesResult(success) @@ -2925,7 +2939,7 @@ class PermissionsNotifyPromptShownResult: """Whether the operation succeeded""" @staticmethod - def from_dict(obj: Any) -> 'PermissionsNotifyPromptShownResult': + def from_dict(obj: Any) -> PermissionsNotifyPromptShownResult: assert isinstance(obj, dict) success = from_bool(obj.get("success")) return PermissionsNotifyPromptShownResult(success) @@ -2944,7 +2958,7 @@ class PermissionsPathsAddResult: """Whether the operation succeeded""" @staticmethod - def from_dict(obj: Any) -> 'PermissionsPathsAddResult': + def from_dict(obj: Any) -> PermissionsPathsAddResult: assert isinstance(obj, dict) success = from_bool(obj.get("success")) return PermissionsPathsAddResult(success) @@ -2959,7 +2973,7 @@ def to_dict(self) -> dict: class PermissionsPathsListRequest: """No parameters; returns the session's allow-listed directories.""" @staticmethod - def from_dict(obj: Any) -> 'PermissionsPathsListRequest': + def from_dict(obj: Any) -> PermissionsPathsListRequest: assert isinstance(obj, dict) return PermissionsPathsListRequest() @@ -2976,7 +2990,7 @@ class PermissionsPathsUpdatePrimaryResult: """Whether the operation succeeded""" @staticmethod - def from_dict(obj: Any) -> 'PermissionsPathsUpdatePrimaryResult': + def from_dict(obj: Any) -> PermissionsPathsUpdatePrimaryResult: assert isinstance(obj, dict) success = from_bool(obj.get("success")) return PermissionsPathsUpdatePrimaryResult(success) @@ -2991,7 +3005,7 @@ def to_dict(self) -> dict: class PermissionsPendingRequestsRequest: """No parameters; returns currently-pending permission requests for the session.""" @staticmethod - def from_dict(obj: Any) -> 'PermissionsPendingRequestsRequest': + def from_dict(obj: Any) -> PermissionsPendingRequestsRequest: assert isinstance(obj, dict) return PermissionsPendingRequestsRequest() @@ -3004,7 +3018,7 @@ def to_dict(self) -> dict: class PermissionsResetSessionApprovalsRequest: """No parameters; clears all session-scoped tool permission approvals.""" @staticmethod - def from_dict(obj: Any) -> 'PermissionsResetSessionApprovalsRequest': + def from_dict(obj: Any) -> PermissionsResetSessionApprovalsRequest: assert isinstance(obj, dict) return PermissionsResetSessionApprovalsRequest() @@ -3021,7 +3035,7 @@ class PermissionsResetSessionApprovalsResult: """Whether the operation succeeded""" @staticmethod - def from_dict(obj: Any) -> 'PermissionsResetSessionApprovalsResult': + def from_dict(obj: Any) -> PermissionsResetSessionApprovalsResult: assert isinstance(obj, dict) success = from_bool(obj.get("success")) return PermissionsResetSessionApprovalsResult(success) @@ -3040,7 +3054,7 @@ class PermissionsSetApproveAllResult: """Whether the operation succeeded""" @staticmethod - def from_dict(obj: Any) -> 'PermissionsSetApproveAllResult': + def from_dict(obj: Any) -> PermissionsSetApproveAllResult: assert isinstance(obj, dict) success = from_bool(obj.get("success")) return PermissionsSetApproveAllResult(success) @@ -3062,7 +3076,7 @@ class PermissionsSetRequiredRequest: """ @staticmethod - def from_dict(obj: Any) -> 'PermissionsSetRequiredRequest': + def from_dict(obj: Any) -> PermissionsSetRequiredRequest: assert isinstance(obj, dict) required = from_bool(obj.get("required")) return PermissionsSetRequiredRequest(required) @@ -3081,7 +3095,7 @@ class PermissionsSetRequiredResult: """Whether the operation succeeded""" @staticmethod - def from_dict(obj: Any) -> 'PermissionsSetRequiredResult': + def from_dict(obj: Any) -> PermissionsSetRequiredResult: assert isinstance(obj, dict) success = from_bool(obj.get("success")) return PermissionsSetRequiredResult(success) @@ -3100,7 +3114,7 @@ class PermissionsUrlsSetUnrestrictedModeResult: """Whether the operation succeeded""" @staticmethod - def from_dict(obj: Any) -> 'PermissionsUrlsSetUnrestrictedModeResult': + def from_dict(obj: Any) -> PermissionsUrlsSetUnrestrictedModeResult: assert isinstance(obj, dict) success = from_bool(obj.get("success")) return PermissionsUrlsSetUnrestrictedModeResult(success) @@ -3118,7 +3132,7 @@ class PingRequest: """Optional message to echo back""" @staticmethod - def from_dict(obj: Any) -> 'PingRequest': + def from_dict(obj: Any) -> PingRequest: assert isinstance(obj, dict) message = from_union([from_str, from_none], obj.get("message")) return PingRequest(message) @@ -3144,7 +3158,7 @@ class PingResult: """ISO 8601 timestamp when the server handled the ping""" @staticmethod - def from_dict(obj: Any) -> 'PingResult': + def from_dict(obj: Any) -> PingResult: assert isinstance(obj, dict) message = from_str(obj.get("message")) protocol_version = from_int(obj.get("protocolVersion")) @@ -3173,7 +3187,7 @@ class PlanReadResult: """Absolute file path of the plan file, or null if workspace is not enabled""" @staticmethod - def from_dict(obj: Any) -> 'PlanReadResult': + def from_dict(obj: Any) -> PlanReadResult: assert isinstance(obj, dict) exists = from_bool(obj.get("exists")) content = from_union([from_none, from_str], obj.get("content")) @@ -3196,7 +3210,7 @@ class PlanUpdateRequest: """The new content for the plan file""" @staticmethod - def from_dict(obj: Any) -> 'PlanUpdateRequest': + def from_dict(obj: Any) -> PlanUpdateRequest: assert isinstance(obj, dict) content = from_str(obj.get("content")) return PlanUpdateRequest(content) @@ -3224,7 +3238,7 @@ class Plugin: """Installed version""" @staticmethod - def from_dict(obj: Any) -> 'Plugin': + def from_dict(obj: Any) -> Plugin: assert isinstance(obj, dict) enabled = from_bool(obj.get("enabled")) marketplace = from_str(obj.get("marketplace")) @@ -3259,7 +3273,7 @@ class QueueRemoveMostRecentResult: """ @staticmethod - def from_dict(obj: Any) -> 'QueueRemoveMostRecentResult': + def from_dict(obj: Any) -> QueueRemoveMostRecentResult: assert isinstance(obj, dict) removed = from_bool(obj.get("removed")) return QueueRemoveMostRecentResult(removed) @@ -3283,7 +3297,7 @@ class QueuedCommandHandled: """ @staticmethod - def from_dict(obj: Any) -> 'QueuedCommandHandled': + def from_dict(obj: Any) -> QueuedCommandHandled: assert isinstance(obj, dict) stop_processing_queue = from_union([from_bool, from_none], obj.get("stopProcessingQueue")) return QueuedCommandHandled(stop_processing_queue) @@ -3306,7 +3320,7 @@ class QueuedCommandNotHandled: """ @staticmethod - def from_dict(obj: Any) -> 'QueuedCommandNotHandled': + def from_dict(obj: Any) -> QueuedCommandNotHandled: assert isinstance(obj, dict) return QueuedCommandNotHandled() @@ -3337,7 +3351,7 @@ class RegisterEventInterestParams: """ @staticmethod - def from_dict(obj: Any) -> 'RegisterEventInterestParams': + def from_dict(obj: Any) -> RegisterEventInterestParams: assert isinstance(obj, dict) event_type = from_str(obj.get("eventType")) return RegisterEventInterestParams(event_type) @@ -3359,7 +3373,7 @@ class RegisterEventInterestResult: """ @staticmethod - def from_dict(obj: Any) -> 'RegisterEventInterestResult': + def from_dict(obj: Any) -> RegisterEventInterestResult: assert isinstance(obj, dict) handle = from_str(obj.get("handle")) return RegisterEventInterestResult(handle) @@ -3382,7 +3396,7 @@ class ReleaseEventInterestParams: """ @staticmethod - def from_dict(obj: Any) -> 'ReleaseEventInterestParams': + def from_dict(obj: Any) -> ReleaseEventInterestParams: assert isinstance(obj, dict) handle = from_str(obj.get("handle")) return ReleaseEventInterestParams(handle) @@ -3413,7 +3427,7 @@ class RemoteEnableResult: """GitHub frontend URL for this session""" @staticmethod - def from_dict(obj: Any) -> 'RemoteEnableResult': + def from_dict(obj: Any) -> RemoteEnableResult: assert isinstance(obj, dict) remote_steerable = from_bool(obj.get("remoteSteerable")) url = from_union([from_str, from_none], obj.get("url")) @@ -3438,7 +3452,7 @@ class RemoteNotifySteerableChangedRequest: """ @staticmethod - def from_dict(obj: Any) -> 'RemoteNotifySteerableChangedRequest': + def from_dict(obj: Any) -> RemoteNotifySteerableChangedRequest: assert isinstance(obj, dict) remote_steerable = from_bool(obj.get("remoteSteerable")) return RemoteNotifySteerableChangedRequest(remote_steerable) @@ -3456,7 +3470,7 @@ class RemoteNotifySteerableChangedResult: remote exporter that the runtime does not directly own. """ @staticmethod - def from_dict(obj: Any) -> 'RemoteNotifySteerableChangedResult': + def from_dict(obj: Any) -> RemoteNotifySteerableChangedResult: assert isinstance(obj, dict) return RemoteNotifySteerableChangedResult() @@ -3493,7 +3507,7 @@ class ScheduleEntry: """ @staticmethod - def from_dict(obj: Any) -> 'ScheduleEntry': + def from_dict(obj: Any) -> ScheduleEntry: assert isinstance(obj, dict) id = from_int(obj.get("id")) interval_ms = from_int(obj.get("intervalMs")) @@ -3523,7 +3537,7 @@ class ScheduleStopRequest: """Id of the scheduled prompt to remove.""" @staticmethod - def from_dict(obj: Any) -> 'ScheduleStopRequest': + def from_dict(obj: Any) -> ScheduleStopRequest: assert isinstance(obj, dict) id = from_int(obj.get("id")) return ScheduleStopRequest(id) @@ -3541,7 +3555,7 @@ class SecretsAddFilterValuesRequest: """Raw secret values to register for redaction""" @staticmethod - def from_dict(obj: Any) -> 'SecretsAddFilterValuesRequest': + def from_dict(obj: Any) -> SecretsAddFilterValuesRequest: assert isinstance(obj, dict) values = from_list(from_str, obj.get("values")) return SecretsAddFilterValuesRequest(values) @@ -3559,7 +3573,7 @@ class SecretsAddFilterValuesResult: """Whether the values were successfully registered""" @staticmethod - def from_dict(obj: Any) -> 'SecretsAddFilterValuesResult': + def from_dict(obj: Any) -> SecretsAddFilterValuesResult: assert isinstance(obj, dict) ok = from_bool(obj.get("ok")) return SecretsAddFilterValuesResult(ok) @@ -3591,7 +3605,7 @@ class SendAttachmentFileLineRange: """Start line number (1-based)""" @staticmethod - def from_dict(obj: Any) -> 'SendAttachmentFileLineRange': + def from_dict(obj: Any) -> SendAttachmentFileLineRange: assert isinstance(obj, dict) end = from_int(obj.get("end")) start = from_int(obj.get("start")) @@ -3622,7 +3636,7 @@ class SendAttachmentSelectionDetailsEnd: """End line number (0-based)""" @staticmethod - def from_dict(obj: Any) -> 'SendAttachmentSelectionDetailsEnd': + def from_dict(obj: Any) -> SendAttachmentSelectionDetailsEnd: assert isinstance(obj, dict) character = from_int(obj.get("character")) line = from_int(obj.get("line")) @@ -3646,7 +3660,7 @@ class SendAttachmentSelectionDetailsStart: """Start line number (0-based)""" @staticmethod - def from_dict(obj: Any) -> 'SendAttachmentSelectionDetailsStart': + def from_dict(obj: Any) -> SendAttachmentSelectionDetailsStart: assert isinstance(obj, dict) character = from_int(obj.get("character")) line = from_int(obj.get("line")) @@ -3695,7 +3709,7 @@ class SendResult: """Unique identifier assigned to the message""" @staticmethod - def from_dict(obj: Any) -> 'SendResult': + def from_dict(obj: Any) -> SendResult: assert isinstance(obj, dict) message_id = from_str(obj.get("messageId")) return SendResult(message_id) @@ -3731,7 +3745,7 @@ class ServerSkill: """The project path this skill belongs to (only for project/inherited skills)""" @staticmethod - def from_dict(obj: Any) -> 'ServerSkill': + def from_dict(obj: Any) -> ServerSkill: assert isinstance(obj, dict) description = from_str(obj.get("description")) enabled = from_bool(obj.get("enabled")) @@ -3767,7 +3781,7 @@ class SessionBulkDeleteResult: """ @staticmethod - def from_dict(obj: Any) -> 'SessionBulkDeleteResult': + def from_dict(obj: Any) -> SessionBulkDeleteResult: assert isinstance(obj, dict) freed_bytes = from_dict(from_int, obj.get("freedBytes")) return SessionBulkDeleteResult(freed_bytes) @@ -3795,7 +3809,7 @@ class SessionFSAppendFileRequest: """Optional POSIX-style mode for newly created files""" @staticmethod - def from_dict(obj: Any) -> 'SessionFSAppendFileRequest': + def from_dict(obj: Any) -> SessionFSAppendFileRequest: assert isinstance(obj, dict) content = from_str(obj.get("content")) path = from_str(obj.get("path")) @@ -3829,7 +3843,7 @@ class SessionFSExistsRequest: """Target session identifier""" @staticmethod - def from_dict(obj: Any) -> 'SessionFSExistsRequest': + def from_dict(obj: Any) -> SessionFSExistsRequest: assert isinstance(obj, dict) path = from_str(obj.get("path")) session_id = from_str(obj.get("sessionId")) @@ -3849,7 +3863,7 @@ class SessionFSExistsResult: """Whether the path exists""" @staticmethod - def from_dict(obj: Any) -> 'SessionFSExistsResult': + def from_dict(obj: Any) -> SessionFSExistsResult: assert isinstance(obj, dict) exists = from_bool(obj.get("exists")) return SessionFSExistsResult(exists) @@ -3877,7 +3891,7 @@ class SessionFSMkdirRequest: """Create parent directories as needed""" @staticmethod - def from_dict(obj: Any) -> 'SessionFSMkdirRequest': + def from_dict(obj: Any) -> SessionFSMkdirRequest: assert isinstance(obj, dict) path = from_str(obj.get("path")) session_id = from_str(obj.get("sessionId")) @@ -3906,7 +3920,7 @@ class SessionFSReadFileRequest: """Target session identifier""" @staticmethod - def from_dict(obj: Any) -> 'SessionFSReadFileRequest': + def from_dict(obj: Any) -> SessionFSReadFileRequest: assert isinstance(obj, dict) path = from_str(obj.get("path")) session_id = from_str(obj.get("sessionId")) @@ -3929,7 +3943,7 @@ class SessionFSReaddirRequest: """Target session identifier""" @staticmethod - def from_dict(obj: Any) -> 'SessionFSReaddirRequest': + def from_dict(obj: Any) -> SessionFSReaddirRequest: assert isinstance(obj, dict) path = from_str(obj.get("path")) session_id = from_str(obj.get("sessionId")) @@ -3959,7 +3973,7 @@ class SessionFSReaddirWithTypesRequest: """Target session identifier""" @staticmethod - def from_dict(obj: Any) -> 'SessionFSReaddirWithTypesRequest': + def from_dict(obj: Any) -> SessionFSReaddirWithTypesRequest: assert isinstance(obj, dict) path = from_str(obj.get("path")) session_id = from_str(obj.get("sessionId")) @@ -3986,7 +4000,7 @@ class SessionFSRenameRequest: """Source path using SessionFs conventions""" @staticmethod - def from_dict(obj: Any) -> 'SessionFSRenameRequest': + def from_dict(obj: Any) -> SessionFSRenameRequest: assert isinstance(obj, dict) dest = from_str(obj.get("dest")) session_id = from_str(obj.get("sessionId")) @@ -4018,7 +4032,7 @@ class SessionFSRmRequest: """Remove directories and their contents recursively""" @staticmethod - def from_dict(obj: Any) -> 'SessionFSRmRequest': + def from_dict(obj: Any) -> SessionFSRmRequest: assert isinstance(obj, dict) path = from_str(obj.get("path")) session_id = from_str(obj.get("sessionId")) @@ -4044,7 +4058,7 @@ class SessionFSSetProviderCapabilities: """Whether the provider supports SQLite query/exists operations""" @staticmethod - def from_dict(obj: Any) -> 'SessionFSSetProviderCapabilities': + def from_dict(obj: Any) -> SessionFSSetProviderCapabilities: assert isinstance(obj, dict) sqlite = from_union([from_bool, from_none], obj.get("sqlite")) return SessionFSSetProviderCapabilities(sqlite) @@ -4069,7 +4083,7 @@ class SessionFSSetProviderResult: """Whether the provider was set successfully""" @staticmethod - def from_dict(obj: Any) -> 'SessionFSSetProviderResult': + def from_dict(obj: Any) -> SessionFSSetProviderResult: assert isinstance(obj, dict) success = from_bool(obj.get("success")) return SessionFSSetProviderResult(success) @@ -4087,7 +4101,7 @@ class SessionFSSqliteExistsRequest: """Target session identifier""" @staticmethod - def from_dict(obj: Any) -> 'SessionFSSqliteExistsRequest': + def from_dict(obj: Any) -> SessionFSSqliteExistsRequest: assert isinstance(obj, dict) session_id = from_str(obj.get("sessionId")) return SessionFSSqliteExistsRequest(session_id) @@ -4105,7 +4119,7 @@ class SessionFSSqliteExistsResult: """Whether the session database already exists""" @staticmethod - def from_dict(obj: Any) -> 'SessionFSSqliteExistsResult': + def from_dict(obj: Any) -> SessionFSSqliteExistsResult: assert isinstance(obj, dict) exists = from_bool(obj.get("exists")) return SessionFSSqliteExistsResult(exists) @@ -4134,7 +4148,7 @@ class SessionFSStatRequest: """Target session identifier""" @staticmethod - def from_dict(obj: Any) -> 'SessionFSStatRequest': + def from_dict(obj: Any) -> SessionFSStatRequest: assert isinstance(obj, dict) path = from_str(obj.get("path")) session_id = from_str(obj.get("sessionId")) @@ -4163,7 +4177,7 @@ class SessionFSWriteFileRequest: """Optional POSIX-style mode for newly created files""" @staticmethod - def from_dict(obj: Any) -> 'SessionFSWriteFileRequest': + def from_dict(obj: Any) -> SessionFSWriteFileRequest: assert isinstance(obj, dict) content = from_str(obj.get("content")) path = from_str(obj.get("path")) @@ -4198,7 +4212,7 @@ class SessionListFilter: """Match sessions whose context.repository equals this value""" @staticmethod - def from_dict(obj: Any) -> 'SessionListFilter': + def from_dict(obj: Any) -> SessionListFilter: assert isinstance(obj, dict) branch = from_union([from_str, from_none], obj.get("branch")) cwd = from_union([from_str, from_none], obj.get("cwd")) @@ -4233,7 +4247,7 @@ class SessionLoadDeferredRepoHooksResult: """ @staticmethod - def from_dict(obj: Any) -> 'SessionLoadDeferredRepoHooksResult': + def from_dict(obj: Any) -> SessionLoadDeferredRepoHooksResult: assert isinstance(obj, dict) hook_count = from_int(obj.get("hookCount")) startup_prompts = from_list(from_str, obj.get("startupPrompts")) @@ -4267,7 +4281,7 @@ class SessionPruneResult: """Session IDs that were skipped (e.g., named sessions)""" @staticmethod - def from_dict(obj: Any) -> 'SessionPruneResult': + def from_dict(obj: Any) -> SessionPruneResult: assert isinstance(obj, dict) candidates = from_list(from_str, obj.get("candidates")) deleted = from_list(from_str, obj.get("deleted")) @@ -4299,7 +4313,7 @@ class SessionSetCredentialsParams: """ @staticmethod - def from_dict(obj: Any) -> 'SessionSetCredentialsParams': + def from_dict(obj: Any) -> SessionSetCredentialsParams: assert isinstance(obj, dict) credentials = from_union([_load_AuthInfo, from_none], obj.get("credentials")) return SessionSetCredentialsParams(credentials) @@ -4319,7 +4333,7 @@ class SessionSetCredentialsResult: """Whether the operation succeeded""" @staticmethod - def from_dict(obj: Any) -> 'SessionSetCredentialsResult': + def from_dict(obj: Any) -> SessionSetCredentialsResult: assert isinstance(obj, dict) success = from_bool(obj.get("success")) return SessionSetCredentialsResult(success) @@ -4338,7 +4352,7 @@ class SessionSizes: """Map of sessionId -> on-disk size in bytes for the session's workspace directory""" @staticmethod - def from_dict(obj: Any) -> 'SessionSizes': + def from_dict(obj: Any) -> SessionSizes: assert isinstance(obj, dict) sizes = from_dict(from_int, obj.get("sizes")) return SessionSizes(sizes) @@ -4357,7 +4371,7 @@ class SessionUpdateOptionsResult: """Whether the operation succeeded""" @staticmethod - def from_dict(obj: Any) -> 'SessionUpdateOptionsResult': + def from_dict(obj: Any) -> SessionUpdateOptionsResult: assert isinstance(obj, dict) success = from_bool(obj.get("success")) return SessionUpdateOptionsResult(success) @@ -4376,7 +4390,7 @@ class SessionsBulkDeleteRequest: """Session IDs to close, deactivate, and delete from disk""" @staticmethod - def from_dict(obj: Any) -> 'SessionsBulkDeleteRequest': + def from_dict(obj: Any) -> SessionsBulkDeleteRequest: assert isinstance(obj, dict) session_ids = from_list(from_str, obj.get("sessionIds")) return SessionsBulkDeleteRequest(session_ids) @@ -4395,7 +4409,7 @@ class SessionsCheckInUseRequest: """Session IDs to test for live in-use locks""" @staticmethod - def from_dict(obj: Any) -> 'SessionsCheckInUseRequest': + def from_dict(obj: Any) -> SessionsCheckInUseRequest: assert isinstance(obj, dict) session_ids = from_list(from_str, obj.get("sessionIds")) return SessionsCheckInUseRequest(session_ids) @@ -4416,7 +4430,7 @@ class SessionsCheckInUseResult: """ @staticmethod - def from_dict(obj: Any) -> 'SessionsCheckInUseResult': + def from_dict(obj: Any) -> SessionsCheckInUseResult: assert isinstance(obj, dict) in_use = from_list(from_str, obj.get("inUse")) return SessionsCheckInUseResult(in_use) @@ -4435,7 +4449,7 @@ class SessionsCloseRequest: """Session ID to close""" @staticmethod - def from_dict(obj: Any) -> 'SessionsCloseRequest': + def from_dict(obj: Any) -> SessionsCloseRequest: assert isinstance(obj, dict) session_id = from_str(obj.get("sessionId")) return SessionsCloseRequest(session_id) @@ -4453,7 +4467,7 @@ class SessionsCloseResult: currently active. """ @staticmethod - def from_dict(obj: Any) -> 'SessionsCloseResult': + def from_dict(obj: Any) -> SessionsCloseResult: assert isinstance(obj, dict) return SessionsCloseResult() @@ -4472,7 +4486,7 @@ class SessionsFindByPrefixRequest: """ @staticmethod - def from_dict(obj: Any) -> 'SessionsFindByPrefixRequest': + def from_dict(obj: Any) -> SessionsFindByPrefixRequest: assert isinstance(obj, dict) prefix = from_str(obj.get("prefix")) return SessionsFindByPrefixRequest(prefix) @@ -4491,7 +4505,7 @@ class SessionsFindByPrefixResult: """Omitted when no unique session matches the prefix (no match or ambiguous)""" @staticmethod - def from_dict(obj: Any) -> 'SessionsFindByPrefixResult': + def from_dict(obj: Any) -> SessionsFindByPrefixResult: assert isinstance(obj, dict) session_id = from_union([from_str, from_none], obj.get("sessionId")) return SessionsFindByPrefixResult(session_id) @@ -4511,7 +4525,7 @@ class SessionsFindByTaskIDRequest: """GitHub task ID to look up""" @staticmethod - def from_dict(obj: Any) -> 'SessionsFindByTaskIDRequest': + def from_dict(obj: Any) -> SessionsFindByTaskIDRequest: assert isinstance(obj, dict) task_id = from_str(obj.get("taskId")) return SessionsFindByTaskIDRequest(task_id) @@ -4530,7 +4544,7 @@ class SessionsFindByTaskIDResult: """Omitted when no local session is bound to that GitHub task""" @staticmethod - def from_dict(obj: Any) -> 'SessionsFindByTaskIDResult': + def from_dict(obj: Any) -> SessionsFindByTaskIDResult: assert isinstance(obj, dict) session_id = from_union([from_str, from_none], obj.get("sessionId")) return SessionsFindByTaskIDResult(session_id) @@ -4559,7 +4573,7 @@ class SessionsForkRequest: """ @staticmethod - def from_dict(obj: Any) -> 'SessionsForkRequest': + def from_dict(obj: Any) -> SessionsForkRequest: assert isinstance(obj, dict) session_id = from_str(obj.get("sessionId")) name = from_union([from_str, from_none], obj.get("name")) @@ -4587,7 +4601,7 @@ class SessionsForkResult: """Friendly name assigned to the forked session, if any.""" @staticmethod - def from_dict(obj: Any) -> 'SessionsForkResult': + def from_dict(obj: Any) -> SessionsForkResult: assert isinstance(obj, dict) session_id = from_str(obj.get("sessionId")) name = from_union([from_str, from_none], obj.get("name")) @@ -4609,7 +4623,7 @@ class SessionsGetEventFilePathRequest: """Session ID whose event-log file path to compute""" @staticmethod - def from_dict(obj: Any) -> 'SessionsGetEventFilePathRequest': + def from_dict(obj: Any) -> SessionsGetEventFilePathRequest: assert isinstance(obj, dict) session_id = from_str(obj.get("sessionId")) return SessionsGetEventFilePathRequest(session_id) @@ -4628,7 +4642,7 @@ class SessionsGetEventFilePathResult: """Absolute path to the session's events.jsonl file""" @staticmethod - def from_dict(obj: Any) -> 'SessionsGetEventFilePathResult': + def from_dict(obj: Any) -> SessionsGetEventFilePathResult: assert isinstance(obj, dict) file_path = from_str(obj.get("filePath")) return SessionsGetEventFilePathResult(file_path) @@ -4647,7 +4661,7 @@ class SessionsGetLastForContextResult: """Most-relevant session ID for the supplied context, or omitted when no sessions exist""" @staticmethod - def from_dict(obj: Any) -> 'SessionsGetLastForContextResult': + def from_dict(obj: Any) -> SessionsGetLastForContextResult: assert isinstance(obj, dict) session_id = from_union([from_str, from_none], obj.get("sessionId")) return SessionsGetLastForContextResult(session_id) @@ -4667,7 +4681,7 @@ class SessionsGetPersistedRemoteSteerableRequest: """Session ID to look up the persisted remote-steerable flag for""" @staticmethod - def from_dict(obj: Any) -> 'SessionsGetPersistedRemoteSteerableRequest': + def from_dict(obj: Any) -> SessionsGetPersistedRemoteSteerableRequest: assert isinstance(obj, dict) session_id = from_str(obj.get("sessionId")) return SessionsGetPersistedRemoteSteerableRequest(session_id) @@ -4689,7 +4703,7 @@ class SessionsGetPersistedRemoteSteerableResult: """ @staticmethod - def from_dict(obj: Any) -> 'SessionsGetPersistedRemoteSteerableResult': + def from_dict(obj: Any) -> SessionsGetPersistedRemoteSteerableResult: assert isinstance(obj, dict) remote_steerable = from_union([from_bool, from_none], obj.get("remoteSteerable")) return SessionsGetPersistedRemoteSteerableResult(remote_steerable) @@ -4709,7 +4723,7 @@ class SessionsLoadDeferredRepoHooksRequest: """Active session ID whose deferred repo-level hooks should be loaded""" @staticmethod - def from_dict(obj: Any) -> 'SessionsLoadDeferredRepoHooksRequest': + def from_dict(obj: Any) -> SessionsLoadDeferredRepoHooksRequest: assert isinstance(obj, dict) session_id = from_str(obj.get("sessionId")) return SessionsLoadDeferredRepoHooksRequest(session_id) @@ -4738,7 +4752,7 @@ class SessionsPruneOldRequest: """When true, named sessions (set via /rename) are also eligible for pruning""" @staticmethod - def from_dict(obj: Any) -> 'SessionsPruneOldRequest': + def from_dict(obj: Any) -> SessionsPruneOldRequest: assert isinstance(obj, dict) older_than_days = from_int(obj.get("olderThanDays")) dry_run = from_union([from_bool, from_none], obj.get("dryRun")) @@ -4766,7 +4780,7 @@ class SessionsReleaseLockRequest: """Session ID whose in-use lock should be released""" @staticmethod - def from_dict(obj: Any) -> 'SessionsReleaseLockRequest': + def from_dict(obj: Any) -> SessionsReleaseLockRequest: assert isinstance(obj, dict) session_id = from_str(obj.get("sessionId")) return SessionsReleaseLockRequest(session_id) @@ -4783,7 +4797,7 @@ class SessionsReleaseLockResult: process does not currently hold a lock for the session. """ @staticmethod - def from_dict(obj: Any) -> 'SessionsReleaseLockResult': + def from_dict(obj: Any) -> SessionsReleaseLockResult: assert isinstance(obj, dict) return SessionsReleaseLockResult() @@ -4805,7 +4819,7 @@ class SessionsReloadPluginHooksRequest: """ @staticmethod - def from_dict(obj: Any) -> 'SessionsReloadPluginHooksRequest': + def from_dict(obj: Any) -> SessionsReloadPluginHooksRequest: assert isinstance(obj, dict) session_id = from_str(obj.get("sessionId")) defer_repo_hooks = from_union([from_bool, from_none], obj.get("deferRepoHooks")) @@ -4826,7 +4840,7 @@ class SessionsReloadPluginHooksResult: when no active session matches the given sessionId. """ @staticmethod - def from_dict(obj: Any) -> 'SessionsReloadPluginHooksResult': + def from_dict(obj: Any) -> SessionsReloadPluginHooksResult: assert isinstance(obj, dict) return SessionsReloadPluginHooksResult() @@ -4843,7 +4857,7 @@ class SessionsSaveRequest: """Session ID whose pending events should be flushed to disk""" @staticmethod - def from_dict(obj: Any) -> 'SessionsSaveRequest': + def from_dict(obj: Any) -> SessionsSaveRequest: assert isinstance(obj, dict) session_id = from_str(obj.get("sessionId")) return SessionsSaveRequest(session_id) @@ -4860,7 +4874,7 @@ class SessionsSaveResult: (e.g., already closed). """ @staticmethod - def from_dict(obj: Any) -> 'SessionsSaveResult': + def from_dict(obj: Any) -> SessionsSaveResult: assert isinstance(obj, dict) return SessionsSaveResult() @@ -4876,7 +4890,7 @@ class SessionsSetAdditionalPluginsResult: until the next reload. """ @staticmethod - def from_dict(obj: Any) -> 'SessionsSetAdditionalPluginsResult': + def from_dict(obj: Any) -> SessionsSetAdditionalPluginsResult: assert isinstance(obj, dict) return SessionsSetAdditionalPluginsResult() @@ -4899,7 +4913,7 @@ class ShellExecRequest: """Timeout in milliseconds (default: 30000)""" @staticmethod - def from_dict(obj: Any) -> 'ShellExecRequest': + def from_dict(obj: Any) -> ShellExecRequest: assert isinstance(obj, dict) command = from_str(obj.get("command")) cwd = from_union([from_str, from_none], obj.get("cwd")) @@ -4925,7 +4939,7 @@ class ShellExecResult: """Unique identifier for tracking streamed output""" @staticmethod - def from_dict(obj: Any) -> 'ShellExecResult': + def from_dict(obj: Any) -> ShellExecResult: assert isinstance(obj, dict) process_id = from_str(obj.get("processId")) return ShellExecResult(process_id) @@ -4953,7 +4967,7 @@ class ShellKillResult: """Whether the signal was sent successfully""" @staticmethod - def from_dict(obj: Any) -> 'ShellKillResult': + def from_dict(obj: Any) -> ShellKillResult: assert isinstance(obj, dict) killed = from_bool(obj.get("killed")) return ShellKillResult(killed) @@ -4976,7 +4990,7 @@ class ShutdownRequest: """Why the session is being shut down. Defaults to "routine" when omitted.""" @staticmethod - def from_dict(obj: Any) -> 'ShutdownRequest': + def from_dict(obj: Any) -> ShutdownRequest: assert isinstance(obj, dict) reason = from_union([from_str, from_none], obj.get("reason")) type = from_union([ShutdownType, from_none], obj.get("type")) @@ -5017,7 +5031,7 @@ class Skill: """Name of the plugin that provides the skill, when source is 'plugin'""" @staticmethod - def from_dict(obj: Any) -> 'Skill': + def from_dict(obj: Any) -> Skill: assert isinstance(obj, dict) description = from_str(obj.get("description")) enabled = from_bool(obj.get("enabled")) @@ -5050,7 +5064,7 @@ class SkillsDisableRequest: """Name of the skill to disable""" @staticmethod - def from_dict(obj: Any) -> 'SkillsDisableRequest': + def from_dict(obj: Any) -> SkillsDisableRequest: assert isinstance(obj, dict) name = from_str(obj.get("name")) return SkillsDisableRequest(name) @@ -5071,7 +5085,7 @@ class SkillsDiscoverRequest: """Optional list of additional skill directory paths to include""" @staticmethod - def from_dict(obj: Any) -> 'SkillsDiscoverRequest': + def from_dict(obj: Any) -> SkillsDiscoverRequest: assert isinstance(obj, dict) project_paths = from_union([lambda x: from_list(from_str, x), from_none], obj.get("projectPaths")) skill_directories = from_union([lambda x: from_list(from_str, x), from_none], obj.get("skillDirectories")) @@ -5094,7 +5108,7 @@ class SkillsEnableRequest: """Name of the skill to enable""" @staticmethod - def from_dict(obj: Any) -> 'SkillsEnableRequest': + def from_dict(obj: Any) -> SkillsEnableRequest: assert isinstance(obj, dict) name = from_str(obj.get("name")) return SkillsEnableRequest(name) @@ -5125,7 +5139,7 @@ class SkillsInvokedSkill: """Tools that should be auto-approved when this skill is active, captured at invocation time""" @staticmethod - def from_dict(obj: Any) -> 'SkillsInvokedSkill': + def from_dict(obj: Any) -> SkillsInvokedSkill: assert isinstance(obj, dict) content = from_str(obj.get("content")) invoked_at_turn = from_int(obj.get("invokedAtTurn")) @@ -5156,7 +5170,7 @@ class SkillsLoadDiagnostics: """Warnings emitted while loading skills (e.g. skills that loaded but had issues)""" @staticmethod - def from_dict(obj: Any) -> 'SkillsLoadDiagnostics': + def from_dict(obj: Any) -> SkillsLoadDiagnostics: assert isinstance(obj, dict) errors = from_list(from_str, obj.get("errors")) warnings = from_list(from_str, obj.get("warnings")) @@ -5195,7 +5209,7 @@ class SlashCommandSelectSubcommandOption: """Optional group label for organizing options""" @staticmethod - def from_dict(obj: Any) -> 'SlashCommandSelectSubcommandOption': + def from_dict(obj: Any) -> SlashCommandSelectSubcommandOption: assert isinstance(obj, dict) description = from_str(obj.get("description")) name = from_str(obj.get("name")) @@ -5245,7 +5259,7 @@ class TaskProgressLine: """ISO 8601 timestamp when this event occurred""" @staticmethod - def from_dict(obj: Any) -> 'TaskProgressLine': + def from_dict(obj: Any) -> TaskProgressLine: assert isinstance(obj, dict) message = from_str(obj.get("message")) timestamp = from_datetime(obj.get("timestamp")) @@ -5278,7 +5292,7 @@ class TaskList: """Currently tracked tasks""" @staticmethod - def from_dict(obj: Any) -> 'TaskList': + def from_dict(obj: Any) -> TaskList: assert isinstance(obj, dict) tasks = from_list(_load_TaskInfo, obj.get("tasks")) return TaskList(tasks) @@ -5300,7 +5314,7 @@ class TasksCancelRequest: """Task identifier""" @staticmethod - def from_dict(obj: Any) -> 'TasksCancelRequest': + def from_dict(obj: Any) -> TasksCancelRequest: assert isinstance(obj, dict) id = from_str(obj.get("id")) return TasksCancelRequest(id) @@ -5319,7 +5333,7 @@ class TasksCancelResult: """Whether the task was successfully cancelled""" @staticmethod - def from_dict(obj: Any) -> 'TasksCancelResult': + def from_dict(obj: Any) -> TasksCancelResult: assert isinstance(obj, dict) cancelled = from_bool(obj.get("cancelled")) return TasksCancelResult(cancelled) @@ -5341,7 +5355,7 @@ class TasksGetCurrentPromotableResult: """ @staticmethod - def from_dict(obj: Any) -> 'TasksGetCurrentPromotableResult': + def from_dict(obj: Any) -> TasksGetCurrentPromotableResult: assert isinstance(obj, dict) task = from_union([_load_TaskInfo, from_none], obj.get("task")) return TasksGetCurrentPromotableResult(task) @@ -5361,7 +5375,7 @@ class TasksGetProgressRequest: """Task identifier (agent ID or shell ID)""" @staticmethod - def from_dict(obj: Any) -> 'TasksGetProgressRequest': + def from_dict(obj: Any) -> TasksGetProgressRequest: assert isinstance(obj, dict) id = from_str(obj.get("id")) return TasksGetProgressRequest(id) @@ -5384,7 +5398,7 @@ class TasksPromoteCurrentToBackgroundResult: """ @staticmethod - def from_dict(obj: Any) -> 'TasksPromoteCurrentToBackgroundResult': + def from_dict(obj: Any) -> TasksPromoteCurrentToBackgroundResult: assert isinstance(obj, dict) task = from_union([_load_TaskInfo, from_none], obj.get("task")) return TasksPromoteCurrentToBackgroundResult(task) @@ -5404,7 +5418,7 @@ class TasksPromoteToBackgroundRequest: """Task identifier""" @staticmethod - def from_dict(obj: Any) -> 'TasksPromoteToBackgroundRequest': + def from_dict(obj: Any) -> TasksPromoteToBackgroundRequest: assert isinstance(obj, dict) id = from_str(obj.get("id")) return TasksPromoteToBackgroundRequest(id) @@ -5423,7 +5437,7 @@ class TasksPromoteToBackgroundResult: """Whether the task was successfully promoted to background mode""" @staticmethod - def from_dict(obj: Any) -> 'TasksPromoteToBackgroundResult': + def from_dict(obj: Any) -> TasksPromoteToBackgroundResult: assert isinstance(obj, dict) promoted = from_bool(obj.get("promoted")) return TasksPromoteToBackgroundResult(promoted) @@ -5440,7 +5454,7 @@ class TasksRefreshResult: long pause to pick up exit/output state for shells running outside the agent loop. """ @staticmethod - def from_dict(obj: Any) -> 'TasksRefreshResult': + def from_dict(obj: Any) -> TasksRefreshResult: assert isinstance(obj, dict) return TasksRefreshResult() @@ -5457,7 +5471,7 @@ class TasksRemoveRequest: """Task identifier""" @staticmethod - def from_dict(obj: Any) -> 'TasksRemoveRequest': + def from_dict(obj: Any) -> TasksRemoveRequest: assert isinstance(obj, dict) id = from_str(obj.get("id")) return TasksRemoveRequest(id) @@ -5479,7 +5493,7 @@ class TasksRemoveResult: """ @staticmethod - def from_dict(obj: Any) -> 'TasksRemoveResult': + def from_dict(obj: Any) -> TasksRemoveResult: assert isinstance(obj, dict) removed = from_bool(obj.get("removed")) return TasksRemoveResult(removed) @@ -5504,7 +5518,7 @@ class TasksSendMessageRequest: """Agent ID of the sender, if sent on behalf of another agent""" @staticmethod - def from_dict(obj: Any) -> 'TasksSendMessageRequest': + def from_dict(obj: Any) -> TasksSendMessageRequest: assert isinstance(obj, dict) id = from_str(obj.get("id")) message = from_str(obj.get("message")) @@ -5531,7 +5545,7 @@ class TasksSendMessageResult: """Error message if delivery failed""" @staticmethod - def from_dict(obj: Any) -> 'TasksSendMessageResult': + def from_dict(obj: Any) -> TasksSendMessageResult: assert isinstance(obj, dict) sent = from_bool(obj.get("sent")) error = from_union([from_str, from_none], obj.get("error")) @@ -5565,7 +5579,7 @@ class TasksStartAgentRequest: """Optional model override""" @staticmethod - def from_dict(obj: Any) -> 'TasksStartAgentRequest': + def from_dict(obj: Any) -> TasksStartAgentRequest: assert isinstance(obj, dict) agent_type = from_str(obj.get("agentType")) name = from_str(obj.get("name")) @@ -5594,7 +5608,7 @@ class TasksStartAgentResult: """Generated agent ID for the background task""" @staticmethod - def from_dict(obj: Any) -> 'TasksStartAgentResult': + def from_dict(obj: Any) -> TasksStartAgentResult: assert isinstance(obj, dict) agent_id = from_str(obj.get("agentId")) return TasksStartAgentResult(agent_id) @@ -5613,7 +5627,7 @@ class TasksWaitForPendingResult: COPILOT_TASK_WAIT_TIMEOUT_SECONDS). """ @staticmethod - def from_dict(obj: Any) -> 'TasksWaitForPendingResult': + def from_dict(obj: Any) -> TasksWaitForPendingResult: assert isinstance(obj, dict) return TasksWaitForPendingResult() @@ -5633,7 +5647,7 @@ class TelemetrySetFeatureOverridesRequest: """ @staticmethod - def from_dict(obj: Any) -> 'TelemetrySetFeatureOverridesRequest': + def from_dict(obj: Any) -> TelemetrySetFeatureOverridesRequest: assert isinstance(obj, dict) features = from_dict(from_str, obj.get("features")) return TelemetrySetFeatureOverridesRequest(features) @@ -5667,7 +5681,7 @@ class Tool: """JSON Schema for the tool's input parameters""" @staticmethod - def from_dict(obj: Any) -> 'Tool': + def from_dict(obj: Any) -> Tool: assert isinstance(obj, dict) description = from_str(obj.get("description")) name = from_str(obj.get("name")) @@ -5696,7 +5710,7 @@ class ToolsInitializeAndValidateResult: base-class implementation is a no-op for sessions that don't support tool validation. """ @staticmethod - def from_dict(obj: Any) -> 'ToolsInitializeAndValidateResult': + def from_dict(obj: Any) -> ToolsInitializeAndValidateResult: assert isinstance(obj, dict) return ToolsInitializeAndValidateResult() @@ -5714,7 +5728,7 @@ class ToolsListRequest: """ @staticmethod - def from_dict(obj: Any) -> 'ToolsListRequest': + def from_dict(obj: Any) -> ToolsListRequest: assert isinstance(obj, dict) model = from_union([from_str, from_none], obj.get("model")) return ToolsListRequest(model) @@ -5746,7 +5760,7 @@ class UIElicitationArrayAnyOfFieldItemsAnyOf: """Display label for this option.""" @staticmethod - def from_dict(obj: Any) -> 'UIElicitationArrayAnyOfFieldItemsAnyOf': + def from_dict(obj: Any) -> UIElicitationArrayAnyOfFieldItemsAnyOf: assert isinstance(obj, dict) const = from_str(obj.get("const")) title = from_str(obj.get("title")) @@ -5785,7 +5799,7 @@ class UIElicitationStringOneOfFieldOneOf: """Display label for this option.""" @staticmethod - def from_dict(obj: Any) -> 'UIElicitationStringOneOfFieldOneOf': + def from_dict(obj: Any) -> UIElicitationStringOneOfFieldOneOf: assert isinstance(obj, dict) const = from_str(obj.get("const")) title = from_str(obj.get("title")) @@ -5829,7 +5843,7 @@ class UIElicitationResult: """ @staticmethod - def from_dict(obj: Any) -> 'UIElicitationResult': + def from_dict(obj: Any) -> UIElicitationResult: assert isinstance(obj, dict) success = from_bool(obj.get("success")) return UIElicitationResult(success) @@ -5871,7 +5885,7 @@ class UIHandlePendingResult: """ @staticmethod - def from_dict(obj: Any) -> 'UIHandlePendingResult': + def from_dict(obj: Any) -> UIHandlePendingResult: assert isinstance(obj, dict) success = from_bool(obj.get("success")) return UIHandlePendingResult(success) @@ -5896,7 +5910,7 @@ class UIHandlePendingSamplingRequest: """ @staticmethod - def from_dict(obj: Any) -> 'UIHandlePendingSamplingRequest': + def from_dict(obj: Any) -> UIHandlePendingSamplingRequest: assert isinstance(obj, dict) request_id = from_str(obj.get("requestId")) response = from_union([lambda x: from_dict(lambda x: x, x), from_none], obj.get("response")) @@ -5923,7 +5937,7 @@ class UIUserInputResponse: """ @staticmethod - def from_dict(obj: Any) -> 'UIUserInputResponse': + def from_dict(obj: Any) -> UIUserInputResponse: assert isinstance(obj, dict) answer = from_str(obj.get("answer")) was_freeform = from_bool(obj.get("wasFreeform")) @@ -5951,7 +5965,7 @@ class UIRegisterDirectAutoModeSwitchHandlerResult: """ @staticmethod - def from_dict(obj: Any) -> 'UIRegisterDirectAutoModeSwitchHandlerResult': + def from_dict(obj: Any) -> UIRegisterDirectAutoModeSwitchHandlerResult: assert isinstance(obj, dict) handle = from_str(obj.get("handle")) return UIRegisterDirectAutoModeSwitchHandlerResult(handle) @@ -5970,7 +5984,7 @@ class UIUnregisterDirectAutoModeSwitchHandlerRequest: """Handle previously returned by `registerDirectAutoModeSwitchHandler`""" @staticmethod - def from_dict(obj: Any) -> 'UIUnregisterDirectAutoModeSwitchHandlerRequest': + def from_dict(obj: Any) -> UIUnregisterDirectAutoModeSwitchHandlerRequest: assert isinstance(obj, dict) handle = from_str(obj.get("handle")) return UIUnregisterDirectAutoModeSwitchHandlerRequest(handle) @@ -5991,7 +6005,7 @@ class UIUnregisterDirectAutoModeSwitchHandlerResult: """ @staticmethod - def from_dict(obj: Any) -> 'UIUnregisterDirectAutoModeSwitchHandlerResult': + def from_dict(obj: Any) -> UIUnregisterDirectAutoModeSwitchHandlerResult: assert isinstance(obj, dict) unregistered = from_bool(obj.get("unregistered")) return UIUnregisterDirectAutoModeSwitchHandlerResult(unregistered) @@ -6019,7 +6033,7 @@ class UsageMetricsCodeChanges: """Total lines of code removed""" @staticmethod - def from_dict(obj: Any) -> 'UsageMetricsCodeChanges': + def from_dict(obj: Any) -> UsageMetricsCodeChanges: assert isinstance(obj, dict) files_modified = from_list(from_str, obj.get("filesModified")) files_modified_count = from_int(obj.get("filesModifiedCount")) @@ -6047,7 +6061,7 @@ class UsageMetricsModelMetricRequests: """Number of API requests made with this model""" @staticmethod - def from_dict(obj: Any) -> 'UsageMetricsModelMetricRequests': + def from_dict(obj: Any) -> UsageMetricsModelMetricRequests: assert isinstance(obj, dict) cost = from_float(obj.get("cost")) count = from_int(obj.get("count")) @@ -6068,7 +6082,7 @@ class UsageMetricsModelMetricTokenDetail: """Accumulated token count for this token type""" @staticmethod - def from_dict(obj: Any) -> 'UsageMetricsModelMetricTokenDetail': + def from_dict(obj: Any) -> UsageMetricsModelMetricTokenDetail: assert isinstance(obj, dict) token_count = from_int(obj.get("tokenCount")) return UsageMetricsModelMetricTokenDetail(token_count) @@ -6099,7 +6113,7 @@ class UsageMetricsModelMetricUsage: """Total output tokens used for reasoning""" @staticmethod - def from_dict(obj: Any) -> 'UsageMetricsModelMetricUsage': + def from_dict(obj: Any) -> UsageMetricsModelMetricUsage: assert isinstance(obj, dict) cache_read_tokens = from_int(obj.get("cacheReadTokens")) cache_write_tokens = from_int(obj.get("cacheWriteTokens")) @@ -6127,7 +6141,7 @@ class UsageMetricsTokenDetail: """Accumulated token count for this token type""" @staticmethod - def from_dict(obj: Any) -> 'UsageMetricsTokenDetail': + def from_dict(obj: Any) -> UsageMetricsTokenDetail: assert isinstance(obj, dict) token_count = from_int(obj.get("tokenCount")) return UsageMetricsTokenDetail(token_count) @@ -6155,7 +6169,7 @@ class WorkspacesCheckpoints: """Human-readable checkpoint title""" @staticmethod - def from_dict(obj: Any) -> 'WorkspacesCheckpoints': + def from_dict(obj: Any) -> WorkspacesCheckpoints: assert isinstance(obj, dict) filename = from_str(obj.get("filename")) number = from_int(obj.get("number")) @@ -6181,7 +6195,7 @@ class WorkspacesCreateFileRequest: """Relative path within the workspace files directory""" @staticmethod - def from_dict(obj: Any) -> 'WorkspacesCreateFileRequest': + def from_dict(obj: Any) -> WorkspacesCreateFileRequest: assert isinstance(obj, dict) content = from_str(obj.get("content")) path = from_str(obj.get("path")) @@ -6202,7 +6216,7 @@ class WorkspacesListFilesResult: """Relative file paths in the workspace files directory""" @staticmethod - def from_dict(obj: Any) -> 'WorkspacesListFilesResult': + def from_dict(obj: Any) -> WorkspacesListFilesResult: assert isinstance(obj, dict) files = from_list(from_str, obj.get("files")) return WorkspacesListFilesResult(files) @@ -6221,7 +6235,7 @@ class WorkspacesReadCheckpointRequest: """Checkpoint number to read""" @staticmethod - def from_dict(obj: Any) -> 'WorkspacesReadCheckpointRequest': + def from_dict(obj: Any) -> WorkspacesReadCheckpointRequest: assert isinstance(obj, dict) number = from_int(obj.get("number")) return WorkspacesReadCheckpointRequest(number) @@ -6240,7 +6254,7 @@ class WorkspacesReadCheckpointResult: """Checkpoint content as a UTF-8 string, or null when the checkpoint or workspace is missing""" @staticmethod - def from_dict(obj: Any) -> 'WorkspacesReadCheckpointResult': + def from_dict(obj: Any) -> WorkspacesReadCheckpointResult: assert isinstance(obj, dict) content = from_union([from_none, from_str], obj.get("content")) return WorkspacesReadCheckpointResult(content) @@ -6259,7 +6273,7 @@ class WorkspacesReadFileRequest: """Relative path within the workspace files directory""" @staticmethod - def from_dict(obj: Any) -> 'WorkspacesReadFileRequest': + def from_dict(obj: Any) -> WorkspacesReadFileRequest: assert isinstance(obj, dict) path = from_str(obj.get("path")) return WorkspacesReadFileRequest(path) @@ -6278,7 +6292,7 @@ class WorkspacesReadFileResult: """File content as a UTF-8 string""" @staticmethod - def from_dict(obj: Any) -> 'WorkspacesReadFileResult': + def from_dict(obj: Any) -> WorkspacesReadFileResult: assert isinstance(obj, dict) content = from_str(obj.get("content")) return WorkspacesReadFileResult(content) @@ -6297,7 +6311,7 @@ class WorkspacesSaveLargePasteRequest: """Pasted content to save as a UTF-8 file""" @staticmethod - def from_dict(obj: Any) -> 'WorkspacesSaveLargePasteRequest': + def from_dict(obj: Any) -> WorkspacesSaveLargePasteRequest: assert isinstance(obj, dict) content = from_str(obj.get("content")) return WorkspacesSaveLargePasteRequest(content) @@ -6319,7 +6333,7 @@ class Saved: """Size of the saved file in bytes""" @staticmethod - def from_dict(obj: Any) -> 'Saved': + def from_dict(obj: Any) -> Saved: assert isinstance(obj, dict) filename = from_str(obj.get("filename")) file_path = from_str(obj.get("filePath")) @@ -6341,7 +6355,7 @@ class AccountGetQuotaResult: """Quota snapshots keyed by type (e.g., chat, completions, premium_interactions)""" @staticmethod - def from_dict(obj: Any) -> 'AccountGetQuotaResult': + def from_dict(obj: Any) -> AccountGetQuotaResult: assert isinstance(obj, dict) quota_snapshots = from_dict(AccountQuotaSnapshot.from_dict, obj.get("quotaSnapshots")) return AccountGetQuotaResult(quota_snapshots) @@ -6375,7 +6389,7 @@ class SessionAuthStatus: """Human-readable authentication status description""" @staticmethod - def from_dict(obj: Any) -> 'SessionAuthStatus': + def from_dict(obj: Any) -> SessionAuthStatus: assert isinstance(obj, dict) is_authenticated = from_bool(obj.get("isAuthenticated")) auth_type = from_union([AuthInfoType, from_none], obj.get("authType")) @@ -6421,7 +6435,7 @@ class SlashCommandInput: """ @staticmethod - def from_dict(obj: Any) -> 'SlashCommandInput': + def from_dict(obj: Any) -> SlashCommandInput: assert isinstance(obj, dict) hint = from_str(obj.get("hint")) completion = from_union([SlashCommandInputCompletion, from_none], obj.get("completion")) @@ -6455,7 +6469,7 @@ class SendAttachmentDirectory: """Attachment type discriminator""" @staticmethod - def from_dict(obj: Any) -> 'SendAttachmentDirectory': + def from_dict(obj: Any) -> SendAttachmentDirectory: assert isinstance(obj, dict) display_name = from_str(obj.get("displayName")) path = from_str(obj.get("path")) @@ -6507,7 +6521,7 @@ class ConnectedRemoteSessionMetadata: """Optional session summary.""" @staticmethod - def from_dict(obj: Any) -> 'ConnectedRemoteSessionMetadata': + def from_dict(obj: Any) -> ConnectedRemoteSessionMetadata: assert isinstance(obj, dict) kind = ConnectedRemoteSessionMetadataKind(obj.get("kind")) modified_time = from_datetime(obj.get("modifiedTime")) @@ -6574,7 +6588,7 @@ class MCPServerConfigStdio: """Tools to include. Defaults to all tools if not specified.""" @staticmethod - def from_dict(obj: Any) -> 'MCPServerConfigStdio': + def from_dict(obj: Any) -> MCPServerConfigStdio: assert isinstance(obj, dict) command = from_str(obj.get("command")) args = from_union([lambda x: from_list(from_str, x), from_none], obj.get("args")) @@ -6628,7 +6642,7 @@ class CopilotUserResponseQuotaSnapshots: unlimited: bool | None = None @staticmethod - def from_dict(obj: Any) -> 'CopilotUserResponseQuotaSnapshots': + def from_dict(obj: Any) -> CopilotUserResponseQuotaSnapshots: assert isinstance(obj, dict) entitlement = from_union([from_float, from_none], obj.get("entitlement")) has_quota = from_union([from_bool, from_none], obj.get("has_quota")) @@ -6689,7 +6703,7 @@ class DiscoveredMCPServer: """Server transport type: stdio, http, sse, or memory""" @staticmethod - def from_dict(obj: Any) -> 'DiscoveredMCPServer': + def from_dict(obj: Any) -> DiscoveredMCPServer: assert isinstance(obj, dict) enabled = from_bool(obj.get("enabled")) name = from_str(obj.get("name")) @@ -6736,7 +6750,7 @@ class EventLogReadRequest: """ @staticmethod - def from_dict(obj: Any) -> 'EventLogReadRequest': + def from_dict(obj: Any) -> EventLogReadRequest: assert isinstance(obj, dict) agent_scope = from_union([EventsAgentScope, from_none], obj.get("agentScope")) cursor = from_union([from_str, from_none], obj.get("cursor")) @@ -6786,7 +6800,7 @@ class EventsReadResult: """ @staticmethod - def from_dict(obj: Any) -> 'EventsReadResult': + def from_dict(obj: Any) -> EventsReadResult: assert isinstance(obj, dict) cursor = from_str(obj.get("cursor")) cursor_status = EventsCursorStatus(obj.get("cursorStatus")) @@ -6823,7 +6837,7 @@ class Extension: """Process ID if the extension is running""" @staticmethod - def from_dict(obj: Any) -> 'Extension': + def from_dict(obj: Any) -> Extension: assert isinstance(obj, dict) id = from_str(obj.get("id")) name = from_str(obj.get("name")) @@ -6861,7 +6875,7 @@ class ExternalToolTextResultForLlmBinaryResultsForLlm: """Human-readable description of the binary data""" @staticmethod - def from_dict(obj: Any) -> 'ExternalToolTextResultForLlmBinaryResultsForLlm': + def from_dict(obj: Any) -> ExternalToolTextResultForLlmBinaryResultsForLlm: assert isinstance(obj, dict) data = from_str(obj.get("data")) mime_type = from_str(obj.get("mimeType")) @@ -6896,7 +6910,7 @@ class ExternalToolTextResultForLlmContentResourceLinkIcon: """Theme variant this icon is intended for""" @staticmethod - def from_dict(obj: Any) -> 'ExternalToolTextResultForLlmContentResourceLinkIcon': + def from_dict(obj: Any) -> ExternalToolTextResultForLlmContentResourceLinkIcon: assert isinstance(obj, dict) src = from_str(obj.get("src")) mime_type = from_union([from_str, from_none], obj.get("mimeType")) @@ -6932,7 +6946,7 @@ class ExternalToolTextResultForLlmContentAudio: """Content block type discriminator""" @staticmethod - def from_dict(obj: Any) -> 'ExternalToolTextResultForLlmContentAudio': + def from_dict(obj: Any) -> ExternalToolTextResultForLlmContentAudio: assert isinstance(obj, dict) data = from_str(obj.get("data")) mime_type = from_str(obj.get("mimeType")) @@ -6960,7 +6974,7 @@ class ExternalToolTextResultForLlmContentImage: """Content block type discriminator""" @staticmethod - def from_dict(obj: Any) -> 'ExternalToolTextResultForLlmContentImage': + def from_dict(obj: Any) -> ExternalToolTextResultForLlmContentImage: assert isinstance(obj, dict) data = from_str(obj.get("data")) mime_type = from_str(obj.get("mimeType")) @@ -6985,7 +6999,7 @@ class ExternalToolTextResultForLlmContentResource: """Content block type discriminator""" @staticmethod - def from_dict(obj: Any) -> 'ExternalToolTextResultForLlmContentResource': + def from_dict(obj: Any) -> ExternalToolTextResultForLlmContentResource: assert isinstance(obj, dict) resource = (lambda x: from_union([EmbeddedTextResourceContents.from_dict, EmbeddedBlobResourceContents.from_dict], x))(obj.get("resource")) return ExternalToolTextResultForLlmContentResource(resource) @@ -7014,7 +7028,7 @@ class ExternalToolTextResultForLlmContentTerminal: """Process exit code, if the command has completed""" @staticmethod - def from_dict(obj: Any) -> 'ExternalToolTextResultForLlmContentTerminal': + def from_dict(obj: Any) -> ExternalToolTextResultForLlmContentTerminal: assert isinstance(obj, dict) text = from_str(obj.get("text")) cwd = from_union([from_str, from_none], obj.get("cwd")) @@ -7043,7 +7057,7 @@ class ExternalToolTextResultForLlmContentText: """Content block type discriminator""" @staticmethod - def from_dict(obj: Any) -> 'ExternalToolTextResultForLlmContentText': + def from_dict(obj: Any) -> ExternalToolTextResultForLlmContentText: assert isinstance(obj, dict) text = from_str(obj.get("text")) return ExternalToolTextResultForLlmContentText(text) @@ -7077,7 +7091,7 @@ class SlashCommandTextResult: """ @staticmethod - def from_dict(obj: Any) -> 'SlashCommandTextResult': + def from_dict(obj: Any) -> SlashCommandTextResult: assert isinstance(obj, dict) text = from_str(obj.get("text")) markdown = from_union([from_bool, from_none], obj.get("markdown")) @@ -7121,7 +7135,7 @@ class HistoryCompactResult: """ @staticmethod - def from_dict(obj: Any) -> 'HistoryCompactResult': + def from_dict(obj: Any) -> HistoryCompactResult: assert isinstance(obj, dict) messages_removed = from_int(obj.get("messagesRemoved")) success = from_bool(obj.get("success")) @@ -7154,7 +7168,7 @@ class InstalledPluginSourceGithub: ref: str | None = None @staticmethod - def from_dict(obj: Any) -> 'InstalledPluginSourceGithub': + def from_dict(obj: Any) -> InstalledPluginSourceGithub: assert isinstance(obj, dict) repo = from_str(obj.get("repo")) source = FluffySource(obj.get("source")) @@ -7185,7 +7199,7 @@ class SessionInstalledPluginSourceGithub: ref: str | None = None @staticmethod - def from_dict(obj: Any) -> 'SessionInstalledPluginSourceGithub': + def from_dict(obj: Any) -> SessionInstalledPluginSourceGithub: assert isinstance(obj, dict) repo = from_str(obj.get("repo")) source = FluffySource(obj.get("source")) @@ -7213,7 +7227,7 @@ class InstalledPluginSourceLocal: """Constant value. Always "local".""" @staticmethod - def from_dict(obj: Any) -> 'InstalledPluginSourceLocal': + def from_dict(obj: Any) -> InstalledPluginSourceLocal: assert isinstance(obj, dict) path = from_str(obj.get("path")) source = TentacledSource(obj.get("source")) @@ -7235,7 +7249,7 @@ class SessionInstalledPluginSourceLocal: """Constant value. Always "local".""" @staticmethod - def from_dict(obj: Any) -> 'SessionInstalledPluginSourceLocal': + def from_dict(obj: Any) -> SessionInstalledPluginSourceLocal: assert isinstance(obj, dict) path = from_str(obj.get("path")) source = TentacledSource(obj.get("source")) @@ -7260,7 +7274,7 @@ class InstalledPluginSourceURL: ref: str | None = None @staticmethod - def from_dict(obj: Any) -> 'InstalledPluginSourceURL': + def from_dict(obj: Any) -> InstalledPluginSourceURL: assert isinstance(obj, dict) source = StickySource(obj.get("source")) url = from_str(obj.get("url")) @@ -7291,7 +7305,7 @@ class SessionInstalledPluginSourceURL: ref: str | None = None @staticmethod - def from_dict(obj: Any) -> 'SessionInstalledPluginSourceURL': + def from_dict(obj: Any) -> SessionInstalledPluginSourceURL: assert isinstance(obj, dict) source = StickySource(obj.get("source")) url = from_str(obj.get("url")) @@ -7343,7 +7357,7 @@ class InstructionsSources: """Short description (body after frontmatter) for use in instruction tables""" @staticmethod - def from_dict(obj: Any) -> 'InstructionsSources': + def from_dict(obj: Any) -> InstructionsSources: assert isinstance(obj, dict) content = from_str(obj.get("content")) id = from_str(obj.get("id")) @@ -7399,7 +7413,7 @@ class LogRequest: """Optional URL the user can open in their browser for more details""" @staticmethod - def from_dict(obj: Any) -> 'LogRequest': + def from_dict(obj: Any) -> LogRequest: assert isinstance(obj, dict) message = from_str(obj.get("message")) ephemeral = from_union([from_bool, from_none], obj.get("ephemeral")) @@ -7480,7 +7494,7 @@ class MCPServerConfig: """URL of the remote MCP server endpoint.""" @staticmethod - def from_dict(obj: Any) -> 'MCPServerConfig': + def from_dict(obj: Any) -> MCPServerConfig: assert isinstance(obj, dict) args = from_union([lambda x: from_list(from_str, x), from_none], obj.get("args")) command = from_union([from_str, from_none], obj.get("command")) @@ -7573,7 +7587,7 @@ class MCPServerConfigHTTP: """Remote transport type. Defaults to "http" when omitted.""" @staticmethod - def from_dict(obj: Any) -> 'MCPServerConfigHTTP': + def from_dict(obj: Any) -> MCPServerConfigHTTP: assert isinstance(obj, dict) url = from_str(obj.get("url")) auth = from_union([MCPServerConfigHTTPAuth.from_dict, from_none], obj.get("auth")) @@ -7633,7 +7647,7 @@ class MCPSamplingExecutionResult: """ @staticmethod - def from_dict(obj: Any) -> 'MCPSamplingExecutionResult': + def from_dict(obj: Any) -> MCPSamplingExecutionResult: assert isinstance(obj, dict) action = MCPSamplingExecutionAction(obj.get("action")) error = from_union([from_str, from_none], obj.get("error")) @@ -7658,7 +7672,7 @@ class MCPServerList: """Configured MCP servers""" @staticmethod - def from_dict(obj: Any) -> 'MCPServerList': + def from_dict(obj: Any) -> MCPServerList: assert isinstance(obj, dict) servers = from_list(MCPServer.from_dict, obj.get("servers")) return MCPServerList(servers) @@ -7682,7 +7696,7 @@ class MCPSetEnvValueModeParams: """ @staticmethod - def from_dict(obj: Any) -> 'MCPSetEnvValueModeParams': + def from_dict(obj: Any) -> MCPSetEnvValueModeParams: assert isinstance(obj, dict) mode = MCPSetEnvValueModeDetails(obj.get("mode")) return MCPSetEnvValueModeParams(mode) @@ -7701,7 +7715,7 @@ class MCPSetEnvValueModeResult: """Mode recorded on the session after the update""" @staticmethod - def from_dict(obj: Any) -> 'MCPSetEnvValueModeResult': + def from_dict(obj: Any) -> MCPSetEnvValueModeResult: assert isinstance(obj, dict) mode = MCPSetEnvValueModeDetails(obj.get("mode")) return MCPSetEnvValueModeResult(mode) @@ -7722,7 +7736,7 @@ class MetadataContextInfoResult: """ @staticmethod - def from_dict(obj: Any) -> 'MetadataContextInfoResult': + def from_dict(obj: Any) -> MetadataContextInfoResult: assert isinstance(obj, dict) context_info = from_union([SessionContextInfo.from_dict, from_none], obj.get("contextInfo")) return MetadataContextInfoResult(context_info) @@ -7765,7 +7779,7 @@ class SessionWorkingDirectoryContext: """Raw host string from the git remote URL (e.g. "github.com", "dev.azure.com")""" @staticmethod - def from_dict(obj: Any) -> 'SessionWorkingDirectoryContext': + def from_dict(obj: Any) -> SessionWorkingDirectoryContext: assert isinstance(obj, dict) cwd = from_str(obj.get("cwd")) base_commit = from_union([from_str, from_none], obj.get("baseCommit")) @@ -7820,7 +7834,7 @@ class SessionContext: """Repository slug in `owner/name` form, when known""" @staticmethod - def from_dict(obj: Any) -> 'SessionContext': + def from_dict(obj: Any) -> SessionContext: assert isinstance(obj, dict) cwd = from_str(obj.get("cwd")) branch = from_union([from_str, from_none], obj.get("branch")) @@ -7864,7 +7878,7 @@ class Workspace: user_named: bool | None = None @staticmethod - def from_dict(obj: Any) -> 'Workspace': + def from_dict(obj: Any) -> Workspace: assert isinstance(obj, dict) id = from_str(obj.get("id")) branch = from_union([from_str, from_none], obj.get("branch")) @@ -7941,7 +7955,7 @@ class MetadataSnapshotRemoteMetadata: """ @staticmethod - def from_dict(obj: Any) -> 'MetadataSnapshotRemoteMetadata': + def from_dict(obj: Any) -> MetadataSnapshotRemoteMetadata: assert isinstance(obj, dict) repository = MetadataSnapshotRemoteMetadataRepository.from_dict(obj.get("repository")) pull_request_number = from_union([from_int, from_none], obj.get("pullRequestNumber")) @@ -7971,7 +7985,7 @@ class ModelBilling: """Token-level pricing information for this model""" @staticmethod - def from_dict(obj: Any) -> 'ModelBilling': + def from_dict(obj: Any) -> ModelBilling: assert isinstance(obj, dict) multiplier = from_union([from_float, from_none], obj.get("multiplier")) token_prices = from_union([ModelBillingTokenPrices.from_dict, from_none], obj.get("tokenPrices")) @@ -8002,7 +8016,7 @@ class ModelCapabilitiesLimits: """Vision-specific limits""" @staticmethod - def from_dict(obj: Any) -> 'ModelCapabilitiesLimits': + def from_dict(obj: Any) -> ModelCapabilitiesLimits: assert isinstance(obj, dict) max_context_window_tokens = from_union([from_int, from_none], obj.get("max_context_window_tokens")) max_output_tokens = from_union([from_int, from_none], obj.get("max_output_tokens")) @@ -8033,7 +8047,7 @@ class ModelPolicy: """Usage terms or conditions for this model""" @staticmethod - def from_dict(obj: Any) -> 'ModelPolicy': + def from_dict(obj: Any) -> ModelPolicy: assert isinstance(obj, dict) state = ModelPolicyState(obj.get("state")) terms = from_union([from_str, from_none], obj.get("terms")) @@ -8064,7 +8078,7 @@ class ModelCapabilitiesOverrideLimits: """Vision-specific limits""" @staticmethod - def from_dict(obj: Any) -> 'ModelCapabilitiesOverrideLimits': + def from_dict(obj: Any) -> ModelCapabilitiesOverrideLimits: assert isinstance(obj, dict) max_context_window_tokens = from_union([from_int, from_none], obj.get("max_context_window_tokens")) max_output_tokens = from_union([from_int, from_none], obj.get("max_output_tokens")) @@ -8097,7 +8111,7 @@ class PendingPermissionRequestList: """ @staticmethod - def from_dict(obj: Any) -> 'PendingPermissionRequestList': + def from_dict(obj: Any) -> PendingPermissionRequestList: assert isinstance(obj, dict) items = from_list(PendingPermissionRequest.from_dict, obj.get("items")) return PendingPermissionRequestList(items) @@ -8122,7 +8136,7 @@ class PermissionDecisionApproveForLocation: """Location key (git root or cwd) to persist the approval to""" @staticmethod - def from_dict(obj: Any) -> 'PermissionDecisionApproveForLocation': + def from_dict(obj: Any) -> PermissionDecisionApproveForLocation: assert isinstance(obj, dict) approval = _load_PermissionDecisionApproveForLocationApproval(obj.get("approval")) location_key = from_str(obj.get("locationKey")) @@ -8147,7 +8161,7 @@ class PermissionDecisionApproveForLocationApprovalCommands: """Approval scoped to specific command identifiers.""" @staticmethod - def from_dict(obj: Any) -> 'PermissionDecisionApproveForLocationApprovalCommands': + def from_dict(obj: Any) -> PermissionDecisionApproveForLocationApprovalCommands: assert isinstance(obj, dict) command_identifiers = from_list(from_str, obj.get("commandIdentifiers")) return PermissionDecisionApproveForLocationApprovalCommands(command_identifiers) @@ -8170,7 +8184,7 @@ class PermissionDecisionApproveForSessionApprovalCommands: """Approval scoped to specific command identifiers.""" @staticmethod - def from_dict(obj: Any) -> 'PermissionDecisionApproveForSessionApprovalCommands': + def from_dict(obj: Any) -> PermissionDecisionApproveForSessionApprovalCommands: assert isinstance(obj, dict) command_identifiers = from_list(from_str, obj.get("commandIdentifiers")) return PermissionDecisionApproveForSessionApprovalCommands(command_identifiers) @@ -8193,7 +8207,7 @@ class PermissionsLocationsAddToolApprovalDetailsCommands: """Approval scoped to specific command identifiers.""" @staticmethod - def from_dict(obj: Any) -> 'PermissionsLocationsAddToolApprovalDetailsCommands': + def from_dict(obj: Any) -> PermissionsLocationsAddToolApprovalDetailsCommands: assert isinstance(obj, dict) command_identifiers = from_list(from_str, obj.get("commandIdentifiers")) return PermissionsLocationsAddToolApprovalDetailsCommands(command_identifiers) @@ -8216,7 +8230,7 @@ class PermissionDecisionApproveForLocationApprovalCustomTool: """Custom tool name.""" @staticmethod - def from_dict(obj: Any) -> 'PermissionDecisionApproveForLocationApprovalCustomTool': + def from_dict(obj: Any) -> PermissionDecisionApproveForLocationApprovalCustomTool: assert isinstance(obj, dict) tool_name = from_str(obj.get("toolName")) return PermissionDecisionApproveForLocationApprovalCustomTool(tool_name) @@ -8239,7 +8253,7 @@ class PermissionDecisionApproveForSessionApprovalCustomTool: """Custom tool name.""" @staticmethod - def from_dict(obj: Any) -> 'PermissionDecisionApproveForSessionApprovalCustomTool': + def from_dict(obj: Any) -> PermissionDecisionApproveForSessionApprovalCustomTool: assert isinstance(obj, dict) tool_name = from_str(obj.get("toolName")) return PermissionDecisionApproveForSessionApprovalCustomTool(tool_name) @@ -8262,7 +8276,7 @@ class PermissionsLocationsAddToolApprovalDetailsCustomTool: """Custom tool name.""" @staticmethod - def from_dict(obj: Any) -> 'PermissionsLocationsAddToolApprovalDetailsCustomTool': + def from_dict(obj: Any) -> PermissionsLocationsAddToolApprovalDetailsCustomTool: assert isinstance(obj, dict) tool_name = from_str(obj.get("toolName")) return PermissionsLocationsAddToolApprovalDetailsCustomTool(tool_name) @@ -8287,7 +8301,7 @@ class PermissionDecisionApproveForLocationApprovalExtensionManagement: """ @staticmethod - def from_dict(obj: Any) -> 'PermissionDecisionApproveForLocationApprovalExtensionManagement': + def from_dict(obj: Any) -> PermissionDecisionApproveForLocationApprovalExtensionManagement: assert isinstance(obj, dict) operation = from_union([from_str, from_none], obj.get("operation")) return PermissionDecisionApproveForLocationApprovalExtensionManagement(operation) @@ -8313,7 +8327,7 @@ class PermissionDecisionApproveForSessionApprovalExtensionManagement: """ @staticmethod - def from_dict(obj: Any) -> 'PermissionDecisionApproveForSessionApprovalExtensionManagement': + def from_dict(obj: Any) -> PermissionDecisionApproveForSessionApprovalExtensionManagement: assert isinstance(obj, dict) operation = from_union([from_str, from_none], obj.get("operation")) return PermissionDecisionApproveForSessionApprovalExtensionManagement(operation) @@ -8339,7 +8353,7 @@ class PermissionsLocationsAddToolApprovalDetailsExtensionManagement: """ @staticmethod - def from_dict(obj: Any) -> 'PermissionsLocationsAddToolApprovalDetailsExtensionManagement': + def from_dict(obj: Any) -> PermissionsLocationsAddToolApprovalDetailsExtensionManagement: assert isinstance(obj, dict) operation = from_union([from_str, from_none], obj.get("operation")) return PermissionsLocationsAddToolApprovalDetailsExtensionManagement(operation) @@ -8366,7 +8380,7 @@ class PermissionDecisionApproveForLocationApprovalMCP: """MCP tool name, or null to cover every tool on the server.""" @staticmethod - def from_dict(obj: Any) -> 'PermissionDecisionApproveForLocationApprovalMCP': + def from_dict(obj: Any) -> PermissionDecisionApproveForLocationApprovalMCP: assert isinstance(obj, dict) server_name = from_str(obj.get("serverName")) tool_name = from_union([from_none, from_str], obj.get("toolName")) @@ -8394,7 +8408,7 @@ class PermissionDecisionApproveForSessionApprovalMCP: """MCP tool name, or null to cover every tool on the server.""" @staticmethod - def from_dict(obj: Any) -> 'PermissionDecisionApproveForSessionApprovalMCP': + def from_dict(obj: Any) -> PermissionDecisionApproveForSessionApprovalMCP: assert isinstance(obj, dict) server_name = from_str(obj.get("serverName")) tool_name = from_union([from_none, from_str], obj.get("toolName")) @@ -8422,7 +8436,7 @@ class PermissionsLocationsAddToolApprovalDetailsMCP: """MCP tool name, or null to cover every tool on the server.""" @staticmethod - def from_dict(obj: Any) -> 'PermissionsLocationsAddToolApprovalDetailsMCP': + def from_dict(obj: Any) -> PermissionsLocationsAddToolApprovalDetailsMCP: assert isinstance(obj, dict) server_name = from_str(obj.get("serverName")) tool_name = from_union([from_none, from_str], obj.get("toolName")) @@ -8447,7 +8461,7 @@ class PermissionDecisionApproveForLocationApprovalMCPSampling: """MCP server name.""" @staticmethod - def from_dict(obj: Any) -> 'PermissionDecisionApproveForLocationApprovalMCPSampling': + def from_dict(obj: Any) -> PermissionDecisionApproveForLocationApprovalMCPSampling: assert isinstance(obj, dict) server_name = from_str(obj.get("serverName")) return PermissionDecisionApproveForLocationApprovalMCPSampling(server_name) @@ -8470,7 +8484,7 @@ class PermissionDecisionApproveForSessionApprovalMCPSampling: """MCP server name.""" @staticmethod - def from_dict(obj: Any) -> 'PermissionDecisionApproveForSessionApprovalMCPSampling': + def from_dict(obj: Any) -> PermissionDecisionApproveForSessionApprovalMCPSampling: assert isinstance(obj, dict) server_name = from_str(obj.get("serverName")) return PermissionDecisionApproveForSessionApprovalMCPSampling(server_name) @@ -8493,7 +8507,7 @@ class PermissionsLocationsAddToolApprovalDetailsMCPSampling: """MCP server name.""" @staticmethod - def from_dict(obj: Any) -> 'PermissionsLocationsAddToolApprovalDetailsMCPSampling': + def from_dict(obj: Any) -> PermissionsLocationsAddToolApprovalDetailsMCPSampling: assert isinstance(obj, dict) server_name = from_str(obj.get("serverName")) return PermissionsLocationsAddToolApprovalDetailsMCPSampling(server_name) @@ -8513,7 +8527,7 @@ class PermissionDecisionApproveForLocationApprovalMemory: """Approval covering writes to long-term memory.""" @staticmethod - def from_dict(obj: Any) -> 'PermissionDecisionApproveForLocationApprovalMemory': + def from_dict(obj: Any) -> PermissionDecisionApproveForLocationApprovalMemory: assert isinstance(obj, dict) return PermissionDecisionApproveForLocationApprovalMemory() @@ -8531,7 +8545,7 @@ class PermissionDecisionApproveForSessionApprovalMemory: """Approval covering writes to long-term memory.""" @staticmethod - def from_dict(obj: Any) -> 'PermissionDecisionApproveForSessionApprovalMemory': + def from_dict(obj: Any) -> PermissionDecisionApproveForSessionApprovalMemory: assert isinstance(obj, dict) return PermissionDecisionApproveForSessionApprovalMemory() @@ -8549,7 +8563,7 @@ class PermissionsLocationsAddToolApprovalDetailsMemory: """Approval covering writes to long-term memory.""" @staticmethod - def from_dict(obj: Any) -> 'PermissionsLocationsAddToolApprovalDetailsMemory': + def from_dict(obj: Any) -> PermissionsLocationsAddToolApprovalDetailsMemory: assert isinstance(obj, dict) return PermissionsLocationsAddToolApprovalDetailsMemory() @@ -8567,7 +8581,7 @@ class PermissionDecisionApproveForLocationApprovalRead: """Approval covering read-only filesystem operations.""" @staticmethod - def from_dict(obj: Any) -> 'PermissionDecisionApproveForLocationApprovalRead': + def from_dict(obj: Any) -> PermissionDecisionApproveForLocationApprovalRead: assert isinstance(obj, dict) return PermissionDecisionApproveForLocationApprovalRead() @@ -8585,7 +8599,7 @@ class PermissionDecisionApproveForSessionApprovalRead: """Approval covering read-only filesystem operations.""" @staticmethod - def from_dict(obj: Any) -> 'PermissionDecisionApproveForSessionApprovalRead': + def from_dict(obj: Any) -> PermissionDecisionApproveForSessionApprovalRead: assert isinstance(obj, dict) return PermissionDecisionApproveForSessionApprovalRead() @@ -8603,7 +8617,7 @@ class PermissionsLocationsAddToolApprovalDetailsRead: """Approval covering read-only filesystem operations.""" @staticmethod - def from_dict(obj: Any) -> 'PermissionsLocationsAddToolApprovalDetailsRead': + def from_dict(obj: Any) -> PermissionsLocationsAddToolApprovalDetailsRead: assert isinstance(obj, dict) return PermissionsLocationsAddToolApprovalDetailsRead() @@ -8621,7 +8635,7 @@ class PermissionDecisionApproveForLocationApprovalWrite: """Approval covering filesystem write operations.""" @staticmethod - def from_dict(obj: Any) -> 'PermissionDecisionApproveForLocationApprovalWrite': + def from_dict(obj: Any) -> PermissionDecisionApproveForLocationApprovalWrite: assert isinstance(obj, dict) return PermissionDecisionApproveForLocationApprovalWrite() @@ -8639,7 +8653,7 @@ class PermissionDecisionApproveForSessionApprovalWrite: """Approval covering filesystem write operations.""" @staticmethod - def from_dict(obj: Any) -> 'PermissionDecisionApproveForSessionApprovalWrite': + def from_dict(obj: Any) -> PermissionDecisionApproveForSessionApprovalWrite: assert isinstance(obj, dict) return PermissionDecisionApproveForSessionApprovalWrite() @@ -8657,7 +8671,7 @@ class PermissionsLocationsAddToolApprovalDetailsWrite: """Approval covering filesystem write operations.""" @staticmethod - def from_dict(obj: Any) -> 'PermissionsLocationsAddToolApprovalDetailsWrite': + def from_dict(obj: Any) -> PermissionsLocationsAddToolApprovalDetailsWrite: assert isinstance(obj, dict) return PermissionsLocationsAddToolApprovalDetailsWrite() @@ -8681,7 +8695,7 @@ class PermissionDecisionApproveForSession: """URL domain to approve for the rest of the session (URL prompts only)""" @staticmethod - def from_dict(obj: Any) -> 'PermissionDecisionApproveForSession': + def from_dict(obj: Any) -> PermissionDecisionApproveForSession: assert isinstance(obj, dict) approval = from_union([_load_PermissionDecisionApproveForSessionApproval, from_none], obj.get("approval")) domain = from_union([from_str, from_none], obj.get("domain")) @@ -8705,7 +8719,7 @@ class PermissionDecisionApproveOnce: """Approve this single request only""" @staticmethod - def from_dict(obj: Any) -> 'PermissionDecisionApproveOnce': + def from_dict(obj: Any) -> PermissionDecisionApproveOnce: assert isinstance(obj, dict) return PermissionDecisionApproveOnce() @@ -8726,7 +8740,7 @@ class PermissionDecisionApprovePermanently: """Approve and persist across sessions (URL prompts only)""" @staticmethod - def from_dict(obj: Any) -> 'PermissionDecisionApprovePermanently': + def from_dict(obj: Any) -> PermissionDecisionApprovePermanently: assert isinstance(obj, dict) domain = from_str(obj.get("domain")) return PermissionDecisionApprovePermanently(domain) @@ -8746,7 +8760,7 @@ class PermissionDecisionApproved: """The permission request was approved""" @staticmethod - def from_dict(obj: Any) -> 'PermissionDecisionApproved': + def from_dict(obj: Any) -> PermissionDecisionApproved: assert isinstance(obj, dict) return PermissionDecisionApproved() @@ -8770,7 +8784,7 @@ class PermissionDecisionApprovedForLocation: """The location key (git root or cwd) to persist the approval to""" @staticmethod - def from_dict(obj: Any) -> 'PermissionDecisionApprovedForLocation': + def from_dict(obj: Any) -> PermissionDecisionApprovedForLocation: assert isinstance(obj, dict) approval = UserToolSessionApproval.from_dict(obj.get("approval")) location_key = from_str(obj.get("locationKey")) @@ -8795,7 +8809,7 @@ class PermissionDecisionApprovedForSession: """Approved and remembered for the rest of the session""" @staticmethod - def from_dict(obj: Any) -> 'PermissionDecisionApprovedForSession': + def from_dict(obj: Any) -> PermissionDecisionApprovedForSession: assert isinstance(obj, dict) approval = UserToolSessionApproval.from_dict(obj.get("approval")) return PermissionDecisionApprovedForSession(approval) @@ -8818,7 +8832,7 @@ class PermissionDecisionCancelled: """Optional explanation of why the request was cancelled""" @staticmethod - def from_dict(obj: Any) -> 'PermissionDecisionCancelled': + def from_dict(obj: Any) -> PermissionDecisionCancelled: assert isinstance(obj, dict) reason = from_union([from_str, from_none], obj.get("reason")) return PermissionDecisionCancelled(reason) @@ -8845,7 +8859,7 @@ class PermissionDecisionDeniedByContentExclusionPolicy: """File path that triggered the exclusion""" @staticmethod - def from_dict(obj: Any) -> 'PermissionDecisionDeniedByContentExclusionPolicy': + def from_dict(obj: Any) -> PermissionDecisionDeniedByContentExclusionPolicy: assert isinstance(obj, dict) message = from_str(obj.get("message")) path = from_str(obj.get("path")) @@ -8873,7 +8887,7 @@ class PermissionDecisionDeniedByPermissionRequestHook: """Optional message from the hook explaining the denial""" @staticmethod - def from_dict(obj: Any) -> 'PermissionDecisionDeniedByPermissionRequestHook': + def from_dict(obj: Any) -> PermissionDecisionDeniedByPermissionRequestHook: assert isinstance(obj, dict) interrupt = from_union([from_bool, from_none], obj.get("interrupt")) message = from_union([from_str, from_none], obj.get("message")) @@ -8900,7 +8914,7 @@ class PermissionDecisionDeniedByRules: """Rules that denied the request""" @staticmethod - def from_dict(obj: Any) -> 'PermissionDecisionDeniedByRules': + def from_dict(obj: Any) -> PermissionDecisionDeniedByRules: assert isinstance(obj, dict) rules = from_list(PermissionRule.from_dict, obj.get("rules")) return PermissionDecisionDeniedByRules(rules) @@ -8926,7 +8940,7 @@ class PermissionDecisionDeniedInteractivelyByUser: """Whether to force-reject the current agent turn""" @staticmethod - def from_dict(obj: Any) -> 'PermissionDecisionDeniedInteractivelyByUser': + def from_dict(obj: Any) -> PermissionDecisionDeniedInteractivelyByUser: assert isinstance(obj, dict) feedback = from_union([from_str, from_none], obj.get("feedback")) force_reject = from_union([from_bool, from_none], obj.get("forceReject")) @@ -8950,7 +8964,7 @@ class PermissionDecisionDeniedNoApprovalRuleAndCouldNotRequestFromUser: """Denied because no approval rule matched and user confirmation was unavailable""" @staticmethod - def from_dict(obj: Any) -> 'PermissionDecisionDeniedNoApprovalRuleAndCouldNotRequestFromUser': + def from_dict(obj: Any) -> PermissionDecisionDeniedNoApprovalRuleAndCouldNotRequestFromUser: assert isinstance(obj, dict) return PermissionDecisionDeniedNoApprovalRuleAndCouldNotRequestFromUser() @@ -8971,7 +8985,7 @@ class PermissionDecisionReject: """Optional feedback explaining the rejection""" @staticmethod - def from_dict(obj: Any) -> 'PermissionDecisionReject': + def from_dict(obj: Any) -> PermissionDecisionReject: assert isinstance(obj, dict) feedback = from_union([from_str, from_none], obj.get("feedback")) return PermissionDecisionReject(feedback) @@ -8992,7 +9006,7 @@ class PermissionDecisionUserNotAvailable: """No user is available to confirm the request""" @staticmethod - def from_dict(obj: Any) -> 'PermissionDecisionUserNotAvailable': + def from_dict(obj: Any) -> PermissionDecisionUserNotAvailable: assert isinstance(obj, dict) return PermissionDecisionUserNotAvailable() @@ -9025,7 +9039,7 @@ class PermissionLocationApplyResult: """Whether the location is a git repo or directory""" @staticmethod - def from_dict(obj: Any) -> 'PermissionLocationApplyResult': + def from_dict(obj: Any) -> PermissionLocationApplyResult: assert isinstance(obj, dict) applied_directory_count = from_int(obj.get("appliedDirectoryCount")) applied_rule_count = from_int(obj.get("appliedRuleCount")) @@ -9057,7 +9071,7 @@ class PermissionLocationResolveResult: """Whether the location is a git repo or directory""" @staticmethod - def from_dict(obj: Any) -> 'PermissionLocationResolveResult': + def from_dict(obj: Any) -> PermissionLocationResolveResult: assert isinstance(obj, dict) location_key = from_str(obj.get("locationKey")) location_type = PermissionLocationType(obj.get("locationType")) @@ -9082,7 +9096,7 @@ class PermissionsConfigureAdditionalContentExclusionPolicyRule: if_none_match: list[str] | None = None @staticmethod - def from_dict(obj: Any) -> 'PermissionsConfigureAdditionalContentExclusionPolicyRule': + def from_dict(obj: Any) -> PermissionsConfigureAdditionalContentExclusionPolicyRule: assert isinstance(obj, dict) paths = from_list(from_str, obj.get("paths")) source = PermissionsConfigureAdditionalContentExclusionPolicyRuleSource.from_dict(obj.get("source")) @@ -9122,7 +9136,7 @@ class PermissionsModifyRulesParams: """ @staticmethod - def from_dict(obj: Any) -> 'PermissionsModifyRulesParams': + def from_dict(obj: Any) -> PermissionsModifyRulesParams: assert isinstance(obj, dict) scope = PermissionsModifyRulesScope(obj.get("scope")) add = from_union([lambda x: from_list(PermissionRule.from_dict, x), from_none], obj.get("add")) @@ -9150,7 +9164,7 @@ class PluginList: """Installed plugins""" @staticmethod - def from_dict(obj: Any) -> 'PluginList': + def from_dict(obj: Any) -> PluginList: assert isinstance(obj, dict) plugins = from_list(Plugin.from_dict, obj.get("plugins")) return PluginList(plugins) @@ -9172,7 +9186,7 @@ class QueuePendingItems: """Whether this item is a queued user message or a queued slash command / model change""" @staticmethod - def from_dict(obj: Any) -> 'QueuePendingItems': + def from_dict(obj: Any) -> QueuePendingItems: assert isinstance(obj, dict) display_text = from_str(obj.get("displayText")) kind = QueuePendingItemsKind(obj.get("kind")) @@ -9196,7 +9210,7 @@ class RemoteEnableRequest: """ @staticmethod - def from_dict(obj: Any) -> 'RemoteEnableRequest': + def from_dict(obj: Any) -> RemoteEnableRequest: assert isinstance(obj, dict) mode = from_union([RemoteSessionMode, from_none], obj.get("mode")) return RemoteEnableRequest(mode) @@ -9216,7 +9230,7 @@ class ScheduleList: """Active scheduled prompts, ordered by id.""" @staticmethod - def from_dict(obj: Any) -> 'ScheduleList': + def from_dict(obj: Any) -> ScheduleList: assert isinstance(obj, dict) entries = from_list(ScheduleEntry.from_dict, obj.get("entries")) return ScheduleList(entries) @@ -9235,7 +9249,7 @@ class ScheduleStopResult: """The removed entry, or omitted if no entry matched.""" @staticmethod - def from_dict(obj: Any) -> 'ScheduleStopResult': + def from_dict(obj: Any) -> ScheduleStopResult: assert isinstance(obj, dict) entry = from_union([ScheduleEntry.from_dict, from_none], obj.get("entry")) return ScheduleStopResult(entry) @@ -9258,7 +9272,7 @@ class SendAttachmentSelectionDetails: """Start position of the selection""" @staticmethod - def from_dict(obj: Any) -> 'SendAttachmentSelectionDetails': + def from_dict(obj: Any) -> SendAttachmentSelectionDetails: assert isinstance(obj, dict) end = SendAttachmentSelectionDetailsEnd.from_dict(obj.get("end")) start = SendAttachmentSelectionDetailsStart.from_dict(obj.get("start")) @@ -9288,7 +9302,7 @@ class SendAttachmentBlob: """User-facing display name for the attachment""" @staticmethod - def from_dict(obj: Any) -> 'SendAttachmentBlob': + def from_dict(obj: Any) -> SendAttachmentBlob: assert isinstance(obj, dict) data = from_str(obj.get("data")) mime_type = from_str(obj.get("mimeType")) @@ -9322,7 +9336,7 @@ class SendAttachmentFile: """Optional line range to scope the attachment to a specific section of the file""" @staticmethod - def from_dict(obj: Any) -> 'SendAttachmentFile': + def from_dict(obj: Any) -> SendAttachmentFile: assert isinstance(obj, dict) display_name = from_str(obj.get("displayName")) path = from_str(obj.get("path")) @@ -9362,7 +9376,7 @@ class SendAttachmentGithubReference: """URL to the referenced item on GitHub""" @staticmethod - def from_dict(obj: Any) -> 'SendAttachmentGithubReference': + def from_dict(obj: Any) -> SendAttachmentGithubReference: assert isinstance(obj, dict) number = from_int(obj.get("number")) reference_type = SendAttachmentGithubReferenceTypeEnum(obj.get("referenceType")) @@ -9437,7 +9451,7 @@ class SendRequest: """ @staticmethod - def from_dict(obj: Any) -> 'SendRequest': + def from_dict(obj: Any) -> SendRequest: assert isinstance(obj, dict) prompt = from_str(obj.get("prompt")) agent_mode = from_union([SendAgentMode, from_none], obj.get("agentMode")) @@ -9491,7 +9505,7 @@ class ServerSkillList: """All discovered skills across all sources""" @staticmethod - def from_dict(obj: Any) -> 'ServerSkillList': + def from_dict(obj: Any) -> ServerSkillList: assert isinstance(obj, dict) skills = from_list(ServerSkill.from_dict, obj.get("skills")) return ServerSkillList(skills) @@ -9512,7 +9526,7 @@ class SessionFSError: """Free-form detail about the error, for logging/diagnostics""" @staticmethod - def from_dict(obj: Any) -> 'SessionFSError': + def from_dict(obj: Any) -> SessionFSError: assert isinstance(obj, dict) code = SessionFSErrorCode(obj.get("code")) message = from_union([from_str, from_none], obj.get("message")) @@ -9536,7 +9550,7 @@ class SessionFSReaddirWithTypesEntry: """Entry type""" @staticmethod - def from_dict(obj: Any) -> 'SessionFSReaddirWithTypesEntry': + def from_dict(obj: Any) -> SessionFSReaddirWithTypesEntry: assert isinstance(obj, dict) name = from_str(obj.get("name")) type = SessionFSReaddirWithTypesEntryType(obj.get("type")) @@ -9566,7 +9580,7 @@ class SessionFSSetProviderRequest: """Optional capabilities declared by the provider""" @staticmethod - def from_dict(obj: Any) -> 'SessionFSSetProviderRequest': + def from_dict(obj: Any) -> SessionFSSetProviderRequest: assert isinstance(obj, dict) conventions = SessionFSSetProviderConventions(obj.get("conventions")) initial_cwd = from_str(obj.get("initialCwd")) @@ -9602,7 +9616,7 @@ class SessionFSSqliteQueryRequest: """Optional named bind parameters""" @staticmethod - def from_dict(obj: Any) -> 'SessionFSSqliteQueryRequest': + def from_dict(obj: Any) -> SessionFSSqliteQueryRequest: assert isinstance(obj, dict) query = from_str(obj.get("query")) query_type = SessionFSSqliteQueryType(obj.get("queryType")) @@ -9634,7 +9648,7 @@ class SessionsListRequest: """ @staticmethod - def from_dict(obj: Any) -> 'SessionsListRequest': + def from_dict(obj: Any) -> SessionsListRequest: assert isinstance(obj, dict) filter = from_union([SessionListFilter.from_dict, from_none], obj.get("filter")) metadata_limit = from_union([from_int, from_none], obj.get("metadataLimit")) @@ -9660,7 +9674,7 @@ class ShellKillRequest: """Signal to send (default: SIGTERM)""" @staticmethod - def from_dict(obj: Any) -> 'ShellKillRequest': + def from_dict(obj: Any) -> ShellKillRequest: assert isinstance(obj, dict) process_id = from_str(obj.get("processId")) signal = from_union([ShellKillSignal, from_none], obj.get("signal")) @@ -9720,7 +9734,7 @@ class AgentInfo: """ @staticmethod - def from_dict(obj: Any) -> 'AgentInfo': + def from_dict(obj: Any) -> AgentInfo: assert isinstance(obj, dict) description = from_str(obj.get("description")) display_name = from_str(obj.get("displayName")) @@ -9766,7 +9780,7 @@ class SkillList: """Available skills""" @staticmethod - def from_dict(obj: Any) -> 'SkillList': + def from_dict(obj: Any) -> SkillList: assert isinstance(obj, dict) skills = from_list(Skill.from_dict, obj.get("skills")) return SkillList(skills) @@ -9784,7 +9798,7 @@ class SkillsConfigSetDisabledSkillsRequest: """List of skill names to disable""" @staticmethod - def from_dict(obj: Any) -> 'SkillsConfigSetDisabledSkillsRequest': + def from_dict(obj: Any) -> SkillsConfigSetDisabledSkillsRequest: assert isinstance(obj, dict) disabled_skills = from_list(from_str, obj.get("disabledSkills")) return SkillsConfigSetDisabledSkillsRequest(disabled_skills) @@ -9803,7 +9817,7 @@ class SkillsGetInvokedResult: """Skills invoked during this session, ordered by invocation time (most recent last)""" @staticmethod - def from_dict(obj: Any) -> 'SkillsGetInvokedResult': + def from_dict(obj: Any) -> SkillsGetInvokedResult: assert isinstance(obj, dict) skills = from_list(SkillsInvokedSkill.from_dict, obj.get("skills")) return SkillsGetInvokedResult(skills) @@ -9836,7 +9850,7 @@ class SlashCommandAgentPromptResult: """ @staticmethod - def from_dict(obj: Any) -> 'SlashCommandAgentPromptResult': + def from_dict(obj: Any) -> SlashCommandAgentPromptResult: assert isinstance(obj, dict) display_prompt = from_str(obj.get("displayPrompt")) prompt = from_str(obj.get("prompt")) @@ -9872,7 +9886,7 @@ class SlashCommandCompletedResult: """ @staticmethod - def from_dict(obj: Any) -> 'SlashCommandCompletedResult': + def from_dict(obj: Any) -> SlashCommandCompletedResult: assert isinstance(obj, dict) message = from_union([from_str, from_none], obj.get("message")) runtime_settings_changed = from_union([from_bool, from_none], obj.get("runtimeSettingsChanged")) @@ -9910,7 +9924,7 @@ class SlashCommandSelectSubcommandResult: """ @staticmethod - def from_dict(obj: Any) -> 'SlashCommandSelectSubcommandResult': + def from_dict(obj: Any) -> SlashCommandSelectSubcommandResult: assert isinstance(obj, dict) command = from_str(obj.get("command")) options = from_list(SlashCommandSelectSubcommandOption.from_dict, obj.get("options")) @@ -9943,7 +9957,7 @@ class TaskAgentProgress: """The most recent intent reported by the agent""" @staticmethod - def from_dict(obj: Any) -> 'TaskAgentProgress': + def from_dict(obj: Any) -> TaskAgentProgress: assert isinstance(obj, dict) recent_activity = from_list(TaskProgressLine.from_dict, obj.get("recentActivity")) type = TaskAgentInfoType(obj.get("type")) @@ -10001,7 +10015,7 @@ class TaskShellInfo: """Process ID when available""" @staticmethod - def from_dict(obj: Any) -> 'TaskShellInfo': + def from_dict(obj: Any) -> TaskShellInfo: assert isinstance(obj, dict) attachment_mode = TaskShellInfoAttachmentMode(obj.get("attachmentMode")) command = from_str(obj.get("command")) @@ -10052,7 +10066,7 @@ class TaskShellProgress: """Process ID when available""" @staticmethod - def from_dict(obj: Any) -> 'TaskShellProgress': + def from_dict(obj: Any) -> TaskShellProgress: assert isinstance(obj, dict) recent_output = from_str(obj.get("recentOutput")) type = TaskShellInfoType(obj.get("type")) @@ -10079,7 +10093,7 @@ class PermissionLocationAddToolApprovalParams: """Location key (git root or cwd) to persist the approval to""" @staticmethod - def from_dict(obj: Any) -> 'PermissionLocationAddToolApprovalParams': + def from_dict(obj: Any) -> PermissionLocationAddToolApprovalParams: assert isinstance(obj, dict) approval = _load_PermissionsLocationsAddToolApprovalDetails(obj.get("approval")) location_key = from_str(obj.get("locationKey")) @@ -10099,7 +10113,7 @@ class ToolList: """List of available built-in tools with metadata""" @staticmethod - def from_dict(obj: Any) -> 'ToolList': + def from_dict(obj: Any) -> ToolList: assert isinstance(obj, dict) tools = from_list(Tool.from_dict, obj.get("tools")) return ToolList(tools) @@ -10123,7 +10137,7 @@ class UIHandlePendingAutoModeSwitchRequest: """ @staticmethod - def from_dict(obj: Any) -> 'UIHandlePendingAutoModeSwitchRequest': + def from_dict(obj: Any) -> UIHandlePendingAutoModeSwitchRequest: assert isinstance(obj, dict) request_id = from_str(obj.get("requestId")) response = UIAutoModeSwitchResponse(obj.get("response")) @@ -10144,7 +10158,7 @@ class UIElicitationArrayAnyOfFieldItems: """Selectable options, each with a value and a display label.""" @staticmethod - def from_dict(obj: Any) -> 'UIElicitationArrayAnyOfFieldItems': + def from_dict(obj: Any) -> UIElicitationArrayAnyOfFieldItems: assert isinstance(obj, dict) any_of = from_list(UIElicitationArrayAnyOfFieldItemsAnyOf.from_dict, obj.get("anyOf")) return UIElicitationArrayAnyOfFieldItems(any_of) @@ -10166,7 +10180,7 @@ class UIElicitationArrayEnumFieldItems: """Type discriminator. Always "string".""" @staticmethod - def from_dict(obj: Any) -> 'UIElicitationArrayEnumFieldItems': + def from_dict(obj: Any) -> UIElicitationArrayEnumFieldItems: assert isinstance(obj, dict) enum = from_list(from_str, obj.get("enum")) type = UIElicitationArrayEnumFieldItemsType(obj.get("type")) @@ -10192,7 +10206,7 @@ class UIElicitationArrayFieldItems: """Selectable options, each with a value and a display label.""" @staticmethod - def from_dict(obj: Any) -> 'UIElicitationArrayFieldItems': + def from_dict(obj: Any) -> UIElicitationArrayFieldItems: assert isinstance(obj, dict) enum = from_union([lambda x: from_list(from_str, x), from_none], obj.get("enum")) type = from_union([UIElicitationArrayEnumFieldItemsType, from_none], obj.get("type")) @@ -10233,7 +10247,7 @@ class UIElicitationStringEnumField: """Human-readable label for the field.""" @staticmethod - def from_dict(obj: Any) -> 'UIElicitationStringEnumField': + def from_dict(obj: Any) -> UIElicitationStringEnumField: assert isinstance(obj, dict) enum = from_list(from_str, obj.get("enum")) type = UIElicitationArrayEnumFieldItemsType(obj.get("type")) @@ -10284,7 +10298,7 @@ class UIElicitationSchemaPropertyString: """Human-readable label for the field.""" @staticmethod - def from_dict(obj: Any) -> 'UIElicitationSchemaPropertyString': + def from_dict(obj: Any) -> UIElicitationSchemaPropertyString: assert isinstance(obj, dict) type = UIElicitationArrayEnumFieldItemsType(obj.get("type")) default = from_union([from_str, from_none], obj.get("default")) @@ -10333,7 +10347,7 @@ class UIElicitationStringOneOfField: """Human-readable label for the field.""" @staticmethod - def from_dict(obj: Any) -> 'UIElicitationStringOneOfField': + def from_dict(obj: Any) -> UIElicitationStringOneOfField: assert isinstance(obj, dict) one_of = from_list(UIElicitationStringOneOfFieldOneOf.from_dict, obj.get("oneOf")) type = UIElicitationArrayEnumFieldItemsType(obj.get("type")) @@ -10366,7 +10380,7 @@ class UIElicitationResponse: """The form values submitted by the user (present when action is 'accept')""" @staticmethod - def from_dict(obj: Any) -> 'UIElicitationResponse': + def from_dict(obj: Any) -> UIElicitationResponse: assert isinstance(obj, dict) action = UIElicitationResponseAction(obj.get("action")) content = from_union([lambda x: from_dict(lambda x: from_union([from_float, from_bool, lambda x: from_list(from_str, x), from_str], x), x), from_none], obj.get("content")) @@ -10397,7 +10411,7 @@ class UIElicitationSchemaPropertyBoolean: """Human-readable label for the field.""" @staticmethod - def from_dict(obj: Any) -> 'UIElicitationSchemaPropertyBoolean': + def from_dict(obj: Any) -> UIElicitationSchemaPropertyBoolean: assert isinstance(obj, dict) type = UIElicitationSchemaPropertyBooleanType(obj.get("type")) default = from_union([from_bool, from_none], obj.get("default")) @@ -10440,7 +10454,7 @@ class UIElicitationSchemaPropertyNumber: """Human-readable label for the field.""" @staticmethod - def from_dict(obj: Any) -> 'UIElicitationSchemaPropertyNumber': + def from_dict(obj: Any) -> UIElicitationSchemaPropertyNumber: assert isinstance(obj, dict) type = UIElicitationSchemaPropertyNumberType(obj.get("type")) default = from_union([from_float, from_none], obj.get("default")) @@ -10485,7 +10499,7 @@ class UIExitPlanModeResponse: """ @staticmethod - def from_dict(obj: Any) -> 'UIExitPlanModeResponse': + def from_dict(obj: Any) -> UIExitPlanModeResponse: assert isinstance(obj, dict) approved = from_bool(obj.get("approved")) auto_approve_edits = from_union([from_bool, from_none], obj.get("autoApproveEdits")) @@ -10516,7 +10530,7 @@ class UIHandlePendingUserInputRequest: """Schema for the `UIUserInputResponse` type.""" @staticmethod - def from_dict(obj: Any) -> 'UIHandlePendingUserInputRequest': + def from_dict(obj: Any) -> UIHandlePendingUserInputRequest: assert isinstance(obj, dict) request_id = from_str(obj.get("requestId")) response = UIUserInputResponse.from_dict(obj.get("response")) @@ -10546,7 +10560,7 @@ class UsageMetricsModelMetric: """Accumulated nano-AI units cost for this model""" @staticmethod - def from_dict(obj: Any) -> 'UsageMetricsModelMetric': + def from_dict(obj: Any) -> UsageMetricsModelMetric: assert isinstance(obj, dict) requests = UsageMetricsModelMetricRequests.from_dict(obj.get("requests")) usage = UsageMetricsModelMetricUsage.from_dict(obj.get("usage")) @@ -10575,7 +10589,7 @@ class WorkspacesSaveLargePasteResult: """ @staticmethod - def from_dict(obj: Any) -> 'WorkspacesSaveLargePasteResult': + def from_dict(obj: Any) -> WorkspacesSaveLargePasteResult: assert isinstance(obj, dict) saved = from_union([Saved.from_dict, from_none], obj.get("saved")) return WorkspacesSaveLargePasteResult(saved) @@ -10613,7 +10627,7 @@ class SlashCommandInfo: """Optional unstructured input hint""" @staticmethod - def from_dict(obj: Any) -> 'SlashCommandInfo': + def from_dict(obj: Any) -> SlashCommandInfo: assert isinstance(obj, dict) allow_during_agent_execution = from_bool(obj.get("allowDuringAgentExecution")) description = from_str(obj.get("description")) @@ -10650,7 +10664,7 @@ class RemoteSessionConnectionResult: """SDK session ID for the connected remote session.""" @staticmethod - def from_dict(obj: Any) -> 'RemoteSessionConnectionResult': + def from_dict(obj: Any) -> RemoteSessionConnectionResult: assert isinstance(obj, dict) metadata = ConnectedRemoteSessionMetadata.from_dict(obj.get("metadata")) session_id = from_str(obj.get("sessionId")) @@ -10698,7 +10712,7 @@ class CopilotUserResponse: token_based_billing: bool | None = None @staticmethod - def from_dict(obj: Any) -> 'CopilotUserResponse': + def from_dict(obj: Any) -> CopilotUserResponse: assert isinstance(obj, dict) access_type_sku = from_union([from_str, from_none], obj.get("access_type_sku")) analytics_tracking_id = from_union([from_str, from_none], obj.get("analytics_tracking_id")) @@ -10783,7 +10797,7 @@ class MCPDiscoverResult: """MCP servers discovered from all sources""" @staticmethod - def from_dict(obj: Any) -> 'MCPDiscoverResult': + def from_dict(obj: Any) -> MCPDiscoverResult: assert isinstance(obj, dict) servers = from_list(DiscoveredMCPServer.from_dict, obj.get("servers")) return MCPDiscoverResult(servers) @@ -10802,7 +10816,7 @@ class ExtensionList: """Discovered extensions and their current status""" @staticmethod - def from_dict(obj: Any) -> 'ExtensionList': + def from_dict(obj: Any) -> ExtensionList: assert isinstance(obj, dict) extensions = from_list(Extension.from_dict, obj.get("extensions")) return ExtensionList(extensions) @@ -10825,7 +10839,7 @@ class PermissionDecisionApproveForLocationApprovalExtensionPermissionAccess: """Approval covering an extension's request to access a permission-gated capability.""" @staticmethod - def from_dict(obj: Any) -> 'PermissionDecisionApproveForLocationApprovalExtensionPermissionAccess': + def from_dict(obj: Any) -> PermissionDecisionApproveForLocationApprovalExtensionPermissionAccess: assert isinstance(obj, dict) extension_name = from_str(obj.get("extensionName")) return PermissionDecisionApproveForLocationApprovalExtensionPermissionAccess(extension_name) @@ -10849,7 +10863,7 @@ class PermissionDecisionApproveForSessionApprovalExtensionPermissionAccess: """Approval covering an extension's request to access a permission-gated capability.""" @staticmethod - def from_dict(obj: Any) -> 'PermissionDecisionApproveForSessionApprovalExtensionPermissionAccess': + def from_dict(obj: Any) -> PermissionDecisionApproveForSessionApprovalExtensionPermissionAccess: assert isinstance(obj, dict) extension_name = from_str(obj.get("extensionName")) return PermissionDecisionApproveForSessionApprovalExtensionPermissionAccess(extension_name) @@ -10872,7 +10886,7 @@ class PermissionsLocationsAddToolApprovalDetailsExtensionPermissionAccess: """Approval covering an extension's request to access a permission-gated capability.""" @staticmethod - def from_dict(obj: Any) -> 'PermissionsLocationsAddToolApprovalDetailsExtensionPermissionAccess': + def from_dict(obj: Any) -> PermissionsLocationsAddToolApprovalDetailsExtensionPermissionAccess: assert isinstance(obj, dict) extension_name = from_str(obj.get("extensionName")) return PermissionsLocationsAddToolApprovalDetailsExtensionPermissionAccess(extension_name) @@ -10911,7 +10925,7 @@ class ExternalToolTextResultForLlm: """Optional tool-specific telemetry""" @staticmethod - def from_dict(obj: Any) -> 'ExternalToolTextResultForLlm': + def from_dict(obj: Any) -> ExternalToolTextResultForLlm: assert isinstance(obj, dict) text_result_for_llm = from_str(obj.get("textResultForLlm")) binary_results_for_llm = from_union([lambda x: from_list(ExternalToolTextResultForLlmBinaryResultsForLlm.from_dict, x), from_none], obj.get("binaryResultsForLlm")) @@ -10969,7 +10983,7 @@ class ExternalToolTextResultForLlmContentResourceLink: """Human-readable display title for the resource""" @staticmethod - def from_dict(obj: Any) -> 'ExternalToolTextResultForLlmContentResourceLink': + def from_dict(obj: Any) -> ExternalToolTextResultForLlmContentResourceLink: assert isinstance(obj, dict) name = from_str(obj.get("name")) uri = from_str(obj.get("uri")) @@ -11019,7 +11033,7 @@ class InstalledPluginSource: url: str | None = None @staticmethod - def from_dict(obj: Any) -> 'InstalledPluginSource': + def from_dict(obj: Any) -> InstalledPluginSource: assert isinstance(obj, dict) source = PurpleSource(obj.get("source")) path = from_union([from_str, from_none], obj.get("path")) @@ -11063,7 +11077,7 @@ class SessionInstalledPluginSource: url: str | None = None @staticmethod - def from_dict(obj: Any) -> 'SessionInstalledPluginSource': + def from_dict(obj: Any) -> SessionInstalledPluginSource: assert isinstance(obj, dict) source = PurpleSource(obj.get("source")) path = from_union([from_str, from_none], obj.get("path")) @@ -11094,7 +11108,7 @@ class InstructionsGetSourcesResult: """Instruction sources for the session""" @staticmethod - def from_dict(obj: Any) -> 'InstructionsGetSourcesResult': + def from_dict(obj: Any) -> InstructionsGetSourcesResult: assert isinstance(obj, dict) sources = from_list(InstructionsSources.from_dict, obj.get("sources")) return InstructionsGetSourcesResult(sources) @@ -11115,7 +11129,7 @@ class MCPConfigAddRequest: """Unique name for the MCP server""" @staticmethod - def from_dict(obj: Any) -> 'MCPConfigAddRequest': + def from_dict(obj: Any) -> MCPConfigAddRequest: assert isinstance(obj, dict) config = MCPServerConfig.from_dict(obj.get("config")) name = from_str(obj.get("name")) @@ -11135,7 +11149,7 @@ class MCPConfigList: """All MCP servers from user config, keyed by name""" @staticmethod - def from_dict(obj: Any) -> 'MCPConfigList': + def from_dict(obj: Any) -> MCPConfigList: assert isinstance(obj, dict) servers = from_dict(MCPServerConfig.from_dict, obj.get("servers")) return MCPConfigList(servers) @@ -11156,7 +11170,7 @@ class MCPConfigUpdateRequest: """Name of the MCP server to update""" @staticmethod - def from_dict(obj: Any) -> 'MCPConfigUpdateRequest': + def from_dict(obj: Any) -> MCPConfigUpdateRequest: assert isinstance(obj, dict) config = MCPServerConfig.from_dict(obj.get("config")) name = from_str(obj.get("name")) @@ -11179,7 +11193,7 @@ class MetadataRecordContextChangeRequest: """ @staticmethod - def from_dict(obj: Any) -> 'MetadataRecordContextChangeRequest': + def from_dict(obj: Any) -> MetadataRecordContextChangeRequest: assert isinstance(obj, dict) context = SessionWorkingDirectoryContext.from_dict(obj.get("context")) return MetadataRecordContextChangeRequest(context) @@ -11220,7 +11234,7 @@ class SessionMetadata: """Short summary of the session, when one has been derived""" @staticmethod - def from_dict(obj: Any) -> 'SessionMetadata': + def from_dict(obj: Any) -> SessionMetadata: assert isinstance(obj, dict) is_remote = from_bool(obj.get("isRemote")) modified_time = from_str(obj.get("modifiedTime")) @@ -11259,7 +11273,7 @@ class SessionsGetLastForContextRequest: """ @staticmethod - def from_dict(obj: Any) -> 'SessionsGetLastForContextRequest': + def from_dict(obj: Any) -> SessionsGetLastForContextRequest: assert isinstance(obj, dict) context = from_union([SessionContext.from_dict, from_none], obj.get("context")) return SessionsGetLastForContextRequest(context) @@ -11297,7 +11311,7 @@ class PermissionPathsConfig: """ @staticmethod - def from_dict(obj: Any) -> 'PermissionPathsConfig': + def from_dict(obj: Any) -> PermissionPathsConfig: assert isinstance(obj, dict) additional_directories = from_union([lambda x: from_list(from_str, x), from_none], obj.get("additionalDirectories")) include_temp_directory = from_union([from_bool, from_none], obj.get("includeTempDirectory")) @@ -11350,7 +11364,7 @@ class WorkspaceSummary: """ISO 8601 timestamp when the workspace was last updated""" @staticmethod - def from_dict(obj: Any) -> 'WorkspaceSummary': + def from_dict(obj: Any) -> WorkspaceSummary: assert isinstance(obj, dict) id = from_str(obj.get("id")) branch = from_union([from_str, from_none], obj.get("branch")) @@ -11398,7 +11412,7 @@ class WorkspacesGetWorkspaceResult: """Current workspace metadata, or null if not available""" @staticmethod - def from_dict(obj: Any) -> 'WorkspacesGetWorkspaceResult': + def from_dict(obj: Any) -> WorkspacesGetWorkspaceResult: assert isinstance(obj, dict) path = from_union([from_str, from_none], obj.get("path")) workspace = from_union([Workspace.from_dict, from_none], obj.get("workspace")) @@ -11420,7 +11434,7 @@ class WorkspacesListCheckpointsResult: """Workspace checkpoints in chronological order. Empty when workspace is not enabled.""" @staticmethod - def from_dict(obj: Any) -> 'WorkspacesListCheckpointsResult': + def from_dict(obj: Any) -> WorkspacesListCheckpointsResult: assert isinstance(obj, dict) checkpoints = from_list(WorkspacesCheckpoints.from_dict, obj.get("checkpoints")) return WorkspacesListCheckpointsResult(checkpoints) @@ -11442,7 +11456,7 @@ class ModelCapabilitiesOverride: """Feature flags indicating what the model supports""" @staticmethod - def from_dict(obj: Any) -> 'ModelCapabilitiesOverride': + def from_dict(obj: Any) -> ModelCapabilitiesOverride: assert isinstance(obj, dict) limits = from_union([ModelCapabilitiesOverrideLimits.from_dict, from_none], obj.get("limits")) supports = from_union([ModelCapabilitiesOverrideSupports.from_dict, from_none], obj.get("supports")) @@ -11469,7 +11483,7 @@ class PermissionsConfigureAdditionalContentExclusionPolicy: """ @staticmethod - def from_dict(obj: Any) -> 'PermissionsConfigureAdditionalContentExclusionPolicy': + def from_dict(obj: Any) -> PermissionsConfigureAdditionalContentExclusionPolicy: assert isinstance(obj, dict) last_updated_at = from_union([from_float, from_str], obj.get("last_updated_at")) rules = from_list(PermissionsConfigureAdditionalContentExclusionPolicyRule.from_dict, obj.get("rules")) @@ -11498,7 +11512,7 @@ class QueuePendingItemsResult: """ @staticmethod - def from_dict(obj: Any) -> 'QueuePendingItemsResult': + def from_dict(obj: Any) -> QueuePendingItemsResult: assert isinstance(obj, dict) items = from_list(QueuePendingItems.from_dict, obj.get("items")) steering_messages = from_list(from_str, obj.get("steeringMessages")) @@ -11531,7 +11545,7 @@ class SendAttachmentSelection: """Attachment type discriminator""" @staticmethod - def from_dict(obj: Any) -> 'SendAttachmentSelection': + def from_dict(obj: Any) -> SendAttachmentSelection: assert isinstance(obj, dict) display_name = from_str(obj.get("displayName")) file_path = from_str(obj.get("filePath")) @@ -11559,7 +11573,7 @@ class SessionFSReadFileResult: """Describes a filesystem error.""" @staticmethod - def from_dict(obj: Any) -> 'SessionFSReadFileResult': + def from_dict(obj: Any) -> SessionFSReadFileResult: assert isinstance(obj, dict) content = from_str(obj.get("content")) error = from_union([SessionFSError.from_dict, from_none], obj.get("error")) @@ -11583,7 +11597,7 @@ class SessionFSReaddirResult: """Describes a filesystem error.""" @staticmethod - def from_dict(obj: Any) -> 'SessionFSReaddirResult': + def from_dict(obj: Any) -> SessionFSReaddirResult: assert isinstance(obj, dict) entries = from_list(from_str, obj.get("entries")) error = from_union([SessionFSError.from_dict, from_none], obj.get("error")) @@ -11617,7 +11631,7 @@ class SessionFSSqliteQueryResult: """SQLite last_insert_rowid() value for INSERT.""" @staticmethod - def from_dict(obj: Any) -> 'SessionFSSqliteQueryResult': + def from_dict(obj: Any) -> SessionFSSqliteQueryResult: assert isinstance(obj, dict) columns = from_list(from_str, obj.get("columns")) rows = from_list(lambda x: from_dict(lambda x: x, x), obj.get("rows")) @@ -11660,7 +11674,7 @@ class SessionFSStatResult: """Describes a filesystem error.""" @staticmethod - def from_dict(obj: Any) -> 'SessionFSStatResult': + def from_dict(obj: Any) -> SessionFSStatResult: assert isinstance(obj, dict) birthtime = from_datetime(obj.get("birthtime")) is_directory = from_bool(obj.get("isDirectory")) @@ -11693,7 +11707,7 @@ class SessionFSReaddirWithTypesResult: """Describes a filesystem error.""" @staticmethod - def from_dict(obj: Any) -> 'SessionFSReaddirWithTypesResult': + def from_dict(obj: Any) -> SessionFSReaddirWithTypesResult: assert isinstance(obj, dict) entries = from_list(SessionFSReaddirWithTypesEntry.from_dict, obj.get("entries")) error = from_union([SessionFSError.from_dict, from_none], obj.get("error")) @@ -11715,7 +11729,7 @@ class AgentGetCurrentResult: """Currently selected custom agent, or null if using the default agent""" @staticmethod - def from_dict(obj: Any) -> 'AgentGetCurrentResult': + def from_dict(obj: Any) -> AgentGetCurrentResult: assert isinstance(obj, dict) agent = from_union([AgentInfo.from_dict, from_none], obj.get("agent")) return AgentGetCurrentResult(agent) @@ -11735,7 +11749,7 @@ class AgentList: """Available custom agents""" @staticmethod - def from_dict(obj: Any) -> 'AgentList': + def from_dict(obj: Any) -> AgentList: assert isinstance(obj, dict) agents = from_list(AgentInfo.from_dict, obj.get("agents")) return AgentList(agents) @@ -11754,7 +11768,7 @@ class AgentReloadResult: """Reloaded custom agents""" @staticmethod - def from_dict(obj: Any) -> 'AgentReloadResult': + def from_dict(obj: Any) -> AgentReloadResult: assert isinstance(obj, dict) agents = from_list(AgentInfo.from_dict, obj.get("agents")) return AgentReloadResult(agents) @@ -11773,7 +11787,7 @@ class AgentSelectResult: """The newly selected custom agent""" @staticmethod - def from_dict(obj: Any) -> 'AgentSelectResult': + def from_dict(obj: Any) -> AgentSelectResult: assert isinstance(obj, dict) agent = AgentInfo.from_dict(obj.get("agent")) return AgentSelectResult(agent) @@ -11806,7 +11820,7 @@ class TaskProgress: """Recent stdout/stderr lines from the running shell command""" @staticmethod - def from_dict(obj: Any) -> 'TaskProgress': + def from_dict(obj: Any) -> TaskProgress: assert isinstance(obj, dict) type = TaskInfoType(obj.get("type")) latest_intent = from_union([from_str, from_none], obj.get("latestIntent")) @@ -11855,7 +11869,7 @@ class UIElicitationArrayAnyOfField: """Human-readable label for the field.""" @staticmethod - def from_dict(obj: Any) -> 'UIElicitationArrayAnyOfField': + def from_dict(obj: Any) -> UIElicitationArrayAnyOfField: assert isinstance(obj, dict) items = UIElicitationArrayAnyOfFieldItems.from_dict(obj.get("items")) type = UIElicitationArrayAnyOfFieldType(obj.get("type")) @@ -11909,7 +11923,7 @@ class UIElicitationArrayEnumField: """Human-readable label for the field.""" @staticmethod - def from_dict(obj: Any) -> 'UIElicitationArrayEnumField': + def from_dict(obj: Any) -> UIElicitationArrayEnumField: assert isinstance(obj, dict) items = UIElicitationArrayEnumFieldItems.from_dict(obj.get("items")) type = UIElicitationArrayAnyOfFieldType(obj.get("type")) @@ -12011,7 +12025,7 @@ class UIElicitationSchemaProperty: """Minimum allowed value (inclusive).""" @staticmethod - def from_dict(obj: Any) -> 'UIElicitationSchemaProperty': + def from_dict(obj: Any) -> UIElicitationSchemaProperty: assert isinstance(obj, dict) type = UIElicitationSchemaPropertyType(obj.get("type")) default = from_union([from_float, from_bool, lambda x: from_list(from_str, x), from_str, from_none], obj.get("default")) @@ -12076,7 +12090,7 @@ class UIHandlePendingElicitationRequest: """The elicitation response (accept with form values, decline, or cancel)""" @staticmethod - def from_dict(obj: Any) -> 'UIHandlePendingElicitationRequest': + def from_dict(obj: Any) -> UIHandlePendingElicitationRequest: assert isinstance(obj, dict) request_id = from_str(obj.get("requestId")) result = UIElicitationResponse.from_dict(obj.get("result")) @@ -12100,7 +12114,7 @@ class UIHandlePendingExitPlanModeRequest: """Schema for the `UIExitPlanModeResponse` type.""" @staticmethod - def from_dict(obj: Any) -> 'UIHandlePendingExitPlanModeRequest': + def from_dict(obj: Any) -> UIHandlePendingExitPlanModeRequest: assert isinstance(obj, dict) request_id = from_str(obj.get("requestId")) response = UIExitPlanModeResponse.from_dict(obj.get("response")) @@ -12153,7 +12167,7 @@ class UsageGetMetricsResult: """Session-wide accumulated nano-AI units cost""" @staticmethod - def from_dict(obj: Any) -> 'UsageGetMetricsResult': + def from_dict(obj: Any) -> UsageGetMetricsResult: assert isinstance(obj, dict) code_changes = UsageMetricsCodeChanges.from_dict(obj.get("codeChanges")) last_call_input_tokens = from_int(obj.get("lastCallInputTokens")) @@ -12195,7 +12209,7 @@ class CommandList: """Commands available in this session""" @staticmethod - def from_dict(obj: Any) -> 'CommandList': + def from_dict(obj: Any) -> CommandList: assert isinstance(obj, dict) commands = from_list(SlashCommandInfo.from_dict, obj.get("commands")) return CommandList(commands) @@ -12226,7 +12240,7 @@ class APIKeyAuthInfo: """ @staticmethod - def from_dict(obj: Any) -> 'APIKeyAuthInfo': + def from_dict(obj: Any) -> APIKeyAuthInfo: assert isinstance(obj, dict) api_key = from_str(obj.get("apiKey")) host = from_str(obj.get("host")) @@ -12262,7 +12276,7 @@ class CopilotAPITokenAuthInfo: """ @staticmethod - def from_dict(obj: Any) -> 'CopilotAPITokenAuthInfo': + def from_dict(obj: Any) -> CopilotAPITokenAuthInfo: assert isinstance(obj, dict) host = Host(obj.get("host")) copilot_user = from_union([CopilotUserResponse.from_dict, from_none], obj.get("copilotUser")) @@ -12305,7 +12319,7 @@ class EnvAuthInfo: """ @staticmethod - def from_dict(obj: Any) -> 'EnvAuthInfo': + def from_dict(obj: Any) -> EnvAuthInfo: assert isinstance(obj, dict) env_var = from_str(obj.get("envVar")) host = from_str(obj.get("host")) @@ -12350,7 +12364,7 @@ class GhCLIAuthInfo: """ @staticmethod - def from_dict(obj: Any) -> 'GhCLIAuthInfo': + def from_dict(obj: Any) -> GhCLIAuthInfo: assert isinstance(obj, dict) host = from_str(obj.get("host")) login = from_str(obj.get("login")) @@ -12389,7 +12403,7 @@ class HMACAuthInfo: """ @staticmethod - def from_dict(obj: Any) -> 'HMACAuthInfo': + def from_dict(obj: Any) -> HMACAuthInfo: assert isinstance(obj, dict) hmac = from_str(obj.get("hmac")) host = Host(obj.get("host")) @@ -12426,7 +12440,7 @@ class TokenAuthInfo: """ @staticmethod - def from_dict(obj: Any) -> 'TokenAuthInfo': + def from_dict(obj: Any) -> TokenAuthInfo: assert isinstance(obj, dict) host = from_str(obj.get("host")) token = from_str(obj.get("token")) @@ -12464,7 +12478,7 @@ class UserAuthInfo: """ @staticmethod - def from_dict(obj: Any) -> 'UserAuthInfo': + def from_dict(obj: Any) -> UserAuthInfo: assert isinstance(obj, dict) host = from_str(obj.get("host")) login = from_str(obj.get("login")) @@ -12568,7 +12582,7 @@ class PermissionDecisionApproveForIonApproval: external_ref_marker_external_ref_user_tool_session_approval: str | None = None @staticmethod - def from_dict(obj: Any) -> 'PermissionDecisionApproveForIonApproval': + def from_dict(obj: Any) -> PermissionDecisionApproveForIonApproval: assert isinstance(obj, dict) command_identifiers = from_union([lambda x: from_list(from_str, x), from_none], obj.get("commandIdentifiers")) kind = from_union([ApprovalKind, from_none], obj.get("kind")) @@ -12613,7 +12627,7 @@ class HandlePendingToolCallRequest: """Tool call result (string or expanded result object)""" @staticmethod - def from_dict(obj: Any) -> 'HandlePendingToolCallRequest': + def from_dict(obj: Any) -> HandlePendingToolCallRequest: assert isinstance(obj, dict) request_id = from_str(obj.get("requestId")) error = from_union([from_str, from_none], obj.get("error")) @@ -12656,7 +12670,7 @@ class InstalledPlugin: """Version installed (if available)""" @staticmethod - def from_dict(obj: Any) -> 'InstalledPlugin': + def from_dict(obj: Any) -> InstalledPlugin: assert isinstance(obj, dict) enabled = from_bool(obj.get("enabled")) installed_at = from_str(obj.get("installed_at")) @@ -12708,7 +12722,7 @@ class SessionInstalledPlugin: """Installed version, if known""" @staticmethod - def from_dict(obj: Any) -> 'SessionInstalledPlugin': + def from_dict(obj: Any) -> SessionInstalledPlugin: assert isinstance(obj, dict) enabled = from_bool(obj.get("enabled")) installed_at = from_str(obj.get("installed_at")) @@ -12742,7 +12756,7 @@ class SessionEnrichMetadataResult: """Same records, with summary and context backfilled""" @staticmethod - def from_dict(obj: Any) -> 'SessionEnrichMetadataResult': + def from_dict(obj: Any) -> SessionEnrichMetadataResult: assert isinstance(obj, dict) sessions = from_list(SessionMetadata.from_dict, obj.get("sessions")) return SessionEnrichMetadataResult(sessions) @@ -12761,7 +12775,7 @@ class SessionList: """Sessions ordered most-recently-modified first""" @staticmethod - def from_dict(obj: Any) -> 'SessionList': + def from_dict(obj: Any) -> SessionList: assert isinstance(obj, dict) sessions = from_list(SessionMetadata.from_dict, obj.get("sessions")) return SessionList(sessions) @@ -12782,7 +12796,7 @@ class SessionsEnrichMetadataRequest: """ @staticmethod - def from_dict(obj: Any) -> 'SessionsEnrichMetadataRequest': + def from_dict(obj: Any) -> SessionsEnrichMetadataRequest: assert isinstance(obj, dict) sessions = from_list(SessionMetadata.from_dict, obj.get("sessions")) return SessionsEnrichMetadataRequest(sessions) @@ -12849,7 +12863,7 @@ class SessionMetadataSnapshot: """ @staticmethod - def from_dict(obj: Any) -> 'SessionMetadataSnapshot': + def from_dict(obj: Any) -> SessionMetadataSnapshot: assert isinstance(obj, dict) already_in_use = from_bool(obj.get("alreadyInUse")) current_mode = MetadataSnapshotCurrentMode(obj.get("currentMode")) @@ -12922,7 +12936,7 @@ class PermissionsConfigureParams: """ @staticmethod - def from_dict(obj: Any) -> 'PermissionsConfigureParams': + def from_dict(obj: Any) -> PermissionsConfigureParams: assert isinstance(obj, dict) additional_content_exclusion_policies = from_union([lambda x: from_list(PermissionsConfigureAdditionalContentExclusionPolicy.from_dict, x), from_none], obj.get("additionalContentExclusionPolicies")) approve_all_read_permission_requests = from_union([from_bool, from_none], obj.get("approveAllReadPermissionRequests")) @@ -12959,7 +12973,7 @@ class TasksGetProgressResult: """ @staticmethod - def from_dict(obj: Any) -> 'TasksGetProgressResult': + def from_dict(obj: Any) -> TasksGetProgressResult: assert isinstance(obj, dict) progress = from_union([TaskProgress.from_dict, from_none], obj.get("progress")) return TasksGetProgressResult(progress) @@ -12985,7 +12999,7 @@ class UIElicitationSchema: """List of required field names""" @staticmethod - def from_dict(obj: Any) -> 'UIElicitationSchema': + def from_dict(obj: Any) -> UIElicitationSchema: assert isinstance(obj, dict) properties = from_dict(UIElicitationSchemaProperty.from_dict, obj.get("properties")) type = UIElicitationSchemaType(obj.get("type")) @@ -13011,7 +13025,7 @@ class SessionsSetAdditionalPluginsRequest: """ @staticmethod - def from_dict(obj: Any) -> 'SessionsSetAdditionalPluginsRequest': + def from_dict(obj: Any) -> SessionsSetAdditionalPluginsRequest: assert isinstance(obj, dict) plugins = from_list(InstalledPlugin.from_dict, obj.get("plugins")) return SessionsSetAdditionalPluginsRequest(plugins) @@ -13144,7 +13158,7 @@ class SessionUpdateOptionsParams: """Absolute working-directory path for shell tools.""" @staticmethod - def from_dict(obj: Any) -> 'SessionUpdateOptionsParams': + def from_dict(obj: Any) -> SessionUpdateOptionsParams: assert isinstance(obj, dict) additional_content_exclusion_policies = from_union([lambda x: from_list(lambda x: x, x), from_none], obj.get("additionalContentExclusionPolicies")) agent_context = from_union([from_str, from_none], obj.get("agentContext")) @@ -13272,7 +13286,7 @@ class UIElicitationRequest: """JSON Schema describing the form fields to present to the user""" @staticmethod - def from_dict(obj: Any) -> 'UIElicitationRequest': + def from_dict(obj: Any) -> UIElicitationRequest: assert isinstance(obj, dict) message = from_str(obj.get("message")) requested_schema = UIElicitationSchema.from_dict(obj.get("requestedSchema")) @@ -13308,7 +13322,7 @@ class MCPExecuteSamplingParams: """Name of the MCP server that initiated the sampling request""" @staticmethod - def from_dict(obj: Any) -> 'MCPExecuteSamplingParams': + def from_dict(obj: Any) -> MCPExecuteSamplingParams: assert isinstance(obj, dict) mcp_request_id = from_union([from_float, from_str], obj.get("mcpRequestId")) request = from_dict(lambda x: x, obj.get("request")) @@ -13341,7 +13355,7 @@ class MetadataContextInfoRequest: """ @staticmethod - def from_dict(obj: Any) -> 'MetadataContextInfoRequest': + def from_dict(obj: Any) -> MetadataContextInfoRequest: assert isinstance(obj, dict) output_token_limit = from_int(obj.get("outputTokenLimit")) prompt_token_limit = from_int(obj.get("promptTokenLimit")) @@ -13367,7 +13381,7 @@ class MetadataRecomputeContextTokensRequest: """ @staticmethod - def from_dict(obj: Any) -> 'MetadataRecomputeContextTokensRequest': + def from_dict(obj: Any) -> MetadataRecomputeContextTokensRequest: assert isinstance(obj, dict) model_id = from_str(obj.get("modelId")) return MetadataRecomputeContextTokensRequest(model_id) @@ -13388,7 +13402,7 @@ class ModelCapabilities: """Feature flags indicating what the model supports""" @staticmethod - def from_dict(obj: Any) -> 'ModelCapabilities': + def from_dict(obj: Any) -> ModelCapabilities: assert isinstance(obj, dict) limits = from_union([ModelCapabilitiesLimits.from_dict, from_none], obj.get("limits")) supports = from_union([ModelCapabilitiesSupports.from_dict, from_none], obj.get("supports")) @@ -13441,7 +13455,7 @@ class Model: """Supported reasoning effort levels (only present if model supports reasoning effort)""" @staticmethod - def from_dict(obj: Any) -> 'Model': + def from_dict(obj: Any) -> Model: assert isinstance(obj, dict) capabilities = ModelCapabilities.from_dict(obj.get("capabilities")) id = from_str(obj.get("id")) @@ -13482,7 +13496,7 @@ class ModelList: """List of available models with full metadata""" @staticmethod - def from_dict(obj: Any) -> 'ModelList': + def from_dict(obj: Any) -> ModelList: assert isinstance(obj, dict) models = from_list(Model.from_dict, obj.get("models")) return ModelList(models) @@ -13510,7 +13524,7 @@ class ModelSwitchToRequest: """Reasoning summary mode to request for supported model clients""" @staticmethod - def from_dict(obj: Any) -> 'ModelSwitchToRequest': + def from_dict(obj: Any) -> ModelSwitchToRequest: assert isinstance(obj, dict) model_id = from_str(obj.get("modelId")) model_capabilities = from_union([ModelCapabilitiesOverride.from_dict, from_none], obj.get("modelCapabilities")) @@ -13550,7 +13564,7 @@ class PermissionsSetApproveAllRequest: """Optional source for allow-all telemetry. Defaults to `rpc` when omitted for SDK callers.""" @staticmethod - def from_dict(obj: Any) -> 'PermissionsSetApproveAllRequest': + def from_dict(obj: Any) -> PermissionsSetApproveAllRequest: assert isinstance(obj, dict) enabled = from_bool(obj.get("enabled")) source = from_union([PermissionsSetApproveAllSource, from_none], obj.get("source")) @@ -13625,7 +13639,7 @@ class TaskAgentInfo: """Result text from the task when available""" @staticmethod - def from_dict(obj: Any) -> 'TaskAgentInfo': + def from_dict(obj: Any) -> TaskAgentInfo: assert isinstance(obj, dict) agent_type = from_str(obj.get("agentType")) description = from_str(obj.get("description")) @@ -14189,7 +14203,7 @@ class RPC: workspace_summary: WorkspaceSummary | None = None @staticmethod - def from_dict(obj: Any) -> 'RPC': + def from_dict(obj: Any) -> RPC: assert isinstance(obj, dict) abort_request = AbortRequest.from_dict(obj.get("AbortRequest")) abort_result = AbortResult.from_dict(obj.get("AbortResult")) @@ -15220,7 +15234,7 @@ def rpc_to_dict(x: RPC) -> Any: # The new auth credentials to install on the session. When omitted or `undefined`, the call is a no-op and the session's existing credentials are preserved. The runtime stores the value verbatim and uses it for outbound model/API requests; it does NOT re-validate or re-fetch the associated Copilot user response. Several variants carry secret material; treat this method's params as containing secrets at rest and in transit. AuthInfo = HMACAuthInfo | EnvAuthInfo | TokenAuthInfo | CopilotAPITokenAuthInfo | UserAuthInfo | GhCLIAuthInfo | APIKeyAuthInfo -def _load_AuthInfo(obj: Any) -> "AuthInfo": +def _load_AuthInfo(obj: Any) -> AuthInfo: assert isinstance(obj, dict) kind = obj.get("type") match kind: @@ -15236,7 +15250,7 @@ def _load_AuthInfo(obj: Any) -> "AuthInfo": # A content block within a tool result, which may be text, terminal output, image, audio, or a resource ExternalToolTextResultForLlmContent = ExternalToolTextResultForLlmContentText | ExternalToolTextResultForLlmContentTerminal | ExternalToolTextResultForLlmContentImage | ExternalToolTextResultForLlmContentAudio | ExternalToolTextResultForLlmContentResourceLink | ExternalToolTextResultForLlmContentResource -def _load_ExternalToolTextResultForLlmContent(obj: Any) -> "ExternalToolTextResultForLlmContent": +def _load_ExternalToolTextResultForLlmContent(obj: Any) -> ExternalToolTextResultForLlmContent: assert isinstance(obj, dict) kind = obj.get("type") match kind: @@ -15251,7 +15265,7 @@ def _load_ExternalToolTextResultForLlmContent(obj: Any) -> "ExternalToolTextResu # The client's response to the pending permission prompt PermissionDecision = PermissionDecisionApproveOnce | PermissionDecisionApproveForSession | PermissionDecisionApproveForLocation | PermissionDecisionApprovePermanently | PermissionDecisionReject | PermissionDecisionUserNotAvailable | PermissionDecisionApproved | PermissionDecisionApprovedForSession | PermissionDecisionApprovedForLocation | PermissionDecisionCancelled | PermissionDecisionDeniedByRules | PermissionDecisionDeniedNoApprovalRuleAndCouldNotRequestFromUser | PermissionDecisionDeniedInteractivelyByUser | PermissionDecisionDeniedByContentExclusionPolicy | PermissionDecisionDeniedByPermissionRequestHook -def _load_PermissionDecision(obj: Any) -> "PermissionDecision": +def _load_PermissionDecision(obj: Any) -> PermissionDecision: assert isinstance(obj, dict) kind = obj.get("kind") match kind: @@ -15275,7 +15289,7 @@ def _load_PermissionDecision(obj: Any) -> "PermissionDecision": # Approval to persist for this location PermissionDecisionApproveForLocationApproval = PermissionDecisionApproveForLocationApprovalCommands | PermissionDecisionApproveForLocationApprovalRead | PermissionDecisionApproveForLocationApprovalWrite | PermissionDecisionApproveForLocationApprovalMCP | PermissionDecisionApproveForLocationApprovalMCPSampling | PermissionDecisionApproveForLocationApprovalMemory | PermissionDecisionApproveForLocationApprovalCustomTool | PermissionDecisionApproveForLocationApprovalExtensionManagement | PermissionDecisionApproveForLocationApprovalExtensionPermissionAccess -def _load_PermissionDecisionApproveForLocationApproval(obj: Any) -> "PermissionDecisionApproveForLocationApproval": +def _load_PermissionDecisionApproveForLocationApproval(obj: Any) -> PermissionDecisionApproveForLocationApproval: assert isinstance(obj, dict) kind = obj.get("kind") match kind: @@ -15293,7 +15307,7 @@ def _load_PermissionDecisionApproveForLocationApproval(obj: Any) -> "PermissionD # Session-scoped approval to remember (tool prompts only; omitted for path/url prompts) PermissionDecisionApproveForSessionApproval = PermissionDecisionApproveForSessionApprovalCommands | PermissionDecisionApproveForSessionApprovalRead | PermissionDecisionApproveForSessionApprovalWrite | PermissionDecisionApproveForSessionApprovalMCP | PermissionDecisionApproveForSessionApprovalMCPSampling | PermissionDecisionApproveForSessionApprovalMemory | PermissionDecisionApproveForSessionApprovalCustomTool | PermissionDecisionApproveForSessionApprovalExtensionManagement | PermissionDecisionApproveForSessionApprovalExtensionPermissionAccess -def _load_PermissionDecisionApproveForSessionApproval(obj: Any) -> "PermissionDecisionApproveForSessionApproval": +def _load_PermissionDecisionApproveForSessionApproval(obj: Any) -> PermissionDecisionApproveForSessionApproval: assert isinstance(obj, dict) kind = obj.get("kind") match kind: @@ -15311,7 +15325,7 @@ def _load_PermissionDecisionApproveForSessionApproval(obj: Any) -> "PermissionDe # Tool approval to persist and apply PermissionsLocationsAddToolApprovalDetails = PermissionsLocationsAddToolApprovalDetailsCommands | PermissionsLocationsAddToolApprovalDetailsRead | PermissionsLocationsAddToolApprovalDetailsWrite | PermissionsLocationsAddToolApprovalDetailsMCP | PermissionsLocationsAddToolApprovalDetailsMCPSampling | PermissionsLocationsAddToolApprovalDetailsMemory | PermissionsLocationsAddToolApprovalDetailsCustomTool | PermissionsLocationsAddToolApprovalDetailsExtensionManagement | PermissionsLocationsAddToolApprovalDetailsExtensionPermissionAccess -def _load_PermissionsLocationsAddToolApprovalDetails(obj: Any) -> "PermissionsLocationsAddToolApprovalDetails": +def _load_PermissionsLocationsAddToolApprovalDetails(obj: Any) -> PermissionsLocationsAddToolApprovalDetails: assert isinstance(obj, dict) kind = obj.get("kind") match kind: @@ -15329,7 +15343,7 @@ def _load_PermissionsLocationsAddToolApprovalDetails(obj: Any) -> "PermissionsLo # Result of the queued command execution. QueuedCommandResult = QueuedCommandHandled | QueuedCommandNotHandled -def _load_QueuedCommandResult(obj: Any) -> "QueuedCommandResult": +def _load_QueuedCommandResult(obj: Any) -> QueuedCommandResult: assert isinstance(obj, dict) kind = obj.get("handled") match kind: @@ -15340,7 +15354,7 @@ def _load_QueuedCommandResult(obj: Any) -> "QueuedCommandResult": # A user message attachment — a file, directory, code selection, blob, or GitHub reference SendAttachment = SendAttachmentFile | SendAttachmentDirectory | SendAttachmentSelection | SendAttachmentGithubReference | SendAttachmentBlob -def _load_SendAttachment(obj: Any) -> "SendAttachment": +def _load_SendAttachment(obj: Any) -> SendAttachment: assert isinstance(obj, dict) kind = obj.get("type") match kind: @@ -15354,7 +15368,7 @@ def _load_SendAttachment(obj: Any) -> "SendAttachment": # Result of invoking the slash command (text output, prompt to send to the agent, or completion). SlashCommandInvocationResult = SlashCommandTextResult | SlashCommandAgentPromptResult | SlashCommandCompletedResult | SlashCommandSelectSubcommandResult -def _load_SlashCommandInvocationResult(obj: Any) -> "SlashCommandInvocationResult": +def _load_SlashCommandInvocationResult(obj: Any) -> SlashCommandInvocationResult: assert isinstance(obj, dict) kind = obj.get("kind") match kind: @@ -15367,7 +15381,7 @@ def _load_SlashCommandInvocationResult(obj: Any) -> "SlashCommandInvocationResul # Schema for the `TaskInfo` type. TaskInfo = TaskAgentInfo | TaskShellInfo -def _load_TaskInfo(obj: Any) -> "TaskInfo": +def _load_TaskInfo(obj: Any) -> TaskInfo: assert isinstance(obj, dict) kind = obj.get("type") match kind: @@ -15417,7 +15431,7 @@ def _patch_model_capabilities(data: dict) -> dict: class ServerModelsApi: - def __init__(self, client: "JsonRpcClient"): + def __init__(self, client: JsonRpcClient): self._client = client async def list(self, params: ModelsListRequest, *, timeout: float | None = None) -> ModelList: @@ -15427,7 +15441,7 @@ async def list(self, params: ModelsListRequest, *, timeout: float | None = None) class ServerToolsApi: - def __init__(self, client: "JsonRpcClient"): + def __init__(self, client: JsonRpcClient): self._client = client async def list(self, params: ToolsListRequest, *, timeout: float | None = None) -> ToolList: @@ -15437,7 +15451,7 @@ async def list(self, params: ToolsListRequest, *, timeout: float | None = None) class ServerAccountApi: - def __init__(self, client: "JsonRpcClient"): + def __init__(self, client: JsonRpcClient): self._client = client async def get_quota(self, params: AccountGetQuotaRequest, *, timeout: float | None = None) -> AccountGetQuotaResult: @@ -15447,7 +15461,7 @@ async def get_quota(self, params: AccountGetQuotaRequest, *, timeout: float | No class ServerSecretsApi: - def __init__(self, client: "JsonRpcClient"): + def __init__(self, client: JsonRpcClient): self._client = client async def add_filter_values(self, params: SecretsAddFilterValuesRequest, *, timeout: float | None = None) -> SecretsAddFilterValuesResult: @@ -15457,7 +15471,7 @@ async def add_filter_values(self, params: SecretsAddFilterValuesRequest, *, time class ServerMcpConfigApi: - def __init__(self, client: "JsonRpcClient"): + def __init__(self, client: JsonRpcClient): self._client = client async def list(self, *, timeout: float | None = None) -> MCPConfigList: @@ -15491,7 +15505,7 @@ async def disable(self, params: MCPConfigDisableRequest, *, timeout: float | Non class ServerMcpApi: - def __init__(self, client: "JsonRpcClient"): + def __init__(self, client: JsonRpcClient): self._client = client self.config = ServerMcpConfigApi(client) @@ -15502,7 +15516,7 @@ async def discover(self, params: MCPDiscoverRequest, *, timeout: float | None = class ServerSkillsConfigApi: - def __init__(self, client: "JsonRpcClient"): + def __init__(self, client: JsonRpcClient): self._client = client async def set_disabled_skills(self, params: SkillsConfigSetDisabledSkillsRequest, *, timeout: float | None = None) -> None: @@ -15512,7 +15526,7 @@ async def set_disabled_skills(self, params: SkillsConfigSetDisabledSkillsRequest class ServerSkillsApi: - def __init__(self, client: "JsonRpcClient"): + def __init__(self, client: JsonRpcClient): self._client = client self.config = ServerSkillsConfigApi(client) @@ -15523,7 +15537,7 @@ async def discover(self, params: SkillsDiscoverRequest, *, timeout: float | None class ServerSessionFsApi: - def __init__(self, client: "JsonRpcClient"): + def __init__(self, client: JsonRpcClient): self._client = client async def set_provider(self, params: SessionFSSetProviderRequest, *, timeout: float | None = None) -> SessionFSSetProviderResult: @@ -15534,7 +15548,7 @@ async def set_provider(self, params: SessionFSSetProviderRequest, *, timeout: fl # Experimental: this API group is experimental and may change or be removed. class ServerSessionsApi: - def __init__(self, client: "JsonRpcClient"): + def __init__(self, client: JsonRpcClient): self._client = client async def fork(self, params: SessionsForkRequest, *, timeout: float | None = None) -> SessionsForkResult: @@ -15634,7 +15648,7 @@ async def set_additional_plugins(self, params: SessionsSetAdditionalPluginsReque class ServerRpc: """Typed server-scoped RPC methods.""" - def __init__(self, client: "JsonRpcClient"): + def __init__(self, client: JsonRpcClient): self._client = client self.models = ServerModelsApi(client) self.tools = ServerToolsApi(client) @@ -15653,7 +15667,7 @@ async def ping(self, params: PingRequest, *, timeout: float | None = None) -> Pi class _InternalServerRpc: """Internal SDK server-scoped RPC methods. Not part of the public API.""" - def __init__(self, client: "JsonRpcClient"): + def __init__(self, client: JsonRpcClient): self._client = client async def connect(self, params: ConnectRequest, *, timeout: float | None = None) -> ConnectResult: @@ -15664,7 +15678,7 @@ async def connect(self, params: ConnectRequest, *, timeout: float | None = None) # Experimental: this API group is experimental and may change or be removed. class AuthApi: - def __init__(self, client: "JsonRpcClient", session_id: str): + def __init__(self, client: JsonRpcClient, session_id: str): self._client = client self._session_id = session_id @@ -15681,7 +15695,7 @@ async def set_credentials(self, params: SessionSetCredentialsParams, *, timeout: # Experimental: this API group is experimental and may change or be removed. class ModelApi: - def __init__(self, client: "JsonRpcClient", session_id: str): + def __init__(self, client: JsonRpcClient, session_id: str): self._client = client self._session_id = session_id @@ -15704,7 +15718,7 @@ async def set_reasoning_effort(self, params: ModelSetReasoningEffortRequest, *, # Experimental: this API group is experimental and may change or be removed. class ModeApi: - def __init__(self, client: "JsonRpcClient", session_id: str): + def __init__(self, client: JsonRpcClient, session_id: str): self._client = client self._session_id = session_id @@ -15721,7 +15735,7 @@ async def set(self, params: ModeSetRequest, *, timeout: float | None = None) -> # Experimental: this API group is experimental and may change or be removed. class NameApi: - def __init__(self, client: "JsonRpcClient", session_id: str): + def __init__(self, client: JsonRpcClient, session_id: str): self._client = client self._session_id = session_id @@ -15744,7 +15758,7 @@ async def set_auto(self, params: NameSetAutoRequest, *, timeout: float | None = # Experimental: this API group is experimental and may change or be removed. class PlanApi: - def __init__(self, client: "JsonRpcClient", session_id: str): + def __init__(self, client: JsonRpcClient, session_id: str): self._client = client self._session_id = session_id @@ -15765,7 +15779,7 @@ async def delete(self, *, timeout: float | None = None) -> None: # Experimental: this API group is experimental and may change or be removed. class WorkspacesApi: - def __init__(self, client: "JsonRpcClient", session_id: str): + def __init__(self, client: JsonRpcClient, session_id: str): self._client = client self._session_id = session_id @@ -15808,7 +15822,7 @@ async def save_large_paste(self, params: WorkspacesSaveLargePasteRequest, *, tim # Experimental: this API group is experimental and may change or be removed. class InstructionsApi: - def __init__(self, client: "JsonRpcClient", session_id: str): + def __init__(self, client: JsonRpcClient, session_id: str): self._client = client self._session_id = session_id @@ -15819,7 +15833,7 @@ async def get_sources(self, *, timeout: float | None = None) -> InstructionsGetS # Experimental: this API group is experimental and may change or be removed. class FleetApi: - def __init__(self, client: "JsonRpcClient", session_id: str): + def __init__(self, client: JsonRpcClient, session_id: str): self._client = client self._session_id = session_id @@ -15832,7 +15846,7 @@ async def start(self, params: FleetStartRequest, *, timeout: float | None = None # Experimental: this API group is experimental and may change or be removed. class AgentApi: - def __init__(self, client: "JsonRpcClient", session_id: str): + def __init__(self, client: JsonRpcClient, session_id: str): self._client = client self._session_id = session_id @@ -15861,7 +15875,7 @@ async def reload(self, *, timeout: float | None = None) -> AgentReloadResult: # Experimental: this API group is experimental and may change or be removed. class TasksApi: - def __init__(self, client: "JsonRpcClient", session_id: str): + def __init__(self, client: JsonRpcClient, session_id: str): self._client = client self._session_id = session_id @@ -15924,7 +15938,7 @@ async def send_message(self, params: TasksSendMessageRequest, *, timeout: float # Experimental: this API group is experimental and may change or be removed. class SkillsApi: - def __init__(self, client: "JsonRpcClient", session_id: str): + def __init__(self, client: JsonRpcClient, session_id: str): self._client = client self._session_id = session_id @@ -15959,7 +15973,7 @@ async def ensure_loaded(self, *, timeout: float | None = None) -> None: # Experimental: this API group is experimental and may change or be removed. class McpOauthApi: - def __init__(self, client: "JsonRpcClient", session_id: str): + def __init__(self, client: JsonRpcClient, session_id: str): self._client = client self._session_id = session_id @@ -15972,7 +15986,7 @@ async def login(self, params: MCPOauthLoginRequest, *, timeout: float | None = N # Experimental: this API group is experimental and may change or be removed. class McpApi: - def __init__(self, client: "JsonRpcClient", session_id: str): + def __init__(self, client: JsonRpcClient, session_id: str): self._client = client self._session_id = session_id self.oauth = McpOauthApi(client, session_id) @@ -16022,7 +16036,7 @@ async def remove_git_hub(self, *, timeout: float | None = None) -> MCPRemoveGitH # Experimental: this API group is experimental and may change or be removed. class PluginsApi: - def __init__(self, client: "JsonRpcClient", session_id: str): + def __init__(self, client: JsonRpcClient, session_id: str): self._client = client self._session_id = session_id @@ -16033,7 +16047,7 @@ async def list(self, *, timeout: float | None = None) -> PluginList: # Experimental: this API group is experimental and may change or be removed. class OptionsApi: - def __init__(self, client: "JsonRpcClient", session_id: str): + def __init__(self, client: JsonRpcClient, session_id: str): self._client = client self._session_id = session_id @@ -16046,7 +16060,7 @@ async def update(self, params: SessionUpdateOptionsParams, *, timeout: float | N # Experimental: this API group is experimental and may change or be removed. class LspApi: - def __init__(self, client: "JsonRpcClient", session_id: str): + def __init__(self, client: JsonRpcClient, session_id: str): self._client = client self._session_id = session_id @@ -16059,7 +16073,7 @@ async def initialize(self, params: LspInitializeRequest, *, timeout: float | Non # Experimental: this API group is experimental and may change or be removed. class ExtensionsApi: - def __init__(self, client: "JsonRpcClient", session_id: str): + def __init__(self, client: JsonRpcClient, session_id: str): self._client = client self._session_id = session_id @@ -16086,7 +16100,7 @@ async def reload(self, *, timeout: float | None = None) -> None: # Experimental: this API group is experimental and may change or be removed. class ToolsApi: - def __init__(self, client: "JsonRpcClient", session_id: str): + def __init__(self, client: JsonRpcClient, session_id: str): self._client = client self._session_id = session_id @@ -16103,7 +16117,7 @@ async def initialize_and_validate(self, *, timeout: float | None = None) -> Tool # Experimental: this API group is experimental and may change or be removed. class CommandsApi: - def __init__(self, client: "JsonRpcClient", session_id: str): + def __init__(self, client: JsonRpcClient, session_id: str): self._client = client self._session_id = session_id @@ -16146,7 +16160,7 @@ async def respond_to_queued_command(self, params: CommandsRespondToQueuedCommand # Experimental: this API group is experimental and may change or be removed. class TelemetryApi: - def __init__(self, client: "JsonRpcClient", session_id: str): + def __init__(self, client: JsonRpcClient, session_id: str): self._client = client self._session_id = session_id @@ -16159,7 +16173,7 @@ async def set_feature_overrides(self, params: TelemetrySetFeatureOverridesReques # Experimental: this API group is experimental and may change or be removed. class UiApi: - def __init__(self, client: "JsonRpcClient", session_id: str): + def __init__(self, client: JsonRpcClient, session_id: str): self._client = client self._session_id = session_id @@ -16212,7 +16226,7 @@ async def unregister_direct_auto_mode_switch_handler(self, params: UIUnregisterD # Experimental: this API group is experimental and may change or be removed. class PermissionsPathsApi: - def __init__(self, client: "JsonRpcClient", session_id: str): + def __init__(self, client: JsonRpcClient, session_id: str): self._client = client self._session_id = session_id @@ -16247,7 +16261,7 @@ async def is_path_within_workspace(self, params: PermissionPathsWorkspaceCheckPa # Experimental: this API group is experimental and may change or be removed. class PermissionsLocationsApi: - def __init__(self, client: "JsonRpcClient", session_id: str): + def __init__(self, client: JsonRpcClient, session_id: str): self._client = client self._session_id = session_id @@ -16272,7 +16286,7 @@ async def add_tool_approval(self, params: PermissionLocationAddToolApprovalParam # Experimental: this API group is experimental and may change or be removed. class PermissionsFolderTrustApi: - def __init__(self, client: "JsonRpcClient", session_id: str): + def __init__(self, client: JsonRpcClient, session_id: str): self._client = client self._session_id = session_id @@ -16291,7 +16305,7 @@ async def add_trusted(self, params: FolderTrustAddParams, *, timeout: float | No # Experimental: this API group is experimental and may change or be removed. class PermissionsUrlsApi: - def __init__(self, client: "JsonRpcClient", session_id: str): + def __init__(self, client: JsonRpcClient, session_id: str): self._client = client self._session_id = session_id @@ -16304,7 +16318,7 @@ async def set_unrestricted_mode(self, params: PermissionUrlsSetUnrestrictedModeP # Experimental: this API group is experimental and may change or be removed. class PermissionsApi: - def __init__(self, client: "JsonRpcClient", session_id: str): + def __init__(self, client: JsonRpcClient, session_id: str): self._client = client self._session_id = session_id self.paths = PermissionsPathsApi(client, session_id) @@ -16359,7 +16373,7 @@ async def notify_prompt_shown(self, params: PermissionPromptShownNotification, * # Experimental: this API group is experimental and may change or be removed. class MetadataApi: - def __init__(self, client: "JsonRpcClient", session_id: str): + def __init__(self, client: JsonRpcClient, session_id: str): self._client = client self._session_id = session_id @@ -16398,7 +16412,7 @@ async def recompute_context_tokens(self, params: MetadataRecomputeContextTokensR # Experimental: this API group is experimental and may change or be removed. class ShellApi: - def __init__(self, client: "JsonRpcClient", session_id: str): + def __init__(self, client: JsonRpcClient, session_id: str): self._client = client self._session_id = session_id @@ -16417,7 +16431,7 @@ async def kill(self, params: ShellKillRequest, *, timeout: float | None = None) # Experimental: this API group is experimental and may change or be removed. class HistoryApi: - def __init__(self, client: "JsonRpcClient", session_id: str): + def __init__(self, client: JsonRpcClient, session_id: str): self._client = client self._session_id = session_id @@ -16448,7 +16462,7 @@ async def summarize_for_handoff(self, *, timeout: float | None = None) -> Histor # Experimental: this API group is experimental and may change or be removed. class QueueApi: - def __init__(self, client: "JsonRpcClient", session_id: str): + def __init__(self, client: JsonRpcClient, session_id: str): self._client = client self._session_id = session_id @@ -16467,7 +16481,7 @@ async def clear(self, *, timeout: float | None = None) -> None: # Experimental: this API group is experimental and may change or be removed. class EventLogApi: - def __init__(self, client: "JsonRpcClient", session_id: str): + def __init__(self, client: JsonRpcClient, session_id: str): self._client = client self._session_id = session_id @@ -16496,7 +16510,7 @@ async def release_interest(self, params: ReleaseEventInterestParams, *, timeout: # Experimental: this API group is experimental and may change or be removed. class UsageApi: - def __init__(self, client: "JsonRpcClient", session_id: str): + def __init__(self, client: JsonRpcClient, session_id: str): self._client = client self._session_id = session_id @@ -16507,7 +16521,7 @@ async def get_metrics(self, *, timeout: float | None = None) -> UsageGetMetricsR # Experimental: this API group is experimental and may change or be removed. class RemoteApi: - def __init__(self, client: "JsonRpcClient", session_id: str): + def __init__(self, client: JsonRpcClient, session_id: str): self._client = client self._session_id = session_id @@ -16530,7 +16544,7 @@ async def notify_steerable_changed(self, params: RemoteNotifySteerableChangedReq # Experimental: this API group is experimental and may change or be removed. class ScheduleApi: - def __init__(self, client: "JsonRpcClient", session_id: str): + def __init__(self, client: JsonRpcClient, session_id: str): self._client = client self._session_id = session_id @@ -16547,7 +16561,7 @@ async def stop(self, params: ScheduleStopRequest, *, timeout: float | None = Non class SessionRpc: """Typed session-scoped RPC methods.""" - def __init__(self, client: "JsonRpcClient", session_id: str): + def __init__(self, client: JsonRpcClient, session_id: str): self._client = client self._session_id = session_id self.auth = AuthApi(client, session_id) @@ -16652,7 +16666,7 @@ class ClientSessionApiHandlers: session_fs: SessionFsHandler | None = None def register_client_session_api_handlers( - client: "JsonRpcClient", + client: JsonRpcClient, get_handlers: Callable[[str], ClientSessionApiHandlers], ) -> None: """Register client-session request handlers on a JSON-RPC connection.""" diff --git a/python/copilot/generated/session_events.py b/python/copilot/generated/session_events.py index 4b72621df..c0bd58f3f 100644 --- a/python/copilot/generated/session_events.py +++ b/python/copilot/generated/session_events.py @@ -205,7 +205,7 @@ class SessionEventType(Enum): UNKNOWN = "unknown" @classmethod - def _missing_(cls, value: object) -> "SessionEventType": + def _missing_(cls, value: object) -> SessionEventType: return cls.UNKNOWN @@ -214,7 +214,7 @@ class RawSessionEventData: raw: Any @staticmethod - def from_dict(obj: Any) -> "RawSessionEventData": + def from_dict(obj: Any) -> RawSessionEventData: return RawSessionEventData(obj) def to_dict(self) -> Any: @@ -269,7 +269,7 @@ def __init__(self, **kwargs: Any): setattr(self, key, value) @staticmethod - def from_dict(obj: Any) -> "Data": + def from_dict(obj: Any) -> Data: assert isinstance(obj, dict) return Data(**{_compat_to_python_key(key): _compat_from_json_value(value) for key, value in obj.items()}) @@ -283,7 +283,7 @@ class AbortData: reason: AbortReason @staticmethod - def from_dict(obj: Any) -> "AbortData": + def from_dict(obj: Any) -> AbortData: assert isinstance(obj, dict) reason = parse_enum(AbortReason, obj.get("reason")) return AbortData( @@ -302,7 +302,7 @@ class AssistantIntentData: intent: str @staticmethod - def from_dict(obj: Any) -> "AssistantIntentData": + def from_dict(obj: Any) -> AssistantIntentData: assert isinstance(obj, dict) intent = from_str(obj.get("intent")) return AssistantIntentData( @@ -336,7 +336,7 @@ class AssistantMessageData: turn_id: str | None = None @staticmethod - def from_dict(obj: Any) -> "AssistantMessageData": + def from_dict(obj: Any) -> AssistantMessageData: assert isinstance(obj, dict) content = from_str(obj.get("content")) message_id = from_str(obj.get("messageId")) @@ -413,7 +413,7 @@ class AssistantMessageDeltaData: parent_tool_call_id: str | None = None @staticmethod - def from_dict(obj: Any) -> "AssistantMessageDeltaData": + def from_dict(obj: Any) -> AssistantMessageDeltaData: assert isinstance(obj, dict) delta_content = from_str(obj.get("deltaContent")) message_id = from_str(obj.get("messageId")) @@ -440,7 +440,7 @@ class AssistantMessageStartData: phase: str | None = None @staticmethod - def from_dict(obj: Any) -> "AssistantMessageStartData": + def from_dict(obj: Any) -> AssistantMessageStartData: assert isinstance(obj, dict) message_id = from_str(obj.get("messageId")) phase = from_union([from_none, from_str], obj.get("phase")) @@ -470,7 +470,7 @@ class AssistantMessageToolRequest: type: AssistantMessageToolRequestType | None = None @staticmethod - def from_dict(obj: Any) -> "AssistantMessageToolRequest": + def from_dict(obj: Any) -> AssistantMessageToolRequest: assert isinstance(obj, dict) name = from_str(obj.get("name")) tool_call_id = from_str(obj.get("toolCallId")) @@ -517,7 +517,7 @@ class AssistantReasoningData: reasoning_id: str @staticmethod - def from_dict(obj: Any) -> "AssistantReasoningData": + def from_dict(obj: Any) -> AssistantReasoningData: assert isinstance(obj, dict) content = from_str(obj.get("content")) reasoning_id = from_str(obj.get("reasoningId")) @@ -540,7 +540,7 @@ class AssistantReasoningDeltaData: reasoning_id: str @staticmethod - def from_dict(obj: Any) -> "AssistantReasoningDeltaData": + def from_dict(obj: Any) -> AssistantReasoningDeltaData: assert isinstance(obj, dict) delta_content = from_str(obj.get("deltaContent")) reasoning_id = from_str(obj.get("reasoningId")) @@ -562,7 +562,7 @@ class AssistantStreamingDeltaData: total_response_size_bytes: int @staticmethod - def from_dict(obj: Any) -> "AssistantStreamingDeltaData": + def from_dict(obj: Any) -> AssistantStreamingDeltaData: assert isinstance(obj, dict) total_response_size_bytes = from_int(obj.get("totalResponseSizeBytes")) return AssistantStreamingDeltaData( @@ -581,7 +581,7 @@ class AssistantTurnEndData: turn_id: str @staticmethod - def from_dict(obj: Any) -> "AssistantTurnEndData": + def from_dict(obj: Any) -> AssistantTurnEndData: assert isinstance(obj, dict) turn_id = from_str(obj.get("turnId")) return AssistantTurnEndData( @@ -601,7 +601,7 @@ class AssistantTurnStartData: interaction_id: str | None = None @staticmethod - def from_dict(obj: Any) -> "AssistantTurnStartData": + def from_dict(obj: Any) -> AssistantTurnStartData: assert isinstance(obj, dict) turn_id = from_str(obj.get("turnId")) interaction_id = from_union([from_none, from_str], obj.get("interactionId")) @@ -625,7 +625,7 @@ class AssistantUsageCopilotUsage: total_nano_aiu: float @staticmethod - def from_dict(obj: Any) -> "AssistantUsageCopilotUsage": + def from_dict(obj: Any) -> AssistantUsageCopilotUsage: assert isinstance(obj, dict) token_details = from_list(AssistantUsageCopilotUsageTokenDetail.from_dict, obj.get("tokenDetails")) total_nano_aiu = from_float(obj.get("totalNanoAiu")) @@ -650,7 +650,7 @@ class AssistantUsageCopilotUsageTokenDetail: token_type: str @staticmethod - def from_dict(obj: Any) -> "AssistantUsageCopilotUsageTokenDetail": + def from_dict(obj: Any) -> AssistantUsageCopilotUsageTokenDetail: assert isinstance(obj, dict) batch_size = from_int(obj.get("batchSize")) cost_per_batch = from_int(obj.get("costPerBatch")) @@ -696,7 +696,7 @@ class AssistantUsageData: time_to_first_token: timedelta | None = None @staticmethod - def from_dict(obj: Any) -> "AssistantUsageData": + def from_dict(obj: Any) -> AssistantUsageData: assert isinstance(obj, dict) model = from_str(obj.get("model")) api_call_id = from_union([from_none, from_str], obj.get("apiCallId")) @@ -790,7 +790,7 @@ class AssistantUsageQuotaSnapshot: reset_date: datetime | None = None @staticmethod - def from_dict(obj: Any) -> "AssistantUsageQuotaSnapshot": + def from_dict(obj: Any) -> AssistantUsageQuotaSnapshot: assert isinstance(obj, dict) entitlement_requests = from_int(obj.get("entitlementRequests")) is_unlimited_entitlement = from_bool(obj.get("isUnlimitedEntitlement")) @@ -832,7 +832,7 @@ class AutoModeSwitchCompletedData: response: AutoModeSwitchResponse @staticmethod - def from_dict(obj: Any) -> "AutoModeSwitchCompletedData": + def from_dict(obj: Any) -> AutoModeSwitchCompletedData: assert isinstance(obj, dict) request_id = from_str(obj.get("requestId")) response = parse_enum(AutoModeSwitchResponse, obj.get("response")) @@ -856,7 +856,7 @@ class AutoModeSwitchRequestedData: retry_after_seconds: int | None = None @staticmethod - def from_dict(obj: Any) -> "AutoModeSwitchRequestedData": + def from_dict(obj: Any) -> AutoModeSwitchRequestedData: assert isinstance(obj, dict) request_id = from_str(obj.get("requestId")) error_code = from_union([from_none, from_str], obj.get("errorCode")) @@ -883,7 +883,7 @@ class CapabilitiesChangedData: ui: CapabilitiesChangedUI | None = None @staticmethod - def from_dict(obj: Any) -> "CapabilitiesChangedData": + def from_dict(obj: Any) -> CapabilitiesChangedData: assert isinstance(obj, dict) ui = from_union([from_none, CapabilitiesChangedUI.from_dict], obj.get("ui")) return CapabilitiesChangedData( @@ -903,7 +903,7 @@ class CapabilitiesChangedUI: elicitation: bool | None = None @staticmethod - def from_dict(obj: Any) -> "CapabilitiesChangedUI": + def from_dict(obj: Any) -> CapabilitiesChangedUI: assert isinstance(obj, dict) elicitation = from_union([from_none, from_bool], obj.get("elicitation")) return CapabilitiesChangedUI( @@ -923,7 +923,7 @@ class CommandCompletedData: request_id: str @staticmethod - def from_dict(obj: Any) -> "CommandCompletedData": + def from_dict(obj: Any) -> CommandCompletedData: assert isinstance(obj, dict) request_id = from_str(obj.get("requestId")) return CommandCompletedData( @@ -945,7 +945,7 @@ class CommandExecuteData: request_id: str @staticmethod - def from_dict(obj: Any) -> "CommandExecuteData": + def from_dict(obj: Any) -> CommandExecuteData: assert isinstance(obj, dict) args = from_str(obj.get("args")) command = from_str(obj.get("command")) @@ -974,7 +974,7 @@ class CommandQueuedData: request_id: str @staticmethod - def from_dict(obj: Any) -> "CommandQueuedData": + def from_dict(obj: Any) -> CommandQueuedData: assert isinstance(obj, dict) command = from_str(obj.get("command")) request_id = from_str(obj.get("requestId")) @@ -997,7 +997,7 @@ class CommandsChangedCommand: description: str | None = None @staticmethod - def from_dict(obj: Any) -> "CommandsChangedCommand": + def from_dict(obj: Any) -> CommandsChangedCommand: assert isinstance(obj, dict) name = from_str(obj.get("name")) description = from_union([from_none, from_str], obj.get("description")) @@ -1020,7 +1020,7 @@ class CommandsChangedData: commands: list[CommandsChangedCommand] @staticmethod - def from_dict(obj: Any) -> "CommandsChangedData": + def from_dict(obj: Any) -> CommandsChangedData: assert isinstance(obj, dict) commands = from_list(CommandsChangedCommand.from_dict, obj.get("commands")) return CommandsChangedData( @@ -1045,7 +1045,7 @@ class CompactionCompleteCompactionTokensUsed: output_tokens: int | None = None @staticmethod - def from_dict(obj: Any) -> "CompactionCompleteCompactionTokensUsed": + def from_dict(obj: Any) -> CompactionCompleteCompactionTokensUsed: assert isinstance(obj, dict) cache_read_tokens = from_union([from_none, from_int], obj.get("cacheReadTokens")) cache_write_tokens = from_union([from_none, from_int], obj.get("cacheWriteTokens")) @@ -1090,7 +1090,7 @@ class CompactionCompleteCompactionTokensUsedCopilotUsage: total_nano_aiu: float @staticmethod - def from_dict(obj: Any) -> "CompactionCompleteCompactionTokensUsedCopilotUsage": + def from_dict(obj: Any) -> CompactionCompleteCompactionTokensUsedCopilotUsage: assert isinstance(obj, dict) token_details = from_list(CompactionCompleteCompactionTokensUsedCopilotUsageTokenDetail.from_dict, obj.get("tokenDetails")) total_nano_aiu = from_float(obj.get("totalNanoAiu")) @@ -1115,7 +1115,7 @@ class CompactionCompleteCompactionTokensUsedCopilotUsageTokenDetail: token_type: str @staticmethod - def from_dict(obj: Any) -> "CompactionCompleteCompactionTokensUsedCopilotUsageTokenDetail": + def from_dict(obj: Any) -> CompactionCompleteCompactionTokensUsedCopilotUsageTokenDetail: assert isinstance(obj, dict) batch_size = from_int(obj.get("batchSize")) cost_per_batch = from_int(obj.get("costPerBatch")) @@ -1150,7 +1150,7 @@ class CustomAgentsUpdatedAgent: model: str | None = None @staticmethod - def from_dict(obj: Any) -> "CustomAgentsUpdatedAgent": + def from_dict(obj: Any) -> CustomAgentsUpdatedAgent: assert isinstance(obj, dict) description = from_str(obj.get("description")) display_name = from_str(obj.get("displayName")) @@ -1193,7 +1193,7 @@ class ElicitationCompletedData: content: dict[str, Any] | None = None @staticmethod - def from_dict(obj: Any) -> "ElicitationCompletedData": + def from_dict(obj: Any) -> ElicitationCompletedData: assert isinstance(obj, dict) request_id = from_str(obj.get("requestId")) action = from_union([from_none, lambda x: parse_enum(ElicitationCompletedAction, x)], obj.get("action")) @@ -1226,7 +1226,7 @@ class ElicitationRequestedData: url: str | None = None @staticmethod - def from_dict(obj: Any) -> "ElicitationRequestedData": + def from_dict(obj: Any) -> ElicitationRequestedData: assert isinstance(obj, dict) message = from_str(obj.get("message")) request_id = from_str(obj.get("requestId")) @@ -1270,7 +1270,7 @@ class ElicitationRequestedSchema: required: list[str] | None = None @staticmethod - def from_dict(obj: Any) -> "ElicitationRequestedSchema": + def from_dict(obj: Any) -> ElicitationRequestedSchema: assert isinstance(obj, dict) properties = from_dict(lambda x: x, obj.get("properties")) type = from_str(obj.get("type")) @@ -1298,7 +1298,7 @@ class EmbeddedBlobResourceContents: mime_type: str | None = None @staticmethod - def from_dict(obj: Any) -> "EmbeddedBlobResourceContents": + def from_dict(obj: Any) -> EmbeddedBlobResourceContents: assert isinstance(obj, dict) blob = from_str(obj.get("blob")) uri = from_str(obj.get("uri")) @@ -1326,7 +1326,7 @@ class EmbeddedTextResourceContents: mime_type: str | None = None @staticmethod - def from_dict(obj: Any) -> "EmbeddedTextResourceContents": + def from_dict(obj: Any) -> EmbeddedTextResourceContents: assert isinstance(obj, dict) text = from_str(obj.get("text")) uri = from_str(obj.get("uri")) @@ -1356,7 +1356,7 @@ class ExitPlanModeCompletedData: selected_action: ExitPlanModeAction | None = None @staticmethod - def from_dict(obj: Any) -> "ExitPlanModeCompletedData": + def from_dict(obj: Any) -> ExitPlanModeCompletedData: assert isinstance(obj, dict) request_id = from_str(obj.get("requestId")) approved = from_union([from_none, from_bool], obj.get("approved")) @@ -1395,7 +1395,7 @@ class ExitPlanModeRequestedData: summary: str @staticmethod - def from_dict(obj: Any) -> "ExitPlanModeRequestedData": + def from_dict(obj: Any) -> ExitPlanModeRequestedData: assert isinstance(obj, dict) actions = from_list(lambda x: parse_enum(ExitPlanModeAction, x), obj.get("actions")) plan_content = from_str(obj.get("planContent")) @@ -1429,7 +1429,7 @@ class ExtensionsLoadedExtension: status: ExtensionsLoadedExtensionStatus @staticmethod - def from_dict(obj: Any) -> "ExtensionsLoadedExtension": + def from_dict(obj: Any) -> ExtensionsLoadedExtension: assert isinstance(obj, dict) id = from_str(obj.get("id")) name = from_str(obj.get("name")) @@ -1457,7 +1457,7 @@ class ExternalToolCompletedData: request_id: str @staticmethod - def from_dict(obj: Any) -> "ExternalToolCompletedData": + def from_dict(obj: Any) -> ExternalToolCompletedData: assert isinstance(obj, dict) request_id = from_str(obj.get("requestId")) return ExternalToolCompletedData( @@ -1482,7 +1482,7 @@ class ExternalToolRequestedData: tracestate: str | None = None @staticmethod - def from_dict(obj: Any) -> "ExternalToolRequestedData": + def from_dict(obj: Any) -> ExternalToolRequestedData: assert isinstance(obj, dict) request_id = from_str(obj.get("requestId")) session_id = from_str(obj.get("sessionId")) @@ -1524,7 +1524,7 @@ class HandoffRepository: branch: str | None = None @staticmethod - def from_dict(obj: Any) -> "HandoffRepository": + def from_dict(obj: Any) -> HandoffRepository: assert isinstance(obj, dict) name = from_str(obj.get("name")) owner = from_str(obj.get("owner")) @@ -1554,7 +1554,7 @@ class HookEndData: output: Any = None @staticmethod - def from_dict(obj: Any) -> "HookEndData": + def from_dict(obj: Any) -> HookEndData: assert isinstance(obj, dict) hook_invocation_id = from_str(obj.get("hookInvocationId")) hook_type = from_str(obj.get("hookType")) @@ -1588,7 +1588,7 @@ class HookEndError: stack: str | None = None @staticmethod - def from_dict(obj: Any) -> "HookEndError": + def from_dict(obj: Any) -> HookEndError: assert isinstance(obj, dict) message = from_str(obj.get("message")) stack = from_union([from_none, from_str], obj.get("stack")) @@ -1613,7 +1613,7 @@ class HookStartData: input: Any = None @staticmethod - def from_dict(obj: Any) -> "HookStartData": + def from_dict(obj: Any) -> HookStartData: assert isinstance(obj, dict) hook_invocation_id = from_str(obj.get("hookInvocationId")) hook_type = from_str(obj.get("hookType")) @@ -1639,7 +1639,7 @@ class McpOauthCompletedData: request_id: str @staticmethod - def from_dict(obj: Any) -> "McpOauthCompletedData": + def from_dict(obj: Any) -> McpOauthCompletedData: assert isinstance(obj, dict) request_id = from_str(obj.get("requestId")) return McpOauthCompletedData( @@ -1661,7 +1661,7 @@ class McpOauthRequiredData: static_client_config: McpOauthRequiredStaticClientConfig | None = None @staticmethod - def from_dict(obj: Any) -> "McpOauthRequiredData": + def from_dict(obj: Any) -> McpOauthRequiredData: assert isinstance(obj, dict) request_id = from_str(obj.get("requestId")) server_name = from_str(obj.get("serverName")) @@ -1692,7 +1692,7 @@ class McpOauthRequiredStaticClientConfig: public_client: bool | None = None @staticmethod - def from_dict(obj: Any) -> "McpOauthRequiredStaticClientConfig": + def from_dict(obj: Any) -> McpOauthRequiredStaticClientConfig: assert isinstance(obj, dict) client_id = from_str(obj.get("clientId")) grant_type = from_union([from_none, from_str], obj.get("grantType")) @@ -1722,7 +1722,7 @@ class McpServersLoadedServer: source: McpServerSource | None = None @staticmethod - def from_dict(obj: Any) -> "McpServersLoadedServer": + def from_dict(obj: Any) -> McpServersLoadedServer: assert isinstance(obj, dict) name = from_str(obj.get("name")) status = parse_enum(McpServerStatus, obj.get("status")) @@ -1759,7 +1759,7 @@ class ModelCallFailureData: status_code: int | None = None @staticmethod - def from_dict(obj: Any) -> "ModelCallFailureData": + def from_dict(obj: Any) -> ModelCallFailureData: assert isinstance(obj, dict) source = parse_enum(ModelCallFailureSource, obj.get("source")) api_call_id = from_union([from_none, from_str], obj.get("apiCallId")) @@ -1804,7 +1804,7 @@ def to_dict(self) -> dict: class PendingMessagesModifiedData: "Empty payload; the event signals that the pending message queue has changed" @staticmethod - def from_dict(obj: Any) -> "PendingMessagesModifiedData": + def from_dict(obj: Any) -> PendingMessagesModifiedData: assert isinstance(obj, dict) return PendingMessagesModifiedData() @@ -1818,7 +1818,7 @@ class PermissionApproved: kind: ClassVar[str] = "approved" @staticmethod - def from_dict(obj: Any) -> "PermissionApproved": + def from_dict(obj: Any) -> PermissionApproved: assert isinstance(obj, dict) return PermissionApproved( ) @@ -1837,7 +1837,7 @@ class PermissionApprovedForLocation: location_key: str @staticmethod - def from_dict(obj: Any) -> "PermissionApprovedForLocation": + def from_dict(obj: Any) -> PermissionApprovedForLocation: assert isinstance(obj, dict) approval = _load_UserToolSessionApproval(obj.get("approval")) location_key = from_str(obj.get("locationKey")) @@ -1861,7 +1861,7 @@ class PermissionApprovedForSession: kind: ClassVar[str] = "approved-for-session" @staticmethod - def from_dict(obj: Any) -> "PermissionApprovedForSession": + def from_dict(obj: Any) -> PermissionApprovedForSession: assert isinstance(obj, dict) approval = _load_UserToolSessionApproval(obj.get("approval")) return PermissionApprovedForSession( @@ -1882,7 +1882,7 @@ class PermissionCancelled: reason: str | None = None @staticmethod - def from_dict(obj: Any) -> "PermissionCancelled": + def from_dict(obj: Any) -> PermissionCancelled: assert isinstance(obj, dict) reason = from_union([from_none, from_str], obj.get("reason")) return PermissionCancelled( @@ -1905,7 +1905,7 @@ class PermissionCompletedData: tool_call_id: str | None = None @staticmethod - def from_dict(obj: Any) -> "PermissionCompletedData": + def from_dict(obj: Any) -> PermissionCompletedData: assert isinstance(obj, dict) request_id = from_str(obj.get("requestId")) result = _load_PermissionResult(obj.get("result")) @@ -1933,7 +1933,7 @@ class PermissionDeniedByContentExclusionPolicy: path: str @staticmethod - def from_dict(obj: Any) -> "PermissionDeniedByContentExclusionPolicy": + def from_dict(obj: Any) -> PermissionDeniedByContentExclusionPolicy: assert isinstance(obj, dict) message = from_str(obj.get("message")) path = from_str(obj.get("path")) @@ -1958,7 +1958,7 @@ class PermissionDeniedByPermissionRequestHook: message: str | None = None @staticmethod - def from_dict(obj: Any) -> "PermissionDeniedByPermissionRequestHook": + def from_dict(obj: Any) -> PermissionDeniedByPermissionRequestHook: assert isinstance(obj, dict) interrupt = from_union([from_none, from_bool], obj.get("interrupt")) message = from_union([from_none, from_str], obj.get("message")) @@ -1984,7 +1984,7 @@ class PermissionDeniedByRules: rules: list[PermissionRule] @staticmethod - def from_dict(obj: Any) -> "PermissionDeniedByRules": + def from_dict(obj: Any) -> PermissionDeniedByRules: assert isinstance(obj, dict) rules = from_list(PermissionRule.from_dict, obj.get("rules")) return PermissionDeniedByRules( @@ -2006,7 +2006,7 @@ class PermissionDeniedInteractivelyByUser: force_reject: bool | None = None @staticmethod - def from_dict(obj: Any) -> "PermissionDeniedInteractivelyByUser": + def from_dict(obj: Any) -> PermissionDeniedInteractivelyByUser: assert isinstance(obj, dict) feedback = from_union([from_none, from_str], obj.get("feedback")) force_reject = from_union([from_none, from_bool], obj.get("forceReject")) @@ -2031,7 +2031,7 @@ class PermissionDeniedNoApprovalRuleAndCouldNotRequestFromUser: kind: ClassVar[str] = "denied-no-approval-rule-and-could-not-request-from-user" @staticmethod - def from_dict(obj: Any) -> "PermissionDeniedNoApprovalRuleAndCouldNotRequestFromUser": + def from_dict(obj: Any) -> PermissionDeniedNoApprovalRuleAndCouldNotRequestFromUser: assert isinstance(obj, dict) return PermissionDeniedNoApprovalRuleAndCouldNotRequestFromUser( ) @@ -2054,7 +2054,7 @@ class PermissionPromptRequestCommands: warning: str | None = None @staticmethod - def from_dict(obj: Any) -> "PermissionPromptRequestCommands": + def from_dict(obj: Any) -> PermissionPromptRequestCommands: assert isinstance(obj, dict) can_offer_session_approval = from_bool(obj.get("canOfferSessionApproval")) command_identifiers = from_list(from_str, obj.get("commandIdentifiers")) @@ -2095,7 +2095,7 @@ class PermissionPromptRequestCustomTool: tool_call_id: str | None = None @staticmethod - def from_dict(obj: Any) -> "PermissionPromptRequestCustomTool": + def from_dict(obj: Any) -> PermissionPromptRequestCustomTool: assert isinstance(obj, dict) tool_description = from_str(obj.get("toolDescription")) tool_name = from_str(obj.get("toolName")) @@ -2129,7 +2129,7 @@ class PermissionPromptRequestExtensionManagement: tool_call_id: str | None = None @staticmethod - def from_dict(obj: Any) -> "PermissionPromptRequestExtensionManagement": + def from_dict(obj: Any) -> PermissionPromptRequestExtensionManagement: assert isinstance(obj, dict) operation = from_str(obj.get("operation")) extension_name = from_union([from_none, from_str], obj.get("extensionName")) @@ -2160,7 +2160,7 @@ class PermissionPromptRequestExtensionPermissionAccess: tool_call_id: str | None = None @staticmethod - def from_dict(obj: Any) -> "PermissionPromptRequestExtensionPermissionAccess": + def from_dict(obj: Any) -> PermissionPromptRequestExtensionPermissionAccess: assert isinstance(obj, dict) capabilities = from_list(from_str, obj.get("capabilities")) extension_name = from_str(obj.get("extensionName")) @@ -2191,7 +2191,7 @@ class PermissionPromptRequestHook: tool_call_id: str | None = None @staticmethod - def from_dict(obj: Any) -> "PermissionPromptRequestHook": + def from_dict(obj: Any) -> PermissionPromptRequestHook: assert isinstance(obj, dict) tool_name = from_str(obj.get("toolName")) hook_message = from_union([from_none, from_str], obj.get("hookMessage")) @@ -2228,7 +2228,7 @@ class PermissionPromptRequestMcp: tool_call_id: str | None = None @staticmethod - def from_dict(obj: Any) -> "PermissionPromptRequestMcp": + def from_dict(obj: Any) -> PermissionPromptRequestMcp: assert isinstance(obj, dict) server_name = from_str(obj.get("serverName")) tool_name = from_str(obj.get("toolName")) @@ -2269,7 +2269,7 @@ class PermissionPromptRequestMemory: tool_call_id: str | None = None @staticmethod - def from_dict(obj: Any) -> "PermissionPromptRequestMemory": + def from_dict(obj: Any) -> PermissionPromptRequestMemory: assert isinstance(obj, dict) fact = from_str(obj.get("fact")) action = from_union([from_none, lambda x: parse_enum(PermissionRequestMemoryAction, x)], obj.get("action")) @@ -2316,7 +2316,7 @@ class PermissionPromptRequestPath: tool_call_id: str | None = None @staticmethod - def from_dict(obj: Any) -> "PermissionPromptRequestPath": + def from_dict(obj: Any) -> PermissionPromptRequestPath: assert isinstance(obj, dict) access_kind = parse_enum(PermissionPromptRequestPathAccessKind, obj.get("accessKind")) paths = from_list(from_str, obj.get("paths")) @@ -2346,7 +2346,7 @@ class PermissionPromptRequestRead: tool_call_id: str | None = None @staticmethod - def from_dict(obj: Any) -> "PermissionPromptRequestRead": + def from_dict(obj: Any) -> PermissionPromptRequestRead: assert isinstance(obj, dict) intention = from_str(obj.get("intention")) path = from_str(obj.get("path")) @@ -2376,7 +2376,7 @@ class PermissionPromptRequestUrl: tool_call_id: str | None = None @staticmethod - def from_dict(obj: Any) -> "PermissionPromptRequestUrl": + def from_dict(obj: Any) -> PermissionPromptRequestUrl: assert isinstance(obj, dict) intention = from_str(obj.get("intention")) url = from_str(obj.get("url")) @@ -2409,7 +2409,7 @@ class PermissionPromptRequestWrite: tool_call_id: str | None = None @staticmethod - def from_dict(obj: Any) -> "PermissionPromptRequestWrite": + def from_dict(obj: Any) -> PermissionPromptRequestWrite: assert isinstance(obj, dict) can_offer_session_approval = from_bool(obj.get("canOfferSessionApproval")) diff = from_str(obj.get("diff")) @@ -2450,7 +2450,7 @@ class PermissionRequestCustomTool: tool_call_id: str | None = None @staticmethod - def from_dict(obj: Any) -> "PermissionRequestCustomTool": + def from_dict(obj: Any) -> PermissionRequestCustomTool: assert isinstance(obj, dict) tool_description = from_str(obj.get("toolDescription")) tool_name = from_str(obj.get("toolName")) @@ -2484,7 +2484,7 @@ class PermissionRequestExtensionManagement: tool_call_id: str | None = None @staticmethod - def from_dict(obj: Any) -> "PermissionRequestExtensionManagement": + def from_dict(obj: Any) -> PermissionRequestExtensionManagement: assert isinstance(obj, dict) operation = from_str(obj.get("operation")) extension_name = from_union([from_none, from_str], obj.get("extensionName")) @@ -2515,7 +2515,7 @@ class PermissionRequestExtensionPermissionAccess: tool_call_id: str | None = None @staticmethod - def from_dict(obj: Any) -> "PermissionRequestExtensionPermissionAccess": + def from_dict(obj: Any) -> PermissionRequestExtensionPermissionAccess: assert isinstance(obj, dict) capabilities = from_list(from_str, obj.get("capabilities")) extension_name = from_str(obj.get("extensionName")) @@ -2546,7 +2546,7 @@ class PermissionRequestHook: tool_call_id: str | None = None @staticmethod - def from_dict(obj: Any) -> "PermissionRequestHook": + def from_dict(obj: Any) -> PermissionRequestHook: assert isinstance(obj, dict) tool_name = from_str(obj.get("toolName")) hook_message = from_union([from_none, from_str], obj.get("hookMessage")) @@ -2584,7 +2584,7 @@ class PermissionRequestMcp: tool_call_id: str | None = None @staticmethod - def from_dict(obj: Any) -> "PermissionRequestMcp": + def from_dict(obj: Any) -> PermissionRequestMcp: assert isinstance(obj, dict) read_only = from_bool(obj.get("readOnly")) server_name = from_str(obj.get("serverName")) @@ -2628,7 +2628,7 @@ class PermissionRequestMemory: tool_call_id: str | None = None @staticmethod - def from_dict(obj: Any) -> "PermissionRequestMemory": + def from_dict(obj: Any) -> PermissionRequestMemory: assert isinstance(obj, dict) fact = from_str(obj.get("fact")) action = from_union([from_none, lambda x: parse_enum(PermissionRequestMemoryAction, x)], obj.get("action")) @@ -2675,7 +2675,7 @@ class PermissionRequestRead: tool_call_id: str | None = None @staticmethod - def from_dict(obj: Any) -> "PermissionRequestRead": + def from_dict(obj: Any) -> PermissionRequestRead: assert isinstance(obj, dict) intention = from_str(obj.get("intention")) path = from_str(obj.get("path")) @@ -2711,7 +2711,7 @@ class PermissionRequestShell: warning: str | None = None @staticmethod - def from_dict(obj: Any) -> "PermissionRequestShell": + def from_dict(obj: Any) -> PermissionRequestShell: assert isinstance(obj, dict) can_offer_session_approval = from_bool(obj.get("canOfferSessionApproval")) commands = from_list(PermissionRequestShellCommand.from_dict, obj.get("commands")) @@ -2758,7 +2758,7 @@ class PermissionRequestShellCommand: read_only: bool @staticmethod - def from_dict(obj: Any) -> "PermissionRequestShellCommand": + def from_dict(obj: Any) -> PermissionRequestShellCommand: assert isinstance(obj, dict) identifier = from_str(obj.get("identifier")) read_only = from_bool(obj.get("readOnly")) @@ -2780,7 +2780,7 @@ class PermissionRequestShellPossibleUrl: url: str @staticmethod - def from_dict(obj: Any) -> "PermissionRequestShellPossibleUrl": + def from_dict(obj: Any) -> PermissionRequestShellPossibleUrl: assert isinstance(obj, dict) url = from_str(obj.get("url")) return PermissionRequestShellPossibleUrl( @@ -2802,7 +2802,7 @@ class PermissionRequestUrl: tool_call_id: str | None = None @staticmethod - def from_dict(obj: Any) -> "PermissionRequestUrl": + def from_dict(obj: Any) -> PermissionRequestUrl: assert isinstance(obj, dict) intention = from_str(obj.get("intention")) url = from_str(obj.get("url")) @@ -2835,7 +2835,7 @@ class PermissionRequestWrite: tool_call_id: str | None = None @staticmethod - def from_dict(obj: Any) -> "PermissionRequestWrite": + def from_dict(obj: Any) -> PermissionRequestWrite: assert isinstance(obj, dict) can_offer_session_approval = from_bool(obj.get("canOfferSessionApproval")) diff = from_str(obj.get("diff")) @@ -2875,7 +2875,7 @@ class PermissionRequestedData: resolved_by_hook: bool | None = None @staticmethod - def from_dict(obj: Any) -> "PermissionRequestedData": + def from_dict(obj: Any) -> PermissionRequestedData: assert isinstance(obj, dict) permission_request = _load_PermissionRequest(obj.get("permissionRequest")) request_id = from_str(obj.get("requestId")) @@ -2906,7 +2906,7 @@ class PermissionRule: kind: str @staticmethod - def from_dict(obj: Any) -> "PermissionRule": + def from_dict(obj: Any) -> PermissionRule: assert isinstance(obj, dict) argument = from_union([from_none, from_str], obj.get("argument")) kind = from_str(obj.get("kind")) @@ -2928,7 +2928,7 @@ class SamplingCompletedData: request_id: str @staticmethod - def from_dict(obj: Any) -> "SamplingCompletedData": + def from_dict(obj: Any) -> SamplingCompletedData: assert isinstance(obj, dict) request_id = from_str(obj.get("requestId")) return SamplingCompletedData( @@ -2949,7 +2949,7 @@ class SamplingRequestedData: server_name: str @staticmethod - def from_dict(obj: Any) -> "SamplingRequestedData": + def from_dict(obj: Any) -> SamplingRequestedData: assert isinstance(obj, dict) mcp_request_id = obj.get("mcpRequestId") request_id = from_str(obj.get("requestId")) @@ -2972,7 +2972,7 @@ def to_dict(self) -> dict: class SessionBackgroundTasksChangedData: "Schema for the `BackgroundTasksChangedData` type." @staticmethod - def from_dict(obj: Any) -> "SessionBackgroundTasksChangedData": + def from_dict(obj: Any) -> SessionBackgroundTasksChangedData: assert isinstance(obj, dict) return SessionBackgroundTasksChangedData() @@ -3001,7 +3001,7 @@ class SessionCompactionCompleteData: tool_definitions_tokens: int | None = None @staticmethod - def from_dict(obj: Any) -> "SessionCompactionCompleteData": + def from_dict(obj: Any) -> SessionCompactionCompleteData: assert isinstance(obj, dict) success = from_bool(obj.get("success")) checkpoint_number = from_union([from_none, from_int], obj.get("checkpointNumber")) @@ -3082,7 +3082,7 @@ class SessionCompactionStartData: tool_definitions_tokens: int | None = None @staticmethod - def from_dict(obj: Any) -> "SessionCompactionStartData": + def from_dict(obj: Any) -> SessionCompactionStartData: assert isinstance(obj, dict) conversation_tokens = from_union([from_none, from_int], obj.get("conversationTokens")) system_tokens = from_union([from_none, from_int], obj.get("systemTokens")) @@ -3117,7 +3117,7 @@ class SessionContextChangedData: repository_host: str | None = None @staticmethod - def from_dict(obj: Any) -> "SessionContextChangedData": + def from_dict(obj: Any) -> SessionContextChangedData: assert isinstance(obj, dict) cwd = from_str(obj.get("cwd")) base_commit = from_union([from_none, from_str], obj.get("baseCommit")) @@ -3166,7 +3166,7 @@ class SessionCustomAgentsUpdatedData: warnings: list[str] @staticmethod - def from_dict(obj: Any) -> "SessionCustomAgentsUpdatedData": + def from_dict(obj: Any) -> SessionCustomAgentsUpdatedData: assert isinstance(obj, dict) agents = from_list(CustomAgentsUpdatedAgent.from_dict, obj.get("agents")) errors = from_list(from_str, obj.get("errors")) @@ -3195,7 +3195,7 @@ class SessionCustomNotificationData: version: int | None = None @staticmethod - def from_dict(obj: Any) -> "SessionCustomNotificationData": + def from_dict(obj: Any) -> SessionCustomNotificationData: assert isinstance(obj, dict) name = from_str(obj.get("name")) payload = obj.get("payload") @@ -3235,7 +3235,7 @@ class SessionErrorData: url: str | None = None @staticmethod - def from_dict(obj: Any) -> "SessionErrorData": + def from_dict(obj: Any) -> SessionErrorData: assert isinstance(obj, dict) error_type = from_str(obj.get("errorType")) message = from_str(obj.get("message")) @@ -3281,7 +3281,7 @@ class SessionExtensionsLoadedData: extensions: list[ExtensionsLoadedExtension] @staticmethod - def from_dict(obj: Any) -> "SessionExtensionsLoadedData": + def from_dict(obj: Any) -> SessionExtensionsLoadedData: assert isinstance(obj, dict) extensions = from_list(ExtensionsLoadedExtension.from_dict, obj.get("extensions")) return SessionExtensionsLoadedData( @@ -3306,7 +3306,7 @@ class SessionHandoffData: summary: str | None = None @staticmethod - def from_dict(obj: Any) -> "SessionHandoffData": + def from_dict(obj: Any) -> SessionHandoffData: assert isinstance(obj, dict) handoff_time = from_datetime(obj.get("handoffTime")) source_type = parse_enum(HandoffSourceType, obj.get("sourceType")) @@ -3348,7 +3348,7 @@ class SessionIdleData: aborted: bool | None = None @staticmethod - def from_dict(obj: Any) -> "SessionIdleData": + def from_dict(obj: Any) -> SessionIdleData: assert isinstance(obj, dict) aborted = from_union([from_none, from_bool], obj.get("aborted")) return SessionIdleData( @@ -3371,7 +3371,7 @@ class SessionInfoData: url: str | None = None @staticmethod - def from_dict(obj: Any) -> "SessionInfoData": + def from_dict(obj: Any) -> SessionInfoData: assert isinstance(obj, dict) info_type = from_str(obj.get("infoType")) message = from_str(obj.get("message")) @@ -3402,7 +3402,7 @@ class SessionMcpServerStatusChangedData: status: McpServerStatus @staticmethod - def from_dict(obj: Any) -> "SessionMcpServerStatusChangedData": + def from_dict(obj: Any) -> SessionMcpServerStatusChangedData: assert isinstance(obj, dict) server_name = from_str(obj.get("serverName")) status = parse_enum(McpServerStatus, obj.get("status")) @@ -3424,7 +3424,7 @@ class SessionMcpServersLoadedData: servers: list[McpServersLoadedServer] @staticmethod - def from_dict(obj: Any) -> "SessionMcpServersLoadedData": + def from_dict(obj: Any) -> SessionMcpServersLoadedData: assert isinstance(obj, dict) servers = from_list(McpServersLoadedServer.from_dict, obj.get("servers")) return SessionMcpServersLoadedData( @@ -3444,7 +3444,7 @@ class SessionModeChangedData: previous_mode: SessionMode @staticmethod - def from_dict(obj: Any) -> "SessionModeChangedData": + def from_dict(obj: Any) -> SessionModeChangedData: assert isinstance(obj, dict) new_mode = parse_enum(SessionMode, obj.get("newMode")) previous_mode = parse_enum(SessionMode, obj.get("previousMode")) @@ -3472,7 +3472,7 @@ class SessionModelChangeData: reasoning_summary: ReasoningSummary | None = None @staticmethod - def from_dict(obj: Any) -> "SessionModelChangeData": + def from_dict(obj: Any) -> SessionModelChangeData: assert isinstance(obj, dict) new_model = from_str(obj.get("newModel")) cause = from_union([from_none, from_str], obj.get("cause")) @@ -3515,7 +3515,7 @@ class SessionPlanChangedData: operation: PlanChangedOperation @staticmethod - def from_dict(obj: Any) -> "SessionPlanChangedData": + def from_dict(obj: Any) -> SessionPlanChangedData: assert isinstance(obj, dict) operation = parse_enum(PlanChangedOperation, obj.get("operation")) return SessionPlanChangedData( @@ -3534,7 +3534,7 @@ class SessionRemoteSteerableChangedData: remote_steerable: bool @staticmethod - def from_dict(obj: Any) -> "SessionRemoteSteerableChangedData": + def from_dict(obj: Any) -> SessionRemoteSteerableChangedData: assert isinstance(obj, dict) remote_steerable = from_bool(obj.get("remoteSteerable")) return SessionRemoteSteerableChangedData( @@ -3562,7 +3562,7 @@ class SessionResumeData: session_was_active: bool | None = None @staticmethod - def from_dict(obj: Any) -> "SessionResumeData": + def from_dict(obj: Any) -> SessionResumeData: assert isinstance(obj, dict) event_count = from_int(obj.get("eventCount")) resume_time = from_datetime(obj.get("resumeTime")) @@ -3616,7 +3616,7 @@ class SessionScheduleCancelledData: id: int @staticmethod - def from_dict(obj: Any) -> "SessionScheduleCancelledData": + def from_dict(obj: Any) -> SessionScheduleCancelledData: assert isinstance(obj, dict) id = from_int(obj.get("id")) return SessionScheduleCancelledData( @@ -3639,7 +3639,7 @@ class SessionScheduleCreatedData: recurring: bool | None = None @staticmethod - def from_dict(obj: Any) -> "SessionScheduleCreatedData": + def from_dict(obj: Any) -> SessionScheduleCreatedData: assert isinstance(obj, dict) id = from_int(obj.get("id")) interval = from_timedelta(obj.get("intervalMs")) @@ -3685,7 +3685,7 @@ class SessionShutdownData: total_premium_requests: float | None = None @staticmethod - def from_dict(obj: Any) -> "SessionShutdownData": + def from_dict(obj: Any) -> SessionShutdownData: assert isinstance(obj, dict) code_changes = ShutdownCodeChanges.from_dict(obj.get("codeChanges")) model_metrics = from_dict(ShutdownModelMetric.from_dict, obj.get("modelMetrics")) @@ -3752,7 +3752,7 @@ class SessionSkillsLoadedData: skills: list[SkillsLoadedSkill] @staticmethod - def from_dict(obj: Any) -> "SessionSkillsLoadedData": + def from_dict(obj: Any) -> SessionSkillsLoadedData: assert isinstance(obj, dict) skills = from_list(SkillsLoadedSkill.from_dict, obj.get("skills")) return SessionSkillsLoadedData( @@ -3772,7 +3772,7 @@ class SessionSnapshotRewindData: up_to_event_id: str @staticmethod - def from_dict(obj: Any) -> "SessionSnapshotRewindData": + def from_dict(obj: Any) -> SessionSnapshotRewindData: assert isinstance(obj, dict) events_removed = from_int(obj.get("eventsRemoved")) up_to_event_id = from_str(obj.get("upToEventId")) @@ -3805,7 +3805,7 @@ class SessionStartData: selected_model: str | None = None @staticmethod - def from_dict(obj: Any) -> "SessionStartData": + def from_dict(obj: Any) -> SessionStartData: assert isinstance(obj, dict) copilot_version = from_str(obj.get("copilotVersion")) producer = from_str(obj.get("producer")) @@ -3865,7 +3865,7 @@ class SessionTaskCompleteData: summary: str | None = None @staticmethod - def from_dict(obj: Any) -> "SessionTaskCompleteData": + def from_dict(obj: Any) -> SessionTaskCompleteData: assert isinstance(obj, dict) success = from_union([from_none, from_bool], obj.get("success")) summary = from_union([from_none, from_str], obj.get("summary")) @@ -3889,7 +3889,7 @@ class SessionTitleChangedData: title: str @staticmethod - def from_dict(obj: Any) -> "SessionTitleChangedData": + def from_dict(obj: Any) -> SessionTitleChangedData: assert isinstance(obj, dict) title = from_str(obj.get("title")) return SessionTitleChangedData( @@ -3908,7 +3908,7 @@ class SessionToolsUpdatedData: model: str @staticmethod - def from_dict(obj: Any) -> "SessionToolsUpdatedData": + def from_dict(obj: Any) -> SessionToolsUpdatedData: assert isinstance(obj, dict) model = from_str(obj.get("model")) return SessionToolsUpdatedData( @@ -3934,7 +3934,7 @@ class SessionTruncationData: tokens_removed_during_truncation: int @staticmethod - def from_dict(obj: Any) -> "SessionTruncationData": + def from_dict(obj: Any) -> SessionTruncationData: assert isinstance(obj, dict) messages_removed_during_truncation = from_int(obj.get("messagesRemovedDuringTruncation")) performed_by = from_str(obj.get("performedBy")) @@ -3980,7 +3980,7 @@ class SessionUsageInfoData: tool_definitions_tokens: int | None = None @staticmethod - def from_dict(obj: Any) -> "SessionUsageInfoData": + def from_dict(obj: Any) -> SessionUsageInfoData: assert isinstance(obj, dict) current_tokens = from_int(obj.get("currentTokens")) messages_length = from_int(obj.get("messagesLength")) @@ -4023,7 +4023,7 @@ class SessionWarningData: url: str | None = None @staticmethod - def from_dict(obj: Any) -> "SessionWarningData": + def from_dict(obj: Any) -> SessionWarningData: assert isinstance(obj, dict) message = from_str(obj.get("message")) warning_type = from_str(obj.get("warningType")) @@ -4050,7 +4050,7 @@ class SessionWorkspaceFileChangedData: path: str @staticmethod - def from_dict(obj: Any) -> "SessionWorkspaceFileChangedData": + def from_dict(obj: Any) -> SessionWorkspaceFileChangedData: assert isinstance(obj, dict) operation = parse_enum(WorkspaceFileChangedOperation, obj.get("operation")) path = from_str(obj.get("path")) @@ -4074,7 +4074,7 @@ class ShutdownCodeChanges: lines_removed: int @staticmethod - def from_dict(obj: Any) -> "ShutdownCodeChanges": + def from_dict(obj: Any) -> ShutdownCodeChanges: assert isinstance(obj, dict) files_modified = from_list(from_str, obj.get("filesModified")) lines_added = from_int(obj.get("linesAdded")) @@ -4102,7 +4102,7 @@ class ShutdownModelMetric: total_nano_aiu: float | None = None @staticmethod - def from_dict(obj: Any) -> "ShutdownModelMetric": + def from_dict(obj: Any) -> ShutdownModelMetric: assert isinstance(obj, dict) requests = ShutdownModelMetricRequests.from_dict(obj.get("requests")) usage = ShutdownModelMetricUsage.from_dict(obj.get("usage")) @@ -4133,7 +4133,7 @@ class ShutdownModelMetricRequests: count: int | None = None @staticmethod - def from_dict(obj: Any) -> "ShutdownModelMetricRequests": + def from_dict(obj: Any) -> ShutdownModelMetricRequests: assert isinstance(obj, dict) cost = from_union([from_none, from_float], obj.get("cost")) count = from_union([from_none, from_int], obj.get("count")) @@ -4157,7 +4157,7 @@ class ShutdownModelMetricTokenDetail: token_count: int @staticmethod - def from_dict(obj: Any) -> "ShutdownModelMetricTokenDetail": + def from_dict(obj: Any) -> ShutdownModelMetricTokenDetail: assert isinstance(obj, dict) token_count = from_int(obj.get("tokenCount")) return ShutdownModelMetricTokenDetail( @@ -4180,7 +4180,7 @@ class ShutdownModelMetricUsage: reasoning_tokens: int | None = None @staticmethod - def from_dict(obj: Any) -> "ShutdownModelMetricUsage": + def from_dict(obj: Any) -> ShutdownModelMetricUsage: assert isinstance(obj, dict) cache_read_tokens = from_int(obj.get("cacheReadTokens")) cache_write_tokens = from_int(obj.get("cacheWriteTokens")) @@ -4212,7 +4212,7 @@ class ShutdownTokenDetail: token_count: int @staticmethod - def from_dict(obj: Any) -> "ShutdownTokenDetail": + def from_dict(obj: Any) -> ShutdownTokenDetail: assert isinstance(obj, dict) token_count = from_int(obj.get("tokenCount")) return ShutdownTokenDetail( @@ -4237,7 +4237,7 @@ class SkillInvokedData: plugin_version: str | None = None @staticmethod - def from_dict(obj: Any) -> "SkillInvokedData": + def from_dict(obj: Any) -> SkillInvokedData: assert isinstance(obj, dict) content = from_str(obj.get("content")) name = from_str(obj.get("name")) @@ -4283,7 +4283,7 @@ class SkillsLoadedSkill: path: str | None = None @staticmethod - def from_dict(obj: Any) -> "SkillsLoadedSkill": + def from_dict(obj: Any) -> SkillsLoadedSkill: assert isinstance(obj, dict) description = from_str(obj.get("description")) enabled = from_bool(obj.get("enabled")) @@ -4324,7 +4324,7 @@ class SubagentCompletedData: total_tool_calls: int | None = None @staticmethod - def from_dict(obj: Any) -> "SubagentCompletedData": + def from_dict(obj: Any) -> SubagentCompletedData: assert isinstance(obj, dict) agent_display_name = from_str(obj.get("agentDisplayName")) agent_name = from_str(obj.get("agentName")) @@ -4363,7 +4363,7 @@ def to_dict(self) -> dict: class SubagentDeselectedData: "Empty payload; the event signals that the custom agent was deselected, returning to the default agent" @staticmethod - def from_dict(obj: Any) -> "SubagentDeselectedData": + def from_dict(obj: Any) -> SubagentDeselectedData: assert isinstance(obj, dict) return SubagentDeselectedData() @@ -4384,7 +4384,7 @@ class SubagentFailedData: total_tool_calls: int | None = None @staticmethod - def from_dict(obj: Any) -> "SubagentFailedData": + def from_dict(obj: Any) -> SubagentFailedData: assert isinstance(obj, dict) agent_display_name = from_str(obj.get("agentDisplayName")) agent_name = from_str(obj.get("agentName")) @@ -4430,7 +4430,7 @@ class SubagentSelectedData: tools: list[str] | None @staticmethod - def from_dict(obj: Any) -> "SubagentSelectedData": + def from_dict(obj: Any) -> SubagentSelectedData: assert isinstance(obj, dict) agent_display_name = from_str(obj.get("agentDisplayName")) agent_name = from_str(obj.get("agentName")) @@ -4459,7 +4459,7 @@ class SubagentStartedData: model: str | None = None @staticmethod - def from_dict(obj: Any) -> "SubagentStartedData": + def from_dict(obj: Any) -> SubagentStartedData: assert isinstance(obj, dict) agent_description = from_str(obj.get("agentDescription")) agent_display_name = from_str(obj.get("agentDisplayName")) @@ -4494,7 +4494,7 @@ class SystemMessageData: name: str | None = None @staticmethod - def from_dict(obj: Any) -> "SystemMessageData": + def from_dict(obj: Any) -> SystemMessageData: assert isinstance(obj, dict) content = from_str(obj.get("content")) role = parse_enum(SystemMessageRole, obj.get("role")) @@ -4525,7 +4525,7 @@ class SystemMessageMetadata: variables: dict[str, Any] | None = None @staticmethod - def from_dict(obj: Any) -> "SystemMessageMetadata": + def from_dict(obj: Any) -> SystemMessageMetadata: assert isinstance(obj, dict) prompt_version = from_union([from_none, from_str], obj.get("promptVersion")) variables = from_union([from_none, lambda x: from_dict(lambda x: x, x)], obj.get("variables")) @@ -4554,7 +4554,7 @@ class SystemNotificationAgentCompleted: prompt: str | None = None @staticmethod - def from_dict(obj: Any) -> "SystemNotificationAgentCompleted": + def from_dict(obj: Any) -> SystemNotificationAgentCompleted: assert isinstance(obj, dict) agent_id = from_str(obj.get("agentId")) agent_type = from_str(obj.get("agentType")) @@ -4591,7 +4591,7 @@ class SystemNotificationAgentIdle: description: str | None = None @staticmethod - def from_dict(obj: Any) -> "SystemNotificationAgentIdle": + def from_dict(obj: Any) -> SystemNotificationAgentIdle: assert isinstance(obj, dict) agent_id = from_str(obj.get("agentId")) agent_type = from_str(obj.get("agentType")) @@ -4619,7 +4619,7 @@ class SystemNotificationData: kind: SystemNotification @staticmethod - def from_dict(obj: Any) -> "SystemNotificationData": + def from_dict(obj: Any) -> SystemNotificationData: assert isinstance(obj, dict) content = from_str(obj.get("content")) kind = _load_SystemNotification(obj.get("kind")) @@ -4645,7 +4645,7 @@ class SystemNotificationInstructionDiscovered: description: str | None = None @staticmethod - def from_dict(obj: Any) -> "SystemNotificationInstructionDiscovered": + def from_dict(obj: Any) -> SystemNotificationInstructionDiscovered: assert isinstance(obj, dict) source_path = from_str(obj.get("sourcePath")) trigger_file = from_str(obj.get("triggerFile")) @@ -4679,7 +4679,7 @@ class SystemNotificationNewInboxMessage: type: ClassVar[str] = "new_inbox_message" @staticmethod - def from_dict(obj: Any) -> "SystemNotificationNewInboxMessage": + def from_dict(obj: Any) -> SystemNotificationNewInboxMessage: assert isinstance(obj, dict) entry_id = from_str(obj.get("entryId")) sender_name = from_str(obj.get("senderName")) @@ -4711,7 +4711,7 @@ class SystemNotificationShellCompleted: exit_code: int | None = None @staticmethod - def from_dict(obj: Any) -> "SystemNotificationShellCompleted": + def from_dict(obj: Any) -> SystemNotificationShellCompleted: assert isinstance(obj, dict) shell_id = from_str(obj.get("shellId")) description = from_union([from_none, from_str], obj.get("description")) @@ -4741,7 +4741,7 @@ class SystemNotificationShellDetachedCompleted: description: str | None = None @staticmethod - def from_dict(obj: Any) -> "SystemNotificationShellDetachedCompleted": + def from_dict(obj: Any) -> SystemNotificationShellDetachedCompleted: assert isinstance(obj, dict) shell_id = from_str(obj.get("shellId")) description = from_union([from_none, from_str], obj.get("description")) @@ -4767,7 +4767,7 @@ class ToolExecutionCompleteContentAudio: type: ClassVar[str] = "audio" @staticmethod - def from_dict(obj: Any) -> "ToolExecutionCompleteContentAudio": + def from_dict(obj: Any) -> ToolExecutionCompleteContentAudio: assert isinstance(obj, dict) data = from_str(obj.get("data")) mime_type = from_str(obj.get("mimeType")) @@ -4792,7 +4792,7 @@ class ToolExecutionCompleteContentImage: type: ClassVar[str] = "image" @staticmethod - def from_dict(obj: Any) -> "ToolExecutionCompleteContentImage": + def from_dict(obj: Any) -> ToolExecutionCompleteContentImage: assert isinstance(obj, dict) data = from_str(obj.get("data")) mime_type = from_str(obj.get("mimeType")) @@ -4816,7 +4816,7 @@ class ToolExecutionCompleteContentResource: type: ClassVar[str] = "resource" @staticmethod - def from_dict(obj: Any) -> "ToolExecutionCompleteContentResource": + def from_dict(obj: Any) -> ToolExecutionCompleteContentResource: assert isinstance(obj, dict) resource = from_union([EmbeddedTextResourceContents.from_dict, EmbeddedBlobResourceContents.from_dict], obj.get("resource")) return ToolExecutionCompleteContentResource( @@ -4843,7 +4843,7 @@ class ToolExecutionCompleteContentResourceLink: title: str | None = None @staticmethod - def from_dict(obj: Any) -> "ToolExecutionCompleteContentResourceLink": + def from_dict(obj: Any) -> ToolExecutionCompleteContentResourceLink: assert isinstance(obj, dict) name = from_str(obj.get("name")) uri = from_str(obj.get("uri")) @@ -4889,7 +4889,7 @@ class ToolExecutionCompleteContentResourceLinkIcon: theme: ToolExecutionCompleteContentResourceLinkIconTheme | None = None @staticmethod - def from_dict(obj: Any) -> "ToolExecutionCompleteContentResourceLinkIcon": + def from_dict(obj: Any) -> ToolExecutionCompleteContentResourceLinkIcon: assert isinstance(obj, dict) src = from_str(obj.get("src")) mime_type = from_union([from_none, from_str], obj.get("mimeType")) @@ -4923,7 +4923,7 @@ class ToolExecutionCompleteContentTerminal: exit_code: int | None = None @staticmethod - def from_dict(obj: Any) -> "ToolExecutionCompleteContentTerminal": + def from_dict(obj: Any) -> ToolExecutionCompleteContentTerminal: assert isinstance(obj, dict) text = from_str(obj.get("text")) cwd = from_union([from_none, from_str], obj.get("cwd")) @@ -4952,7 +4952,7 @@ class ToolExecutionCompleteContentText: type: ClassVar[str] = "text" @staticmethod - def from_dict(obj: Any) -> "ToolExecutionCompleteContentText": + def from_dict(obj: Any) -> ToolExecutionCompleteContentText: assert isinstance(obj, dict) text = from_str(obj.get("text")) return ToolExecutionCompleteContentText( @@ -4983,7 +4983,7 @@ class ToolExecutionCompleteData: turn_id: str | None = None @staticmethod - def from_dict(obj: Any) -> "ToolExecutionCompleteData": + def from_dict(obj: Any) -> ToolExecutionCompleteData: assert isinstance(obj, dict) success = from_bool(obj.get("success")) tool_call_id = from_str(obj.get("toolCallId")) @@ -5042,7 +5042,7 @@ class ToolExecutionCompleteError: code: str | None = None @staticmethod - def from_dict(obj: Any) -> "ToolExecutionCompleteError": + def from_dict(obj: Any) -> ToolExecutionCompleteError: assert isinstance(obj, dict) message = from_str(obj.get("message")) code = from_union([from_none, from_str], obj.get("code")) @@ -5067,7 +5067,7 @@ class ToolExecutionCompleteResult: detailed_content: str | None = None @staticmethod - def from_dict(obj: Any) -> "ToolExecutionCompleteResult": + def from_dict(obj: Any) -> ToolExecutionCompleteResult: assert isinstance(obj, dict) content = from_str(obj.get("content")) contents = from_union([from_none, lambda x: from_list(_load_ToolExecutionCompleteContent, x)], obj.get("contents")) @@ -5095,7 +5095,7 @@ class ToolExecutionPartialResultData: tool_call_id: str @staticmethod - def from_dict(obj: Any) -> "ToolExecutionPartialResultData": + def from_dict(obj: Any) -> ToolExecutionPartialResultData: assert isinstance(obj, dict) partial_output = from_str(obj.get("partialOutput")) tool_call_id = from_str(obj.get("toolCallId")) @@ -5118,7 +5118,7 @@ class ToolExecutionProgressData: tool_call_id: str @staticmethod - def from_dict(obj: Any) -> "ToolExecutionProgressData": + def from_dict(obj: Any) -> ToolExecutionProgressData: assert isinstance(obj, dict) progress_message = from_str(obj.get("progressMessage")) tool_call_id = from_str(obj.get("toolCallId")) @@ -5147,7 +5147,7 @@ class ToolExecutionStartData: turn_id: str | None = None @staticmethod - def from_dict(obj: Any) -> "ToolExecutionStartData": + def from_dict(obj: Any) -> ToolExecutionStartData: assert isinstance(obj, dict) tool_call_id = from_str(obj.get("toolCallId")) tool_name = from_str(obj.get("toolName")) @@ -5191,7 +5191,7 @@ class ToolUserRequestedData: arguments: Any = None @staticmethod - def from_dict(obj: Any) -> "ToolUserRequestedData": + def from_dict(obj: Any) -> ToolUserRequestedData: assert isinstance(obj, dict) tool_call_id = from_str(obj.get("toolCallId")) tool_name = from_str(obj.get("toolName")) @@ -5219,7 +5219,7 @@ class UserInputCompletedData: was_freeform: bool | None = None @staticmethod - def from_dict(obj: Any) -> "UserInputCompletedData": + def from_dict(obj: Any) -> UserInputCompletedData: assert isinstance(obj, dict) request_id = from_str(obj.get("requestId")) answer = from_union([from_none, from_str], obj.get("answer")) @@ -5250,7 +5250,7 @@ class UserInputRequestedData: tool_call_id: str | None = None @staticmethod - def from_dict(obj: Any) -> "UserInputRequestedData": + def from_dict(obj: Any) -> UserInputRequestedData: assert isinstance(obj, dict) question = from_str(obj.get("question")) request_id = from_str(obj.get("requestId")) @@ -5287,7 +5287,7 @@ class UserMessageAttachmentBlob: display_name: str | None = None @staticmethod - def from_dict(obj: Any) -> "UserMessageAttachmentBlob": + def from_dict(obj: Any) -> UserMessageAttachmentBlob: assert isinstance(obj, dict) data = from_str(obj.get("data")) mime_type = from_str(obj.get("mimeType")) @@ -5316,7 +5316,7 @@ class UserMessageAttachmentDirectory: type: ClassVar[str] = "directory" @staticmethod - def from_dict(obj: Any) -> "UserMessageAttachmentDirectory": + def from_dict(obj: Any) -> UserMessageAttachmentDirectory: assert isinstance(obj, dict) display_name = from_str(obj.get("displayName")) path = from_str(obj.get("path")) @@ -5342,7 +5342,7 @@ class UserMessageAttachmentFile: line_range: UserMessageAttachmentFileLineRange | None = None @staticmethod - def from_dict(obj: Any) -> "UserMessageAttachmentFile": + def from_dict(obj: Any) -> UserMessageAttachmentFile: assert isinstance(obj, dict) display_name = from_str(obj.get("displayName")) path = from_str(obj.get("path")) @@ -5370,7 +5370,7 @@ class UserMessageAttachmentFileLineRange: start: int @staticmethod - def from_dict(obj: Any) -> "UserMessageAttachmentFileLineRange": + def from_dict(obj: Any) -> UserMessageAttachmentFileLineRange: assert isinstance(obj, dict) end = from_int(obj.get("end")) start = from_int(obj.get("start")) @@ -5397,7 +5397,7 @@ class UserMessageAttachmentGithubReference: url: str @staticmethod - def from_dict(obj: Any) -> "UserMessageAttachmentGithubReference": + def from_dict(obj: Any) -> UserMessageAttachmentGithubReference: assert isinstance(obj, dict) number = from_int(obj.get("number")) reference_type = parse_enum(UserMessageAttachmentGithubReferenceType, obj.get("referenceType")) @@ -5433,7 +5433,7 @@ class UserMessageAttachmentSelection: type: ClassVar[str] = "selection" @staticmethod - def from_dict(obj: Any) -> "UserMessageAttachmentSelection": + def from_dict(obj: Any) -> UserMessageAttachmentSelection: assert isinstance(obj, dict) display_name = from_str(obj.get("displayName")) file_path = from_str(obj.get("filePath")) @@ -5463,7 +5463,7 @@ class UserMessageAttachmentSelectionDetails: start: UserMessageAttachmentSelectionDetailsStart @staticmethod - def from_dict(obj: Any) -> "UserMessageAttachmentSelectionDetails": + def from_dict(obj: Any) -> UserMessageAttachmentSelectionDetails: assert isinstance(obj, dict) end = UserMessageAttachmentSelectionDetailsEnd.from_dict(obj.get("end")) start = UserMessageAttachmentSelectionDetailsStart.from_dict(obj.get("start")) @@ -5486,7 +5486,7 @@ class UserMessageAttachmentSelectionDetailsEnd: line: int @staticmethod - def from_dict(obj: Any) -> "UserMessageAttachmentSelectionDetailsEnd": + def from_dict(obj: Any) -> UserMessageAttachmentSelectionDetailsEnd: assert isinstance(obj, dict) character = from_int(obj.get("character")) line = from_int(obj.get("line")) @@ -5509,7 +5509,7 @@ class UserMessageAttachmentSelectionDetailsStart: line: int @staticmethod - def from_dict(obj: Any) -> "UserMessageAttachmentSelectionDetailsStart": + def from_dict(obj: Any) -> UserMessageAttachmentSelectionDetailsStart: assert isinstance(obj, dict) character = from_int(obj.get("character")) line = from_int(obj.get("line")) @@ -5540,7 +5540,7 @@ class UserMessageData: transformed_content: str | None = None @staticmethod - def from_dict(obj: Any) -> "UserMessageData": + def from_dict(obj: Any) -> UserMessageData: assert isinstance(obj, dict) content = from_str(obj.get("content")) agent_mode = from_union([from_none, lambda x: parse_enum(UserMessageAgentMode, x)], obj.get("agentMode")) @@ -5596,7 +5596,7 @@ class UserToolSessionApprovalCommands: kind: ClassVar[str] = "commands" @staticmethod - def from_dict(obj: Any) -> "UserToolSessionApprovalCommands": + def from_dict(obj: Any) -> UserToolSessionApprovalCommands: assert isinstance(obj, dict) command_identifiers = from_list(from_str, obj.get("commandIdentifiers")) return UserToolSessionApprovalCommands( @@ -5617,7 +5617,7 @@ class UserToolSessionApprovalCustomTool: tool_name: str @staticmethod - def from_dict(obj: Any) -> "UserToolSessionApprovalCustomTool": + def from_dict(obj: Any) -> UserToolSessionApprovalCustomTool: assert isinstance(obj, dict) tool_name = from_str(obj.get("toolName")) return UserToolSessionApprovalCustomTool( @@ -5638,7 +5638,7 @@ class UserToolSessionApprovalExtensionManagement: operation: str | None = None @staticmethod - def from_dict(obj: Any) -> "UserToolSessionApprovalExtensionManagement": + def from_dict(obj: Any) -> UserToolSessionApprovalExtensionManagement: assert isinstance(obj, dict) operation = from_union([from_none, from_str], obj.get("operation")) return UserToolSessionApprovalExtensionManagement( @@ -5660,7 +5660,7 @@ class UserToolSessionApprovalExtensionPermissionAccess: kind: ClassVar[str] = "extension-permission-access" @staticmethod - def from_dict(obj: Any) -> "UserToolSessionApprovalExtensionPermissionAccess": + def from_dict(obj: Any) -> UserToolSessionApprovalExtensionPermissionAccess: assert isinstance(obj, dict) extension_name = from_str(obj.get("extensionName")) return UserToolSessionApprovalExtensionPermissionAccess( @@ -5682,7 +5682,7 @@ class UserToolSessionApprovalMcp: tool_name: str | None @staticmethod - def from_dict(obj: Any) -> "UserToolSessionApprovalMcp": + def from_dict(obj: Any) -> UserToolSessionApprovalMcp: assert isinstance(obj, dict) server_name = from_str(obj.get("serverName")) tool_name = from_union([from_none, from_str], obj.get("toolName")) @@ -5705,7 +5705,7 @@ class UserToolSessionApprovalMemory: kind: ClassVar[str] = "memory" @staticmethod - def from_dict(obj: Any) -> "UserToolSessionApprovalMemory": + def from_dict(obj: Any) -> UserToolSessionApprovalMemory: assert isinstance(obj, dict) return UserToolSessionApprovalMemory( ) @@ -5722,7 +5722,7 @@ class UserToolSessionApprovalRead: kind: ClassVar[str] = "read" @staticmethod - def from_dict(obj: Any) -> "UserToolSessionApprovalRead": + def from_dict(obj: Any) -> UserToolSessionApprovalRead: assert isinstance(obj, dict) return UserToolSessionApprovalRead( ) @@ -5739,7 +5739,7 @@ class UserToolSessionApprovalWrite: kind: ClassVar[str] = "write" @staticmethod - def from_dict(obj: Any) -> "UserToolSessionApprovalWrite": + def from_dict(obj: Any) -> UserToolSessionApprovalWrite: assert isinstance(obj, dict) return UserToolSessionApprovalWrite( ) @@ -5763,7 +5763,7 @@ class WorkingDirectoryContext: repository_host: str | None = None @staticmethod - def from_dict(obj: Any) -> "WorkingDirectoryContext": + def from_dict(obj: Any) -> WorkingDirectoryContext: assert isinstance(obj, dict) cwd = from_str(obj.get("cwd")) base_commit = from_union([from_none, from_str], obj.get("baseCommit")) @@ -5804,7 +5804,7 @@ def to_dict(self) -> dict: return result -def _load_PermissionPromptRequest(obj: Any) -> "PermissionPromptRequest": +def _load_PermissionPromptRequest(obj: Any) -> PermissionPromptRequest: assert isinstance(obj, dict) kind = obj.get("kind") match kind: @@ -5822,7 +5822,7 @@ def _load_PermissionPromptRequest(obj: Any) -> "PermissionPromptRequest": case _: raise ValueError(f"Unknown PermissionPromptRequest kind: {kind!r}") -def _load_PermissionRequest(obj: Any) -> "PermissionRequest": +def _load_PermissionRequest(obj: Any) -> PermissionRequest: assert isinstance(obj, dict) kind = obj.get("kind") match kind: @@ -5839,7 +5839,7 @@ def _load_PermissionRequest(obj: Any) -> "PermissionRequest": case _: raise ValueError(f"Unknown PermissionRequest kind: {kind!r}") -def _load_PermissionResult(obj: Any) -> "PermissionResult": +def _load_PermissionResult(obj: Any) -> PermissionResult: assert isinstance(obj, dict) kind = obj.get("kind") match kind: @@ -5855,7 +5855,7 @@ def _load_PermissionResult(obj: Any) -> "PermissionResult": case _: raise ValueError(f"Unknown PermissionResult kind: {kind!r}") -def _load_SystemNotification(obj: Any) -> "SystemNotification": +def _load_SystemNotification(obj: Any) -> SystemNotification: assert isinstance(obj, dict) kind = obj.get("type") match kind: @@ -5868,7 +5868,7 @@ def _load_SystemNotification(obj: Any) -> "SystemNotification": case _: raise ValueError(f"Unknown SystemNotification type: {kind!r}") -def _load_ToolExecutionCompleteContent(obj: Any) -> "ToolExecutionCompleteContent": +def _load_ToolExecutionCompleteContent(obj: Any) -> ToolExecutionCompleteContent: assert isinstance(obj, dict) kind = obj.get("type") match kind: @@ -5881,7 +5881,7 @@ def _load_ToolExecutionCompleteContent(obj: Any) -> "ToolExecutionCompleteConten case _: raise ValueError(f"Unknown ToolExecutionCompleteContent type: {kind!r}") -def _load_UserMessageAttachment(obj: Any) -> "UserMessageAttachment": +def _load_UserMessageAttachment(obj: Any) -> UserMessageAttachment: assert isinstance(obj, dict) kind = obj.get("type") match kind: @@ -5893,7 +5893,7 @@ def _load_UserMessageAttachment(obj: Any) -> "UserMessageAttachment": case _: raise ValueError(f"Unknown UserMessageAttachment type: {kind!r}") -def _load_UserToolSessionApproval(obj: Any) -> "UserToolSessionApproval": +def _load_UserToolSessionApproval(obj: Any) -> UserToolSessionApproval: assert isinstance(obj, dict) kind = obj.get("kind") match kind: @@ -6235,7 +6235,7 @@ class SessionEvent: raw_type: str | None = None @staticmethod - def from_dict(obj: Any) -> "SessionEvent": + def from_dict(obj: Any) -> SessionEvent: assert isinstance(obj, dict) raw_type = from_str(obj.get("type")) event_type = SessionEventType(raw_type) diff --git a/python/e2e/test_agent_and_compact_rpc_e2e.py b/python/e2e/test_agent_and_compact_rpc_e2e.py index f4773a798..60128a9e8 100644 --- a/python/e2e/test_agent_and_compact_rpc_e2e.py +++ b/python/e2e/test_agent_and_compact_rpc_e2e.py @@ -4,8 +4,7 @@ import pytest -from copilot import CopilotClient -from copilot.client import SubprocessConfig +from copilot import CopilotClient, CopilotClientOptions, RuntimeConnection from copilot.generated.rpc import AgentSelectRequest from copilot.session import PermissionHandler @@ -18,7 +17,9 @@ class TestAgentSelectionRpc: @pytest.mark.asyncio async def test_should_list_available_custom_agents(self): """Test listing available custom agents via RPC.""" - client = CopilotClient(SubprocessConfig(cli_path=CLI_PATH, use_stdio=True)) + client = CopilotClient( + CopilotClientOptions(connection=RuntimeConnection.stdio(path=CLI_PATH)) + ) try: await client.start() @@ -56,7 +57,9 @@ async def test_should_list_available_custom_agents(self): @pytest.mark.asyncio async def test_should_return_null_when_no_agent_is_selected(self): """Test getCurrent returns null when no agent is selected.""" - client = CopilotClient(SubprocessConfig(cli_path=CLI_PATH, use_stdio=True)) + client = CopilotClient( + CopilotClientOptions(connection=RuntimeConnection.stdio(path=CLI_PATH)) + ) try: await client.start() @@ -83,7 +86,9 @@ async def test_should_return_null_when_no_agent_is_selected(self): @pytest.mark.asyncio async def test_should_select_and_get_current_agent(self): """Test selecting an agent and verifying getCurrent returns it.""" - client = CopilotClient(SubprocessConfig(cli_path=CLI_PATH, use_stdio=True)) + client = CopilotClient( + CopilotClientOptions(connection=RuntimeConnection.stdio(path=CLI_PATH)) + ) try: await client.start() @@ -118,7 +123,9 @@ async def test_should_select_and_get_current_agent(self): @pytest.mark.asyncio async def test_should_deselect_current_agent(self): """Test deselecting the current agent.""" - client = CopilotClient(SubprocessConfig(cli_path=CLI_PATH, use_stdio=True)) + client = CopilotClient( + CopilotClientOptions(connection=RuntimeConnection.stdio(path=CLI_PATH)) + ) try: await client.start() @@ -150,7 +157,9 @@ async def test_should_deselect_current_agent(self): @pytest.mark.asyncio async def test_should_return_empty_list_when_no_custom_agents_configured(self): """Test listing agents returns no custom agents when none configured.""" - client = CopilotClient(SubprocessConfig(cli_path=CLI_PATH, use_stdio=True)) + client = CopilotClient( + CopilotClientOptions(connection=RuntimeConnection.stdio(path=CLI_PATH)) + ) try: await client.start() @@ -175,7 +184,9 @@ async def test_should_return_empty_list_when_no_custom_agents_configured(self): @pytest.mark.asyncio async def test_should_call_agent_reload(self): """Test reloading agents via RPC.""" - client = CopilotClient(SubprocessConfig(cli_path=CLI_PATH, use_stdio=True)) + client = CopilotClient( + CopilotClientOptions(connection=RuntimeConnection.stdio(path=CLI_PATH)) + ) reload_agent = { "name": f"reload-test-agent-{uuid.uuid4().hex}", "display_name": "Reload Agent", diff --git a/python/e2e/test_client_e2e.py b/python/e2e/test_client_e2e.py index fc7315a58..34444081d 100644 --- a/python/e2e/test_client_e2e.py +++ b/python/e2e/test_client_e2e.py @@ -2,14 +2,13 @@ import pytest -from copilot import CopilotClient +from copilot import CopilotClient, CopilotClientOptions, RuntimeConnection from copilot.client import ( ModelCapabilities, ModelInfo, ModelLimits, ModelSupports, StopError, - SubprocessConfig, ) from copilot.session import PermissionHandler @@ -19,7 +18,9 @@ class TestClient: @pytest.mark.asyncio async def test_should_start_and_connect_to_server_using_stdio(self): - client = CopilotClient(SubprocessConfig(cli_path=CLI_PATH, use_stdio=True)) + client = CopilotClient( + CopilotClientOptions(connection=RuntimeConnection.stdio(path=CLI_PATH)) + ) try: await client.start() @@ -36,7 +37,9 @@ async def test_should_start_and_connect_to_server_using_stdio(self): @pytest.mark.asyncio async def test_should_start_and_connect_to_server_using_tcp(self): - client = CopilotClient(SubprocessConfig(cli_path=CLI_PATH, use_stdio=False)) + client = CopilotClient( + CopilotClientOptions(connection=RuntimeConnection.tcp(path=CLI_PATH)) + ) try: await client.start() @@ -55,7 +58,9 @@ async def test_should_start_and_connect_to_server_using_tcp(self): async def test_should_raise_exception_group_on_failed_cleanup(self): import asyncio - client = CopilotClient(SubprocessConfig(cli_path=CLI_PATH)) + client = CopilotClient( + CopilotClientOptions(connection=RuntimeConnection.stdio(path=CLI_PATH)) + ) try: await client.create_session(on_permission_request=PermissionHandler.approve_all) @@ -79,7 +84,9 @@ async def test_should_raise_exception_group_on_failed_cleanup(self): @pytest.mark.asyncio async def test_should_force_stop_without_cleanup(self): - client = CopilotClient(SubprocessConfig(cli_path=CLI_PATH)) + client = CopilotClient( + CopilotClientOptions(connection=RuntimeConnection.stdio(path=CLI_PATH)) + ) await client.create_session(on_permission_request=PermissionHandler.approve_all) await client.force_stop() @@ -87,7 +94,9 @@ async def test_should_force_stop_without_cleanup(self): @pytest.mark.asyncio async def test_should_get_status_with_version_and_protocol_info(self): - client = CopilotClient(SubprocessConfig(cli_path=CLI_PATH, use_stdio=True)) + client = CopilotClient( + CopilotClientOptions(connection=RuntimeConnection.stdio(path=CLI_PATH)) + ) try: await client.start() @@ -105,7 +114,9 @@ async def test_should_get_status_with_version_and_protocol_info(self): @pytest.mark.asyncio async def test_should_get_auth_status(self): - client = CopilotClient(SubprocessConfig(cli_path=CLI_PATH, use_stdio=True)) + client = CopilotClient( + CopilotClientOptions(connection=RuntimeConnection.stdio(path=CLI_PATH)) + ) try: await client.start() @@ -123,7 +134,9 @@ async def test_should_get_auth_status(self): @pytest.mark.asyncio async def test_should_list_models_when_authenticated(self): - client = CopilotClient(SubprocessConfig(cli_path=CLI_PATH, use_stdio=True)) + client = CopilotClient( + CopilotClientOptions(connection=RuntimeConnection.stdio(path=CLI_PATH)) + ) try: await client.start() @@ -151,7 +164,9 @@ async def test_should_list_models_when_authenticated(self): @pytest.mark.asyncio async def test_should_cache_models_list(self): """Test that list_models caches results to avoid rate limiting""" - client = CopilotClient(SubprocessConfig(cli_path=CLI_PATH, use_stdio=True)) + client = CopilotClient( + CopilotClientOptions(connection=RuntimeConnection.stdio(path=CLI_PATH)) + ) try: await client.start() @@ -196,10 +211,10 @@ async def test_should_cache_models_list(self): async def test_should_report_error_with_stderr_when_cli_fails_to_start(self): """Test that CLI startup errors include stderr output in the error message.""" client = CopilotClient( - SubprocessConfig( - cli_path=CLI_PATH, - cli_args=["--nonexistent-flag-for-testing"], - use_stdio=True, + CopilotClientOptions( + connection=RuntimeConnection.stdio( + path=CLI_PATH, args=["--nonexistent-flag-for-testing"] + ) ) ) @@ -231,7 +246,9 @@ async def test_should_report_error_with_stderr_when_cli_fails_to_start(self): @pytest.mark.asyncio async def test_should_not_throw_when_disposing_session_after_stopping_client(self): """Disconnecting a session after the client is stopped must not raise.""" - client = CopilotClient(SubprocessConfig(cli_path=CLI_PATH, use_stdio=True)) + client = CopilotClient( + CopilotClientOptions(connection=RuntimeConnection.stdio(path=CLI_PATH)) + ) try: await client.start() @@ -250,7 +267,9 @@ async def test_should_not_throw_when_disposing_session_after_stopping_client(sel @pytest.mark.asyncio async def test_should_create_session_without_permission_handler(self): """`create_session` allows omitting an `on_permission_request` handler.""" - client = CopilotClient(SubprocessConfig(cli_path=CLI_PATH, use_stdio=True)) + client = CopilotClient( + CopilotClientOptions(connection=RuntimeConnection.stdio(path=CLI_PATH)) + ) try: await client.start() @@ -265,7 +284,9 @@ async def test_should_create_session_without_permission_handler(self): @pytest.mark.asyncio async def test_should_resume_session_without_permission_handler(self): """`resume_session` allows omitting an `on_permission_request` handler.""" - client = CopilotClient(SubprocessConfig(cli_path=CLI_PATH, use_stdio=True)) + client = CopilotClient( + CopilotClientOptions(connection=RuntimeConnection.stdio(path=CLI_PATH)) + ) try: await client.start() @@ -300,7 +321,7 @@ def on_list_models(): return custom_models client = CopilotClient( - SubprocessConfig(cli_path=CLI_PATH, use_stdio=True), + CopilotClientOptions(connection=RuntimeConnection.stdio(path=CLI_PATH)), on_list_models=on_list_models, ) @@ -338,7 +359,7 @@ def on_list_models(): return custom_models client = CopilotClient( - SubprocessConfig(cli_path=CLI_PATH, use_stdio=True), + CopilotClientOptions(connection=RuntimeConnection.stdio(path=CLI_PATH)), on_list_models=on_list_models, ) diff --git a/python/e2e/test_client_lifecycle_e2e.py b/python/e2e/test_client_lifecycle_e2e.py index 90b96d822..b35ff176a 100644 --- a/python/e2e/test_client_lifecycle_e2e.py +++ b/python/e2e/test_client_lifecycle_e2e.py @@ -1,5 +1,5 @@ """ -Client lifecycle tests covering ``client.on(...)`` lifecycle event subscriptions +Client lifecycle tests covering ``client.on_lifecycle(...)`` lifecycle event subscriptions and connection-state transitions across ``start``/``stop``. Mirrors ``dotnet/test/ClientLifecycleTests.cs`` plus the existing ``client_lifecycle`` @@ -14,8 +14,7 @@ import pytest -from copilot import CopilotClient -from copilot.client import SubprocessConfig +from copilot import CopilotClient, CopilotClientOptions, RuntimeConnection from copilot.session import PermissionHandler from .testharness import E2ETestContext @@ -60,8 +59,8 @@ def _make_isolated_client(ctx: E2ETestContext) -> CopilotClient: "fake-token-for-e2e-tests" if os.environ.get("GITHUB_ACTIONS") == "true" else None ) return CopilotClient( - SubprocessConfig( - cli_path=ctx.cli_path, + CopilotClientOptions( + connection=RuntimeConnection.stdio(path=ctx.cli_path), working_directory=ctx.work_dir, env=ctx.get_env(), github_token=github_token, @@ -84,7 +83,7 @@ async def test_should_return_last_session_id_after_sending_a_message(self, ctx: async def test_should_emit_session_lifecycle_events(self, ctx: E2ETestContext): events: list = [] - unsubscribe = ctx.client.on(events.append) + unsubscribe = ctx.client.on_lifecycle(events.append) try: session = await ctx.client.create_session( on_permission_request=PermissionHandler.approve_all, @@ -111,7 +110,7 @@ def handler(event): if event.type == "session.created" and not created.done(): created.set_result(event) - unsubscribe = ctx.client.on(handler) + unsubscribe = ctx.client.on_lifecycle(handler) try: session = await ctx.client.create_session( on_permission_request=PermissionHandler.approve_all, @@ -133,7 +132,7 @@ def handler(event): if not created.done(): created.set_result(event) - unsubscribe = ctx.client.on("session.created", handler) + unsubscribe = ctx.client.on_lifecycle("session.created", handler) try: session = await ctx.client.create_session( on_permission_request=PermissionHandler.approve_all, @@ -157,11 +156,11 @@ def disposed_handler(_event): nonlocal unsubscribed_count unsubscribed_count += 1 - unsubscribe_disposed = ctx.client.on(disposed_handler) + unsubscribe_disposed = ctx.client.on_lifecycle(disposed_handler) unsubscribe_disposed() # Immediately dispose first subscription. active_event: asyncio.Future = loop.create_future() - unsubscribe_active = ctx.client.on( + unsubscribe_active = ctx.client.on_lifecycle( "session.created", lambda evt: active_event.set_result(evt) if not active_event.done() else None, ) @@ -212,7 +211,7 @@ def handler(event): ): updated.set_result(event) - unsubscribe = ctx.client.on(handler) + unsubscribe = ctx.client.on_lifecycle(handler) try: await session.rpc.mode.set(ModeSetRequest(mode=SessionMode.PLAN)) event = await asyncio.wait_for(updated, timeout=15.0) @@ -247,7 +246,7 @@ def handler(event): ): deleted.set_result(event) - unsubscribe = ctx.client.on(handler) + unsubscribe = ctx.client.on_lifecycle(handler) try: await session.disconnect() await ctx.client.delete_session(session_id) diff --git a/python/e2e/test_client_options_e2e.py b/python/e2e/test_client_options_e2e.py index 80a3bf394..f18764b83 100644 --- a/python/e2e/test_client_options_e2e.py +++ b/python/e2e/test_client_options_e2e.py @@ -1,14 +1,15 @@ """ E2E coverage for ``CopilotClient`` configuration options exposed via -``SubprocessConfig`` and ``CopilotClient(..., auto_start=...)``. +``CopilotClientOptions`` / ``RuntimeConnection`` and ``CopilotClient(..., auto_start=...)``. Mirrors ``dotnet/test/ClientOptionsTests.cs``. The two CliUrl-conflict tests (``Should_Throw_When_GitHubToken_Used_With_CliUrl`` and ``Should_Throw_When_UseLoggedInUser_Used_With_CliUrl``) have no Python -equivalent because Python's ``ExternalServerConfig`` does not accept -``github_token`` / ``use_logged_in_user`` fields at all (so the conflict cannot -be expressed in code), and the configurations are therefore intentionally -omitted. +equivalent because Python's ``RuntimeConnection.uri(...)`` does not accept +``github_token`` / ``use_logged_in_user`` fields at all (those live on +``CopilotClientOptions``, but a Uri-connected runtime ignores them), so the +conflict cannot be expressed in code and the configurations are therefore +intentionally omitted. """ from __future__ import annotations @@ -19,8 +20,7 @@ import pytest -from copilot import CopilotClient -from copilot.client import SubprocessConfig +from copilot import CopilotClient, CopilotClientOptions, RuntimeConnection from copilot.generated.rpc import PingRequest from copilot.session import PermissionHandler @@ -29,9 +29,31 @@ pytestmark = pytest.mark.asyncio(loop_scope="module") -def _make_subprocess_config(ctx: E2ETestContext, **overrides) -> SubprocessConfig: +def _make_options( + ctx: E2ETestContext, + *, + use_tcp: bool = False, + port: int = 0, + connection_token: str | None = None, + cli_path: str | None = None, + cli_args: list[str] | None = None, + **overrides, +) -> CopilotClientOptions: + """Build a ``CopilotClientOptions`` pre-populated for the test harness.""" + if use_tcp: + connection = RuntimeConnection.tcp( + port=port, + connection_token=connection_token, + path=cli_path if cli_path is not None else ctx.cli_path, + args=tuple(cli_args or []), + ) + else: + connection = RuntimeConnection.stdio( + path=cli_path if cli_path is not None else ctx.cli_path, + args=tuple(cli_args or []), + ) base = { - "cli_path": ctx.cli_path, + "connection": connection, "working_directory": ctx.work_dir, "env": ctx.get_env(), "github_token": ( @@ -39,7 +61,7 @@ def _make_subprocess_config(ctx: E2ETestContext, **overrides) -> SubprocessConfi ), } base.update(overrides) - return SubprocessConfig(**base) + return CopilotClientOptions(**base) def _get_available_port() -> int: @@ -66,7 +88,7 @@ def _get_available_port() -> int: cwd: process.cwd(), requests, env: { - COPILOT_HOME: process.env.COPILOT_HOME, + COPILOT_HOME: process.env.base_directory, COPILOT_SDK_AUTH_TOKEN: process.env.COPILOT_SDK_AUTH_TOKEN, COPILOT_OTEL_ENABLED: process.env.COPILOT_OTEL_ENABLED, OTEL_EXPORTER_OTLP_ENDPOINT: process.env.OTEL_EXPORTER_OTLP_ENDPOINT, @@ -143,7 +165,7 @@ def _assert_arg_value(args: list[str], name: str, expected_value: str) -> None: class TestClientOptions: async def test_autostart_false_requires_explicit_start(self, ctx: E2ETestContext): - client = CopilotClient(_make_subprocess_config(ctx), auto_start=False) + client = CopilotClient(_make_options(ctx), auto_start=False) try: assert client.get_state() == "disconnected" @@ -170,11 +192,11 @@ async def test_autostart_false_requires_explicit_start(self, ctx: E2ETestContext async def test_should_listen_on_configured_tcp_port(self, ctx: E2ETestContext): port = _get_available_port() - client = CopilotClient(_make_subprocess_config(ctx, use_stdio=False, port=port)) + client = CopilotClient(_make_options(ctx, use_tcp=True, port=port)) try: await client.start() assert client.get_state() == "connected" - assert client.actual_port == port + assert client.runtime_port == port response = await client.rpc.ping(PingRequest(message="fixed-port")) assert "pong" in response.message @@ -187,7 +209,7 @@ async def test_should_use_client_cwd_for_default_workingdirectory(self, ctx: E2E with open(os.path.join(client_cwd, "marker.txt"), "w") as f: f.write("I am in the client cwd") - client = CopilotClient(_make_subprocess_config(ctx, working_directory=client_cwd)) + client = CopilotClient(_make_options(ctx, working_directory=client_cwd)) try: session = await client.create_session( on_permission_request=PermissionHandler.approve_all, @@ -212,12 +234,12 @@ async def test_should_propagate_process_options_to_spawned_cli(self, ctx: E2ETes f.write(FAKE_STDIO_CLI_SCRIPT) client = CopilotClient( - _make_subprocess_config( + _make_options( ctx, cli_path=cli_path, - copilot_home=copilot_home_from_option, + base_directory=copilot_home_from_option, cli_args=["--capture-file", capture_path], - env={**ctx.get_env(), "COPILOT_HOME": copilot_home_from_env}, + env={**ctx.get_env(), "base_directory": copilot_home_from_env}, github_token="process-option-token", log_level="debug", session_idle_timeout_seconds=17, @@ -282,47 +304,47 @@ async def test_should_propagate_process_options_to_spawned_cli(self, ctx: E2ETes # --------------------------------------------------------------------------- # Unit-style tests mirroring the property-only tests in -# dotnet/test/ClientOptionsTests.cs. These exercise the SubprocessConfig +# dotnet/test/ClientOptionsTests.cs. These exercise the CopilotClientOptions # dataclass shape only — no client / proxy required. # --------------------------------------------------------------------------- -class TestSubprocessConfigOptions: +class TestSubprocessOptions: """Mirrors the unit-style ClientOptions tests in the C# baseline.""" async def test_should_accept_github_token_option(self): # Mirrors: Should_Accept_GitHubToken_Option - config = SubprocessConfig(github_token="gho_test_token") + config = CopilotClientOptions(github_token="gho_test_token") assert config.github_token == "gho_test_token" async def test_should_default_use_logged_in_user_to_none(self): # Mirrors: Should_Default_UseLoggedInUser_To_Null - config = SubprocessConfig() + config = CopilotClientOptions() assert config.use_logged_in_user is None async def test_should_allow_explicit_use_logged_in_user_false(self): # Mirrors: Should_Allow_Explicit_UseLoggedInUser_False - config = SubprocessConfig(use_logged_in_user=False) + config = CopilotClientOptions(use_logged_in_user=False) assert config.use_logged_in_user is False async def test_should_allow_explicit_use_logged_in_user_true_with_github_token(self): # Mirrors: Should_Allow_Explicit_UseLoggedInUser_True_With_GitHubToken - config = SubprocessConfig(github_token="gho_test_token", use_logged_in_user=True) + config = CopilotClientOptions(github_token="gho_test_token", use_logged_in_user=True) assert config.use_logged_in_user is True assert config.github_token == "gho_test_token" # NOTE: Should_Throw_When_GitHubToken_Used_With_CliUrl and # Should_Throw_When_UseLoggedInUser_Used_With_CliUrl from the C# baseline # do not apply to Python: ExternalServerConfig has no github_token / - # use_logged_in_user fields at all (they live only on SubprocessConfig), + # use_logged_in_user fields at all (they live only on CopilotClientOptions), # so the conflicting configuration is impossible to express. async def test_should_default_session_idle_timeout_seconds_to_none(self): # Mirrors: Should_Default_SessionIdleTimeoutSeconds_To_Null - config = SubprocessConfig() + config = CopilotClientOptions() assert config.session_idle_timeout_seconds is None async def test_should_accept_session_idle_timeout_seconds_option(self): # Mirrors: Should_Accept_SessionIdleTimeoutSeconds_Option - config = SubprocessConfig(session_idle_timeout_seconds=600) + config = CopilotClientOptions(session_idle_timeout_seconds=600) assert config.session_idle_timeout_seconds == 600 diff --git a/python/e2e/test_commands_e2e.py b/python/e2e/test_commands_e2e.py index 5bf1a274e..b5f2bfa2f 100644 --- a/python/e2e/test_commands_e2e.py +++ b/python/e2e/test_commands_e2e.py @@ -15,8 +15,7 @@ import pytest import pytest_asyncio -from copilot import CopilotClient -from copilot.client import ExternalServerConfig, SubprocessConfig +from copilot import CopilotClient, CopilotClientOptions, RuntimeConnection from copilot.session import CommandDefinition, PermissionHandler from .testharness.context import SNAPSHOTS_DIR, get_cli_path_for_tests @@ -56,13 +55,13 @@ async def setup(self): # Client 1 uses TCP mode so a second client can connect self._client1 = CopilotClient( - SubprocessConfig( - cli_path=self.cli_path, + CopilotClientOptions( + connection=RuntimeConnection.tcp( + path=self.cli_path, connection_token="py-tcp-shared-test-token" + ), working_directory=self.work_dir, env=self._get_env(), - use_stdio=False, github_token=github_token, - tcp_connection_token="py-tcp-shared-test-token", ) ) @@ -72,12 +71,14 @@ async def setup(self): ) await init_session.disconnect() - actual_port = self._client1.actual_port + actual_port = self._client1.runtime_port assert actual_port is not None self._client2 = CopilotClient( - ExternalServerConfig( - url=f"localhost:{actual_port}", tcp_connection_token="py-tcp-shared-test-token" + CopilotClientOptions( + connection=RuntimeConnection.uri( + f"localhost:{actual_port}", connection_token="py-tcp-shared-test-token" + ) ) ) @@ -120,7 +121,7 @@ def _get_env(self) -> dict: env.update( { "COPILOT_API_URL": self.proxy_url, - "COPILOT_HOME": self.home_dir, + "base_directory": self.home_dir, "XDG_CONFIG_HOME": self.home_dir, "XDG_STATE_HOME": self.home_dir, } diff --git a/python/e2e/test_connection_token.py b/python/e2e/test_connection_token.py index 195baaecc..f08820f34 100644 --- a/python/e2e/test_connection_token.py +++ b/python/e2e/test_connection_token.py @@ -11,8 +11,7 @@ import pytest import pytest_asyncio -from copilot import CopilotClient -from copilot.client import ExternalServerConfig, SubprocessConfig +from copilot import CopilotClient, CopilotClientOptions, RuntimeConnection from copilot.session import PermissionHandler from .testharness.proxy import CapiProxy @@ -47,12 +46,10 @@ async def setup(self): ) self._client = CopilotClient( - SubprocessConfig( - cli_path=self.cli_path, + CopilotClientOptions( + connection=RuntimeConnection.tcp(path=self.cli_path, connection_token=self.token), working_directory=self.work_dir, env=self.get_env(), - use_stdio=False, - tcp_connection_token=self.token, github_token=github_token, ) ) @@ -81,7 +78,7 @@ def get_env(self) -> dict: env.update( { "COPILOT_API_URL": self.proxy_url, - "COPILOT_HOME": self.home_dir, + "base_directory": self.home_dir, "XDG_CONFIG_HOME": self.home_dir, "XDG_STATE_HOME": self.home_dir, } @@ -133,11 +130,13 @@ async def test_auto_generated_token_round_trips(self, auto_token_ctx: Connection async def test_wrong_token_is_rejected(self, explicit_token_ctx: ConnectionTokenContext): """A sibling client connecting with the wrong token is rejected.""" - port = explicit_token_ctx.client.actual_port + port = explicit_token_ctx.client.runtime_port assert port is not None wrong = CopilotClient( - ExternalServerConfig(url=f"localhost:{port}", tcp_connection_token="wrong") + CopilotClientOptions( + connection=RuntimeConnection.uri(f"localhost:{port}", connection_token="wrong") + ) ) try: with pytest.raises(Exception, match="AUTHENTICATION_FAILED"): @@ -152,10 +151,12 @@ async def test_wrong_token_is_rejected(self, explicit_token_ctx: ConnectionToken async def test_missing_token_is_rejected(self, explicit_token_ctx: ConnectionTokenContext): """A sibling client with no token is rejected when the server requires one.""" - port = explicit_token_ctx.client.actual_port + port = explicit_token_ctx.client.runtime_port assert port is not None - no_token = CopilotClient(ExternalServerConfig(url=f"localhost:{port}")) + no_token = CopilotClient( + CopilotClientOptions(connection=RuntimeConnection.uri(f"localhost:{port}")) + ) try: with pytest.raises(Exception, match="AUTHENTICATION_FAILED"): await no_token.start() diff --git a/python/e2e/test_mode_handlers_e2e.py b/python/e2e/test_mode_handlers_e2e.py index 1557e67a9..d9917e9f3 100644 --- a/python/e2e/test_mode_handlers_e2e.py +++ b/python/e2e/test_mode_handlers_e2e.py @@ -35,7 +35,7 @@ async def mode_ctx(ctx: E2ETestContext): """Configure per-token user responses for mode-handler tests.""" proxy_url = ctx.proxy_url - ctx.client._config.env["COPILOT_DEBUG_GITHUB_API_URL"] = proxy_url + ctx.client._options.env["COPILOT_DEBUG_GITHUB_API_URL"] = proxy_url await ctx.set_copilot_user_by_token( MODE_HANDLER_TOKEN, diff --git a/python/e2e/test_multi_client_e2e.py b/python/e2e/test_multi_client_e2e.py index 560be31d0..fa85eaac3 100644 --- a/python/e2e/test_multi_client_e2e.py +++ b/python/e2e/test_multi_client_e2e.py @@ -14,8 +14,7 @@ import pytest_asyncio from pydantic import BaseModel, Field -from copilot import CopilotClient, define_tool -from copilot.client import ExternalServerConfig, SubprocessConfig +from copilot import CopilotClient, CopilotClientOptions, RuntimeConnection, define_tool from copilot.generated.rpc import PermissionDecisionApproveOnce, PermissionDecisionReject from copilot.session import PermissionHandler, PermissionNoResult from copilot.tools import ToolInvocation @@ -54,13 +53,13 @@ async def setup(self): # Client 1 uses TCP mode so a second client can connect to the same server self._client1 = CopilotClient( - SubprocessConfig( - cli_path=self.cli_path, + CopilotClientOptions( + connection=RuntimeConnection.tcp( + path=self.cli_path, connection_token="py-tcp-shared-test-token" + ), working_directory=self.work_dir, env=self.get_env(), - use_stdio=False, github_token=github_token, - tcp_connection_token="py-tcp-shared-test-token", ) ) @@ -71,12 +70,14 @@ async def setup(self): await init_session.disconnect() # Read the actual port from client 1 and create client 2 - actual_port = self._client1.actual_port + actual_port = self._client1.runtime_port assert actual_port is not None, "Client 1 should have an actual port after connecting" self._client2 = CopilotClient( - ExternalServerConfig( - url=f"localhost:{actual_port}", tcp_connection_token="py-tcp-shared-test-token" + CopilotClientOptions( + connection=RuntimeConnection.uri( + f"localhost:{actual_port}", connection_token="py-tcp-shared-test-token" + ) ) ) @@ -134,7 +135,7 @@ def get_env(self) -> dict: env.update( { "COPILOT_API_URL": self.proxy_url, - "COPILOT_HOME": self.home_dir, + "base_directory": self.home_dir, "XDG_CONFIG_HOME": self.home_dir, "XDG_STATE_HOME": self.home_dir, } @@ -426,10 +427,12 @@ def ephemeral_tool(params: InputParams, invocation: ToolInvocation) -> str: await asyncio.sleep(0.5) # Recreate client2 for future tests (but don't rejoin the session) - actual_port = mctx.client1.actual_port + actual_port = mctx.client1.runtime_port mctx._client2 = CopilotClient( - ExternalServerConfig( - url=f"localhost:{actual_port}", tcp_connection_token="py-tcp-shared-test-token" + CopilotClientOptions( + connection=RuntimeConnection.uri( + f"localhost:{actual_port}", connection_token="py-tcp-shared-test-token" + ) ) ) diff --git a/python/e2e/test_pending_work_resume_e2e.py b/python/e2e/test_pending_work_resume_e2e.py index 63c658dc6..a1186d87f 100644 --- a/python/e2e/test_pending_work_resume_e2e.py +++ b/python/e2e/test_pending_work_resume_e2e.py @@ -16,8 +16,7 @@ import pytest -from copilot import CopilotClient -from copilot.client import ExternalServerConfig, SubprocessConfig +from copilot import CopilotClient, CopilotClientOptions, RuntimeConnection from copilot.generated.rpc import ( HandlePendingToolCallRequest, PermissionDecisionRequest, @@ -38,13 +37,11 @@ def _make_subprocess_client(ctx: E2ETestContext, *, use_stdio: bool = True) -> C "fake-token-for-e2e-tests" if os.environ.get("GITHUB_ACTIONS") == "true" else None ) return CopilotClient( - SubprocessConfig( - cli_path=ctx.cli_path, + CopilotClientOptions( + connection=RuntimeConnection.stdio(path=ctx.cli_path), working_directory=ctx.work_dir, env=ctx.get_env(), github_token=github_token, - use_stdio=use_stdio, - tcp_connection_token="py-tcp-shared-test-token", ) ) @@ -138,7 +135,7 @@ async def test_should_continue_pending_permission_request_after_resume( server = _make_subprocess_client(ctx, use_stdio=False) await server.start() try: - cli_url = f"localhost:{server.actual_port}" + cli_url = f"localhost:{server.runtime_port}" release_original: asyncio.Future = asyncio.get_event_loop().create_future() captured_request: asyncio.Future = asyncio.get_event_loop().create_future() @@ -153,7 +150,11 @@ def original_tool_handler(args): return f"ORIGINAL_SHOULD_NOT_RUN_{args.get('value', '')}" suspended_client = CopilotClient( - ExternalServerConfig(url=cli_url, tcp_connection_token="py-tcp-shared-test-token") + CopilotClientOptions( + connection=RuntimeConnection.uri( + cli_url, connection_token="py-tcp-shared-test-token" + ) + ) ) session1 = await suspended_client.create_session( on_permission_request=hold_permission, @@ -179,8 +180,10 @@ def resumed_tool_handler(args): return f"PERMISSION_RESUMED_{args['value'].upper()}" resumed_client = CopilotClient( - ExternalServerConfig( - url=cli_url, tcp_connection_token="py-tcp-shared-test-token" + CopilotClientOptions( + connection=RuntimeConnection.uri( + cli_url, connection_token="py-tcp-shared-test-token" + ) ) ) try: @@ -224,7 +227,7 @@ async def test_should_continue_pending_external_tool_request_after_resume( server = _make_subprocess_client(ctx, use_stdio=False) await server.start() try: - cli_url = f"localhost:{server.actual_port}" + cli_url = f"localhost:{server.runtime_port}" tool_started: asyncio.Future = asyncio.get_event_loop().create_future() release_original: asyncio.Future = asyncio.get_event_loop().create_future() @@ -236,7 +239,11 @@ async def blocking_external_tool(args): return await release_original suspended_client = CopilotClient( - ExternalServerConfig(url=cli_url, tcp_connection_token="py-tcp-shared-test-token") + CopilotClientOptions( + connection=RuntimeConnection.uri( + cli_url, connection_token="py-tcp-shared-test-token" + ) + ) ) session1 = await suspended_client.create_session( on_permission_request=PermissionHandler.approve_all, @@ -257,8 +264,10 @@ async def blocking_external_tool(args): await suspended_client.force_stop() resumed_client = CopilotClient( - ExternalServerConfig( - url=cli_url, tcp_connection_token="py-tcp-shared-test-token" + CopilotClientOptions( + connection=RuntimeConnection.uri( + cli_url, connection_token="py-tcp-shared-test-token" + ) ) ) try: @@ -296,7 +305,7 @@ async def test_should_continue_parallel_pending_external_tool_requests_after_res server = _make_subprocess_client(ctx, use_stdio=False) await server.start() try: - cli_url = f"localhost:{server.actual_port}" + cli_url = f"localhost:{server.runtime_port}" tool_a_started: asyncio.Future = asyncio.get_event_loop().create_future() tool_b_started: asyncio.Future = asyncio.get_event_loop().create_future() @@ -314,7 +323,11 @@ async def tool_b(args): return await release_b suspended_client = CopilotClient( - ExternalServerConfig(url=cli_url, tcp_connection_token="py-tcp-shared-test-token") + CopilotClientOptions( + connection=RuntimeConnection.uri( + cli_url, connection_token="py-tcp-shared-test-token" + ) + ) ) session1 = await suspended_client.create_session( on_permission_request=PermissionHandler.approve_all, @@ -345,8 +358,10 @@ async def tool_b(args): await suspended_client.force_stop() resumed_client = CopilotClient( - ExternalServerConfig( - url=cli_url, tcp_connection_token="py-tcp-shared-test-token" + CopilotClientOptions( + connection=RuntimeConnection.uri( + cli_url, connection_token="py-tcp-shared-test-token" + ) ) ) try: @@ -388,10 +403,14 @@ async def test_should_resume_successfully_when_no_pending_work_exists( server = _make_subprocess_client(ctx, use_stdio=False) await server.start() try: - cli_url = f"localhost:{server.actual_port}" + cli_url = f"localhost:{server.runtime_port}" first_client = CopilotClient( - ExternalServerConfig(url=cli_url, tcp_connection_token="py-tcp-shared-test-token") + CopilotClientOptions( + connection=RuntimeConnection.uri( + cli_url, connection_token="py-tcp-shared-test-token" + ) + ) ) try: first_session = await first_client.create_session( @@ -407,7 +426,11 @@ async def test_should_resume_successfully_when_no_pending_work_exists( await _safe_force_stop(first_client) resumed_client = CopilotClient( - ExternalServerConfig(url=cli_url, tcp_connection_token="py-tcp-shared-test-token") + CopilotClientOptions( + connection=RuntimeConnection.uri( + cli_url, connection_token="py-tcp-shared-test-token" + ) + ) ) try: resumed_session = await resumed_client.resume_session( @@ -445,10 +468,14 @@ async def blocking_external_tool(args): server = _make_subprocess_client(ctx, use_stdio=False) await server.start() try: - cli_url = f"localhost:{server.actual_port}" + cli_url = f"localhost:{server.runtime_port}" suspended_client = CopilotClient( - ExternalServerConfig(url=cli_url, tcp_connection_token="py-tcp-shared-test-token") + CopilotClientOptions( + connection=RuntimeConnection.uri( + cli_url, connection_token="py-tcp-shared-test-token" + ) + ) ) session1 = await suspended_client.create_session( on_permission_request=PermissionHandler.approve_all, @@ -469,8 +496,10 @@ async def blocking_external_tool(args): await suspended_client.force_stop() resumed_client = CopilotClient( - ExternalServerConfig( - url=cli_url, tcp_connection_token="py-tcp-shared-test-token" + CopilotClientOptions( + connection=RuntimeConnection.uri( + cli_url, connection_token="py-tcp-shared-test-token" + ) ) ) try: @@ -520,10 +549,14 @@ async def test_should_report_continuependingwork_true_in_resume_event( server = _make_subprocess_client(ctx, use_stdio=False) await server.start() try: - cli_url = f"localhost:{server.actual_port}" + cli_url = f"localhost:{server.runtime_port}" first_client = CopilotClient( - ExternalServerConfig(url=cli_url, tcp_connection_token="py-tcp-shared-test-token") + CopilotClientOptions( + connection=RuntimeConnection.uri( + cli_url, connection_token="py-tcp-shared-test-token" + ) + ) ) try: first_session = await first_client.create_session( @@ -540,7 +573,11 @@ async def test_should_report_continuependingwork_true_in_resume_event( await _safe_force_stop(first_client) resumed_client = CopilotClient( - ExternalServerConfig(url=cli_url, tcp_connection_token="py-tcp-shared-test-token") + CopilotClientOptions( + connection=RuntimeConnection.uri( + cli_url, connection_token="py-tcp-shared-test-token" + ) + ) ) try: resumed_session = await resumed_client.resume_session( diff --git a/python/e2e/test_per_session_auth_e2e.py b/python/e2e/test_per_session_auth_e2e.py index b03945deb..15695d831 100644 --- a/python/e2e/test_per_session_auth_e2e.py +++ b/python/e2e/test_per_session_auth_e2e.py @@ -2,7 +2,7 @@ import pytest -from copilot.client import CopilotClient, SubprocessConfig +from copilot.client import CopilotClient, CopilotClientOptions, RuntimeConnection from copilot.session import PermissionHandler from .testharness import E2ETestContext @@ -18,7 +18,7 @@ async def auth_ctx(ctx: E2ETestContext): # Redirect GitHub API calls to the proxy so per-session auth token # resolution (fetchCopilotUser) is intercepted. Must be set before the # CLI subprocess is spawned (i.e., before the first create_session call). - ctx.client._config.env["COPILOT_DEBUG_GITHUB_API_URL"] = proxy_url + ctx.client._options.env["COPILOT_DEBUG_GITHUB_API_URL"] = proxy_url await ctx.set_copilot_user_by_token( "token-alice", @@ -97,8 +97,8 @@ async def test_should_return_unauthenticated_when_no_token_provided( env = without_auth_env(auth_ctx.get_env()) env["COPILOT_DEBUG_GITHUB_API_URL"] = auth_ctx.proxy_url no_token_client = CopilotClient( - SubprocessConfig( - cli_path=auth_ctx.cli_path, + CopilotClientOptions( + connection=RuntimeConnection.stdio(path=auth_ctx.cli_path), working_directory=auth_ctx.work_dir, env=env, use_logged_in_user=False, diff --git a/python/e2e/test_rpc_e2e.py b/python/e2e/test_rpc_e2e.py index 511b9d1d1..71c5b4f56 100644 --- a/python/e2e/test_rpc_e2e.py +++ b/python/e2e/test_rpc_e2e.py @@ -2,8 +2,7 @@ import pytest -from copilot import CopilotClient -from copilot.client import SubprocessConfig +from copilot import CopilotClient, CopilotClientOptions, RuntimeConnection from copilot.generated.rpc import ModelsListRequest, PingRequest from copilot.session import PermissionHandler @@ -16,7 +15,9 @@ class TestRpc: @pytest.mark.asyncio async def test_should_call_rpc_ping_with_typed_params(self): """Test calling rpc.ping with typed params and result""" - client = CopilotClient(SubprocessConfig(cli_path=CLI_PATH, use_stdio=True)) + client = CopilotClient( + CopilotClientOptions(connection=RuntimeConnection.stdio(path=CLI_PATH)) + ) try: await client.start() @@ -32,7 +33,9 @@ async def test_should_call_rpc_ping_with_typed_params(self): @pytest.mark.asyncio async def test_should_call_rpc_models_list(self): """Test calling rpc.models.list with typed result""" - client = CopilotClient(SubprocessConfig(cli_path=CLI_PATH, use_stdio=True)) + client = CopilotClient( + CopilotClientOptions(connection=RuntimeConnection.stdio(path=CLI_PATH)) + ) try: await client.start() @@ -55,7 +58,9 @@ async def test_should_call_rpc_models_list(self): @pytest.mark.asyncio async def test_should_call_rpc_account_get_quota(self): """Test calling rpc.account.getQuota when authenticated""" - client = CopilotClient(SubprocessConfig(cli_path=CLI_PATH, use_stdio=True)) + client = CopilotClient( + CopilotClientOptions(connection=RuntimeConnection.stdio(path=CLI_PATH)) + ) try: await client.start() @@ -116,7 +121,9 @@ async def test_get_and_set_session_mode(self): """Test getting and setting session mode""" from copilot.generated.rpc import ModeSetRequest, SessionMode - client = CopilotClient(SubprocessConfig(cli_path=CLI_PATH, use_stdio=True)) + client = CopilotClient( + CopilotClientOptions(connection=RuntimeConnection.stdio(path=CLI_PATH)) + ) try: await client.start() @@ -148,7 +155,9 @@ async def test_read_update_and_delete_plan(self): """Test reading, updating, and deleting plan""" from copilot.generated.rpc import PlanUpdateRequest - client = CopilotClient(SubprocessConfig(cli_path=CLI_PATH, use_stdio=True)) + client = CopilotClient( + CopilotClientOptions(connection=RuntimeConnection.stdio(path=CLI_PATH)) + ) try: await client.start() @@ -191,7 +200,9 @@ async def test_create_list_and_read_workspace_files(self): WorkspacesReadFileRequest, ) - client = CopilotClient(SubprocessConfig(cli_path=CLI_PATH, use_stdio=True)) + client = CopilotClient( + CopilotClientOptions(connection=RuntimeConnection.stdio(path=CLI_PATH)) + ) try: await client.start() diff --git a/python/e2e/test_rpc_server_e2e.py b/python/e2e/test_rpc_server_e2e.py index f5dc9920d..742f1eb84 100644 --- a/python/e2e/test_rpc_server_e2e.py +++ b/python/e2e/test_rpc_server_e2e.py @@ -12,8 +12,7 @@ import pytest -from copilot import CopilotClient -from copilot.client import SubprocessConfig +from copilot import CopilotClient, CopilotClientOptions, RuntimeConnection from copilot.generated.rpc import ( AccountGetQuotaRequest, MCPDiscoverRequest, @@ -48,7 +47,7 @@ def _create_skill_directory(work_dir: str, skill_name: str, description: str) -> @pytest.fixture(scope="module") async def authed_ctx(ctx: E2ETestContext): """Configure proxy to redirect GitHub user lookups so per-token auth works.""" - ctx.client._config.env["COPILOT_DEBUG_GITHUB_API_URL"] = ctx.proxy_url + ctx.client._options.env["COPILOT_DEBUG_GITHUB_API_URL"] = ctx.proxy_url return ctx @@ -56,8 +55,8 @@ def _make_authed_client(ctx: E2ETestContext, token: str) -> CopilotClient: env = ctx.get_env() env["COPILOT_DEBUG_GITHUB_API_URL"] = ctx.proxy_url return CopilotClient( - SubprocessConfig( - cli_path=ctx.cli_path, + CopilotClientOptions( + connection=RuntimeConnection.stdio(path=ctx.cli_path), working_directory=ctx.work_dir, env=env, github_token=token, diff --git a/python/e2e/test_session_e2e.py b/python/e2e/test_session_e2e.py index 2e42b8593..7121f3345 100644 --- a/python/e2e/test_session_e2e.py +++ b/python/e2e/test_session_e2e.py @@ -5,8 +5,7 @@ import pytest -from copilot import CopilotClient -from copilot.client import SubprocessConfig +from copilot import CopilotClient, CopilotClientOptions, RuntimeConnection from copilot.generated.session_events import SessionModelChangeData from copilot.session import PermissionHandler from copilot.tools import Tool, ToolResult @@ -243,8 +242,8 @@ async def test_should_resume_a_session_using_a_new_client(self, ctx: E2ETestCont "fake-token-for-e2e-tests" if os.environ.get("GITHUB_ACTIONS") == "true" else None ) new_client = CopilotClient( - SubprocessConfig( - cli_path=ctx.cli_path, + CopilotClientOptions( + connection=RuntimeConnection.stdio(path=ctx.cli_path), working_directory=ctx.work_dir, env=ctx.get_env(), github_token=github_token, diff --git a/python/e2e/test_session_fs_e2e.py b/python/e2e/test_session_fs_e2e.py index 787f0025b..e066d5160 100644 --- a/python/e2e/test_session_fs_e2e.py +++ b/python/e2e/test_session_fs_e2e.py @@ -12,8 +12,13 @@ import pytest import pytest_asyncio -from copilot import CopilotClient, SessionFsConfig, define_tool -from copilot.client import ExternalServerConfig, SubprocessConfig +from copilot import ( + CopilotClient, + CopilotClientOptions, + RuntimeConnection, + SessionFsConfig, + define_tool, +) from copilot.generated.rpc import ( SessionFSReaddirWithTypesEntry, SessionFSReaddirWithTypesEntryType, @@ -45,8 +50,8 @@ @pytest_asyncio.fixture(scope="module", loop_scope="module") async def session_fs_client(ctx: E2ETestContext): client = CopilotClient( - SubprocessConfig( - cli_path=ctx.cli_path, + CopilotClientOptions( + connection=RuntimeConnection.stdio(path=ctx.cli_path), working_directory=ctx.work_dir, env=ctx.get_env(), github_token=DEFAULT_GITHUB_TOKEN, @@ -117,11 +122,10 @@ async def test_should_load_session_data_from_fs_provider_on_resume( async def test_should_reject_setprovider_when_sessions_already_exist(self, ctx: E2ETestContext): client1 = CopilotClient( - SubprocessConfig( - cli_path=ctx.cli_path, + CopilotClientOptions( + connection=RuntimeConnection.tcp(path=ctx.cli_path), working_directory=ctx.work_dir, env=ctx.get_env(), - use_stdio=False, github_token=DEFAULT_GITHUB_TOKEN, ) ) @@ -132,12 +136,12 @@ async def test_should_reject_setprovider_when_sessions_already_exist(self, ctx: session = await client1.create_session( on_permission_request=PermissionHandler.approve_all, ) - actual_port = client1.actual_port + actual_port = client1.runtime_port assert actual_port is not None client2 = CopilotClient( - ExternalServerConfig( - url=f"localhost:{actual_port}", + CopilotClientOptions( + connection=RuntimeConnection.uri(f"localhost:{actual_port}"), session_fs=SESSION_FS_CONFIG, ) ) diff --git a/python/e2e/test_session_fs_sqlite_e2e.py b/python/e2e/test_session_fs_sqlite_e2e.py index 38c15ae08..0bd9a2216 100644 --- a/python/e2e/test_session_fs_sqlite_e2e.py +++ b/python/e2e/test_session_fs_sqlite_e2e.py @@ -12,8 +12,7 @@ import pytest import pytest_asyncio -from copilot import CopilotClient, SessionFsConfig -from copilot.client import SubprocessConfig +from copilot import CopilotClient, CopilotClientOptions, RuntimeConnection, SessionFsConfig from copilot.generated.rpc import ( SessionFSReaddirWithTypesEntry, SessionFSReaddirWithTypesEntryType, @@ -200,8 +199,8 @@ def factory(session): @pytest_asyncio.fixture(scope="module", loop_scope="module") async def sqlite_client(ctx: E2ETestContext): client = CopilotClient( - SubprocessConfig( - cli_path=ctx.cli_path, + CopilotClientOptions( + connection=RuntimeConnection.stdio(path=ctx.cli_path), working_directory=ctx.work_dir, env=ctx.get_env(), github_token=DEFAULT_GITHUB_TOKEN, diff --git a/python/e2e/test_streaming_fidelity_e2e.py b/python/e2e/test_streaming_fidelity_e2e.py index 9b4447bf2..88b9670d5 100644 --- a/python/e2e/test_streaming_fidelity_e2e.py +++ b/python/e2e/test_streaming_fidelity_e2e.py @@ -4,8 +4,7 @@ import pytest -from copilot import CopilotClient -from copilot.client import SubprocessConfig +from copilot import CopilotClient, CopilotClientOptions, RuntimeConnection from copilot.session import PermissionHandler from .testharness import E2ETestContext @@ -79,8 +78,8 @@ async def test_should_produce_deltas_after_session_resume(self, ctx: E2ETestCont "fake-token-for-e2e-tests" if os.environ.get("GITHUB_ACTIONS") == "true" else None ) new_client = CopilotClient( - SubprocessConfig( - cli_path=ctx.cli_path, + CopilotClientOptions( + connection=RuntimeConnection.stdio(path=ctx.cli_path), working_directory=ctx.work_dir, env=ctx.get_env(), github_token=github_token, @@ -131,8 +130,8 @@ async def test_should_not_produce_deltas_after_session_resume_with_streaming_dis # Resume with streaming disabled new_client = CopilotClient( - SubprocessConfig( - cli_path=ctx.cli_path, + CopilotClientOptions( + connection=RuntimeConnection.stdio(path=ctx.cli_path), working_directory=ctx.work_dir, env=ctx.get_env(), github_token=github_token, diff --git a/python/e2e/test_subagent_hooks_e2e.py b/python/e2e/test_subagent_hooks_e2e.py index e5262a23c..da1dba506 100644 --- a/python/e2e/test_subagent_hooks_e2e.py +++ b/python/e2e/test_subagent_hooks_e2e.py @@ -7,7 +7,7 @@ import pytest -from copilot.client import CopilotClient, SubprocessConfig +from copilot.client import CopilotClient, CopilotClientOptions, RuntimeConnection from copilot.session import PermissionHandler from .testharness import E2ETestContext @@ -50,8 +50,8 @@ async def on_post_tool_use(input_data, invocation): "fake-token-for-e2e-tests" if os.environ.get("GITHUB_ACTIONS") == "true" else None ) client = CopilotClient( - SubprocessConfig( - cli_path=ctx.cli_path, + CopilotClientOptions( + connection=RuntimeConnection.stdio(path=ctx.cli_path), working_directory=ctx.work_dir, env=env, github_token=github_token, diff --git a/python/e2e/test_suspend_e2e.py b/python/e2e/test_suspend_e2e.py index 956ee86c2..346121109 100644 --- a/python/e2e/test_suspend_e2e.py +++ b/python/e2e/test_suspend_e2e.py @@ -14,8 +14,7 @@ import pytest -from copilot import CopilotClient -from copilot.client import ExternalServerConfig, SubprocessConfig +from copilot import CopilotClient, CopilotClientOptions, RuntimeConnection from copilot.generated.rpc import PermissionDecisionUserNotAvailable from copilot.session import PermissionHandler from copilot.tools import Tool, ToolInvocation, ToolResult @@ -32,13 +31,11 @@ def _make_subprocess_client(ctx: E2ETestContext, *, use_stdio: bool = True) -> C "fake-token-for-e2e-tests" if os.environ.get("GITHUB_ACTIONS") == "true" else None ) return CopilotClient( - SubprocessConfig( - cli_path=ctx.cli_path, + CopilotClientOptions( + connection=RuntimeConnection.stdio(path=ctx.cli_path), working_directory=ctx.work_dir, env=ctx.get_env(), github_token=github_token, - use_stdio=use_stdio, - tcp_connection_token="py-tcp-shared-test-token", ) ) @@ -100,11 +97,15 @@ async def test_should_allow_resume_and_continue_conversation_after_suspend( server = _make_subprocess_client(ctx, use_stdio=False) await server.start() try: - cli_url = f"localhost:{server.actual_port}" + cli_url = f"localhost:{server.runtime_port}" session_id: str first_client = CopilotClient( - ExternalServerConfig(url=cli_url, tcp_connection_token="py-tcp-shared-test-token") + CopilotClientOptions( + connection=RuntimeConnection.uri( + cli_url, connection_token="py-tcp-shared-test-token" + ) + ) ) try: session1 = await first_client.create_session( @@ -121,7 +122,11 @@ async def test_should_allow_resume_and_continue_conversation_after_suspend( await _safe_force_stop(first_client) resumed_client = CopilotClient( - ExternalServerConfig(url=cli_url, tcp_connection_token="py-tcp-shared-test-token") + CopilotClientOptions( + connection=RuntimeConnection.uri( + cli_url, connection_token="py-tcp-shared-test-token" + ) + ) ) try: session2 = await resumed_client.resume_session( diff --git a/python/e2e/test_telemetry_e2e.py b/python/e2e/test_telemetry_e2e.py index 6b1f7766c..ddb17c899 100644 --- a/python/e2e/test_telemetry_e2e.py +++ b/python/e2e/test_telemetry_e2e.py @@ -22,9 +22,8 @@ import pytest -from copilot import CopilotClient +from copilot import CopilotClient, CopilotClientOptions, RuntimeConnection, TelemetryConfig from copilot._telemetry import get_trace_context, trace_context -from copilot.client import SubprocessConfig, TelemetryConfig from copilot.session import PermissionHandler from copilot.tools import Tool, ToolInvocation, ToolResult @@ -82,8 +81,8 @@ def echo(invocation: ToolInvocation) -> ToolResult: "fake-token-for-e2e-tests" if os.environ.get("GITHUB_ACTIONS") == "true" else None ) client = CopilotClient( - SubprocessConfig( - cli_path=ctx.cli_path, + CopilotClientOptions( + connection=RuntimeConnection.stdio(path=ctx.cli_path), working_directory=ctx.work_dir, env=ctx.get_env(), github_token=github_token, @@ -213,7 +212,7 @@ class TestSubprocessConfigTelemetry: """Mirrors CopilotClientOptions_Telemetry_DefaultsToNull.""" async def test_telemetry_defaults_to_none(self): - config = SubprocessConfig() + config = CopilotClientOptions(connection=RuntimeConnection.stdio()) assert config.telemetry is None # NOTE: CopilotClientOptions_Clone_CopiesTelemetry from the C# baseline has diff --git a/python/e2e/test_ui_elicitation_multi_client_e2e.py b/python/e2e/test_ui_elicitation_multi_client_e2e.py index 97f989ac4..b72890510 100644 --- a/python/e2e/test_ui_elicitation_multi_client_e2e.py +++ b/python/e2e/test_ui_elicitation_multi_client_e2e.py @@ -16,8 +16,7 @@ import pytest import pytest_asyncio -from copilot import CopilotClient -from copilot.client import ExternalServerConfig, SubprocessConfig +from copilot import CopilotClient, CopilotClientOptions, RuntimeConnection from copilot.generated.session_events import CapabilitiesChangedData from copilot.session import ( ElicitationContext, @@ -63,13 +62,13 @@ async def setup(self): # Client 1 uses TCP mode so additional clients can connect self._client1 = CopilotClient( - SubprocessConfig( - cli_path=self.cli_path, + CopilotClientOptions( + connection=RuntimeConnection.tcp( + path=self.cli_path, connection_token="py-tcp-shared-test-token" + ), working_directory=self.work_dir, env=self._get_env(), - use_stdio=False, github_token=github_token, - tcp_connection_token="py-tcp-shared-test-token", ) ) @@ -79,13 +78,14 @@ async def setup(self): ) await init_session.disconnect() - self._actual_port = self._client1.actual_port + self._actual_port = self._client1.runtime_port assert self._actual_port is not None self._client2 = CopilotClient( - ExternalServerConfig( - url=f"localhost:{self._actual_port}", - tcp_connection_token="py-tcp-shared-test-token", + CopilotClientOptions( + connection=RuntimeConnection.uri( + f"localhost:{self._actual_port}", connection_token="py-tcp-shared-test-token" + ) ) ) @@ -128,7 +128,7 @@ def _get_env(self) -> dict: env.update( { "COPILOT_API_URL": self.proxy_url, - "COPILOT_HOME": self.home_dir, + "base_directory": self.home_dir, "XDG_CONFIG_HOME": self.home_dir, "XDG_STATE_HOME": self.home_dir, } @@ -139,9 +139,10 @@ def make_external_client(self) -> CopilotClient: """Create a new external client connected to the same CLI server.""" assert self._actual_port is not None return CopilotClient( - ExternalServerConfig( - url=f"localhost:{self._actual_port}", - tcp_connection_token="py-tcp-shared-test-token", + CopilotClientOptions( + connection=RuntimeConnection.uri( + f"localhost:{self._actual_port}", connection_token="py-tcp-shared-test-token" + ) ) ) diff --git a/python/e2e/testharness/context.py b/python/e2e/testharness/context.py index d67311598..527a0245d 100644 --- a/python/e2e/testharness/context.py +++ b/python/e2e/testharness/context.py @@ -12,8 +12,7 @@ from pathlib import Path from typing import Any -from copilot import CopilotClient -from copilot.client import SubprocessConfig +from copilot import CopilotClient, CopilotClientOptions, RuntimeConnection from .proxy import CapiProxy @@ -80,9 +79,11 @@ async def setup(self, cli_args: list[str] | None = None): # Create the shared client (like Node.js/Go do) self._client = CopilotClient( - SubprocessConfig( - cli_path=self.cli_path, - cli_args=cli_args or [], + CopilotClientOptions( + connection=RuntimeConnection.stdio( + path=self.cli_path, + args=tuple(cli_args or []), + ), working_directory=self.work_dir, env=self.get_env(), github_token=DEFAULT_GITHUB_TOKEN, @@ -148,7 +149,7 @@ def get_env(self) -> dict: env.update( { "COPILOT_API_URL": self.proxy_url, - "COPILOT_HOME": self.home_dir, + "base_directory": self.home_dir, "COPILOT_SDK_AUTH_TOKEN": DEFAULT_GITHUB_TOKEN, "GH_CONFIG_DIR": self.home_dir, "GH_TOKEN": DEFAULT_GITHUB_TOKEN, diff --git a/python/test_client.py b/python/test_client.py index 41ba3c664..bcf1e9d84 100644 --- a/python/test_client.py +++ b/python/test_client.py @@ -8,16 +8,20 @@ import pytest -from copilot import CopilotClient, define_tool +from copilot import ( + CopilotClient, + CopilotClientOptions, + RuntimeConnection, + StdioRuntimeConnection, + define_tool, +) from copilot.client import ( CloudSessionOptions, CloudSessionRepository, - ExternalServerConfig, ModelCapabilities, ModelInfo, ModelLimits, ModelSupports, - SubprocessConfig, ) from copilot.session import PermissionHandler, PermissionNoResult from e2e.testharness import CLI_PATH @@ -26,7 +30,9 @@ class TestPermissionHandlerOptional: @pytest.mark.asyncio async def test_create_session_allows_missing_permission_handler(self): - client = CopilotClient(SubprocessConfig(cli_path=CLI_PATH)) + client = CopilotClient( + CopilotClientOptions(connection=RuntimeConnection.stdio(path=CLI_PATH)) + ) await client.start() try: session = await client.create_session() @@ -36,7 +42,9 @@ async def test_create_session_allows_missing_permission_handler(self): @pytest.mark.asyncio async def test_create_session_allows_none_permission_handler(self): - client = CopilotClient(SubprocessConfig(cli_path=CLI_PATH)) + client = CopilotClient( + CopilotClientOptions(connection=RuntimeConnection.stdio(path=CLI_PATH)) + ) await client.start() try: session = await client.create_session(on_permission_request=None) @@ -46,7 +54,9 @@ async def test_create_session_allows_none_permission_handler(self): @pytest.mark.asyncio async def test_v2_permission_adapter_rejects_no_result(self): - client = CopilotClient(SubprocessConfig(CLI_PATH)) + client = CopilotClient( + CopilotClientOptions(connection=RuntimeConnection.stdio(path=CLI_PATH)) + ) await client.start() try: session = await client.create_session( @@ -70,7 +80,9 @@ async def test_v2_permission_adapter_rejects_no_result(self): @pytest.mark.asyncio async def test_resume_session_allows_none_permission_handler(self): - client = CopilotClient(SubprocessConfig(cli_path=CLI_PATH)) + client = CopilotClient( + CopilotClientOptions(connection=RuntimeConnection.stdio(path=CLI_PATH)) + ) await client.start() try: session = await client.create_session( @@ -85,7 +97,9 @@ async def test_resume_session_allows_none_permission_handler(self): class TestCreateSessionConfig: @pytest.mark.asyncio async def test_create_session_forwards_cloud_options(self): - client = CopilotClient(SubprocessConfig(cli_path=CLI_PATH)) + client = CopilotClient( + CopilotClientOptions(connection=RuntimeConnection.stdio(path=CLI_PATH)) + ) await client.start() try: captured = {} @@ -121,47 +135,55 @@ async def mock_request(method, params): class TestURLParsing: def test_parse_port_only_url(self): - client = CopilotClient(ExternalServerConfig(url="8080")) - assert client._actual_port == 8080 + client = CopilotClient(CopilotClientOptions(connection=RuntimeConnection.uri("8080"))) + assert client._runtime_port == 8080 assert client._actual_host == "localhost" assert client._is_external_server def test_parse_host_port_url(self): - client = CopilotClient(ExternalServerConfig(url="127.0.0.1:9000")) - assert client._actual_port == 9000 + client = CopilotClient( + CopilotClientOptions(connection=RuntimeConnection.uri("127.0.0.1:9000")) + ) + assert client._runtime_port == 9000 assert client._actual_host == "127.0.0.1" assert client._is_external_server def test_parse_http_url(self): - client = CopilotClient(ExternalServerConfig(url="http://localhost:7000")) - assert client._actual_port == 7000 + client = CopilotClient( + CopilotClientOptions(connection=RuntimeConnection.uri("http://localhost:7000")) + ) + assert client._runtime_port == 7000 assert client._actual_host == "localhost" assert client._is_external_server def test_parse_https_url(self): - client = CopilotClient(ExternalServerConfig(url="https://example.com:443")) - assert client._actual_port == 443 + client = CopilotClient( + CopilotClientOptions(connection=RuntimeConnection.uri("https://example.com:443")) + ) + assert client._runtime_port == 443 assert client._actual_host == "example.com" assert client._is_external_server def test_invalid_url_format(self): with pytest.raises(ValueError, match="Invalid cli_url format"): - CopilotClient(ExternalServerConfig(url="invalid-url")) + CopilotClient(CopilotClientOptions(connection=RuntimeConnection.uri("invalid-url"))) def test_invalid_port_too_high(self): with pytest.raises(ValueError, match="Invalid port in cli_url"): - CopilotClient(ExternalServerConfig(url="localhost:99999")) + CopilotClient(CopilotClientOptions(connection=RuntimeConnection.uri("localhost:99999"))) def test_invalid_port_zero(self): with pytest.raises(ValueError, match="Invalid port in cli_url"): - CopilotClient(ExternalServerConfig(url="localhost:0")) + CopilotClient(CopilotClientOptions(connection=RuntimeConnection.uri("localhost:0"))) def test_invalid_port_negative(self): with pytest.raises(ValueError, match="Invalid port in cli_url"): - CopilotClient(ExternalServerConfig(url="localhost:-1")) + CopilotClient(CopilotClientOptions(connection=RuntimeConnection.uri("localhost:-1"))) def test_is_external_server_true(self): - client = CopilotClient(ExternalServerConfig(url="localhost:8080")) + client = CopilotClient( + CopilotClientOptions(connection=RuntimeConnection.uri("localhost:8080")) + ) assert client._is_external_server @@ -169,8 +191,8 @@ class TestSessionFsConfig: def test_missing_initial_cwd(self): with pytest.raises(ValueError, match="session_fs.initial_working_directory is required"): CopilotClient( - SubprocessConfig( - cli_path=CLI_PATH, + CopilotClientOptions( + connection=RuntimeConnection.stdio(path=CLI_PATH), log_level="error", session_fs={ "initial_working_directory": "", @@ -183,8 +205,8 @@ def test_missing_initial_cwd(self): def test_missing_session_state_path(self): with pytest.raises(ValueError, match="session_fs.session_state_path is required"): CopilotClient( - SubprocessConfig( - cli_path=CLI_PATH, + CopilotClientOptions( + connection=RuntimeConnection.stdio(path=CLI_PATH), log_level="error", session_fs={ "initial_working_directory": "/", @@ -198,100 +220,109 @@ def test_missing_session_state_path(self): class TestAuthOptions: def test_accepts_github_token(self): client = CopilotClient( - SubprocessConfig( - cli_path=CLI_PATH, + CopilotClientOptions( + connection=RuntimeConnection.stdio(path=CLI_PATH), github_token="gho_test_token", log_level="error", ) ) - assert isinstance(client._config, SubprocessConfig) - assert client._config.github_token == "gho_test_token" + assert isinstance(client._options.connection, StdioRuntimeConnection) + assert client._options.github_token == "gho_test_token" def test_default_use_logged_in_user_true_without_token(self): - client = CopilotClient(SubprocessConfig(cli_path=CLI_PATH, log_level="error")) - assert isinstance(client._config, SubprocessConfig) - assert client._config.use_logged_in_user is True + client = CopilotClient( + CopilotClientOptions( + connection=RuntimeConnection.stdio(path=CLI_PATH), log_level="error" + ) + ) + assert isinstance(client._options.connection, StdioRuntimeConnection) + assert client._options.use_logged_in_user is True def test_default_use_logged_in_user_false_with_token(self): client = CopilotClient( - SubprocessConfig( - cli_path=CLI_PATH, + CopilotClientOptions( + connection=RuntimeConnection.stdio(path=CLI_PATH), github_token="gho_test_token", log_level="error", ) ) - assert isinstance(client._config, SubprocessConfig) - assert client._config.use_logged_in_user is False + assert isinstance(client._options.connection, StdioRuntimeConnection) + assert client._options.use_logged_in_user is False def test_explicit_use_logged_in_user_true_with_token(self): client = CopilotClient( - SubprocessConfig( - cli_path=CLI_PATH, + CopilotClientOptions( + connection=RuntimeConnection.stdio(path=CLI_PATH), github_token="gho_test_token", use_logged_in_user=True, log_level="error", ) ) - assert isinstance(client._config, SubprocessConfig) - assert client._config.use_logged_in_user is True + assert isinstance(client._options.connection, StdioRuntimeConnection) + assert client._options.use_logged_in_user is True def test_explicit_use_logged_in_user_false_without_token(self): client = CopilotClient( - SubprocessConfig( - cli_path=CLI_PATH, + CopilotClientOptions( + connection=RuntimeConnection.stdio(path=CLI_PATH), use_logged_in_user=False, log_level="error", ) ) - assert isinstance(client._config, SubprocessConfig) - assert client._config.use_logged_in_user is False + assert isinstance(client._options.connection, StdioRuntimeConnection) + assert client._options.use_logged_in_user is False class TestSessionIdleTimeoutSeconds: def test_accepts_session_idle_timeout_seconds(self): client = CopilotClient( - SubprocessConfig( - cli_path=CLI_PATH, + CopilotClientOptions( + connection=RuntimeConnection.stdio(path=CLI_PATH), session_idle_timeout_seconds=600, log_level="error", ) ) - assert isinstance(client._config, SubprocessConfig) - assert client._config.session_idle_timeout_seconds == 600 + assert isinstance(client._options.connection, StdioRuntimeConnection) + assert client._options.session_idle_timeout_seconds == 600 def test_default_session_idle_timeout_seconds_is_none(self): - client = CopilotClient(SubprocessConfig(cli_path=CLI_PATH, log_level="error")) - assert isinstance(client._config, SubprocessConfig) - assert client._config.session_idle_timeout_seconds is None + client = CopilotClient( + CopilotClientOptions( + connection=RuntimeConnection.stdio(path=CLI_PATH), log_level="error" + ) + ) + assert isinstance(client._options.connection, StdioRuntimeConnection) + assert client._options.session_idle_timeout_seconds is None class TestCopilotHome: def test_accepts_copilot_home(self): client = CopilotClient( - SubprocessConfig( - cli_path=CLI_PATH, - copilot_home="/custom/copilot/home", + CopilotClientOptions( + connection=RuntimeConnection.stdio(path=CLI_PATH), + base_directory="/custom/copilot/home", log_level="error", ) ) - assert isinstance(client._config, SubprocessConfig) - assert client._config.copilot_home == "/custom/copilot/home" + assert isinstance(client._options.connection, StdioRuntimeConnection) + assert client._options.base_directory == "/custom/copilot/home" def test_default_copilot_home_is_none(self): client = CopilotClient( - SubprocessConfig( - cli_path=CLI_PATH, - log_level="error", + CopilotClientOptions( + connection=RuntimeConnection.stdio(path=CLI_PATH), log_level="error" ) ) - assert isinstance(client._config, SubprocessConfig) - assert client._config.copilot_home is None + assert isinstance(client._options.connection, StdioRuntimeConnection) + assert client._options.base_directory is None class TestOverridesBuiltInTool: @pytest.mark.asyncio async def test_overrides_built_in_tool_sent_in_tool_definition(self): - client = CopilotClient(SubprocessConfig(cli_path=CLI_PATH)) + client = CopilotClient( + CopilotClientOptions(connection=RuntimeConnection.stdio(path=CLI_PATH)) + ) await client.start() try: @@ -320,7 +351,9 @@ def grep(params) -> str: @pytest.mark.asyncio async def test_resume_session_sends_overrides_built_in_tool(self): - client = CopilotClient(SubprocessConfig(cli_path=CLI_PATH)) + client = CopilotClient( + CopilotClientOptions(connection=RuntimeConnection.stdio(path=CLI_PATH)) + ) await client.start() try: @@ -357,7 +390,9 @@ def grep(params) -> str: class TestInstructionDirectories: @pytest.mark.asyncio async def test_create_session_sends_instruction_directories(self): - client = CopilotClient(SubprocessConfig(cli_path=CLI_PATH)) + client = CopilotClient( + CopilotClientOptions(connection=RuntimeConnection.stdio(path=CLI_PATH)) + ) await client.start() try: @@ -385,7 +420,9 @@ async def mock_request(method, params): @pytest.mark.asyncio async def test_resume_session_sends_instruction_directories(self): - client = CopilotClient(SubprocessConfig(cli_path=CLI_PATH)) + client = CopilotClient( + CopilotClientOptions(connection=RuntimeConnection.stdio(path=CLI_PATH)) + ) await client.start() try: @@ -434,7 +471,7 @@ def handler(): return custom_models client = CopilotClient( - SubprocessConfig(cli_path=CLI_PATH), + CopilotClientOptions(connection=RuntimeConnection.stdio(path=CLI_PATH)), on_list_models=handler, ) await client.start() @@ -466,7 +503,7 @@ def handler(): return custom_models client = CopilotClient( - SubprocessConfig(cli_path=CLI_PATH), + CopilotClientOptions(connection=RuntimeConnection.stdio(path=CLI_PATH)), on_list_models=handler, ) await client.start() @@ -495,7 +532,7 @@ async def handler(): return custom_models client = CopilotClient( - SubprocessConfig(cli_path=CLI_PATH), + CopilotClientOptions(connection=RuntimeConnection.stdio(path=CLI_PATH)), on_list_models=handler, ) await client.start() @@ -526,7 +563,7 @@ def handler(): return custom_models client = CopilotClient( - SubprocessConfig(cli_path=CLI_PATH), + CopilotClientOptions(connection=RuntimeConnection.stdio(path=CLI_PATH)), on_list_models=handler, ) models = await client.list_models() @@ -537,7 +574,9 @@ def handler(): class TestSessionConfigForwarding: @pytest.mark.asyncio async def test_create_session_forwards_client_name(self): - client = CopilotClient(SubprocessConfig(cli_path=CLI_PATH)) + client = CopilotClient( + CopilotClientOptions(connection=RuntimeConnection.stdio(path=CLI_PATH)) + ) await client.start() try: @@ -558,7 +597,9 @@ async def mock_request(method, params): @pytest.mark.asyncio async def test_resume_session_forwards_client_name(self): - client = CopilotClient(SubprocessConfig(cli_path=CLI_PATH)) + client = CopilotClient( + CopilotClientOptions(connection=RuntimeConnection.stdio(path=CLI_PATH)) + ) await client.start() try: @@ -588,7 +629,9 @@ async def mock_request(method, params): @pytest.mark.asyncio async def test_create_session_forwards_enable_session_telemetry(self): - client = CopilotClient(SubprocessConfig(cli_path=CLI_PATH)) + client = CopilotClient( + CopilotClientOptions(connection=RuntimeConnection.stdio(path=CLI_PATH)) + ) await client.start() try: @@ -610,7 +653,9 @@ async def mock_request(method, params): @pytest.mark.asyncio async def test_resume_session_forwards_enable_session_telemetry(self): - client = CopilotClient(SubprocessConfig(cli_path=CLI_PATH)) + client = CopilotClient( + CopilotClientOptions(connection=RuntimeConnection.stdio(path=CLI_PATH)) + ) await client.start() try: @@ -639,7 +684,9 @@ async def mock_request(method, params): @pytest.mark.asyncio async def test_create_session_forwards_provider_headers(self): - client = CopilotClient(SubprocessConfig(cli_path=CLI_PATH)) + client = CopilotClient( + CopilotClientOptions(connection=RuntimeConnection.stdio(path=CLI_PATH)) + ) await client.start() try: @@ -677,7 +724,9 @@ async def mock_request(method, params): @pytest.mark.asyncio async def test_resume_session_forwards_provider_headers(self): - client = CopilotClient(SubprocessConfig(cli_path=CLI_PATH)) + client = CopilotClient( + CopilotClientOptions(connection=RuntimeConnection.stdio(path=CLI_PATH)) + ) await client.start() try: @@ -720,7 +769,9 @@ async def mock_request(method, params): @pytest.mark.asyncio async def test_session_send_forwards_request_headers(self): - client = CopilotClient(SubprocessConfig(cli_path=CLI_PATH)) + client = CopilotClient( + CopilotClientOptions(connection=RuntimeConnection.stdio(path=CLI_PATH)) + ) await client.start() try: @@ -752,7 +803,9 @@ async def mock_request(method, params): @pytest.mark.asyncio async def test_create_session_forwards_agent(self): - client = CopilotClient(SubprocessConfig(cli_path=CLI_PATH)) + client = CopilotClient( + CopilotClientOptions(connection=RuntimeConnection.stdio(path=CLI_PATH)) + ) await client.start() try: @@ -775,7 +828,9 @@ async def mock_request(method, params): @pytest.mark.asyncio async def test_resume_session_forwards_agent(self): - client = CopilotClient(SubprocessConfig(cli_path=CLI_PATH)) + client = CopilotClient( + CopilotClientOptions(connection=RuntimeConnection.stdio(path=CLI_PATH)) + ) await client.start() try: @@ -805,7 +860,9 @@ async def mock_request(method, params): @pytest.mark.asyncio async def test_create_session_defaults_include_sub_agent_streaming_events_to_true(self): - client = CopilotClient(SubprocessConfig(cli_path=CLI_PATH)) + client = CopilotClient( + CopilotClientOptions(connection=RuntimeConnection.stdio(path=CLI_PATH)) + ) await client.start() try: @@ -828,7 +885,9 @@ async def mock_request(method, params): async def test_create_session_preserves_explicit_false_include_sub_agent_streaming_events( self, ): - client = CopilotClient(SubprocessConfig(cli_path=CLI_PATH)) + client = CopilotClient( + CopilotClientOptions(connection=RuntimeConnection.stdio(path=CLI_PATH)) + ) await client.start() try: @@ -850,7 +909,9 @@ async def mock_request(method, params): @pytest.mark.asyncio async def test_resume_session_defaults_include_sub_agent_streaming_events_to_true(self): - client = CopilotClient(SubprocessConfig(cli_path=CLI_PATH)) + client = CopilotClient( + CopilotClientOptions(connection=RuntimeConnection.stdio(path=CLI_PATH)) + ) await client.start() try: @@ -880,7 +941,9 @@ async def mock_request(method, params): async def test_resume_session_preserves_explicit_false_include_sub_agent_streaming_events( self, ): - client = CopilotClient(SubprocessConfig(cli_path=CLI_PATH)) + client = CopilotClient( + CopilotClientOptions(connection=RuntimeConnection.stdio(path=CLI_PATH)) + ) await client.start() try: @@ -909,7 +972,9 @@ async def mock_request(method, params): @pytest.mark.asyncio async def test_resume_session_forwards_continue_pending_work(self): - client = CopilotClient(SubprocessConfig(cli_path=CLI_PATH)) + client = CopilotClient( + CopilotClientOptions(connection=RuntimeConnection.stdio(path=CLI_PATH)) + ) await client.start() try: @@ -938,7 +1003,9 @@ async def mock_request(method, params): @pytest.mark.asyncio async def test_resume_session_omits_continue_pending_work_by_default(self): - client = CopilotClient(SubprocessConfig(cli_path=CLI_PATH)) + client = CopilotClient( + CopilotClientOptions(connection=RuntimeConnection.stdio(path=CLI_PATH)) + ) await client.start() try: @@ -966,7 +1033,9 @@ async def mock_request(method, params): @pytest.mark.asyncio async def test_set_model_sends_correct_rpc(self): - client = CopilotClient(SubprocessConfig(cli_path=CLI_PATH)) + client = CopilotClient( + CopilotClientOptions(connection=RuntimeConnection.stdio(path=CLI_PATH)) + ) await client.start() try: @@ -994,7 +1063,9 @@ async def mock_request(method, params): class TestCopilotClientContextManager: @pytest.mark.asyncio async def test_aenter_calls_start_and_returns_self(self): - client = CopilotClient(SubprocessConfig(cli_path=CLI_PATH)) + client = CopilotClient( + CopilotClientOptions(connection=RuntimeConnection.stdio(path=CLI_PATH)) + ) with patch.object(client, "start", new_callable=AsyncMock) as mock_start: result = await client.__aenter__() mock_start.assert_awaited_once() @@ -1002,7 +1073,9 @@ async def test_aenter_calls_start_and_returns_self(self): @pytest.mark.asyncio async def test_aexit_calls_stop(self): - client = CopilotClient(SubprocessConfig(cli_path=CLI_PATH)) + client = CopilotClient( + CopilotClientOptions(connection=RuntimeConnection.stdio(path=CLI_PATH)) + ) with patch.object(client, "stop", new_callable=AsyncMock) as mock_stop: await client.__aexit__(None, None, None) mock_stop.assert_awaited_once() diff --git a/python/test_commands_and_elicitation.py b/python/test_commands_and_elicitation.py index d031d2967..a8eb18197 100644 --- a/python/test_commands_and_elicitation.py +++ b/python/test_commands_and_elicitation.py @@ -10,8 +10,7 @@ import pytest -from copilot import CopilotClient -from copilot.client import SubprocessConfig +from copilot import CopilotClient, CopilotClientOptions, RuntimeConnection from copilot.session import ( AutoModeSwitchRequest, AutoModeSwitchResponse, @@ -50,7 +49,9 @@ class TestCommands: @pytest.mark.asyncio async def test_forwards_commands_in_session_create_rpc(self): """Verifies that commands (name + description) are serialized in session.create payload.""" - client = CopilotClient(SubprocessConfig(cli_path=CLI_PATH)) + client = CopilotClient( + CopilotClientOptions(connection=RuntimeConnection.stdio(path=CLI_PATH)) + ) await client.start() try: @@ -89,7 +90,9 @@ async def mock_request(method, params): @pytest.mark.asyncio async def test_forwards_commands_in_session_resume_rpc(self): """Verifies that commands are serialized in session.resume payload.""" - client = CopilotClient(SubprocessConfig(cli_path=CLI_PATH)) + client = CopilotClient( + CopilotClientOptions(connection=RuntimeConnection.stdio(path=CLI_PATH)) + ) await client.start() try: @@ -127,7 +130,9 @@ async def mock_request(method, params): @pytest.mark.asyncio async def test_routes_command_execute_event_to_correct_handler(self): """Verifies the command dispatch works for command.execute events.""" - client = CopilotClient(SubprocessConfig(cli_path=CLI_PATH)) + client = CopilotClient( + CopilotClientOptions(connection=RuntimeConnection.stdio(path=CLI_PATH)) + ) await client.start() try: @@ -198,7 +203,9 @@ async def mock_request(method, params): @pytest.mark.asyncio async def test_sends_error_when_command_handler_throws(self): """Verifies error is sent via RPC when a command handler raises.""" - client = CopilotClient(SubprocessConfig(cli_path=CLI_PATH)) + client = CopilotClient( + CopilotClientOptions(connection=RuntimeConnection.stdio(path=CLI_PATH)) + ) await client.start() try: @@ -256,7 +263,9 @@ async def mock_request(method, params): @pytest.mark.asyncio async def test_sends_error_for_unknown_command(self): """Verifies error is sent via RPC for an unrecognized command.""" - client = CopilotClient(SubprocessConfig(cli_path=CLI_PATH)) + client = CopilotClient( + CopilotClientOptions(connection=RuntimeConnection.stdio(path=CLI_PATH)) + ) await client.start() try: @@ -317,7 +326,9 @@ class TestUiElicitation: @pytest.mark.asyncio async def test_reads_capabilities_from_session_create_response(self): """Verifies capabilities are parsed from session.create response.""" - client = CopilotClient(SubprocessConfig(cli_path=CLI_PATH)) + client = CopilotClient( + CopilotClientOptions(connection=RuntimeConnection.stdio(path=CLI_PATH)) + ) await client.start() try: @@ -341,7 +352,9 @@ async def mock_request(method, params): @pytest.mark.asyncio async def test_defaults_capabilities_when_not_injected(self): """Verifies capabilities default to empty when server returns none.""" - client = CopilotClient(SubprocessConfig(cli_path=CLI_PATH)) + client = CopilotClient( + CopilotClientOptions(connection=RuntimeConnection.stdio(path=CLI_PATH)) + ) await client.start() try: @@ -358,7 +371,9 @@ async def test_defaults_capabilities_when_not_injected(self): @pytest.mark.asyncio async def test_elicitation_throws_when_capability_is_missing(self): """Verifies that UI methods throw when elicitation is not supported.""" - client = CopilotClient(SubprocessConfig(cli_path=CLI_PATH)) + client = CopilotClient( + CopilotClientOptions(connection=RuntimeConnection.stdio(path=CLI_PATH)) + ) await client.start() try: @@ -385,7 +400,9 @@ async def test_elicitation_throws_when_capability_is_missing(self): @pytest.mark.asyncio async def test_confirm_throws_when_capability_is_missing(self): """Verifies confirm throws when elicitation is not supported.""" - client = CopilotClient(SubprocessConfig(cli_path=CLI_PATH)) + client = CopilotClient( + CopilotClientOptions(connection=RuntimeConnection.stdio(path=CLI_PATH)) + ) await client.start() try: @@ -409,7 +426,9 @@ class TestOnElicitationContext: @pytest.mark.asyncio async def test_sends_request_elicitation_flag_when_handler_provided(self): """Verifies requestElicitation=true is sent when onElicitationContext is provided.""" - client = CopilotClient(SubprocessConfig(cli_path=CLI_PATH)) + client = CopilotClient( + CopilotClientOptions(connection=RuntimeConnection.stdio(path=CLI_PATH)) + ) await client.start() try: @@ -441,7 +460,9 @@ async def elicitation_handler( @pytest.mark.asyncio async def test_does_not_send_request_elicitation_when_no_handler(self): """Verifies requestElicitation=false when no handler is provided.""" - client = CopilotClient(SubprocessConfig(cli_path=CLI_PATH)) + client = CopilotClient( + CopilotClientOptions(connection=RuntimeConnection.stdio(path=CLI_PATH)) + ) await client.start() try: @@ -469,7 +490,9 @@ async def mock_request(method, params): @pytest.mark.asyncio async def test_sends_mode_callback_flags_when_handlers_provided(self): """Verifies mode callback flags are sent when handlers are provided.""" - client = CopilotClient(SubprocessConfig(cli_path=CLI_PATH)) + client = CopilotClient( + CopilotClientOptions(connection=RuntimeConnection.stdio(path=CLI_PATH)) + ) await client.start() try: @@ -508,7 +531,9 @@ def auto_handler( @pytest.mark.asyncio async def test_sends_mode_callback_flags_on_resume_when_handlers_provided(self): """Verifies mode callback flags are sent on session.resume.""" - client = CopilotClient(SubprocessConfig(cli_path=CLI_PATH)) + client = CopilotClient( + CopilotClientOptions(connection=RuntimeConnection.stdio(path=CLI_PATH)) + ) await client.start() try: @@ -541,7 +566,9 @@ async def mock_request(method, params): @pytest.mark.asyncio async def test_dispatches_mode_callback_requests_to_registered_handlers(self): """Verifies direct mode requests are dispatched to registered handlers.""" - client = CopilotClient(SubprocessConfig(cli_path=CLI_PATH)) + client = CopilotClient( + CopilotClientOptions(connection=RuntimeConnection.stdio(path=CLI_PATH)) + ) await client.start() try: @@ -603,7 +630,9 @@ async def auto_handler( @pytest.mark.asyncio async def test_sends_cancel_when_elicitation_handler_throws(self): """Verifies auto-cancel when the elicitation handler raises.""" - client = CopilotClient(SubprocessConfig(cli_path=CLI_PATH)) + client = CopilotClient( + CopilotClientOptions(connection=RuntimeConnection.stdio(path=CLI_PATH)) + ) await client.start() try: @@ -648,7 +677,9 @@ async def mock_request(method, params): @pytest.mark.asyncio async def test_dispatches_elicitation_requested_event_to_handler(self): """Verifies that an elicitation.requested event dispatches to the handler.""" - client = CopilotClient(SubprocessConfig(cli_path=CLI_PATH)) + client = CopilotClient( + CopilotClientOptions(connection=RuntimeConnection.stdio(path=CLI_PATH)) + ) await client.start() try: @@ -709,7 +740,9 @@ async def mock_request(method, params): @pytest.mark.asyncio async def test_elicitation_handler_receives_full_schema(self): """Verifies that requestedSchema passes type, properties, and required to handler.""" - client = CopilotClient(SubprocessConfig(cli_path=CLI_PATH)) + client = CopilotClient( + CopilotClientOptions(connection=RuntimeConnection.stdio(path=CLI_PATH)) + ) await client.start() try: @@ -785,7 +818,9 @@ class TestCapabilitiesChanged: @pytest.mark.asyncio async def test_capabilities_changed_event_updates_session(self): """Verifies that a capabilities.changed event updates session capabilities.""" - client = CopilotClient(SubprocessConfig(cli_path=CLI_PATH)) + client = CopilotClient( + CopilotClientOptions(connection=RuntimeConnection.stdio(path=CLI_PATH)) + ) await client.start() try: diff --git a/python/test_telemetry.py b/python/test_telemetry.py index d10ffeb9f..81ead8cee 100644 --- a/python/test_telemetry.py +++ b/python/test_telemetry.py @@ -4,8 +4,9 @@ from unittest.mock import patch +from copilot import CopilotClientOptions, RuntimeConnection from copilot._telemetry import get_trace_context, trace_context -from copilot.client import SubprocessConfig, TelemetryConfig +from copilot.client import TelemetryConfig class TestGetTraceContext: @@ -75,11 +76,12 @@ def test_telemetry_config_type(self): def test_telemetry_config_in_subprocess_config(self): """TelemetryConfig can be used in SubprocessConfig.""" - config = SubprocessConfig( + config = CopilotClientOptions( + connection=RuntimeConnection.stdio(), telemetry={ "otlp_endpoint": "http://localhost:4318", "exporter_type": "otlp-http", - } + }, ) assert config.telemetry is not None assert config.telemetry["otlp_endpoint"] == "http://localhost:4318" diff --git a/test/scenarios/auth/byok-anthropic/python/main.py b/test/scenarios/auth/byok-anthropic/python/main.py index 3ad893ba5..12de59303 100644 --- a/test/scenarios/auth/byok-anthropic/python/main.py +++ b/test/scenarios/auth/byok-anthropic/python/main.py @@ -1,8 +1,8 @@ import asyncio import os import sys -from copilot import CopilotClient -from copilot.client import SubprocessConfig + +from copilot import CopilotClient, CopilotClientOptions ANTHROPIC_API_KEY = os.environ.get("ANTHROPIC_API_KEY") ANTHROPIC_MODEL = os.environ.get("ANTHROPIC_MODEL", "claude-sonnet-4-20250514") @@ -14,29 +14,27 @@ async def main(): - client = CopilotClient(SubprocessConfig( - cli_path=os.environ.get("COPILOT_CLI_PATH"), - )) + client = CopilotClient(CopilotClientOptions()) try: - session = await client.create_session({ - "model": ANTHROPIC_MODEL, - "provider": { - "type": "anthropic", - "base_url": ANTHROPIC_BASE_URL, - "api_key": ANTHROPIC_API_KEY, - }, - "available_tools": [], - "system_message": { - "mode": "replace", - "content": "You are a helpful assistant. Answer concisely.", - }, - }) - - response = await session.send_and_wait( - "What is the capital of France?" + session = await client.create_session( + { + "model": ANTHROPIC_MODEL, + "provider": { + "type": "anthropic", + "base_url": ANTHROPIC_BASE_URL, + "api_key": ANTHROPIC_API_KEY, + }, + "available_tools": [], + "system_message": { + "mode": "replace", + "content": "You are a helpful assistant. Answer concisely.", + }, + } ) + response = await session.send_and_wait("What is the capital of France?") + if response: print(response.data.content) diff --git a/test/scenarios/auth/byok-azure/python/main.py b/test/scenarios/auth/byok-azure/python/main.py index 1ae214261..67799bde5 100644 --- a/test/scenarios/auth/byok-azure/python/main.py +++ b/test/scenarios/auth/byok-azure/python/main.py @@ -1,8 +1,8 @@ import asyncio import os import sys -from copilot import CopilotClient -from copilot.client import SubprocessConfig + +from copilot import CopilotClient, CopilotClientOptions AZURE_OPENAI_ENDPOINT = os.environ.get("AZURE_OPENAI_ENDPOINT") AZURE_OPENAI_API_KEY = os.environ.get("AZURE_OPENAI_API_KEY") @@ -15,32 +15,30 @@ async def main(): - client = CopilotClient(SubprocessConfig( - cli_path=os.environ.get("COPILOT_CLI_PATH"), - )) + client = CopilotClient(CopilotClientOptions()) try: - session = await client.create_session({ - "model": AZURE_OPENAI_MODEL, - "provider": { - "type": "azure", - "base_url": AZURE_OPENAI_ENDPOINT, - "api_key": AZURE_OPENAI_API_KEY, - "azure": { - "api_version": AZURE_API_VERSION, + session = await client.create_session( + { + "model": AZURE_OPENAI_MODEL, + "provider": { + "type": "azure", + "base_url": AZURE_OPENAI_ENDPOINT, + "api_key": AZURE_OPENAI_API_KEY, + "azure": { + "api_version": AZURE_API_VERSION, + }, + }, + "available_tools": [], + "system_message": { + "mode": "replace", + "content": "You are a helpful assistant. Answer concisely.", }, - }, - "available_tools": [], - "system_message": { - "mode": "replace", - "content": "You are a helpful assistant. Answer concisely.", - }, - }) - - response = await session.send_and_wait( - "What is the capital of France?" + } ) + response = await session.send_and_wait("What is the capital of France?") + if response: print(response.data.content) diff --git a/test/scenarios/auth/byok-ollama/python/main.py b/test/scenarios/auth/byok-ollama/python/main.py index 78019acd7..a58061c89 100644 --- a/test/scenarios/auth/byok-ollama/python/main.py +++ b/test/scenarios/auth/byok-ollama/python/main.py @@ -1,8 +1,7 @@ import asyncio import os -import sys -from copilot import CopilotClient -from copilot.client import SubprocessConfig + +from copilot import CopilotClient, CopilotClientOptions OLLAMA_BASE_URL = os.environ.get("OLLAMA_BASE_URL", "http://localhost:11434/v1") OLLAMA_MODEL = os.environ.get("OLLAMA_MODEL", "llama3.2:3b") @@ -13,28 +12,26 @@ async def main(): - client = CopilotClient(SubprocessConfig( - cli_path=os.environ.get("COPILOT_CLI_PATH"), - )) + client = CopilotClient(CopilotClientOptions()) try: - session = await client.create_session({ - "model": OLLAMA_MODEL, - "provider": { - "type": "openai", - "base_url": OLLAMA_BASE_URL, - }, - "available_tools": [], - "system_message": { - "mode": "replace", - "content": COMPACT_SYSTEM_PROMPT, - }, - }) - - response = await session.send_and_wait( - "What is the capital of France?" + session = await client.create_session( + { + "model": OLLAMA_MODEL, + "provider": { + "type": "openai", + "base_url": OLLAMA_BASE_URL, + }, + "available_tools": [], + "system_message": { + "mode": "replace", + "content": COMPACT_SYSTEM_PROMPT, + }, + } ) + response = await session.send_and_wait("What is the capital of France?") + if response: print(response.data.content) diff --git a/test/scenarios/auth/byok-openai/python/main.py b/test/scenarios/auth/byok-openai/python/main.py index 8362963b2..7e22149f9 100644 --- a/test/scenarios/auth/byok-openai/python/main.py +++ b/test/scenarios/auth/byok-openai/python/main.py @@ -1,8 +1,8 @@ import asyncio import os import sys -from copilot import CopilotClient -from copilot.client import SubprocessConfig + +from copilot import CopilotClient, CopilotClientOptions 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") @@ -14,24 +14,22 @@ async def main(): - client = CopilotClient(SubprocessConfig( - cli_path=os.environ.get("COPILOT_CLI_PATH"), - )) + client = CopilotClient(CopilotClientOptions()) try: - session = await client.create_session({ - "model": OPENAI_MODEL, - "provider": { - "type": "openai", - "base_url": OPENAI_BASE_URL, - "api_key": OPENAI_API_KEY, - }, - }) - - response = await session.send_and_wait( - "What is the capital of France?" + session = await client.create_session( + { + "model": OPENAI_MODEL, + "provider": { + "type": "openai", + "base_url": OPENAI_BASE_URL, + "api_key": OPENAI_API_KEY, + }, + } ) + response = await session.send_and_wait("What is the capital of France?") + if response: print(response.data.content) diff --git a/test/scenarios/auth/gh-app/python/main.py b/test/scenarios/auth/gh-app/python/main.py index afba29254..eb1d1bd8f 100644 --- a/test/scenarios/auth/gh-app/python/main.py +++ b/test/scenarios/auth/gh-app/python/main.py @@ -4,9 +4,7 @@ import time import urllib.request -from copilot import CopilotClient -from copilot.client import SubprocessConfig - +from copilot import CopilotClient, CopilotClientOptions DEVICE_CODE_URL = "https://github.com/login/device/code" ACCESS_TOKEN_URL = "https://github.com/login/oauth/access_token" @@ -61,7 +59,9 @@ def poll_for_access_token(client_id: str, device_code: str, interval: int) -> st if data.get("error") == "slow_down": delay_seconds = int(data.get("interval", delay_seconds + 5)) continue - raise RuntimeError(data.get("error_description") or data.get("error") or "OAuth polling failed") + raise RuntimeError( + data.get("error_description") or data.get("error") or "OAuth polling failed" + ) async def main(): @@ -79,10 +79,7 @@ async def main(): display_name = f" ({user.get('name')})" if user.get("name") else "" print(f"Authenticated as: {user.get('login')}{display_name}") - client = CopilotClient(SubprocessConfig( - github_token=token, - cli_path=os.environ.get("COPILOT_CLI_PATH"), - )) + client = CopilotClient(CopilotClientOptions(github_token=token)) try: session = await client.create_session({"model": "claude-haiku-4.5"}) 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 2684a30b8..f9340f515 100644 --- a/test/scenarios/bundling/app-backend-to-server/python/main.py +++ b/test/scenarios/bundling/app-backend-to-server/python/main.py @@ -4,9 +4,9 @@ import sys import urllib.request -from flask import Flask, request, jsonify -from copilot import CopilotClient -from copilot.client import ExternalServerConfig +from flask import Flask, jsonify, request + +from copilot import CopilotClient, CopilotClientOptions, RuntimeConnection app = Flask(__name__) @@ -14,7 +14,7 @@ async def ask_copilot(prompt: str) -> str: - client = CopilotClient(ExternalServerConfig(url=CLI_URL)) + client = CopilotClient(CopilotClientOptions(connection=RuntimeConnection.uri(CLI_URL))) try: session = await client.create_session({"model": "claude-haiku-4.5"}) @@ -70,6 +70,7 @@ def self_test(port: int): ) server_thread.start() import time + time.sleep(1) self_test(port) else: diff --git a/test/scenarios/bundling/app-direct-server/python/main.py b/test/scenarios/bundling/app-direct-server/python/main.py index b441bec51..89dc42d50 100644 --- a/test/scenarios/bundling/app-direct-server/python/main.py +++ b/test/scenarios/bundling/app-direct-server/python/main.py @@ -1,20 +1,22 @@ import asyncio import os -from copilot import CopilotClient -from copilot.client import ExternalServerConfig + +from copilot import CopilotClient, CopilotClientOptions, RuntimeConnection async def main(): - client = CopilotClient(ExternalServerConfig( - url=os.environ.get("COPILOT_CLI_URL", "localhost:3000"), - )) + client = CopilotClient( + CopilotClientOptions( + connection=RuntimeConnection.uri( + os.environ.get("COPILOT_CLI_URL", "localhost:3000"), + ), + ) + ) try: session = await client.create_session({"model": "claude-haiku-4.5"}) - response = await session.send_and_wait( - "What is the capital of France?" - ) + response = await session.send_and_wait("What is the capital of France?") if response: print(response.data.content) diff --git a/test/scenarios/bundling/container-proxy/proxy.py b/test/scenarios/bundling/container-proxy/proxy.py index afe999a4c..688b9f8c1 100644 --- a/test/scenarios/bundling/container-proxy/proxy.py +++ b/test/scenarios/bundling/container-proxy/proxy.py @@ -12,7 +12,7 @@ import json import sys import time -from http.server import HTTPServer, BaseHTTPRequestHandler +from http.server import BaseHTTPRequestHandler, HTTPServer class ProxyHandler(BaseHTTPRequestHandler): diff --git a/test/scenarios/bundling/container-proxy/python/main.py b/test/scenarios/bundling/container-proxy/python/main.py index b441bec51..89dc42d50 100644 --- a/test/scenarios/bundling/container-proxy/python/main.py +++ b/test/scenarios/bundling/container-proxy/python/main.py @@ -1,20 +1,22 @@ import asyncio import os -from copilot import CopilotClient -from copilot.client import ExternalServerConfig + +from copilot import CopilotClient, CopilotClientOptions, RuntimeConnection async def main(): - client = CopilotClient(ExternalServerConfig( - url=os.environ.get("COPILOT_CLI_URL", "localhost:3000"), - )) + client = CopilotClient( + CopilotClientOptions( + connection=RuntimeConnection.uri( + os.environ.get("COPILOT_CLI_URL", "localhost:3000"), + ), + ) + ) try: session = await client.create_session({"model": "claude-haiku-4.5"}) - response = await session.send_and_wait( - "What is the capital of France?" - ) + response = await session.send_and_wait("What is the capital of France?") if response: print(response.data.content) diff --git a/test/scenarios/bundling/fully-bundled/python/main.py b/test/scenarios/bundling/fully-bundled/python/main.py index 39ce2bb81..65ad94597 100644 --- a/test/scenarios/bundling/fully-bundled/python/main.py +++ b/test/scenarios/bundling/fully-bundled/python/main.py @@ -1,21 +1,20 @@ import asyncio import os -from copilot import CopilotClient -from copilot.client import SubprocessConfig + +from copilot import CopilotClient, CopilotClientOptions async def main(): - client = CopilotClient(SubprocessConfig( - github_token=os.environ.get("GITHUB_TOKEN"), - cli_path=os.environ.get("COPILOT_CLI_PATH"), - )) + client = CopilotClient( + CopilotClientOptions( + github_token=os.environ.get("GITHUB_TOKEN"), + ) + ) try: session = await client.create_session({"model": "claude-haiku-4.5"}) - response = await session.send_and_wait( - "What is the capital of France?" - ) + response = await session.send_and_wait("What is the capital of France?") if response: print(response.data.content) diff --git a/test/scenarios/callbacks/hooks/python/main.py b/test/scenarios/callbacks/hooks/python/main.py index f7eaf9603..975c75b4f 100644 --- a/test/scenarios/callbacks/hooks/python/main.py +++ b/test/scenarios/callbacks/hooks/python/main.py @@ -1,9 +1,8 @@ import asyncio import os -from copilot import CopilotClient -from copilot.client import SubprocessConfig -from copilot.generated.rpc import PermissionDecisionApproveOnce +from copilot import CopilotClient, CopilotClientOptions +from copilot.generated.rpc import PermissionDecisionApproveOnce hook_log: list[str] = [] @@ -42,10 +41,11 @@ async def on_error_occurred(input_data, invocation): async def main(): - client = CopilotClient(SubprocessConfig( - github_token=os.environ.get("GITHUB_TOKEN"), - cli_path=os.environ.get("COPILOT_CLI_PATH"), - )) + client = CopilotClient( + CopilotClientOptions( + github_token=os.environ.get("GITHUB_TOKEN"), + ) + ) try: session = await client.create_session( diff --git a/test/scenarios/callbacks/permissions/python/main.py b/test/scenarios/callbacks/permissions/python/main.py index 5455548ba..d8d28cf37 100644 --- a/test/scenarios/callbacks/permissions/python/main.py +++ b/test/scenarios/callbacks/permissions/python/main.py @@ -1,7 +1,7 @@ import asyncio import os -from copilot import CopilotClient -from copilot.client import SubprocessConfig + +from copilot import CopilotClient, CopilotClientOptions from copilot.generated.rpc import PermissionDecisionApproveOnce # Track which tools requested permission @@ -18,10 +18,11 @@ async def auto_approve_tool(input_data, invocation): async def main(): - client = CopilotClient(SubprocessConfig( - github_token=os.environ.get("GITHUB_TOKEN"), - cli_path=os.environ.get("COPILOT_CLI_PATH"), - )) + client = CopilotClient( + CopilotClientOptions( + github_token=os.environ.get("GITHUB_TOKEN"), + ) + ) try: session = await client.create_session( diff --git a/test/scenarios/callbacks/user-input/python/main.py b/test/scenarios/callbacks/user-input/python/main.py index 9e58d6a80..bdb65ef1a 100644 --- a/test/scenarios/callbacks/user-input/python/main.py +++ b/test/scenarios/callbacks/user-input/python/main.py @@ -1,9 +1,8 @@ import asyncio import os -from copilot import CopilotClient -from copilot.client import SubprocessConfig -from copilot.generated.rpc import PermissionDecisionApproveOnce +from copilot import CopilotClient, CopilotClientOptions +from copilot.generated.rpc import PermissionDecisionApproveOnce input_log: list[str] = [] @@ -22,10 +21,11 @@ async def handle_user_input(request, invocation): async def main(): - client = CopilotClient(SubprocessConfig( - github_token=os.environ.get("GITHUB_TOKEN"), - cli_path=os.environ.get("COPILOT_CLI_PATH"), - )) + client = CopilotClient( + CopilotClientOptions( + github_token=os.environ.get("GITHUB_TOKEN"), + ) + ) try: session = await client.create_session( diff --git a/test/scenarios/modes/default/python/main.py b/test/scenarios/modes/default/python/main.py index ece50a662..8d3862e5b 100644 --- a/test/scenarios/modes/default/python/main.py +++ b/test/scenarios/modes/default/python/main.py @@ -1,21 +1,26 @@ import asyncio import os -from copilot import CopilotClient -from copilot.client import SubprocessConfig + +from copilot import CopilotClient, CopilotClientOptions async def main(): - client = CopilotClient(SubprocessConfig( - github_token=os.environ.get("GITHUB_TOKEN"), - cli_path=os.environ.get("COPILOT_CLI_PATH"), - )) + client = CopilotClient( + CopilotClientOptions( + github_token=os.environ.get("GITHUB_TOKEN"), + ) + ) try: - session = await client.create_session({ - "model": "claude-haiku-4.5", - }) - - response = await session.send_and_wait("Use the grep tool to search for the word 'SDK' in README.md and show the matching lines.") + session = await client.create_session( + { + "model": "claude-haiku-4.5", + } + ) + + response = await session.send_and_wait( + "Use the grep tool to search for the word 'SDK' in README.md and show the matching lines." + ) if response: print(f"Response: {response.data.content}") diff --git a/test/scenarios/modes/minimal/python/main.py b/test/scenarios/modes/minimal/python/main.py index 722c1e5e1..1cdb016fe 100644 --- a/test/scenarios/modes/minimal/python/main.py +++ b/test/scenarios/modes/minimal/python/main.py @@ -1,26 +1,31 @@ import asyncio import os -from copilot import CopilotClient -from copilot.client import SubprocessConfig + +from copilot import CopilotClient, CopilotClientOptions async def main(): - client = CopilotClient(SubprocessConfig( - github_token=os.environ.get("GITHUB_TOKEN"), - cli_path=os.environ.get("COPILOT_CLI_PATH"), - )) + client = CopilotClient( + CopilotClientOptions( + github_token=os.environ.get("GITHUB_TOKEN"), + ) + ) try: - session = await client.create_session({ - "model": "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("Use the grep tool to search for 'SDK' in README.md.") + session = await client.create_session( + { + "model": "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( + "Use the grep tool to search for 'SDK' in README.md." + ) if response: print(f"Response: {response.data.content}") diff --git a/test/scenarios/prompts/attachments/python/main.py b/test/scenarios/prompts/attachments/python/main.py index fdf259c6a..62dcd152a 100644 --- a/test/scenarios/prompts/attachments/python/main.py +++ b/test/scenarios/prompts/attachments/python/main.py @@ -1,16 +1,17 @@ import asyncio import os -from copilot import CopilotClient -from copilot.client import SubprocessConfig + +from copilot import CopilotClient, CopilotClientOptions SYSTEM_PROMPT = """You are a helpful assistant. Answer questions about attached files concisely.""" async def main(): - client = CopilotClient(SubprocessConfig( - github_token=os.environ.get("GITHUB_TOKEN"), - cli_path=os.environ.get("COPILOT_CLI_PATH"), - )) + client = CopilotClient( + CopilotClientOptions( + github_token=os.environ.get("GITHUB_TOKEN"), + ) + ) try: session = await client.create_session( diff --git a/test/scenarios/prompts/reasoning-effort/python/main.py b/test/scenarios/prompts/reasoning-effort/python/main.py index 122f44895..4920a3ced 100644 --- a/test/scenarios/prompts/reasoning-effort/python/main.py +++ b/test/scenarios/prompts/reasoning-effort/python/main.py @@ -1,30 +1,31 @@ import asyncio import os -from copilot import CopilotClient -from copilot.client import SubprocessConfig + +from copilot import CopilotClient, CopilotClientOptions async def main(): - client = CopilotClient(SubprocessConfig( - github_token=os.environ.get("GITHUB_TOKEN"), - cli_path=os.environ.get("COPILOT_CLI_PATH"), - )) + client = CopilotClient( + CopilotClientOptions( + github_token=os.environ.get("GITHUB_TOKEN"), + ) + ) try: - session = await client.create_session({ - "model": "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( - "What is the capital of France?" + session = await client.create_session( + { + "model": "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("What is the capital of France?") + if response: print("Reasoning effort: low") print(f"Response: {response.data.content}") diff --git a/test/scenarios/prompts/system-message/python/main.py b/test/scenarios/prompts/system-message/python/main.py index b77c1e4a1..1fd648ee1 100644 --- a/test/scenarios/prompts/system-message/python/main.py +++ b/test/scenarios/prompts/system-message/python/main.py @@ -1,16 +1,17 @@ import asyncio import os -from copilot import CopilotClient -from copilot.client import SubprocessConfig + +from copilot import CopilotClient, CopilotClientOptions PIRATE_PROMPT = """You are a pirate. Always respond in pirate speak. Say 'Arrr!' in every response. Use nautical terms and pirate slang throughout.""" async def main(): - client = CopilotClient(SubprocessConfig( - github_token=os.environ.get("GITHUB_TOKEN"), - cli_path=os.environ.get("COPILOT_CLI_PATH"), - )) + client = CopilotClient( + CopilotClientOptions( + github_token=os.environ.get("GITHUB_TOKEN"), + ) + ) try: session = await client.create_session( @@ -21,9 +22,7 @@ async def main(): } ) - response = await session.send_and_wait( - "What is the capital of France?" - ) + response = await session.send_and_wait("What is the capital of France?") if response: print(response.data.content) diff --git a/test/scenarios/sessions/concurrent-sessions/python/main.py b/test/scenarios/sessions/concurrent-sessions/python/main.py index a32dc5e10..3b93608f4 100644 --- a/test/scenarios/sessions/concurrent-sessions/python/main.py +++ b/test/scenarios/sessions/concurrent-sessions/python/main.py @@ -1,17 +1,18 @@ import asyncio import os -from copilot import CopilotClient -from copilot.client import SubprocessConfig + +from copilot import CopilotClient, CopilotClientOptions PIRATE_PROMPT = "You are a pirate. Always say Arrr!" ROBOT_PROMPT = "You are a robot. Always say BEEP BOOP!" async def main(): - client = CopilotClient(SubprocessConfig( - github_token=os.environ.get("GITHUB_TOKEN"), - cli_path=os.environ.get("COPILOT_CLI_PATH"), - )) + client = CopilotClient( + CopilotClientOptions( + github_token=os.environ.get("GITHUB_TOKEN"), + ) + ) try: session1, session2 = await asyncio.gather( @@ -32,12 +33,8 @@ async def main(): ) response1, response2 = await asyncio.gather( - session1.send_and_wait( - "What is the capital of France?" - ), - session2.send_and_wait( - "What is the capital of France?" - ), + session1.send_and_wait("What is the capital of France?"), + session2.send_and_wait("What is the capital of France?"), ) if response1: diff --git a/test/scenarios/sessions/infinite-sessions/python/main.py b/test/scenarios/sessions/infinite-sessions/python/main.py index 724dc155d..0a4724529 100644 --- a/test/scenarios/sessions/infinite-sessions/python/main.py +++ b/test/scenarios/sessions/infinite-sessions/python/main.py @@ -1,29 +1,32 @@ import asyncio import os -from copilot import CopilotClient -from copilot.client import SubprocessConfig + +from copilot import CopilotClient, CopilotClientOptions async def main(): - client = CopilotClient(SubprocessConfig( - github_token=os.environ.get("GITHUB_TOKEN"), - cli_path=os.environ.get("COPILOT_CLI_PATH"), - )) + client = CopilotClient( + CopilotClientOptions( + github_token=os.environ.get("GITHUB_TOKEN"), + ) + ) try: - session = await client.create_session({ - "model": "claude-haiku-4.5", - "available_tools": [], - "system_message": { - "mode": "replace", - "content": "You are a helpful assistant. Answer concisely in one sentence.", - }, - "infinite_sessions": { - "enabled": True, - "background_compaction_threshold": 0.80, - "buffer_exhaustion_threshold": 0.95, - }, - }) + session = await client.create_session( + { + "model": "claude-haiku-4.5", + "available_tools": [], + "system_message": { + "mode": "replace", + "content": "You are a helpful assistant. Answer concisely in one sentence.", + }, + "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 ccb9c69f0..1b3b0c5ac 100644 --- a/test/scenarios/sessions/session-resume/python/main.py +++ b/test/scenarios/sessions/session-resume/python/main.py @@ -1,14 +1,15 @@ import asyncio import os -from copilot import CopilotClient -from copilot.client import SubprocessConfig + +from copilot import CopilotClient, CopilotClientOptions async def main(): - client = CopilotClient(SubprocessConfig( - github_token=os.environ.get("GITHUB_TOKEN"), - cli_path=os.environ.get("COPILOT_CLI_PATH"), - )) + client = CopilotClient( + CopilotClientOptions( + github_token=os.environ.get("GITHUB_TOKEN"), + ) + ) try: # 1. Create a session @@ -20,9 +21,7 @@ async def main(): ) # 2. Send the secret word - await session.send_and_wait( - "Remember this: the secret word is PINEAPPLE." - ) + await session.send_and_wait("Remember this: the secret word is PINEAPPLE.") # 3. Get the session ID (don't disconnect — resume needs the session to persist) session_id = session.session_id @@ -32,9 +31,7 @@ async def main(): print("Session resumed") # 5. Ask for the secret word - response = await resumed.send_and_wait( - "What was the secret word I told you?" - ) + response = await resumed.send_and_wait("What was the secret word I told you?") if response: print(response.data.content) diff --git a/test/scenarios/sessions/streaming/python/main.py b/test/scenarios/sessions/streaming/python/main.py index e2312cd14..72248539d 100644 --- a/test/scenarios/sessions/streaming/python/main.py +++ b/test/scenarios/sessions/streaming/python/main.py @@ -1,14 +1,15 @@ import asyncio import os -from copilot import CopilotClient -from copilot.client import SubprocessConfig + +from copilot import CopilotClient, CopilotClientOptions async def main(): - client = CopilotClient(SubprocessConfig( - github_token=os.environ.get("GITHUB_TOKEN"), - cli_path=os.environ.get("COPILOT_CLI_PATH"), - )) + client = CopilotClient( + CopilotClientOptions( + github_token=os.environ.get("GITHUB_TOKEN"), + ) + ) try: session = await client.create_session( @@ -27,9 +28,7 @@ def on_event(event): session.on(on_event) - response = await session.send_and_wait( - "What is the capital of France?" - ) + response = await session.send_and_wait("What is the capital of France?") if response: print(response.data.content) diff --git a/test/scenarios/tools/custom-agents/python/main.py b/test/scenarios/tools/custom-agents/python/main.py index bf6e3978c..a6572bcb8 100644 --- a/test/scenarios/tools/custom-agents/python/main.py +++ b/test/scenarios/tools/custom-agents/python/main.py @@ -1,7 +1,7 @@ import asyncio import os -from copilot import CopilotClient -from copilot.client import SubprocessConfig + +from copilot import CopilotClient, CopilotClientOptions from copilot.tools import Tool @@ -10,10 +10,11 @@ async def analyze_handler(args): async def main(): - client = CopilotClient(SubprocessConfig( - github_token=os.environ.get("GITHUB_TOKEN"), - cli_path=os.environ.get("COPILOT_CLI_PATH"), - )) + client = CopilotClient( + CopilotClientOptions( + github_token=os.environ.get("GITHUB_TOKEN"), + ) + ) try: session = await client.create_session( diff --git a/test/scenarios/tools/mcp-servers/python/main.py b/test/scenarios/tools/mcp-servers/python/main.py index 2fa81b82d..e7d790289 100644 --- a/test/scenarios/tools/mcp-servers/python/main.py +++ b/test/scenarios/tools/mcp-servers/python/main.py @@ -1,14 +1,15 @@ import asyncio import os -from copilot import CopilotClient -from copilot.client import SubprocessConfig + +from copilot import CopilotClient, CopilotClientOptions async def main(): - client = CopilotClient(SubprocessConfig( - github_token=os.environ.get("GITHUB_TOKEN"), - cli_path=os.environ.get("COPILOT_CLI_PATH"), - )) + client = CopilotClient( + CopilotClientOptions( + github_token=os.environ.get("GITHUB_TOKEN"), + ) + ) try: # MCP server config — demonstrates the configuration pattern. @@ -16,7 +17,11 @@ async def main(): # Otherwise, runs without MCP tools as a build/integration test. mcp_servers = {} if os.environ.get("MCP_SERVER_CMD"): - args = os.environ.get("MCP_SERVER_ARGS", "").split() if os.environ.get("MCP_SERVER_ARGS") else [] + args = ( + os.environ.get("MCP_SERVER_ARGS", "").split() + if os.environ.get("MCP_SERVER_ARGS") + else [] + ) mcp_servers["example"] = { "type": "stdio", "command": os.environ["MCP_SERVER_CMD"], @@ -36,9 +41,7 @@ async def main(): session = await client.create_session(session_config) - response = await session.send_and_wait( - "What is the capital of France?" - ) + response = await session.send_and_wait("What is the capital of France?") if response: print(response.data.content) diff --git a/test/scenarios/tools/no-tools/python/main.py b/test/scenarios/tools/no-tools/python/main.py index c3eeb6a17..853486222 100644 --- a/test/scenarios/tools/no-tools/python/main.py +++ b/test/scenarios/tools/no-tools/python/main.py @@ -1,7 +1,7 @@ import asyncio import os -from copilot import CopilotClient -from copilot.client import SubprocessConfig + +from copilot import CopilotClient, CopilotClientOptions SYSTEM_PROMPT = """You are a minimal assistant with no tools available. You cannot execute code, read files, edit files, search, or perform any actions. @@ -10,10 +10,11 @@ async def main(): - client = CopilotClient(SubprocessConfig( - github_token=os.environ.get("GITHUB_TOKEN"), - cli_path=os.environ.get("COPILOT_CLI_PATH"), - )) + client = CopilotClient( + CopilotClientOptions( + github_token=os.environ.get("GITHUB_TOKEN"), + ) + ) try: session = await client.create_session( @@ -24,9 +25,7 @@ async def main(): } ) - response = await session.send_and_wait( - "Use the bash tool to run 'echo hello'." - ) + response = await session.send_and_wait("Use the bash tool to run 'echo hello'.") if response: print(response.data.content) diff --git a/test/scenarios/tools/skills/python/main.py b/test/scenarios/tools/skills/python/main.py index e7a78b8e1..0c76c36af 100644 --- a/test/scenarios/tools/skills/python/main.py +++ b/test/scenarios/tools/skills/python/main.py @@ -2,16 +2,16 @@ import os from pathlib import Path -from copilot import CopilotClient -from copilot.client import SubprocessConfig +from copilot import CopilotClient, CopilotClientOptions from copilot.generated.rpc import PermissionDecisionApproveOnce async def main(): - client = CopilotClient(SubprocessConfig( - github_token=os.environ.get("GITHUB_TOKEN"), - cli_path=os.environ.get("COPILOT_CLI_PATH"), - )) + client = CopilotClient( + CopilotClientOptions( + github_token=os.environ.get("GITHUB_TOKEN"), + ) + ) try: skills_dir = str(Path(__file__).resolve().parent.parent / "sample-skills") diff --git a/test/scenarios/tools/tool-filtering/python/main.py b/test/scenarios/tools/tool-filtering/python/main.py index 9da4ca571..296866e07 100644 --- a/test/scenarios/tools/tool-filtering/python/main.py +++ b/test/scenarios/tools/tool-filtering/python/main.py @@ -1,16 +1,17 @@ import asyncio import os -from copilot import CopilotClient -from copilot.client import SubprocessConfig + +from copilot import CopilotClient, CopilotClientOptions 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.""" async def main(): - client = CopilotClient(SubprocessConfig( - github_token=os.environ.get("GITHUB_TOKEN"), - cli_path=os.environ.get("COPILOT_CLI_PATH"), - )) + client = CopilotClient( + CopilotClientOptions( + github_token=os.environ.get("GITHUB_TOKEN"), + ) + ) try: session = await client.create_session( diff --git a/test/scenarios/tools/tool-overrides/python/main.py b/test/scenarios/tools/tool-overrides/python/main.py index 687933973..b96cbd0db 100644 --- a/test/scenarios/tools/tool-overrides/python/main.py +++ b/test/scenarios/tools/tool-overrides/python/main.py @@ -3,8 +3,7 @@ from pydantic import BaseModel, Field -from copilot import CopilotClient, define_tool -from copilot.client import SubprocessConfig +from copilot import CopilotClient, CopilotClientOptions, define_tool from copilot.session import PermissionHandler @@ -12,25 +11,30 @@ class GrepParams(BaseModel): query: str = Field(description="Search query") -@define_tool("grep", description="A custom grep implementation that overrides the built-in", overrides_built_in_tool=True) +@define_tool( + "grep", + description="A custom grep implementation that overrides the built-in", + overrides_built_in_tool=True, +) def custom_grep(params: GrepParams) -> str: return f"CUSTOM_GREP_RESULT: {params.query}" async def main(): - client = CopilotClient(SubprocessConfig( - github_token=os.environ.get("GITHUB_TOKEN"), - cli_path=os.environ.get("COPILOT_CLI_PATH"), - )) + client = CopilotClient( + CopilotClientOptions( + github_token=os.environ.get("GITHUB_TOKEN"), + ) + ) try: session = await client.create_session( - on_permission_request=PermissionHandler.approve_all, model="claude-haiku-4.5", tools=[custom_grep] + on_permission_request=PermissionHandler.approve_all, + model="claude-haiku-4.5", + tools=[custom_grep], ) - response = await session.send_and_wait( - "Use grep to search for the word 'hello'" - ) + response = await session.send_and_wait("Use grep to search for the word 'hello'") if response: print(response.data.content) diff --git a/test/scenarios/tools/virtual-filesystem/python/main.py b/test/scenarios/tools/virtual-filesystem/python/main.py index 21a92698a..101d072f3 100644 --- a/test/scenarios/tools/virtual-filesystem/python/main.py +++ b/test/scenarios/tools/virtual-filesystem/python/main.py @@ -1,10 +1,11 @@ import asyncio import os -from copilot import CopilotClient, define_tool -from copilot.client import SubprocessConfig -from copilot.generated.rpc import PermissionDecisionApproveOnce + from pydantic import BaseModel, Field +from copilot import CopilotClient, CopilotClientOptions, define_tool +from copilot.generated.rpc import PermissionDecisionApproveOnce + # In-memory virtual filesystem virtual_fs: dict[str, str] = {} @@ -48,10 +49,11 @@ async def auto_approve_tool(input_data, invocation): async def main(): - client = CopilotClient(SubprocessConfig( - github_token=os.environ.get("GITHUB_TOKEN"), - cli_path=os.environ.get("COPILOT_CLI_PATH"), - )) + client = CopilotClient( + CopilotClientOptions( + github_token=os.environ.get("GITHUB_TOKEN"), + ) + ) try: session = await client.create_session( diff --git a/test/scenarios/transport/reconnect/python/main.py b/test/scenarios/transport/reconnect/python/main.py index d1d4505a8..310326b70 100644 --- a/test/scenarios/transport/reconnect/python/main.py +++ b/test/scenarios/transport/reconnect/python/main.py @@ -1,23 +1,25 @@ import asyncio import os import sys -from copilot import CopilotClient -from copilot.client import ExternalServerConfig + +from copilot import CopilotClient, CopilotClientOptions, RuntimeConnection async def main(): - client = CopilotClient(ExternalServerConfig( - url=os.environ.get("COPILOT_CLI_URL", "localhost:3000"), - )) + client = CopilotClient( + CopilotClientOptions( + connection=RuntimeConnection.uri( + os.environ.get("COPILOT_CLI_URL", "localhost:3000"), + ), + ) + ) try: # First session print("--- Session 1 ---") session1 = await client.create_session({"model": "claude-haiku-4.5"}) - response1 = await session1.send_and_wait( - "What is the capital of France?" - ) + response1 = await session1.send_and_wait("What is the capital of France?") if response1 and response1.data.content: print(response1.data.content) @@ -32,9 +34,7 @@ async def main(): print("--- Session 2 ---") session2 = await client.create_session({"model": "claude-haiku-4.5"}) - response2 = await session2.send_and_wait( - "What is the capital of France?" - ) + response2 = await session2.send_and_wait("What is the capital of France?") if response2 and response2.data.content: print(response2.data.content) diff --git a/test/scenarios/transport/stdio/python/main.py b/test/scenarios/transport/stdio/python/main.py index 39ce2bb81..65ad94597 100644 --- a/test/scenarios/transport/stdio/python/main.py +++ b/test/scenarios/transport/stdio/python/main.py @@ -1,21 +1,20 @@ import asyncio import os -from copilot import CopilotClient -from copilot.client import SubprocessConfig + +from copilot import CopilotClient, CopilotClientOptions async def main(): - client = CopilotClient(SubprocessConfig( - github_token=os.environ.get("GITHUB_TOKEN"), - cli_path=os.environ.get("COPILOT_CLI_PATH"), - )) + client = CopilotClient( + CopilotClientOptions( + github_token=os.environ.get("GITHUB_TOKEN"), + ) + ) try: session = await client.create_session({"model": "claude-haiku-4.5"}) - response = await session.send_and_wait( - "What is the capital of France?" - ) + response = await session.send_and_wait("What is the capital of France?") if response: print(response.data.content) diff --git a/test/scenarios/transport/tcp/python/main.py b/test/scenarios/transport/tcp/python/main.py index b441bec51..89dc42d50 100644 --- a/test/scenarios/transport/tcp/python/main.py +++ b/test/scenarios/transport/tcp/python/main.py @@ -1,20 +1,22 @@ import asyncio import os -from copilot import CopilotClient -from copilot.client import ExternalServerConfig + +from copilot import CopilotClient, CopilotClientOptions, RuntimeConnection async def main(): - client = CopilotClient(ExternalServerConfig( - url=os.environ.get("COPILOT_CLI_URL", "localhost:3000"), - )) + client = CopilotClient( + CopilotClientOptions( + connection=RuntimeConnection.uri( + os.environ.get("COPILOT_CLI_URL", "localhost:3000"), + ), + ) + ) try: session = await client.create_session({"model": "claude-haiku-4.5"}) - response = await session.send_and_wait( - "What is the capital of France?" - ) + response = await session.send_and_wait("What is the capital of France?") if response: print(response.data.content) From f037503a1aded722dedeada17902e5093fee42c6 Mon Sep 17 00:00:00 2001 From: Steve Sanderson Date: Fri, 22 May 2026 11:59:07 +0100 Subject: [PATCH 04/21] Phase B fixup: re-export TelemetryConfig from copilot package Test collection broke on CI because e2e/test_telemetry_e2e.py imports it from the top-level package; only the dataclass option types had been re-exported. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- python/copilot/__init__.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/python/copilot/__init__.py b/python/copilot/__init__.py index 6ca58b3c3..30385a802 100644 --- a/python/copilot/__init__.py +++ b/python/copilot/__init__.py @@ -18,6 +18,7 @@ RuntimeConnection, StdioRuntimeConnection, TcpRuntimeConnection, + TelemetryConfig, UriRuntimeConnection, ) from .session import ( @@ -101,6 +102,7 @@ "SessionUiCapabilities", "StdioRuntimeConnection", "TcpRuntimeConnection", + "TelemetryConfig", "Tool", "ToolBinaryResult", "ToolInvocation", From 156029e7b271d68fc0305c48362834d2ee3d5073 Mon Sep 17 00:00:00 2001 From: Steve Sanderson Date: Fri, 22 May 2026 12:15:21 +0100 Subject: [PATCH 05/21] Phase B fixup: regenerate codegen, fix scenario create_session calls MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two issues surfaced by Phase B CI: 1. The committed Python generated files were stale relative to the rest of the cross-language generators. Re-running `npm run generate` syncs them. No semantic change beyond round-tripping each variant dataclass through `from_dict(obj: Any) -> "Name":` quoting (matches what codegen has produced for a while; the on-disk file just hadn't been resynced). 2. `test/scenarios/**/python/main.py` were calling `client.create_session({...})` and `client.resume_session(session_id, {...})` with positional dicts. `CopilotClient.create_session` and `CopilotClient.resume_session` are kwargs-only — these always TypeError'd at runtime. Converted all 18 scenarios to explicit kwargs (`create_session(model="...", ...)`). Caught by github-code-quality CodeQL on PR #1376. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- python/copilot/generated/rpc.py | 1016 ++++++++--------- python/copilot/generated/session_events.py | 368 +++--- rust/src/generated/api_types.rs | 3 +- .../auth/byok-anthropic/python/main.py | 24 +- test/scenarios/auth/byok-azure/python/main.py | 28 +- .../scenarios/auth/byok-ollama/python/main.py | 22 +- .../scenarios/auth/byok-openai/python/main.py | 14 +- test/scenarios/auth/gh-app/python/main.py | 2 +- .../app-backend-to-server/python/main.py | 2 +- .../bundling/app-direct-server/python/main.py | 2 +- .../bundling/container-proxy/python/main.py | 2 +- .../bundling/fully-bundled/python/main.py | 2 +- test/scenarios/callbacks/hooks/python/main.py | 22 +- .../callbacks/permissions/python/main.py | 8 +- .../callbacks/user-input/python/main.py | 10 +- test/scenarios/modes/default/python/main.py | 6 +- test/scenarios/modes/minimal/python/main.py | 14 +- .../prompts/attachments/python/main.py | 8 +- .../prompts/reasoning-effort/python/main.py | 16 +- .../prompts/system-message/python/main.py | 8 +- .../concurrent-sessions/python/main.py | 16 +- .../sessions/infinite-sessions/python/main.py | 24 +- .../sessions/session-resume/python/main.py | 7 +- .../sessions/streaming/python/main.py | 7 +- test/scenarios/tools/no-tools/python/main.py | 8 +- .../tools/tool-filtering/python/main.py | 8 +- .../transport/reconnect/python/main.py | 4 +- test/scenarios/transport/stdio/python/main.py | 2 +- test/scenarios/transport/tcp/python/main.py | 2 +- 29 files changed, 798 insertions(+), 857 deletions(-) diff --git a/python/copilot/generated/rpc.py b/python/copilot/generated/rpc.py index 31c10f66b..929aa79e6 100644 --- a/python/copilot/generated/rpc.py +++ b/python/copilot/generated/rpc.py @@ -4,23 +4,9 @@ """ from __future__ import annotations -from typing import TYPE_CHECKING, ClassVar - -from .session_events import ( - AbortReason, - EmbeddedBlobResourceContents, - EmbeddedTextResourceContents, - McpServerSource, - McpServerStatus, - PermissionPromptRequest, - PermissionRule, - ReasoningSummary, - SessionEvent, - SessionMode, - ShutdownType, - SkillSource, - UserToolSessionApproval, -) +from typing import ClassVar, TYPE_CHECKING + +from .session_events import AbortReason, EmbeddedBlobResourceContents, EmbeddedTextResourceContents, McpServerSource, McpServerStatus, PermissionPromptRequest, PermissionRule, ReasoningSummary, SessionEvent, SessionMode, ShutdownType, SkillSource, UserToolSessionApproval if TYPE_CHECKING: from .._jsonrpc import JsonRpcClient @@ -98,7 +84,7 @@ class AbortRequest: """Finite reason code describing why the current turn was aborted""" @staticmethod - def from_dict(obj: Any) -> AbortRequest: + def from_dict(obj: Any) -> 'AbortRequest': assert isinstance(obj, dict) reason = from_union([AbortReason, from_none], obj.get("reason")) return AbortRequest(reason) @@ -121,7 +107,7 @@ class AbortResult: """Error message if the abort failed""" @staticmethod - def from_dict(obj: Any) -> AbortResult: + def from_dict(obj: Any) -> 'AbortResult': assert isinstance(obj, dict) success = from_bool(obj.get("success")) error = from_union([from_str, from_none], obj.get("error")) @@ -142,7 +128,7 @@ class AccountGetQuotaRequest: """ @staticmethod - def from_dict(obj: Any) -> AccountGetQuotaRequest: + def from_dict(obj: Any) -> 'AccountGetQuotaRequest': assert isinstance(obj, dict) git_hub_token = from_union([from_str, from_none], obj.get("gitHubToken")) return AccountGetQuotaRequest(git_hub_token) @@ -182,7 +168,7 @@ class AccountQuotaSnapshot: """Date when the quota resets (ISO 8601 string)""" @staticmethod - def from_dict(obj: Any) -> AccountQuotaSnapshot: + def from_dict(obj: Any) -> 'AccountQuotaSnapshot': assert isinstance(obj, dict) entitlement_requests = from_int(obj.get("entitlementRequests")) is_unlimited_entitlement = from_bool(obj.get("isUnlimitedEntitlement")) @@ -227,7 +213,7 @@ class AgentSelectRequest: """Name of the custom agent to select""" @staticmethod - def from_dict(obj: Any) -> AgentSelectRequest: + def from_dict(obj: Any) -> 'AgentSelectRequest': assert isinstance(obj, dict) name = from_str(obj.get("name")) return AgentSelectRequest(name) @@ -248,7 +234,7 @@ class CopilotUserResponseEndpoints: telemetry: str | None = None @staticmethod - def from_dict(obj: Any) -> CopilotUserResponseEndpoints: + def from_dict(obj: Any) -> 'CopilotUserResponseEndpoints': assert isinstance(obj, dict) api = from_union([from_str, from_none], obj.get("api")) origin_tracker = from_union([from_str, from_none], obj.get("origin-tracker")) @@ -310,7 +296,7 @@ class CommandsHandlePendingCommandRequest: """Error message if the command handler failed""" @staticmethod - def from_dict(obj: Any) -> CommandsHandlePendingCommandRequest: + def from_dict(obj: Any) -> 'CommandsHandlePendingCommandRequest': assert isinstance(obj, dict) request_id = from_str(obj.get("requestId")) error = from_union([from_str, from_none], obj.get("error")) @@ -332,7 +318,7 @@ class CommandsHandlePendingCommandResult: """Whether the command was handled successfully""" @staticmethod - def from_dict(obj: Any) -> CommandsHandlePendingCommandResult: + def from_dict(obj: Any) -> 'CommandsHandlePendingCommandResult': assert isinstance(obj, dict) success = from_bool(obj.get("success")) return CommandsHandlePendingCommandResult(success) @@ -354,7 +340,7 @@ class CommandsInvokeRequest: """Raw input after the command name""" @staticmethod - def from_dict(obj: Any) -> CommandsInvokeRequest: + def from_dict(obj: Any) -> 'CommandsInvokeRequest': assert isinstance(obj, dict) name = from_str(obj.get("name")) input = from_union([from_str, from_none], obj.get("input")) @@ -382,7 +368,7 @@ class CommandsListRequest: """Include enabled user-invocable skills and commands""" @staticmethod - def from_dict(obj: Any) -> CommandsListRequest: + def from_dict(obj: Any) -> 'CommandsListRequest': assert isinstance(obj, dict) include_builtins = from_union([from_bool, from_none], obj.get("includeBuiltins")) include_client_commands = from_union([from_bool, from_none], obj.get("includeClientCommands")) @@ -412,7 +398,7 @@ class CommandsRespondToQueuedCommandRequest: """Result of the queued command execution.""" @staticmethod - def from_dict(obj: Any) -> CommandsRespondToQueuedCommandRequest: + def from_dict(obj: Any) -> 'CommandsRespondToQueuedCommandRequest': assert isinstance(obj, dict) request_id = from_str(obj.get("requestId")) result = _load_QueuedCommandResult(obj.get("result")) @@ -435,7 +421,7 @@ class CommandsRespondToQueuedCommandResult: """ @staticmethod - def from_dict(obj: Any) -> CommandsRespondToQueuedCommandResult: + def from_dict(obj: Any) -> 'CommandsRespondToQueuedCommandResult': assert isinstance(obj, dict) success = from_bool(obj.get("success")) return CommandsRespondToQueuedCommandResult(success) @@ -454,7 +440,7 @@ class ConnectRemoteSessionParams: """Session ID to connect to.""" @staticmethod - def from_dict(obj: Any) -> ConnectRemoteSessionParams: + def from_dict(obj: Any) -> 'ConnectRemoteSessionParams': assert isinstance(obj, dict) session_id = from_str(obj.get("sessionId")) return ConnectRemoteSessionParams(session_id) @@ -473,7 +459,7 @@ class ConnectRequest: """Connection token; required when the server was started with COPILOT_CONNECTION_TOKEN""" @staticmethod - def from_dict(obj: Any) -> ConnectRequest: + def from_dict(obj: Any) -> 'ConnectRequest': assert isinstance(obj, dict) token = from_union([from_str, from_none], obj.get("token")) return ConnectRequest(token) @@ -499,7 +485,7 @@ class ConnectResult: """Server package version""" @staticmethod - def from_dict(obj: Any) -> ConnectResult: + def from_dict(obj: Any) -> 'ConnectResult': assert isinstance(obj, dict) ok = from_bool(obj.get("ok")) protocol_version = from_int(obj.get("protocolVersion")) @@ -535,7 +521,7 @@ class ConnectedRemoteSessionMetadataRepository: """Repository owner or organization login.""" @staticmethod - def from_dict(obj: Any) -> ConnectedRemoteSessionMetadataRepository: + def from_dict(obj: Any) -> 'ConnectedRemoteSessionMetadataRepository': assert isinstance(obj, dict) branch = from_str(obj.get("branch")) name = from_str(obj.get("name")) @@ -583,7 +569,7 @@ class CopilotUserResponseQuotaSnapshotsChat: unlimited: bool | None = None @staticmethod - def from_dict(obj: Any) -> CopilotUserResponseQuotaSnapshotsChat: + def from_dict(obj: Any) -> 'CopilotUserResponseQuotaSnapshotsChat': assert isinstance(obj, dict) entitlement = from_union([from_float, from_none], obj.get("entitlement")) has_quota = from_union([from_bool, from_none], obj.get("has_quota")) @@ -646,7 +632,7 @@ class CopilotUserResponseQuotaSnapshotsCompletions: unlimited: bool | None = None @staticmethod - def from_dict(obj: Any) -> CopilotUserResponseQuotaSnapshotsCompletions: + def from_dict(obj: Any) -> 'CopilotUserResponseQuotaSnapshotsCompletions': assert isinstance(obj, dict) entitlement = from_union([from_float, from_none], obj.get("entitlement")) has_quota = from_union([from_bool, from_none], obj.get("has_quota")) @@ -709,7 +695,7 @@ class CopilotUserResponseQuotaSnapshotsPremiumInteractions: unlimited: bool | None = None @staticmethod - def from_dict(obj: Any) -> CopilotUserResponseQuotaSnapshotsPremiumInteractions: + def from_dict(obj: Any) -> 'CopilotUserResponseQuotaSnapshotsPremiumInteractions': assert isinstance(obj, dict) entitlement = from_union([from_float, from_none], obj.get("entitlement")) has_quota = from_union([from_bool, from_none], obj.get("has_quota")) @@ -768,7 +754,7 @@ class CurrentModel: """ @staticmethod - def from_dict(obj: Any) -> CurrentModel: + def from_dict(obj: Any) -> 'CurrentModel': assert isinstance(obj, dict) model_id = from_union([from_str, from_none], obj.get("modelId")) reasoning_effort = from_union([from_str, from_none], obj.get("reasoningEffort")) @@ -801,7 +787,7 @@ class EnqueueCommandParams: """ @staticmethod - def from_dict(obj: Any) -> EnqueueCommandParams: + def from_dict(obj: Any) -> 'EnqueueCommandParams': assert isinstance(obj, dict) command = from_str(obj.get("command")) return EnqueueCommandParams(command) @@ -822,7 +808,7 @@ class EnqueueCommandResult: """ @staticmethod - def from_dict(obj: Any) -> EnqueueCommandResult: + def from_dict(obj: Any) -> 'EnqueueCommandResult': assert isinstance(obj, dict) queued = from_bool(obj.get("queued")) return EnqueueCommandResult(queued) @@ -858,7 +844,7 @@ class EventLogReleaseInterestResult: """Whether the operation succeeded""" @staticmethod - def from_dict(obj: Any) -> EventLogReleaseInterestResult: + def from_dict(obj: Any) -> 'EventLogReleaseInterestResult': assert isinstance(obj, dict) success = from_bool(obj.get("success")) return EventLogReleaseInterestResult(success) @@ -884,7 +870,7 @@ class EventLogTailResult: """ @staticmethod - def from_dict(obj: Any) -> EventLogTailResult: + def from_dict(obj: Any) -> 'EventLogTailResult': assert isinstance(obj, dict) cursor = from_str(obj.get("cursor")) return EventLogTailResult(cursor) @@ -915,7 +901,7 @@ class ExecuteCommandParams: """Name of the slash command to invoke (without the leading '/').""" @staticmethod - def from_dict(obj: Any) -> ExecuteCommandParams: + def from_dict(obj: Any) -> 'ExecuteCommandParams': assert isinstance(obj, dict) args = from_str(obj.get("args")) command_name = from_str(obj.get("commandName")) @@ -938,7 +924,7 @@ class ExecuteCommandResult: """ @staticmethod - def from_dict(obj: Any) -> ExecuteCommandResult: + def from_dict(obj: Any) -> 'ExecuteCommandResult': assert isinstance(obj, dict) error = from_union([from_str, from_none], obj.get("error")) return ExecuteCommandResult(error) @@ -974,7 +960,7 @@ class ExtensionsDisableRequest: """Source-qualified extension ID to disable""" @staticmethod - def from_dict(obj: Any) -> ExtensionsDisableRequest: + def from_dict(obj: Any) -> 'ExtensionsDisableRequest': assert isinstance(obj, dict) id = from_str(obj.get("id")) return ExtensionsDisableRequest(id) @@ -993,7 +979,7 @@ class ExtensionsEnableRequest: """Source-qualified extension ID to enable""" @staticmethod - def from_dict(obj: Any) -> ExtensionsEnableRequest: + def from_dict(obj: Any) -> 'ExtensionsEnableRequest': assert isinstance(obj, dict) id = from_str(obj.get("id")) return ExtensionsEnableRequest(id) @@ -1053,7 +1039,7 @@ class FleetStartRequest: """Optional user prompt to combine with fleet instructions""" @staticmethod - def from_dict(obj: Any) -> FleetStartRequest: + def from_dict(obj: Any) -> 'FleetStartRequest': assert isinstance(obj, dict) prompt = from_union([from_str, from_none], obj.get("prompt")) return FleetStartRequest(prompt) @@ -1073,7 +1059,7 @@ class FleetStartResult: """Whether fleet mode was successfully activated""" @staticmethod - def from_dict(obj: Any) -> FleetStartResult: + def from_dict(obj: Any) -> 'FleetStartResult': assert isinstance(obj, dict) started = from_bool(obj.get("started")) return FleetStartResult(started) @@ -1092,7 +1078,7 @@ class FolderTrustAddParams: """Folder path to mark as trusted""" @staticmethod - def from_dict(obj: Any) -> FolderTrustAddParams: + def from_dict(obj: Any) -> 'FolderTrustAddParams': assert isinstance(obj, dict) path = from_str(obj.get("path")) return FolderTrustAddParams(path) @@ -1111,7 +1097,7 @@ class FolderTrustCheckParams: """Folder path to check""" @staticmethod - def from_dict(obj: Any) -> FolderTrustCheckParams: + def from_dict(obj: Any) -> 'FolderTrustCheckParams': assert isinstance(obj, dict) path = from_str(obj.get("path")) return FolderTrustCheckParams(path) @@ -1130,7 +1116,7 @@ class FolderTrustCheckResult: """Whether the folder is trusted""" @staticmethod - def from_dict(obj: Any) -> FolderTrustCheckResult: + def from_dict(obj: Any) -> 'FolderTrustCheckResult': assert isinstance(obj, dict) trusted = from_bool(obj.get("trusted")) return FolderTrustCheckResult(trusted) @@ -1152,7 +1138,7 @@ class HandlePendingToolCallResult: """Whether the tool call result was handled successfully""" @staticmethod - def from_dict(obj: Any) -> HandlePendingToolCallResult: + def from_dict(obj: Any) -> 'HandlePendingToolCallResult': assert isinstance(obj, dict) success = from_bool(obj.get("success")) return HandlePendingToolCallResult(success) @@ -1173,7 +1159,7 @@ class HistoryAbortManualCompactionResult: """ @staticmethod - def from_dict(obj: Any) -> HistoryAbortManualCompactionResult: + def from_dict(obj: Any) -> 'HistoryAbortManualCompactionResult': assert isinstance(obj, dict) aborted = from_bool(obj.get("aborted")) return HistoryAbortManualCompactionResult(aborted) @@ -1194,7 +1180,7 @@ class HistoryCancelBackgroundCompactionResult: """ @staticmethod - def from_dict(obj: Any) -> HistoryCancelBackgroundCompactionResult: + def from_dict(obj: Any) -> 'HistoryCancelBackgroundCompactionResult': assert isinstance(obj, dict) cancelled = from_bool(obj.get("cancelled")) return HistoryCancelBackgroundCompactionResult(cancelled) @@ -1228,7 +1214,7 @@ class HistoryCompactContextWindow: """Token count from tool definitions""" @staticmethod - def from_dict(obj: Any) -> HistoryCompactContextWindow: + def from_dict(obj: Any) -> 'HistoryCompactContextWindow': assert isinstance(obj, dict) current_tokens = from_int(obj.get("currentTokens")) messages_length = from_int(obj.get("messagesLength")) @@ -1260,7 +1246,7 @@ class HistoryCompactRequest: """Optional user-provided instructions to focus the compaction summary""" @staticmethod - def from_dict(obj: Any) -> HistoryCompactRequest: + def from_dict(obj: Any) -> 'HistoryCompactRequest': assert isinstance(obj, dict) custom_instructions = from_union([from_str, from_none], obj.get("customInstructions")) return HistoryCompactRequest(custom_instructions) @@ -1282,7 +1268,7 @@ class HistorySummarizeForHandoffResult: """ @staticmethod - def from_dict(obj: Any) -> HistorySummarizeForHandoffResult: + def from_dict(obj: Any) -> 'HistorySummarizeForHandoffResult': assert isinstance(obj, dict) summary = from_str(obj.get("summary")) return HistorySummarizeForHandoffResult(summary) @@ -1301,7 +1287,7 @@ class HistoryTruncateRequest: """Event ID to truncate to. This event and all events after it are removed from the session.""" @staticmethod - def from_dict(obj: Any) -> HistoryTruncateRequest: + def from_dict(obj: Any) -> 'HistoryTruncateRequest': assert isinstance(obj, dict) event_id = from_str(obj.get("eventId")) return HistoryTruncateRequest(event_id) @@ -1320,7 +1306,7 @@ class HistoryTruncateResult: """Number of events that were removed""" @staticmethod - def from_dict(obj: Any) -> HistoryTruncateResult: + def from_dict(obj: Any) -> 'HistoryTruncateResult': assert isinstance(obj, dict) events_removed = from_int(obj.get("eventsRemoved")) return HistoryTruncateResult(events_removed) @@ -1386,7 +1372,7 @@ class LogResult: """The unique identifier of the emitted session event""" @staticmethod - def from_dict(obj: Any) -> LogResult: + def from_dict(obj: Any) -> 'LogResult': assert isinstance(obj, dict) event_id = UUID(obj.get("eventId")) return LogResult(event_id) @@ -1415,7 +1401,7 @@ class LspInitializeRequest: """ @staticmethod - def from_dict(obj: Any) -> LspInitializeRequest: + def from_dict(obj: Any) -> 'LspInitializeRequest': assert isinstance(obj, dict) force = from_union([from_bool, from_none], obj.get("force")) git_root = from_union([from_str, from_none], obj.get("gitRoot")) @@ -1441,7 +1427,7 @@ class MCPCancelSamplingExecutionParams: """The requestId previously passed to executeSampling that should be cancelled""" @staticmethod - def from_dict(obj: Any) -> MCPCancelSamplingExecutionParams: + def from_dict(obj: Any) -> 'MCPCancelSamplingExecutionParams': assert isinstance(obj, dict) request_id = from_str(obj.get("requestId")) return MCPCancelSamplingExecutionParams(request_id) @@ -1464,7 +1450,7 @@ class MCPCancelSamplingExecutionResult: """ @staticmethod - def from_dict(obj: Any) -> MCPCancelSamplingExecutionResult: + def from_dict(obj: Any) -> 'MCPCancelSamplingExecutionResult': assert isinstance(obj, dict) cancelled = from_bool(obj.get("cancelled")) return MCPCancelSamplingExecutionResult(cancelled) @@ -1482,7 +1468,7 @@ class MCPServerConfigHTTPAuth: """Fixed port for the OAuth redirect callback server.""" @staticmethod - def from_dict(obj: Any) -> MCPServerConfigHTTPAuth: + def from_dict(obj: Any) -> 'MCPServerConfigHTTPAuth': assert isinstance(obj, dict) redirect_port = from_union([from_int, from_none], obj.get("redirectPort")) return MCPServerConfigHTTPAuth(redirect_port) @@ -1516,7 +1502,7 @@ class MCPConfigDisableRequest: """ @staticmethod - def from_dict(obj: Any) -> MCPConfigDisableRequest: + def from_dict(obj: Any) -> 'MCPConfigDisableRequest': assert isinstance(obj, dict) names = from_list(from_str, obj.get("names")) return MCPConfigDisableRequest(names) @@ -1536,7 +1522,7 @@ class MCPConfigEnableRequest: """ @staticmethod - def from_dict(obj: Any) -> MCPConfigEnableRequest: + def from_dict(obj: Any) -> 'MCPConfigEnableRequest': assert isinstance(obj, dict) names = from_list(from_str, obj.get("names")) return MCPConfigEnableRequest(names) @@ -1554,7 +1540,7 @@ class MCPConfigRemoveRequest: """Name of the MCP server to remove""" @staticmethod - def from_dict(obj: Any) -> MCPConfigRemoveRequest: + def from_dict(obj: Any) -> 'MCPConfigRemoveRequest': assert isinstance(obj, dict) name = from_str(obj.get("name")) return MCPConfigRemoveRequest(name) @@ -1573,7 +1559,7 @@ class MCPDisableRequest: """Name of the MCP server to disable""" @staticmethod - def from_dict(obj: Any) -> MCPDisableRequest: + def from_dict(obj: Any) -> 'MCPDisableRequest': assert isinstance(obj, dict) server_name = from_str(obj.get("serverName")) return MCPDisableRequest(server_name) @@ -1591,7 +1577,7 @@ class MCPDiscoverRequest: """Working directory used as context for discovery (e.g., plugin resolution)""" @staticmethod - def from_dict(obj: Any) -> MCPDiscoverRequest: + def from_dict(obj: Any) -> 'MCPDiscoverRequest': assert isinstance(obj, dict) working_directory = from_union([from_str, from_none], obj.get("workingDirectory")) return MCPDiscoverRequest(working_directory) @@ -1611,7 +1597,7 @@ class MCPEnableRequest: """Name of the MCP server to enable""" @staticmethod - def from_dict(obj: Any) -> MCPEnableRequest: + def from_dict(obj: Any) -> 'MCPEnableRequest': assert isinstance(obj, dict) server_name = from_str(obj.get("serverName")) return MCPEnableRequest(server_name) @@ -1649,7 +1635,7 @@ class MCPOauthLoginRequest: """ @staticmethod - def from_dict(obj: Any) -> MCPOauthLoginRequest: + def from_dict(obj: Any) -> 'MCPOauthLoginRequest': assert isinstance(obj, dict) server_name = from_str(obj.get("serverName")) callback_success_message = from_union([from_str, from_none], obj.get("callbackSuccessMessage")) @@ -1683,7 +1669,7 @@ class MCPOauthLoginResult: """ @staticmethod - def from_dict(obj: Any) -> MCPOauthLoginResult: + def from_dict(obj: Any) -> 'MCPOauthLoginResult': assert isinstance(obj, dict) authorization_url = from_union([from_str, from_none], obj.get("authorizationUrl")) return MCPOauthLoginResult(authorization_url) @@ -1707,7 +1693,7 @@ class MCPRemoveGitHubResult: """ @staticmethod - def from_dict(obj: Any) -> MCPRemoveGitHubResult: + def from_dict(obj: Any) -> 'MCPRemoveGitHubResult': assert isinstance(obj, dict) removed = from_bool(obj.get("removed")) return MCPRemoveGitHubResult(removed) @@ -1745,7 +1731,7 @@ class MCPServer: """Configuration source: user, workspace, plugin, or builtin""" @staticmethod - def from_dict(obj: Any) -> MCPServer: + def from_dict(obj: Any) -> 'MCPServer': assert isinstance(obj, dict) name = from_str(obj.get("name")) status = McpServerStatus(obj.get("status")) @@ -1814,7 +1800,7 @@ class SessionContextInfo: """Sum of system, conversation and tool-definition tokens""" @staticmethod - def from_dict(obj: Any) -> SessionContextInfo: + def from_dict(obj: Any) -> 'SessionContextInfo': assert isinstance(obj, dict) buffer_tokens = from_int(obj.get("bufferTokens")) compaction_threshold = from_int(obj.get("compactionThreshold")) @@ -1853,7 +1839,7 @@ class MetadataIsProcessingResult: """ @staticmethod - def from_dict(obj: Any) -> MetadataIsProcessingResult: + def from_dict(obj: Any) -> 'MetadataIsProcessingResult': assert isinstance(obj, dict) processing = from_bool(obj.get("processing")) return MetadataIsProcessingResult(processing) @@ -1883,7 +1869,7 @@ class MetadataRecomputeContextTokensResult: """ @staticmethod - def from_dict(obj: Any) -> MetadataRecomputeContextTokensResult: + def from_dict(obj: Any) -> 'MetadataRecomputeContextTokensResult': assert isinstance(obj, dict) messages_token_count = from_int(obj.get("messagesTokenCount")) system_token_count = from_int(obj.get("systemTokenCount")) @@ -1919,7 +1905,7 @@ class MetadataRecordContextChangeResult: session's normal lifecycle (e.g., after a shell command in interactive mode). """ @staticmethod - def from_dict(obj: Any) -> MetadataRecordContextChangeResult: + def from_dict(obj: Any) -> 'MetadataRecordContextChangeResult': assert isinstance(obj, dict) return MetadataRecordContextChangeResult() @@ -1939,7 +1925,7 @@ class MetadataSetWorkingDirectoryRequest: """ @staticmethod - def from_dict(obj: Any) -> MetadataSetWorkingDirectoryRequest: + def from_dict(obj: Any) -> 'MetadataSetWorkingDirectoryRequest': assert isinstance(obj, dict) working_directory = from_str(obj.get("workingDirectory")) return MetadataSetWorkingDirectoryRequest(working_directory) @@ -1961,7 +1947,7 @@ class MetadataSetWorkingDirectoryResult: """Working directory after the update""" @staticmethod - def from_dict(obj: Any) -> MetadataSetWorkingDirectoryResult: + def from_dict(obj: Any) -> 'MetadataSetWorkingDirectoryResult': assert isinstance(obj, dict) working_directory = from_str(obj.get("workingDirectory")) return MetadataSetWorkingDirectoryResult(working_directory) @@ -1994,7 +1980,7 @@ class MetadataSnapshotRemoteMetadataRepository: """The GitHub owner (user or organization) of the target repository.""" @staticmethod - def from_dict(obj: Any) -> MetadataSnapshotRemoteMetadataRepository: + def from_dict(obj: Any) -> 'MetadataSnapshotRemoteMetadataRepository': assert isinstance(obj, dict) branch = from_str(obj.get("branch")) name = from_str(obj.get("name")) @@ -2025,7 +2011,7 @@ class ModeSetRequest: """The session mode the agent is operating in""" @staticmethod - def from_dict(obj: Any) -> ModeSetRequest: + def from_dict(obj: Any) -> 'ModeSetRequest': assert isinstance(obj, dict) mode = SessionMode(obj.get("mode")) return ModeSetRequest(mode) @@ -2056,7 +2042,7 @@ class ModelBillingTokenPrices: """ @staticmethod - def from_dict(obj: Any) -> ModelBillingTokenPrices: + def from_dict(obj: Any) -> 'ModelBillingTokenPrices': assert isinstance(obj, dict) batch_size = from_union([from_int, from_none], obj.get("batchSize")) cache_price = from_union([from_int, from_none], obj.get("cachePrice")) @@ -2090,7 +2076,7 @@ class ModelCapabilitiesLimitsVision: """MIME types the model accepts""" @staticmethod - def from_dict(obj: Any) -> ModelCapabilitiesLimitsVision: + def from_dict(obj: Any) -> 'ModelCapabilitiesLimitsVision': assert isinstance(obj, dict) max_prompt_image_size = from_int(obj.get("max_prompt_image_size")) max_prompt_images = from_int(obj.get("max_prompt_images")) @@ -2115,7 +2101,7 @@ class ModelCapabilitiesSupports: """Whether this model supports vision/image input""" @staticmethod - def from_dict(obj: Any) -> ModelCapabilitiesSupports: + def from_dict(obj: Any) -> 'ModelCapabilitiesSupports': assert isinstance(obj, dict) reasoning_effort = from_union([from_bool, from_none], obj.get("reasoningEffort")) vision = from_union([from_bool, from_none], obj.get("vision")) @@ -2159,7 +2145,7 @@ class ModelCapabilitiesOverrideLimitsVision: """MIME types the model accepts""" @staticmethod - def from_dict(obj: Any) -> ModelCapabilitiesOverrideLimitsVision: + def from_dict(obj: Any) -> 'ModelCapabilitiesOverrideLimitsVision': assert isinstance(obj, dict) max_prompt_image_size = from_union([from_int, from_none], obj.get("max_prompt_image_size")) max_prompt_images = from_union([from_int, from_none], obj.get("max_prompt_images")) @@ -2188,7 +2174,7 @@ class ModelCapabilitiesOverrideSupports: """Whether this model supports vision/image input""" @staticmethod - def from_dict(obj: Any) -> ModelCapabilitiesOverrideSupports: + def from_dict(obj: Any) -> 'ModelCapabilitiesOverrideSupports': assert isinstance(obj, dict) reasoning_effort = from_union([from_bool, from_none], obj.get("reasoningEffort")) vision = from_union([from_bool, from_none], obj.get("vision")) @@ -2213,7 +2199,7 @@ class ModelSetReasoningEffortRequest: """ @staticmethod - def from_dict(obj: Any) -> ModelSetReasoningEffortRequest: + def from_dict(obj: Any) -> 'ModelSetReasoningEffortRequest': assert isinstance(obj, dict) reasoning_effort = from_str(obj.get("reasoningEffort")) return ModelSetReasoningEffortRequest(reasoning_effort) @@ -2234,7 +2220,7 @@ class ModelSetReasoningEffortResult: """Reasoning effort level recorded on the session after the update""" @staticmethod - def from_dict(obj: Any) -> ModelSetReasoningEffortResult: + def from_dict(obj: Any) -> 'ModelSetReasoningEffortResult': assert isinstance(obj, dict) reasoning_effort = from_str(obj.get("reasoningEffort")) return ModelSetReasoningEffortResult(reasoning_effort) @@ -2253,7 +2239,7 @@ class ModelSwitchToResult: """Currently active model identifier after the switch""" @staticmethod - def from_dict(obj: Any) -> ModelSwitchToResult: + def from_dict(obj: Any) -> 'ModelSwitchToResult': assert isinstance(obj, dict) model_id = from_union([from_str, from_none], obj.get("modelId")) return ModelSwitchToResult(model_id) @@ -2272,7 +2258,7 @@ class ModelsListRequest: """ @staticmethod - def from_dict(obj: Any) -> ModelsListRequest: + def from_dict(obj: Any) -> 'ModelsListRequest': assert isinstance(obj, dict) git_hub_token = from_union([from_str, from_none], obj.get("gitHubToken")) return ModelsListRequest(git_hub_token) @@ -2292,7 +2278,7 @@ class NameGetResult: """The session name (user-set or auto-generated), or null if not yet set""" @staticmethod - def from_dict(obj: Any) -> NameGetResult: + def from_dict(obj: Any) -> 'NameGetResult': assert isinstance(obj, dict) name = from_union([from_none, from_str], obj.get("name")) return NameGetResult(name) @@ -2314,7 +2300,7 @@ class NameSetAutoRequest: """ @staticmethod - def from_dict(obj: Any) -> NameSetAutoRequest: + def from_dict(obj: Any) -> 'NameSetAutoRequest': assert isinstance(obj, dict) summary = from_str(obj.get("summary")) return NameSetAutoRequest(summary) @@ -2335,7 +2321,7 @@ class NameSetAutoResult: """ @staticmethod - def from_dict(obj: Any) -> NameSetAutoResult: + def from_dict(obj: Any) -> 'NameSetAutoResult': assert isinstance(obj, dict) applied = from_bool(obj.get("applied")) return NameSetAutoResult(applied) @@ -2354,7 +2340,7 @@ class NameSetRequest: """New session name (1–100 characters, trimmed of leading/trailing whitespace)""" @staticmethod - def from_dict(obj: Any) -> NameSetRequest: + def from_dict(obj: Any) -> 'NameSetRequest': assert isinstance(obj, dict) name = from_str(obj.get("name")) return NameSetRequest(name) @@ -2377,7 +2363,7 @@ class PendingPermissionRequest: """Unique identifier for the pending permission request""" @staticmethod - def from_dict(obj: Any) -> PendingPermissionRequest: + def from_dict(obj: Any) -> 'PendingPermissionRequest': assert isinstance(obj, dict) request = PermissionPromptRequest.from_dict(obj.get("request")) request_id = from_str(obj.get("requestId")) @@ -2498,7 +2484,7 @@ class PermissionDecisionRequest: """The client's response to the pending permission prompt""" @staticmethod - def from_dict(obj: Any) -> PermissionDecisionRequest: + def from_dict(obj: Any) -> 'PermissionDecisionRequest': assert isinstance(obj, dict) request_id = from_str(obj.get("requestId")) result = _load_PermissionDecision(obj.get("result")) @@ -2522,7 +2508,7 @@ class PermissionLocationApplyParams: """Working directory whose persisted location permissions should be applied""" @staticmethod - def from_dict(obj: Any) -> PermissionLocationApplyParams: + def from_dict(obj: Any) -> 'PermissionLocationApplyParams': assert isinstance(obj, dict) working_directory = from_str(obj.get("workingDirectory")) return PermissionLocationApplyParams(working_directory) @@ -2548,7 +2534,7 @@ class PermissionLocationResolveParams: """Working directory whose permission location should be resolved""" @staticmethod - def from_dict(obj: Any) -> PermissionLocationResolveParams: + def from_dict(obj: Any) -> 'PermissionLocationResolveParams': assert isinstance(obj, dict) working_directory = from_str(obj.get("workingDirectory")) return PermissionLocationResolveParams(working_directory) @@ -2569,7 +2555,7 @@ class PermissionPathsAddParams: """ @staticmethod - def from_dict(obj: Any) -> PermissionPathsAddParams: + def from_dict(obj: Any) -> 'PermissionPathsAddParams': assert isinstance(obj, dict) path = from_str(obj.get("path")) return PermissionPathsAddParams(path) @@ -2588,7 +2574,7 @@ class PermissionPathsAllowedCheckParams: """Path to check against the session's allowed directories""" @staticmethod - def from_dict(obj: Any) -> PermissionPathsAllowedCheckParams: + def from_dict(obj: Any) -> 'PermissionPathsAllowedCheckParams': assert isinstance(obj, dict) path = from_str(obj.get("path")) return PermissionPathsAllowedCheckParams(path) @@ -2607,7 +2593,7 @@ class PermissionPathsAllowedCheckResult: """Whether the path is within the session's allowed directories""" @staticmethod - def from_dict(obj: Any) -> PermissionPathsAllowedCheckResult: + def from_dict(obj: Any) -> 'PermissionPathsAllowedCheckResult': assert isinstance(obj, dict) allowed = from_bool(obj.get("allowed")) return PermissionPathsAllowedCheckResult(allowed) @@ -2629,7 +2615,7 @@ class PermissionPathsList: """The primary working directory for this session.""" @staticmethod - def from_dict(obj: Any) -> PermissionPathsList: + def from_dict(obj: Any) -> 'PermissionPathsList': assert isinstance(obj, dict) directories = from_list(from_str, obj.get("directories")) primary = from_str(obj.get("primary")) @@ -2650,7 +2636,7 @@ class PermissionPathsUpdatePrimaryParams: """Directory to set as the new primary working directory for the session's permission policy.""" @staticmethod - def from_dict(obj: Any) -> PermissionPathsUpdatePrimaryParams: + def from_dict(obj: Any) -> 'PermissionPathsUpdatePrimaryParams': assert isinstance(obj, dict) path = from_str(obj.get("path")) return PermissionPathsUpdatePrimaryParams(path) @@ -2669,7 +2655,7 @@ class PermissionPathsWorkspaceCheckParams: """Path to check against the session workspace directory""" @staticmethod - def from_dict(obj: Any) -> PermissionPathsWorkspaceCheckParams: + def from_dict(obj: Any) -> 'PermissionPathsWorkspaceCheckParams': assert isinstance(obj, dict) path = from_str(obj.get("path")) return PermissionPathsWorkspaceCheckParams(path) @@ -2688,7 +2674,7 @@ class PermissionPathsWorkspaceCheckResult: """Whether the path is within the session workspace directory""" @staticmethod - def from_dict(obj: Any) -> PermissionPathsWorkspaceCheckResult: + def from_dict(obj: Any) -> 'PermissionPathsWorkspaceCheckResult': assert isinstance(obj, dict) allowed = from_bool(obj.get("allowed")) return PermissionPathsWorkspaceCheckResult(allowed) @@ -2710,7 +2696,7 @@ class PermissionPromptShownNotification: """ @staticmethod - def from_dict(obj: Any) -> PermissionPromptShownNotification: + def from_dict(obj: Any) -> 'PermissionPromptShownNotification': assert isinstance(obj, dict) message = from_str(obj.get("message")) return PermissionPromptShownNotification(message) @@ -2730,7 +2716,7 @@ class PermissionRequestResult: """Whether the permission request was handled successfully""" @staticmethod - def from_dict(obj: Any) -> PermissionRequestResult: + def from_dict(obj: Any) -> 'PermissionRequestResult': assert isinstance(obj, dict) success = from_bool(obj.get("success")) return PermissionRequestResult(success) @@ -2753,7 +2739,7 @@ class PermissionRulesSet: """Rules that auto-deny matching requests""" @staticmethod - def from_dict(obj: Any) -> PermissionRulesSet: + def from_dict(obj: Any) -> 'PermissionRulesSet': assert isinstance(obj, dict) approved = from_list(PermissionRule.from_dict, obj.get("approved")) denied = from_list(PermissionRule.from_dict, obj.get("denied")) @@ -2782,7 +2768,7 @@ class PermissionUrlsConfig: """ @staticmethod - def from_dict(obj: Any) -> PermissionUrlsConfig: + def from_dict(obj: Any) -> 'PermissionUrlsConfig': assert isinstance(obj, dict) initial_allowed = from_union([lambda x: from_list(from_str, x), from_none], obj.get("initialAllowed")) unrestricted = from_union([from_bool, from_none], obj.get("unrestricted")) @@ -2807,7 +2793,7 @@ class PermissionUrlsSetUnrestrictedModeParams: """ @staticmethod - def from_dict(obj: Any) -> PermissionUrlsSetUnrestrictedModeParams: + def from_dict(obj: Any) -> 'PermissionUrlsSetUnrestrictedModeParams': assert isinstance(obj, dict) enabled = from_bool(obj.get("enabled")) return PermissionUrlsSetUnrestrictedModeParams(enabled) @@ -2826,7 +2812,7 @@ class PermissionsConfigureAdditionalContentExclusionPolicyRuleSource: type: str @staticmethod - def from_dict(obj: Any) -> PermissionsConfigureAdditionalContentExclusionPolicyRuleSource: + def from_dict(obj: Any) -> 'PermissionsConfigureAdditionalContentExclusionPolicyRuleSource': assert isinstance(obj, dict) name = from_str(obj.get("name")) type = from_str(obj.get("type")) @@ -2855,7 +2841,7 @@ class PermissionsConfigureResult: """Whether the operation succeeded""" @staticmethod - def from_dict(obj: Any) -> PermissionsConfigureResult: + def from_dict(obj: Any) -> 'PermissionsConfigureResult': assert isinstance(obj, dict) success = from_bool(obj.get("success")) return PermissionsConfigureResult(success) @@ -2874,7 +2860,7 @@ class PermissionsFolderTrustAddTrustedResult: """Whether the operation succeeded""" @staticmethod - def from_dict(obj: Any) -> PermissionsFolderTrustAddTrustedResult: + def from_dict(obj: Any) -> 'PermissionsFolderTrustAddTrustedResult': assert isinstance(obj, dict) success = from_bool(obj.get("success")) return PermissionsFolderTrustAddTrustedResult(success) @@ -2893,7 +2879,7 @@ class PermissionsLocationsAddToolApprovalResult: """Whether the operation succeeded""" @staticmethod - def from_dict(obj: Any) -> PermissionsLocationsAddToolApprovalResult: + def from_dict(obj: Any) -> 'PermissionsLocationsAddToolApprovalResult': assert isinstance(obj, dict) success = from_bool(obj.get("success")) return PermissionsLocationsAddToolApprovalResult(success) @@ -2920,7 +2906,7 @@ class PermissionsModifyRulesResult: """Whether the operation succeeded""" @staticmethod - def from_dict(obj: Any) -> PermissionsModifyRulesResult: + def from_dict(obj: Any) -> 'PermissionsModifyRulesResult': assert isinstance(obj, dict) success = from_bool(obj.get("success")) return PermissionsModifyRulesResult(success) @@ -2939,7 +2925,7 @@ class PermissionsNotifyPromptShownResult: """Whether the operation succeeded""" @staticmethod - def from_dict(obj: Any) -> PermissionsNotifyPromptShownResult: + def from_dict(obj: Any) -> 'PermissionsNotifyPromptShownResult': assert isinstance(obj, dict) success = from_bool(obj.get("success")) return PermissionsNotifyPromptShownResult(success) @@ -2958,7 +2944,7 @@ class PermissionsPathsAddResult: """Whether the operation succeeded""" @staticmethod - def from_dict(obj: Any) -> PermissionsPathsAddResult: + def from_dict(obj: Any) -> 'PermissionsPathsAddResult': assert isinstance(obj, dict) success = from_bool(obj.get("success")) return PermissionsPathsAddResult(success) @@ -2973,7 +2959,7 @@ def to_dict(self) -> dict: class PermissionsPathsListRequest: """No parameters; returns the session's allow-listed directories.""" @staticmethod - def from_dict(obj: Any) -> PermissionsPathsListRequest: + def from_dict(obj: Any) -> 'PermissionsPathsListRequest': assert isinstance(obj, dict) return PermissionsPathsListRequest() @@ -2990,7 +2976,7 @@ class PermissionsPathsUpdatePrimaryResult: """Whether the operation succeeded""" @staticmethod - def from_dict(obj: Any) -> PermissionsPathsUpdatePrimaryResult: + def from_dict(obj: Any) -> 'PermissionsPathsUpdatePrimaryResult': assert isinstance(obj, dict) success = from_bool(obj.get("success")) return PermissionsPathsUpdatePrimaryResult(success) @@ -3005,7 +2991,7 @@ def to_dict(self) -> dict: class PermissionsPendingRequestsRequest: """No parameters; returns currently-pending permission requests for the session.""" @staticmethod - def from_dict(obj: Any) -> PermissionsPendingRequestsRequest: + def from_dict(obj: Any) -> 'PermissionsPendingRequestsRequest': assert isinstance(obj, dict) return PermissionsPendingRequestsRequest() @@ -3018,7 +3004,7 @@ def to_dict(self) -> dict: class PermissionsResetSessionApprovalsRequest: """No parameters; clears all session-scoped tool permission approvals.""" @staticmethod - def from_dict(obj: Any) -> PermissionsResetSessionApprovalsRequest: + def from_dict(obj: Any) -> 'PermissionsResetSessionApprovalsRequest': assert isinstance(obj, dict) return PermissionsResetSessionApprovalsRequest() @@ -3035,7 +3021,7 @@ class PermissionsResetSessionApprovalsResult: """Whether the operation succeeded""" @staticmethod - def from_dict(obj: Any) -> PermissionsResetSessionApprovalsResult: + def from_dict(obj: Any) -> 'PermissionsResetSessionApprovalsResult': assert isinstance(obj, dict) success = from_bool(obj.get("success")) return PermissionsResetSessionApprovalsResult(success) @@ -3054,7 +3040,7 @@ class PermissionsSetApproveAllResult: """Whether the operation succeeded""" @staticmethod - def from_dict(obj: Any) -> PermissionsSetApproveAllResult: + def from_dict(obj: Any) -> 'PermissionsSetApproveAllResult': assert isinstance(obj, dict) success = from_bool(obj.get("success")) return PermissionsSetApproveAllResult(success) @@ -3076,7 +3062,7 @@ class PermissionsSetRequiredRequest: """ @staticmethod - def from_dict(obj: Any) -> PermissionsSetRequiredRequest: + def from_dict(obj: Any) -> 'PermissionsSetRequiredRequest': assert isinstance(obj, dict) required = from_bool(obj.get("required")) return PermissionsSetRequiredRequest(required) @@ -3095,7 +3081,7 @@ class PermissionsSetRequiredResult: """Whether the operation succeeded""" @staticmethod - def from_dict(obj: Any) -> PermissionsSetRequiredResult: + def from_dict(obj: Any) -> 'PermissionsSetRequiredResult': assert isinstance(obj, dict) success = from_bool(obj.get("success")) return PermissionsSetRequiredResult(success) @@ -3114,7 +3100,7 @@ class PermissionsUrlsSetUnrestrictedModeResult: """Whether the operation succeeded""" @staticmethod - def from_dict(obj: Any) -> PermissionsUrlsSetUnrestrictedModeResult: + def from_dict(obj: Any) -> 'PermissionsUrlsSetUnrestrictedModeResult': assert isinstance(obj, dict) success = from_bool(obj.get("success")) return PermissionsUrlsSetUnrestrictedModeResult(success) @@ -3132,7 +3118,7 @@ class PingRequest: """Optional message to echo back""" @staticmethod - def from_dict(obj: Any) -> PingRequest: + def from_dict(obj: Any) -> 'PingRequest': assert isinstance(obj, dict) message = from_union([from_str, from_none], obj.get("message")) return PingRequest(message) @@ -3158,7 +3144,7 @@ class PingResult: """ISO 8601 timestamp when the server handled the ping""" @staticmethod - def from_dict(obj: Any) -> PingResult: + def from_dict(obj: Any) -> 'PingResult': assert isinstance(obj, dict) message = from_str(obj.get("message")) protocol_version = from_int(obj.get("protocolVersion")) @@ -3187,7 +3173,7 @@ class PlanReadResult: """Absolute file path of the plan file, or null if workspace is not enabled""" @staticmethod - def from_dict(obj: Any) -> PlanReadResult: + def from_dict(obj: Any) -> 'PlanReadResult': assert isinstance(obj, dict) exists = from_bool(obj.get("exists")) content = from_union([from_none, from_str], obj.get("content")) @@ -3210,7 +3196,7 @@ class PlanUpdateRequest: """The new content for the plan file""" @staticmethod - def from_dict(obj: Any) -> PlanUpdateRequest: + def from_dict(obj: Any) -> 'PlanUpdateRequest': assert isinstance(obj, dict) content = from_str(obj.get("content")) return PlanUpdateRequest(content) @@ -3238,7 +3224,7 @@ class Plugin: """Installed version""" @staticmethod - def from_dict(obj: Any) -> Plugin: + def from_dict(obj: Any) -> 'Plugin': assert isinstance(obj, dict) enabled = from_bool(obj.get("enabled")) marketplace = from_str(obj.get("marketplace")) @@ -3273,7 +3259,7 @@ class QueueRemoveMostRecentResult: """ @staticmethod - def from_dict(obj: Any) -> QueueRemoveMostRecentResult: + def from_dict(obj: Any) -> 'QueueRemoveMostRecentResult': assert isinstance(obj, dict) removed = from_bool(obj.get("removed")) return QueueRemoveMostRecentResult(removed) @@ -3297,7 +3283,7 @@ class QueuedCommandHandled: """ @staticmethod - def from_dict(obj: Any) -> QueuedCommandHandled: + def from_dict(obj: Any) -> 'QueuedCommandHandled': assert isinstance(obj, dict) stop_processing_queue = from_union([from_bool, from_none], obj.get("stopProcessingQueue")) return QueuedCommandHandled(stop_processing_queue) @@ -3320,7 +3306,7 @@ class QueuedCommandNotHandled: """ @staticmethod - def from_dict(obj: Any) -> QueuedCommandNotHandled: + def from_dict(obj: Any) -> 'QueuedCommandNotHandled': assert isinstance(obj, dict) return QueuedCommandNotHandled() @@ -3351,7 +3337,7 @@ class RegisterEventInterestParams: """ @staticmethod - def from_dict(obj: Any) -> RegisterEventInterestParams: + def from_dict(obj: Any) -> 'RegisterEventInterestParams': assert isinstance(obj, dict) event_type = from_str(obj.get("eventType")) return RegisterEventInterestParams(event_type) @@ -3373,7 +3359,7 @@ class RegisterEventInterestResult: """ @staticmethod - def from_dict(obj: Any) -> RegisterEventInterestResult: + def from_dict(obj: Any) -> 'RegisterEventInterestResult': assert isinstance(obj, dict) handle = from_str(obj.get("handle")) return RegisterEventInterestResult(handle) @@ -3396,7 +3382,7 @@ class ReleaseEventInterestParams: """ @staticmethod - def from_dict(obj: Any) -> ReleaseEventInterestParams: + def from_dict(obj: Any) -> 'ReleaseEventInterestParams': assert isinstance(obj, dict) handle = from_str(obj.get("handle")) return ReleaseEventInterestParams(handle) @@ -3427,7 +3413,7 @@ class RemoteEnableResult: """GitHub frontend URL for this session""" @staticmethod - def from_dict(obj: Any) -> RemoteEnableResult: + def from_dict(obj: Any) -> 'RemoteEnableResult': assert isinstance(obj, dict) remote_steerable = from_bool(obj.get("remoteSteerable")) url = from_union([from_str, from_none], obj.get("url")) @@ -3452,7 +3438,7 @@ class RemoteNotifySteerableChangedRequest: """ @staticmethod - def from_dict(obj: Any) -> RemoteNotifySteerableChangedRequest: + def from_dict(obj: Any) -> 'RemoteNotifySteerableChangedRequest': assert isinstance(obj, dict) remote_steerable = from_bool(obj.get("remoteSteerable")) return RemoteNotifySteerableChangedRequest(remote_steerable) @@ -3470,7 +3456,7 @@ class RemoteNotifySteerableChangedResult: remote exporter that the runtime does not directly own. """ @staticmethod - def from_dict(obj: Any) -> RemoteNotifySteerableChangedResult: + def from_dict(obj: Any) -> 'RemoteNotifySteerableChangedResult': assert isinstance(obj, dict) return RemoteNotifySteerableChangedResult() @@ -3507,7 +3493,7 @@ class ScheduleEntry: """ @staticmethod - def from_dict(obj: Any) -> ScheduleEntry: + def from_dict(obj: Any) -> 'ScheduleEntry': assert isinstance(obj, dict) id = from_int(obj.get("id")) interval_ms = from_int(obj.get("intervalMs")) @@ -3537,7 +3523,7 @@ class ScheduleStopRequest: """Id of the scheduled prompt to remove.""" @staticmethod - def from_dict(obj: Any) -> ScheduleStopRequest: + def from_dict(obj: Any) -> 'ScheduleStopRequest': assert isinstance(obj, dict) id = from_int(obj.get("id")) return ScheduleStopRequest(id) @@ -3555,7 +3541,7 @@ class SecretsAddFilterValuesRequest: """Raw secret values to register for redaction""" @staticmethod - def from_dict(obj: Any) -> SecretsAddFilterValuesRequest: + def from_dict(obj: Any) -> 'SecretsAddFilterValuesRequest': assert isinstance(obj, dict) values = from_list(from_str, obj.get("values")) return SecretsAddFilterValuesRequest(values) @@ -3573,7 +3559,7 @@ class SecretsAddFilterValuesResult: """Whether the values were successfully registered""" @staticmethod - def from_dict(obj: Any) -> SecretsAddFilterValuesResult: + def from_dict(obj: Any) -> 'SecretsAddFilterValuesResult': assert isinstance(obj, dict) ok = from_bool(obj.get("ok")) return SecretsAddFilterValuesResult(ok) @@ -3605,7 +3591,7 @@ class SendAttachmentFileLineRange: """Start line number (1-based)""" @staticmethod - def from_dict(obj: Any) -> SendAttachmentFileLineRange: + def from_dict(obj: Any) -> 'SendAttachmentFileLineRange': assert isinstance(obj, dict) end = from_int(obj.get("end")) start = from_int(obj.get("start")) @@ -3636,7 +3622,7 @@ class SendAttachmentSelectionDetailsEnd: """End line number (0-based)""" @staticmethod - def from_dict(obj: Any) -> SendAttachmentSelectionDetailsEnd: + def from_dict(obj: Any) -> 'SendAttachmentSelectionDetailsEnd': assert isinstance(obj, dict) character = from_int(obj.get("character")) line = from_int(obj.get("line")) @@ -3660,7 +3646,7 @@ class SendAttachmentSelectionDetailsStart: """Start line number (0-based)""" @staticmethod - def from_dict(obj: Any) -> SendAttachmentSelectionDetailsStart: + def from_dict(obj: Any) -> 'SendAttachmentSelectionDetailsStart': assert isinstance(obj, dict) character = from_int(obj.get("character")) line = from_int(obj.get("line")) @@ -3709,7 +3695,7 @@ class SendResult: """Unique identifier assigned to the message""" @staticmethod - def from_dict(obj: Any) -> SendResult: + def from_dict(obj: Any) -> 'SendResult': assert isinstance(obj, dict) message_id = from_str(obj.get("messageId")) return SendResult(message_id) @@ -3745,7 +3731,7 @@ class ServerSkill: """The project path this skill belongs to (only for project/inherited skills)""" @staticmethod - def from_dict(obj: Any) -> ServerSkill: + def from_dict(obj: Any) -> 'ServerSkill': assert isinstance(obj, dict) description = from_str(obj.get("description")) enabled = from_bool(obj.get("enabled")) @@ -3781,7 +3767,7 @@ class SessionBulkDeleteResult: """ @staticmethod - def from_dict(obj: Any) -> SessionBulkDeleteResult: + def from_dict(obj: Any) -> 'SessionBulkDeleteResult': assert isinstance(obj, dict) freed_bytes = from_dict(from_int, obj.get("freedBytes")) return SessionBulkDeleteResult(freed_bytes) @@ -3809,7 +3795,7 @@ class SessionFSAppendFileRequest: """Optional POSIX-style mode for newly created files""" @staticmethod - def from_dict(obj: Any) -> SessionFSAppendFileRequest: + def from_dict(obj: Any) -> 'SessionFSAppendFileRequest': assert isinstance(obj, dict) content = from_str(obj.get("content")) path = from_str(obj.get("path")) @@ -3843,7 +3829,7 @@ class SessionFSExistsRequest: """Target session identifier""" @staticmethod - def from_dict(obj: Any) -> SessionFSExistsRequest: + def from_dict(obj: Any) -> 'SessionFSExistsRequest': assert isinstance(obj, dict) path = from_str(obj.get("path")) session_id = from_str(obj.get("sessionId")) @@ -3863,7 +3849,7 @@ class SessionFSExistsResult: """Whether the path exists""" @staticmethod - def from_dict(obj: Any) -> SessionFSExistsResult: + def from_dict(obj: Any) -> 'SessionFSExistsResult': assert isinstance(obj, dict) exists = from_bool(obj.get("exists")) return SessionFSExistsResult(exists) @@ -3891,7 +3877,7 @@ class SessionFSMkdirRequest: """Create parent directories as needed""" @staticmethod - def from_dict(obj: Any) -> SessionFSMkdirRequest: + def from_dict(obj: Any) -> 'SessionFSMkdirRequest': assert isinstance(obj, dict) path = from_str(obj.get("path")) session_id = from_str(obj.get("sessionId")) @@ -3920,7 +3906,7 @@ class SessionFSReadFileRequest: """Target session identifier""" @staticmethod - def from_dict(obj: Any) -> SessionFSReadFileRequest: + def from_dict(obj: Any) -> 'SessionFSReadFileRequest': assert isinstance(obj, dict) path = from_str(obj.get("path")) session_id = from_str(obj.get("sessionId")) @@ -3943,7 +3929,7 @@ class SessionFSReaddirRequest: """Target session identifier""" @staticmethod - def from_dict(obj: Any) -> SessionFSReaddirRequest: + def from_dict(obj: Any) -> 'SessionFSReaddirRequest': assert isinstance(obj, dict) path = from_str(obj.get("path")) session_id = from_str(obj.get("sessionId")) @@ -3973,7 +3959,7 @@ class SessionFSReaddirWithTypesRequest: """Target session identifier""" @staticmethod - def from_dict(obj: Any) -> SessionFSReaddirWithTypesRequest: + def from_dict(obj: Any) -> 'SessionFSReaddirWithTypesRequest': assert isinstance(obj, dict) path = from_str(obj.get("path")) session_id = from_str(obj.get("sessionId")) @@ -4000,7 +3986,7 @@ class SessionFSRenameRequest: """Source path using SessionFs conventions""" @staticmethod - def from_dict(obj: Any) -> SessionFSRenameRequest: + def from_dict(obj: Any) -> 'SessionFSRenameRequest': assert isinstance(obj, dict) dest = from_str(obj.get("dest")) session_id = from_str(obj.get("sessionId")) @@ -4032,7 +4018,7 @@ class SessionFSRmRequest: """Remove directories and their contents recursively""" @staticmethod - def from_dict(obj: Any) -> SessionFSRmRequest: + def from_dict(obj: Any) -> 'SessionFSRmRequest': assert isinstance(obj, dict) path = from_str(obj.get("path")) session_id = from_str(obj.get("sessionId")) @@ -4058,7 +4044,7 @@ class SessionFSSetProviderCapabilities: """Whether the provider supports SQLite query/exists operations""" @staticmethod - def from_dict(obj: Any) -> SessionFSSetProviderCapabilities: + def from_dict(obj: Any) -> 'SessionFSSetProviderCapabilities': assert isinstance(obj, dict) sqlite = from_union([from_bool, from_none], obj.get("sqlite")) return SessionFSSetProviderCapabilities(sqlite) @@ -4083,7 +4069,7 @@ class SessionFSSetProviderResult: """Whether the provider was set successfully""" @staticmethod - def from_dict(obj: Any) -> SessionFSSetProviderResult: + def from_dict(obj: Any) -> 'SessionFSSetProviderResult': assert isinstance(obj, dict) success = from_bool(obj.get("success")) return SessionFSSetProviderResult(success) @@ -4101,7 +4087,7 @@ class SessionFSSqliteExistsRequest: """Target session identifier""" @staticmethod - def from_dict(obj: Any) -> SessionFSSqliteExistsRequest: + def from_dict(obj: Any) -> 'SessionFSSqliteExistsRequest': assert isinstance(obj, dict) session_id = from_str(obj.get("sessionId")) return SessionFSSqliteExistsRequest(session_id) @@ -4119,7 +4105,7 @@ class SessionFSSqliteExistsResult: """Whether the session database already exists""" @staticmethod - def from_dict(obj: Any) -> SessionFSSqliteExistsResult: + def from_dict(obj: Any) -> 'SessionFSSqliteExistsResult': assert isinstance(obj, dict) exists = from_bool(obj.get("exists")) return SessionFSSqliteExistsResult(exists) @@ -4148,7 +4134,7 @@ class SessionFSStatRequest: """Target session identifier""" @staticmethod - def from_dict(obj: Any) -> SessionFSStatRequest: + def from_dict(obj: Any) -> 'SessionFSStatRequest': assert isinstance(obj, dict) path = from_str(obj.get("path")) session_id = from_str(obj.get("sessionId")) @@ -4177,7 +4163,7 @@ class SessionFSWriteFileRequest: """Optional POSIX-style mode for newly created files""" @staticmethod - def from_dict(obj: Any) -> SessionFSWriteFileRequest: + def from_dict(obj: Any) -> 'SessionFSWriteFileRequest': assert isinstance(obj, dict) content = from_str(obj.get("content")) path = from_str(obj.get("path")) @@ -4212,7 +4198,7 @@ class SessionListFilter: """Match sessions whose context.repository equals this value""" @staticmethod - def from_dict(obj: Any) -> SessionListFilter: + def from_dict(obj: Any) -> 'SessionListFilter': assert isinstance(obj, dict) branch = from_union([from_str, from_none], obj.get("branch")) cwd = from_union([from_str, from_none], obj.get("cwd")) @@ -4247,7 +4233,7 @@ class SessionLoadDeferredRepoHooksResult: """ @staticmethod - def from_dict(obj: Any) -> SessionLoadDeferredRepoHooksResult: + def from_dict(obj: Any) -> 'SessionLoadDeferredRepoHooksResult': assert isinstance(obj, dict) hook_count = from_int(obj.get("hookCount")) startup_prompts = from_list(from_str, obj.get("startupPrompts")) @@ -4281,7 +4267,7 @@ class SessionPruneResult: """Session IDs that were skipped (e.g., named sessions)""" @staticmethod - def from_dict(obj: Any) -> SessionPruneResult: + def from_dict(obj: Any) -> 'SessionPruneResult': assert isinstance(obj, dict) candidates = from_list(from_str, obj.get("candidates")) deleted = from_list(from_str, obj.get("deleted")) @@ -4313,7 +4299,7 @@ class SessionSetCredentialsParams: """ @staticmethod - def from_dict(obj: Any) -> SessionSetCredentialsParams: + def from_dict(obj: Any) -> 'SessionSetCredentialsParams': assert isinstance(obj, dict) credentials = from_union([_load_AuthInfo, from_none], obj.get("credentials")) return SessionSetCredentialsParams(credentials) @@ -4333,7 +4319,7 @@ class SessionSetCredentialsResult: """Whether the operation succeeded""" @staticmethod - def from_dict(obj: Any) -> SessionSetCredentialsResult: + def from_dict(obj: Any) -> 'SessionSetCredentialsResult': assert isinstance(obj, dict) success = from_bool(obj.get("success")) return SessionSetCredentialsResult(success) @@ -4352,7 +4338,7 @@ class SessionSizes: """Map of sessionId -> on-disk size in bytes for the session's workspace directory""" @staticmethod - def from_dict(obj: Any) -> SessionSizes: + def from_dict(obj: Any) -> 'SessionSizes': assert isinstance(obj, dict) sizes = from_dict(from_int, obj.get("sizes")) return SessionSizes(sizes) @@ -4371,7 +4357,7 @@ class SessionUpdateOptionsResult: """Whether the operation succeeded""" @staticmethod - def from_dict(obj: Any) -> SessionUpdateOptionsResult: + def from_dict(obj: Any) -> 'SessionUpdateOptionsResult': assert isinstance(obj, dict) success = from_bool(obj.get("success")) return SessionUpdateOptionsResult(success) @@ -4390,7 +4376,7 @@ class SessionsBulkDeleteRequest: """Session IDs to close, deactivate, and delete from disk""" @staticmethod - def from_dict(obj: Any) -> SessionsBulkDeleteRequest: + def from_dict(obj: Any) -> 'SessionsBulkDeleteRequest': assert isinstance(obj, dict) session_ids = from_list(from_str, obj.get("sessionIds")) return SessionsBulkDeleteRequest(session_ids) @@ -4409,7 +4395,7 @@ class SessionsCheckInUseRequest: """Session IDs to test for live in-use locks""" @staticmethod - def from_dict(obj: Any) -> SessionsCheckInUseRequest: + def from_dict(obj: Any) -> 'SessionsCheckInUseRequest': assert isinstance(obj, dict) session_ids = from_list(from_str, obj.get("sessionIds")) return SessionsCheckInUseRequest(session_ids) @@ -4430,7 +4416,7 @@ class SessionsCheckInUseResult: """ @staticmethod - def from_dict(obj: Any) -> SessionsCheckInUseResult: + def from_dict(obj: Any) -> 'SessionsCheckInUseResult': assert isinstance(obj, dict) in_use = from_list(from_str, obj.get("inUse")) return SessionsCheckInUseResult(in_use) @@ -4449,7 +4435,7 @@ class SessionsCloseRequest: """Session ID to close""" @staticmethod - def from_dict(obj: Any) -> SessionsCloseRequest: + def from_dict(obj: Any) -> 'SessionsCloseRequest': assert isinstance(obj, dict) session_id = from_str(obj.get("sessionId")) return SessionsCloseRequest(session_id) @@ -4467,7 +4453,7 @@ class SessionsCloseResult: currently active. """ @staticmethod - def from_dict(obj: Any) -> SessionsCloseResult: + def from_dict(obj: Any) -> 'SessionsCloseResult': assert isinstance(obj, dict) return SessionsCloseResult() @@ -4486,7 +4472,7 @@ class SessionsFindByPrefixRequest: """ @staticmethod - def from_dict(obj: Any) -> SessionsFindByPrefixRequest: + def from_dict(obj: Any) -> 'SessionsFindByPrefixRequest': assert isinstance(obj, dict) prefix = from_str(obj.get("prefix")) return SessionsFindByPrefixRequest(prefix) @@ -4505,7 +4491,7 @@ class SessionsFindByPrefixResult: """Omitted when no unique session matches the prefix (no match or ambiguous)""" @staticmethod - def from_dict(obj: Any) -> SessionsFindByPrefixResult: + def from_dict(obj: Any) -> 'SessionsFindByPrefixResult': assert isinstance(obj, dict) session_id = from_union([from_str, from_none], obj.get("sessionId")) return SessionsFindByPrefixResult(session_id) @@ -4525,7 +4511,7 @@ class SessionsFindByTaskIDRequest: """GitHub task ID to look up""" @staticmethod - def from_dict(obj: Any) -> SessionsFindByTaskIDRequest: + def from_dict(obj: Any) -> 'SessionsFindByTaskIDRequest': assert isinstance(obj, dict) task_id = from_str(obj.get("taskId")) return SessionsFindByTaskIDRequest(task_id) @@ -4544,7 +4530,7 @@ class SessionsFindByTaskIDResult: """Omitted when no local session is bound to that GitHub task""" @staticmethod - def from_dict(obj: Any) -> SessionsFindByTaskIDResult: + def from_dict(obj: Any) -> 'SessionsFindByTaskIDResult': assert isinstance(obj, dict) session_id = from_union([from_str, from_none], obj.get("sessionId")) return SessionsFindByTaskIDResult(session_id) @@ -4573,7 +4559,7 @@ class SessionsForkRequest: """ @staticmethod - def from_dict(obj: Any) -> SessionsForkRequest: + def from_dict(obj: Any) -> 'SessionsForkRequest': assert isinstance(obj, dict) session_id = from_str(obj.get("sessionId")) name = from_union([from_str, from_none], obj.get("name")) @@ -4601,7 +4587,7 @@ class SessionsForkResult: """Friendly name assigned to the forked session, if any.""" @staticmethod - def from_dict(obj: Any) -> SessionsForkResult: + def from_dict(obj: Any) -> 'SessionsForkResult': assert isinstance(obj, dict) session_id = from_str(obj.get("sessionId")) name = from_union([from_str, from_none], obj.get("name")) @@ -4623,7 +4609,7 @@ class SessionsGetEventFilePathRequest: """Session ID whose event-log file path to compute""" @staticmethod - def from_dict(obj: Any) -> SessionsGetEventFilePathRequest: + def from_dict(obj: Any) -> 'SessionsGetEventFilePathRequest': assert isinstance(obj, dict) session_id = from_str(obj.get("sessionId")) return SessionsGetEventFilePathRequest(session_id) @@ -4642,7 +4628,7 @@ class SessionsGetEventFilePathResult: """Absolute path to the session's events.jsonl file""" @staticmethod - def from_dict(obj: Any) -> SessionsGetEventFilePathResult: + def from_dict(obj: Any) -> 'SessionsGetEventFilePathResult': assert isinstance(obj, dict) file_path = from_str(obj.get("filePath")) return SessionsGetEventFilePathResult(file_path) @@ -4661,7 +4647,7 @@ class SessionsGetLastForContextResult: """Most-relevant session ID for the supplied context, or omitted when no sessions exist""" @staticmethod - def from_dict(obj: Any) -> SessionsGetLastForContextResult: + def from_dict(obj: Any) -> 'SessionsGetLastForContextResult': assert isinstance(obj, dict) session_id = from_union([from_str, from_none], obj.get("sessionId")) return SessionsGetLastForContextResult(session_id) @@ -4681,7 +4667,7 @@ class SessionsGetPersistedRemoteSteerableRequest: """Session ID to look up the persisted remote-steerable flag for""" @staticmethod - def from_dict(obj: Any) -> SessionsGetPersistedRemoteSteerableRequest: + def from_dict(obj: Any) -> 'SessionsGetPersistedRemoteSteerableRequest': assert isinstance(obj, dict) session_id = from_str(obj.get("sessionId")) return SessionsGetPersistedRemoteSteerableRequest(session_id) @@ -4703,7 +4689,7 @@ class SessionsGetPersistedRemoteSteerableResult: """ @staticmethod - def from_dict(obj: Any) -> SessionsGetPersistedRemoteSteerableResult: + def from_dict(obj: Any) -> 'SessionsGetPersistedRemoteSteerableResult': assert isinstance(obj, dict) remote_steerable = from_union([from_bool, from_none], obj.get("remoteSteerable")) return SessionsGetPersistedRemoteSteerableResult(remote_steerable) @@ -4723,7 +4709,7 @@ class SessionsLoadDeferredRepoHooksRequest: """Active session ID whose deferred repo-level hooks should be loaded""" @staticmethod - def from_dict(obj: Any) -> SessionsLoadDeferredRepoHooksRequest: + def from_dict(obj: Any) -> 'SessionsLoadDeferredRepoHooksRequest': assert isinstance(obj, dict) session_id = from_str(obj.get("sessionId")) return SessionsLoadDeferredRepoHooksRequest(session_id) @@ -4752,7 +4738,7 @@ class SessionsPruneOldRequest: """When true, named sessions (set via /rename) are also eligible for pruning""" @staticmethod - def from_dict(obj: Any) -> SessionsPruneOldRequest: + def from_dict(obj: Any) -> 'SessionsPruneOldRequest': assert isinstance(obj, dict) older_than_days = from_int(obj.get("olderThanDays")) dry_run = from_union([from_bool, from_none], obj.get("dryRun")) @@ -4780,7 +4766,7 @@ class SessionsReleaseLockRequest: """Session ID whose in-use lock should be released""" @staticmethod - def from_dict(obj: Any) -> SessionsReleaseLockRequest: + def from_dict(obj: Any) -> 'SessionsReleaseLockRequest': assert isinstance(obj, dict) session_id = from_str(obj.get("sessionId")) return SessionsReleaseLockRequest(session_id) @@ -4797,7 +4783,7 @@ class SessionsReleaseLockResult: process does not currently hold a lock for the session. """ @staticmethod - def from_dict(obj: Any) -> SessionsReleaseLockResult: + def from_dict(obj: Any) -> 'SessionsReleaseLockResult': assert isinstance(obj, dict) return SessionsReleaseLockResult() @@ -4819,7 +4805,7 @@ class SessionsReloadPluginHooksRequest: """ @staticmethod - def from_dict(obj: Any) -> SessionsReloadPluginHooksRequest: + def from_dict(obj: Any) -> 'SessionsReloadPluginHooksRequest': assert isinstance(obj, dict) session_id = from_str(obj.get("sessionId")) defer_repo_hooks = from_union([from_bool, from_none], obj.get("deferRepoHooks")) @@ -4840,7 +4826,7 @@ class SessionsReloadPluginHooksResult: when no active session matches the given sessionId. """ @staticmethod - def from_dict(obj: Any) -> SessionsReloadPluginHooksResult: + def from_dict(obj: Any) -> 'SessionsReloadPluginHooksResult': assert isinstance(obj, dict) return SessionsReloadPluginHooksResult() @@ -4857,7 +4843,7 @@ class SessionsSaveRequest: """Session ID whose pending events should be flushed to disk""" @staticmethod - def from_dict(obj: Any) -> SessionsSaveRequest: + def from_dict(obj: Any) -> 'SessionsSaveRequest': assert isinstance(obj, dict) session_id = from_str(obj.get("sessionId")) return SessionsSaveRequest(session_id) @@ -4874,7 +4860,7 @@ class SessionsSaveResult: (e.g., already closed). """ @staticmethod - def from_dict(obj: Any) -> SessionsSaveResult: + def from_dict(obj: Any) -> 'SessionsSaveResult': assert isinstance(obj, dict) return SessionsSaveResult() @@ -4890,7 +4876,7 @@ class SessionsSetAdditionalPluginsResult: until the next reload. """ @staticmethod - def from_dict(obj: Any) -> SessionsSetAdditionalPluginsResult: + def from_dict(obj: Any) -> 'SessionsSetAdditionalPluginsResult': assert isinstance(obj, dict) return SessionsSetAdditionalPluginsResult() @@ -4913,7 +4899,7 @@ class ShellExecRequest: """Timeout in milliseconds (default: 30000)""" @staticmethod - def from_dict(obj: Any) -> ShellExecRequest: + def from_dict(obj: Any) -> 'ShellExecRequest': assert isinstance(obj, dict) command = from_str(obj.get("command")) cwd = from_union([from_str, from_none], obj.get("cwd")) @@ -4939,7 +4925,7 @@ class ShellExecResult: """Unique identifier for tracking streamed output""" @staticmethod - def from_dict(obj: Any) -> ShellExecResult: + def from_dict(obj: Any) -> 'ShellExecResult': assert isinstance(obj, dict) process_id = from_str(obj.get("processId")) return ShellExecResult(process_id) @@ -4967,7 +4953,7 @@ class ShellKillResult: """Whether the signal was sent successfully""" @staticmethod - def from_dict(obj: Any) -> ShellKillResult: + def from_dict(obj: Any) -> 'ShellKillResult': assert isinstance(obj, dict) killed = from_bool(obj.get("killed")) return ShellKillResult(killed) @@ -4990,7 +4976,7 @@ class ShutdownRequest: """Why the session is being shut down. Defaults to "routine" when omitted.""" @staticmethod - def from_dict(obj: Any) -> ShutdownRequest: + def from_dict(obj: Any) -> 'ShutdownRequest': assert isinstance(obj, dict) reason = from_union([from_str, from_none], obj.get("reason")) type = from_union([ShutdownType, from_none], obj.get("type")) @@ -5031,7 +5017,7 @@ class Skill: """Name of the plugin that provides the skill, when source is 'plugin'""" @staticmethod - def from_dict(obj: Any) -> Skill: + def from_dict(obj: Any) -> 'Skill': assert isinstance(obj, dict) description = from_str(obj.get("description")) enabled = from_bool(obj.get("enabled")) @@ -5064,7 +5050,7 @@ class SkillsDisableRequest: """Name of the skill to disable""" @staticmethod - def from_dict(obj: Any) -> SkillsDisableRequest: + def from_dict(obj: Any) -> 'SkillsDisableRequest': assert isinstance(obj, dict) name = from_str(obj.get("name")) return SkillsDisableRequest(name) @@ -5085,7 +5071,7 @@ class SkillsDiscoverRequest: """Optional list of additional skill directory paths to include""" @staticmethod - def from_dict(obj: Any) -> SkillsDiscoverRequest: + def from_dict(obj: Any) -> 'SkillsDiscoverRequest': assert isinstance(obj, dict) project_paths = from_union([lambda x: from_list(from_str, x), from_none], obj.get("projectPaths")) skill_directories = from_union([lambda x: from_list(from_str, x), from_none], obj.get("skillDirectories")) @@ -5108,7 +5094,7 @@ class SkillsEnableRequest: """Name of the skill to enable""" @staticmethod - def from_dict(obj: Any) -> SkillsEnableRequest: + def from_dict(obj: Any) -> 'SkillsEnableRequest': assert isinstance(obj, dict) name = from_str(obj.get("name")) return SkillsEnableRequest(name) @@ -5139,7 +5125,7 @@ class SkillsInvokedSkill: """Tools that should be auto-approved when this skill is active, captured at invocation time""" @staticmethod - def from_dict(obj: Any) -> SkillsInvokedSkill: + def from_dict(obj: Any) -> 'SkillsInvokedSkill': assert isinstance(obj, dict) content = from_str(obj.get("content")) invoked_at_turn = from_int(obj.get("invokedAtTurn")) @@ -5170,7 +5156,7 @@ class SkillsLoadDiagnostics: """Warnings emitted while loading skills (e.g. skills that loaded but had issues)""" @staticmethod - def from_dict(obj: Any) -> SkillsLoadDiagnostics: + def from_dict(obj: Any) -> 'SkillsLoadDiagnostics': assert isinstance(obj, dict) errors = from_list(from_str, obj.get("errors")) warnings = from_list(from_str, obj.get("warnings")) @@ -5209,7 +5195,7 @@ class SlashCommandSelectSubcommandOption: """Optional group label for organizing options""" @staticmethod - def from_dict(obj: Any) -> SlashCommandSelectSubcommandOption: + def from_dict(obj: Any) -> 'SlashCommandSelectSubcommandOption': assert isinstance(obj, dict) description = from_str(obj.get("description")) name = from_str(obj.get("name")) @@ -5259,7 +5245,7 @@ class TaskProgressLine: """ISO 8601 timestamp when this event occurred""" @staticmethod - def from_dict(obj: Any) -> TaskProgressLine: + def from_dict(obj: Any) -> 'TaskProgressLine': assert isinstance(obj, dict) message = from_str(obj.get("message")) timestamp = from_datetime(obj.get("timestamp")) @@ -5292,7 +5278,7 @@ class TaskList: """Currently tracked tasks""" @staticmethod - def from_dict(obj: Any) -> TaskList: + def from_dict(obj: Any) -> 'TaskList': assert isinstance(obj, dict) tasks = from_list(_load_TaskInfo, obj.get("tasks")) return TaskList(tasks) @@ -5314,7 +5300,7 @@ class TasksCancelRequest: """Task identifier""" @staticmethod - def from_dict(obj: Any) -> TasksCancelRequest: + def from_dict(obj: Any) -> 'TasksCancelRequest': assert isinstance(obj, dict) id = from_str(obj.get("id")) return TasksCancelRequest(id) @@ -5333,7 +5319,7 @@ class TasksCancelResult: """Whether the task was successfully cancelled""" @staticmethod - def from_dict(obj: Any) -> TasksCancelResult: + def from_dict(obj: Any) -> 'TasksCancelResult': assert isinstance(obj, dict) cancelled = from_bool(obj.get("cancelled")) return TasksCancelResult(cancelled) @@ -5355,7 +5341,7 @@ class TasksGetCurrentPromotableResult: """ @staticmethod - def from_dict(obj: Any) -> TasksGetCurrentPromotableResult: + def from_dict(obj: Any) -> 'TasksGetCurrentPromotableResult': assert isinstance(obj, dict) task = from_union([_load_TaskInfo, from_none], obj.get("task")) return TasksGetCurrentPromotableResult(task) @@ -5375,7 +5361,7 @@ class TasksGetProgressRequest: """Task identifier (agent ID or shell ID)""" @staticmethod - def from_dict(obj: Any) -> TasksGetProgressRequest: + def from_dict(obj: Any) -> 'TasksGetProgressRequest': assert isinstance(obj, dict) id = from_str(obj.get("id")) return TasksGetProgressRequest(id) @@ -5398,7 +5384,7 @@ class TasksPromoteCurrentToBackgroundResult: """ @staticmethod - def from_dict(obj: Any) -> TasksPromoteCurrentToBackgroundResult: + def from_dict(obj: Any) -> 'TasksPromoteCurrentToBackgroundResult': assert isinstance(obj, dict) task = from_union([_load_TaskInfo, from_none], obj.get("task")) return TasksPromoteCurrentToBackgroundResult(task) @@ -5418,7 +5404,7 @@ class TasksPromoteToBackgroundRequest: """Task identifier""" @staticmethod - def from_dict(obj: Any) -> TasksPromoteToBackgroundRequest: + def from_dict(obj: Any) -> 'TasksPromoteToBackgroundRequest': assert isinstance(obj, dict) id = from_str(obj.get("id")) return TasksPromoteToBackgroundRequest(id) @@ -5437,7 +5423,7 @@ class TasksPromoteToBackgroundResult: """Whether the task was successfully promoted to background mode""" @staticmethod - def from_dict(obj: Any) -> TasksPromoteToBackgroundResult: + def from_dict(obj: Any) -> 'TasksPromoteToBackgroundResult': assert isinstance(obj, dict) promoted = from_bool(obj.get("promoted")) return TasksPromoteToBackgroundResult(promoted) @@ -5454,7 +5440,7 @@ class TasksRefreshResult: long pause to pick up exit/output state for shells running outside the agent loop. """ @staticmethod - def from_dict(obj: Any) -> TasksRefreshResult: + def from_dict(obj: Any) -> 'TasksRefreshResult': assert isinstance(obj, dict) return TasksRefreshResult() @@ -5471,7 +5457,7 @@ class TasksRemoveRequest: """Task identifier""" @staticmethod - def from_dict(obj: Any) -> TasksRemoveRequest: + def from_dict(obj: Any) -> 'TasksRemoveRequest': assert isinstance(obj, dict) id = from_str(obj.get("id")) return TasksRemoveRequest(id) @@ -5493,7 +5479,7 @@ class TasksRemoveResult: """ @staticmethod - def from_dict(obj: Any) -> TasksRemoveResult: + def from_dict(obj: Any) -> 'TasksRemoveResult': assert isinstance(obj, dict) removed = from_bool(obj.get("removed")) return TasksRemoveResult(removed) @@ -5518,7 +5504,7 @@ class TasksSendMessageRequest: """Agent ID of the sender, if sent on behalf of another agent""" @staticmethod - def from_dict(obj: Any) -> TasksSendMessageRequest: + def from_dict(obj: Any) -> 'TasksSendMessageRequest': assert isinstance(obj, dict) id = from_str(obj.get("id")) message = from_str(obj.get("message")) @@ -5545,7 +5531,7 @@ class TasksSendMessageResult: """Error message if delivery failed""" @staticmethod - def from_dict(obj: Any) -> TasksSendMessageResult: + def from_dict(obj: Any) -> 'TasksSendMessageResult': assert isinstance(obj, dict) sent = from_bool(obj.get("sent")) error = from_union([from_str, from_none], obj.get("error")) @@ -5579,7 +5565,7 @@ class TasksStartAgentRequest: """Optional model override""" @staticmethod - def from_dict(obj: Any) -> TasksStartAgentRequest: + def from_dict(obj: Any) -> 'TasksStartAgentRequest': assert isinstance(obj, dict) agent_type = from_str(obj.get("agentType")) name = from_str(obj.get("name")) @@ -5608,7 +5594,7 @@ class TasksStartAgentResult: """Generated agent ID for the background task""" @staticmethod - def from_dict(obj: Any) -> TasksStartAgentResult: + def from_dict(obj: Any) -> 'TasksStartAgentResult': assert isinstance(obj, dict) agent_id = from_str(obj.get("agentId")) return TasksStartAgentResult(agent_id) @@ -5627,7 +5613,7 @@ class TasksWaitForPendingResult: COPILOT_TASK_WAIT_TIMEOUT_SECONDS). """ @staticmethod - def from_dict(obj: Any) -> TasksWaitForPendingResult: + def from_dict(obj: Any) -> 'TasksWaitForPendingResult': assert isinstance(obj, dict) return TasksWaitForPendingResult() @@ -5647,7 +5633,7 @@ class TelemetrySetFeatureOverridesRequest: """ @staticmethod - def from_dict(obj: Any) -> TelemetrySetFeatureOverridesRequest: + def from_dict(obj: Any) -> 'TelemetrySetFeatureOverridesRequest': assert isinstance(obj, dict) features = from_dict(from_str, obj.get("features")) return TelemetrySetFeatureOverridesRequest(features) @@ -5681,7 +5667,7 @@ class Tool: """JSON Schema for the tool's input parameters""" @staticmethod - def from_dict(obj: Any) -> Tool: + def from_dict(obj: Any) -> 'Tool': assert isinstance(obj, dict) description = from_str(obj.get("description")) name = from_str(obj.get("name")) @@ -5710,7 +5696,7 @@ class ToolsInitializeAndValidateResult: base-class implementation is a no-op for sessions that don't support tool validation. """ @staticmethod - def from_dict(obj: Any) -> ToolsInitializeAndValidateResult: + def from_dict(obj: Any) -> 'ToolsInitializeAndValidateResult': assert isinstance(obj, dict) return ToolsInitializeAndValidateResult() @@ -5728,7 +5714,7 @@ class ToolsListRequest: """ @staticmethod - def from_dict(obj: Any) -> ToolsListRequest: + def from_dict(obj: Any) -> 'ToolsListRequest': assert isinstance(obj, dict) model = from_union([from_str, from_none], obj.get("model")) return ToolsListRequest(model) @@ -5760,7 +5746,7 @@ class UIElicitationArrayAnyOfFieldItemsAnyOf: """Display label for this option.""" @staticmethod - def from_dict(obj: Any) -> UIElicitationArrayAnyOfFieldItemsAnyOf: + def from_dict(obj: Any) -> 'UIElicitationArrayAnyOfFieldItemsAnyOf': assert isinstance(obj, dict) const = from_str(obj.get("const")) title = from_str(obj.get("title")) @@ -5799,7 +5785,7 @@ class UIElicitationStringOneOfFieldOneOf: """Display label for this option.""" @staticmethod - def from_dict(obj: Any) -> UIElicitationStringOneOfFieldOneOf: + def from_dict(obj: Any) -> 'UIElicitationStringOneOfFieldOneOf': assert isinstance(obj, dict) const = from_str(obj.get("const")) title = from_str(obj.get("title")) @@ -5843,7 +5829,7 @@ class UIElicitationResult: """ @staticmethod - def from_dict(obj: Any) -> UIElicitationResult: + def from_dict(obj: Any) -> 'UIElicitationResult': assert isinstance(obj, dict) success = from_bool(obj.get("success")) return UIElicitationResult(success) @@ -5885,7 +5871,7 @@ class UIHandlePendingResult: """ @staticmethod - def from_dict(obj: Any) -> UIHandlePendingResult: + def from_dict(obj: Any) -> 'UIHandlePendingResult': assert isinstance(obj, dict) success = from_bool(obj.get("success")) return UIHandlePendingResult(success) @@ -5910,7 +5896,7 @@ class UIHandlePendingSamplingRequest: """ @staticmethod - def from_dict(obj: Any) -> UIHandlePendingSamplingRequest: + def from_dict(obj: Any) -> 'UIHandlePendingSamplingRequest': assert isinstance(obj, dict) request_id = from_str(obj.get("requestId")) response = from_union([lambda x: from_dict(lambda x: x, x), from_none], obj.get("response")) @@ -5937,7 +5923,7 @@ class UIUserInputResponse: """ @staticmethod - def from_dict(obj: Any) -> UIUserInputResponse: + def from_dict(obj: Any) -> 'UIUserInputResponse': assert isinstance(obj, dict) answer = from_str(obj.get("answer")) was_freeform = from_bool(obj.get("wasFreeform")) @@ -5965,7 +5951,7 @@ class UIRegisterDirectAutoModeSwitchHandlerResult: """ @staticmethod - def from_dict(obj: Any) -> UIRegisterDirectAutoModeSwitchHandlerResult: + def from_dict(obj: Any) -> 'UIRegisterDirectAutoModeSwitchHandlerResult': assert isinstance(obj, dict) handle = from_str(obj.get("handle")) return UIRegisterDirectAutoModeSwitchHandlerResult(handle) @@ -5984,7 +5970,7 @@ class UIUnregisterDirectAutoModeSwitchHandlerRequest: """Handle previously returned by `registerDirectAutoModeSwitchHandler`""" @staticmethod - def from_dict(obj: Any) -> UIUnregisterDirectAutoModeSwitchHandlerRequest: + def from_dict(obj: Any) -> 'UIUnregisterDirectAutoModeSwitchHandlerRequest': assert isinstance(obj, dict) handle = from_str(obj.get("handle")) return UIUnregisterDirectAutoModeSwitchHandlerRequest(handle) @@ -6005,7 +5991,7 @@ class UIUnregisterDirectAutoModeSwitchHandlerResult: """ @staticmethod - def from_dict(obj: Any) -> UIUnregisterDirectAutoModeSwitchHandlerResult: + def from_dict(obj: Any) -> 'UIUnregisterDirectAutoModeSwitchHandlerResult': assert isinstance(obj, dict) unregistered = from_bool(obj.get("unregistered")) return UIUnregisterDirectAutoModeSwitchHandlerResult(unregistered) @@ -6033,7 +6019,7 @@ class UsageMetricsCodeChanges: """Total lines of code removed""" @staticmethod - def from_dict(obj: Any) -> UsageMetricsCodeChanges: + def from_dict(obj: Any) -> 'UsageMetricsCodeChanges': assert isinstance(obj, dict) files_modified = from_list(from_str, obj.get("filesModified")) files_modified_count = from_int(obj.get("filesModifiedCount")) @@ -6061,7 +6047,7 @@ class UsageMetricsModelMetricRequests: """Number of API requests made with this model""" @staticmethod - def from_dict(obj: Any) -> UsageMetricsModelMetricRequests: + def from_dict(obj: Any) -> 'UsageMetricsModelMetricRequests': assert isinstance(obj, dict) cost = from_float(obj.get("cost")) count = from_int(obj.get("count")) @@ -6082,7 +6068,7 @@ class UsageMetricsModelMetricTokenDetail: """Accumulated token count for this token type""" @staticmethod - def from_dict(obj: Any) -> UsageMetricsModelMetricTokenDetail: + def from_dict(obj: Any) -> 'UsageMetricsModelMetricTokenDetail': assert isinstance(obj, dict) token_count = from_int(obj.get("tokenCount")) return UsageMetricsModelMetricTokenDetail(token_count) @@ -6113,7 +6099,7 @@ class UsageMetricsModelMetricUsage: """Total output tokens used for reasoning""" @staticmethod - def from_dict(obj: Any) -> UsageMetricsModelMetricUsage: + def from_dict(obj: Any) -> 'UsageMetricsModelMetricUsage': assert isinstance(obj, dict) cache_read_tokens = from_int(obj.get("cacheReadTokens")) cache_write_tokens = from_int(obj.get("cacheWriteTokens")) @@ -6141,7 +6127,7 @@ class UsageMetricsTokenDetail: """Accumulated token count for this token type""" @staticmethod - def from_dict(obj: Any) -> UsageMetricsTokenDetail: + def from_dict(obj: Any) -> 'UsageMetricsTokenDetail': assert isinstance(obj, dict) token_count = from_int(obj.get("tokenCount")) return UsageMetricsTokenDetail(token_count) @@ -6169,7 +6155,7 @@ class WorkspacesCheckpoints: """Human-readable checkpoint title""" @staticmethod - def from_dict(obj: Any) -> WorkspacesCheckpoints: + def from_dict(obj: Any) -> 'WorkspacesCheckpoints': assert isinstance(obj, dict) filename = from_str(obj.get("filename")) number = from_int(obj.get("number")) @@ -6195,7 +6181,7 @@ class WorkspacesCreateFileRequest: """Relative path within the workspace files directory""" @staticmethod - def from_dict(obj: Any) -> WorkspacesCreateFileRequest: + def from_dict(obj: Any) -> 'WorkspacesCreateFileRequest': assert isinstance(obj, dict) content = from_str(obj.get("content")) path = from_str(obj.get("path")) @@ -6216,7 +6202,7 @@ class WorkspacesListFilesResult: """Relative file paths in the workspace files directory""" @staticmethod - def from_dict(obj: Any) -> WorkspacesListFilesResult: + def from_dict(obj: Any) -> 'WorkspacesListFilesResult': assert isinstance(obj, dict) files = from_list(from_str, obj.get("files")) return WorkspacesListFilesResult(files) @@ -6235,7 +6221,7 @@ class WorkspacesReadCheckpointRequest: """Checkpoint number to read""" @staticmethod - def from_dict(obj: Any) -> WorkspacesReadCheckpointRequest: + def from_dict(obj: Any) -> 'WorkspacesReadCheckpointRequest': assert isinstance(obj, dict) number = from_int(obj.get("number")) return WorkspacesReadCheckpointRequest(number) @@ -6254,7 +6240,7 @@ class WorkspacesReadCheckpointResult: """Checkpoint content as a UTF-8 string, or null when the checkpoint or workspace is missing""" @staticmethod - def from_dict(obj: Any) -> WorkspacesReadCheckpointResult: + def from_dict(obj: Any) -> 'WorkspacesReadCheckpointResult': assert isinstance(obj, dict) content = from_union([from_none, from_str], obj.get("content")) return WorkspacesReadCheckpointResult(content) @@ -6273,7 +6259,7 @@ class WorkspacesReadFileRequest: """Relative path within the workspace files directory""" @staticmethod - def from_dict(obj: Any) -> WorkspacesReadFileRequest: + def from_dict(obj: Any) -> 'WorkspacesReadFileRequest': assert isinstance(obj, dict) path = from_str(obj.get("path")) return WorkspacesReadFileRequest(path) @@ -6292,7 +6278,7 @@ class WorkspacesReadFileResult: """File content as a UTF-8 string""" @staticmethod - def from_dict(obj: Any) -> WorkspacesReadFileResult: + def from_dict(obj: Any) -> 'WorkspacesReadFileResult': assert isinstance(obj, dict) content = from_str(obj.get("content")) return WorkspacesReadFileResult(content) @@ -6311,7 +6297,7 @@ class WorkspacesSaveLargePasteRequest: """Pasted content to save as a UTF-8 file""" @staticmethod - def from_dict(obj: Any) -> WorkspacesSaveLargePasteRequest: + def from_dict(obj: Any) -> 'WorkspacesSaveLargePasteRequest': assert isinstance(obj, dict) content = from_str(obj.get("content")) return WorkspacesSaveLargePasteRequest(content) @@ -6333,7 +6319,7 @@ class Saved: """Size of the saved file in bytes""" @staticmethod - def from_dict(obj: Any) -> Saved: + def from_dict(obj: Any) -> 'Saved': assert isinstance(obj, dict) filename = from_str(obj.get("filename")) file_path = from_str(obj.get("filePath")) @@ -6355,7 +6341,7 @@ class AccountGetQuotaResult: """Quota snapshots keyed by type (e.g., chat, completions, premium_interactions)""" @staticmethod - def from_dict(obj: Any) -> AccountGetQuotaResult: + def from_dict(obj: Any) -> 'AccountGetQuotaResult': assert isinstance(obj, dict) quota_snapshots = from_dict(AccountQuotaSnapshot.from_dict, obj.get("quotaSnapshots")) return AccountGetQuotaResult(quota_snapshots) @@ -6389,7 +6375,7 @@ class SessionAuthStatus: """Human-readable authentication status description""" @staticmethod - def from_dict(obj: Any) -> SessionAuthStatus: + def from_dict(obj: Any) -> 'SessionAuthStatus': assert isinstance(obj, dict) is_authenticated = from_bool(obj.get("isAuthenticated")) auth_type = from_union([AuthInfoType, from_none], obj.get("authType")) @@ -6435,7 +6421,7 @@ class SlashCommandInput: """ @staticmethod - def from_dict(obj: Any) -> SlashCommandInput: + def from_dict(obj: Any) -> 'SlashCommandInput': assert isinstance(obj, dict) hint = from_str(obj.get("hint")) completion = from_union([SlashCommandInputCompletion, from_none], obj.get("completion")) @@ -6469,7 +6455,7 @@ class SendAttachmentDirectory: """Attachment type discriminator""" @staticmethod - def from_dict(obj: Any) -> SendAttachmentDirectory: + def from_dict(obj: Any) -> 'SendAttachmentDirectory': assert isinstance(obj, dict) display_name = from_str(obj.get("displayName")) path = from_str(obj.get("path")) @@ -6521,7 +6507,7 @@ class ConnectedRemoteSessionMetadata: """Optional session summary.""" @staticmethod - def from_dict(obj: Any) -> ConnectedRemoteSessionMetadata: + def from_dict(obj: Any) -> 'ConnectedRemoteSessionMetadata': assert isinstance(obj, dict) kind = ConnectedRemoteSessionMetadataKind(obj.get("kind")) modified_time = from_datetime(obj.get("modifiedTime")) @@ -6588,7 +6574,7 @@ class MCPServerConfigStdio: """Tools to include. Defaults to all tools if not specified.""" @staticmethod - def from_dict(obj: Any) -> MCPServerConfigStdio: + def from_dict(obj: Any) -> 'MCPServerConfigStdio': assert isinstance(obj, dict) command = from_str(obj.get("command")) args = from_union([lambda x: from_list(from_str, x), from_none], obj.get("args")) @@ -6642,7 +6628,7 @@ class CopilotUserResponseQuotaSnapshots: unlimited: bool | None = None @staticmethod - def from_dict(obj: Any) -> CopilotUserResponseQuotaSnapshots: + def from_dict(obj: Any) -> 'CopilotUserResponseQuotaSnapshots': assert isinstance(obj, dict) entitlement = from_union([from_float, from_none], obj.get("entitlement")) has_quota = from_union([from_bool, from_none], obj.get("has_quota")) @@ -6703,7 +6689,7 @@ class DiscoveredMCPServer: """Server transport type: stdio, http, sse, or memory""" @staticmethod - def from_dict(obj: Any) -> DiscoveredMCPServer: + def from_dict(obj: Any) -> 'DiscoveredMCPServer': assert isinstance(obj, dict) enabled = from_bool(obj.get("enabled")) name = from_str(obj.get("name")) @@ -6750,7 +6736,7 @@ class EventLogReadRequest: """ @staticmethod - def from_dict(obj: Any) -> EventLogReadRequest: + def from_dict(obj: Any) -> 'EventLogReadRequest': assert isinstance(obj, dict) agent_scope = from_union([EventsAgentScope, from_none], obj.get("agentScope")) cursor = from_union([from_str, from_none], obj.get("cursor")) @@ -6800,7 +6786,7 @@ class EventsReadResult: """ @staticmethod - def from_dict(obj: Any) -> EventsReadResult: + def from_dict(obj: Any) -> 'EventsReadResult': assert isinstance(obj, dict) cursor = from_str(obj.get("cursor")) cursor_status = EventsCursorStatus(obj.get("cursorStatus")) @@ -6837,7 +6823,7 @@ class Extension: """Process ID if the extension is running""" @staticmethod - def from_dict(obj: Any) -> Extension: + def from_dict(obj: Any) -> 'Extension': assert isinstance(obj, dict) id = from_str(obj.get("id")) name = from_str(obj.get("name")) @@ -6875,7 +6861,7 @@ class ExternalToolTextResultForLlmBinaryResultsForLlm: """Human-readable description of the binary data""" @staticmethod - def from_dict(obj: Any) -> ExternalToolTextResultForLlmBinaryResultsForLlm: + def from_dict(obj: Any) -> 'ExternalToolTextResultForLlmBinaryResultsForLlm': assert isinstance(obj, dict) data = from_str(obj.get("data")) mime_type = from_str(obj.get("mimeType")) @@ -6910,7 +6896,7 @@ class ExternalToolTextResultForLlmContentResourceLinkIcon: """Theme variant this icon is intended for""" @staticmethod - def from_dict(obj: Any) -> ExternalToolTextResultForLlmContentResourceLinkIcon: + def from_dict(obj: Any) -> 'ExternalToolTextResultForLlmContentResourceLinkIcon': assert isinstance(obj, dict) src = from_str(obj.get("src")) mime_type = from_union([from_str, from_none], obj.get("mimeType")) @@ -6946,7 +6932,7 @@ class ExternalToolTextResultForLlmContentAudio: """Content block type discriminator""" @staticmethod - def from_dict(obj: Any) -> ExternalToolTextResultForLlmContentAudio: + def from_dict(obj: Any) -> 'ExternalToolTextResultForLlmContentAudio': assert isinstance(obj, dict) data = from_str(obj.get("data")) mime_type = from_str(obj.get("mimeType")) @@ -6974,7 +6960,7 @@ class ExternalToolTextResultForLlmContentImage: """Content block type discriminator""" @staticmethod - def from_dict(obj: Any) -> ExternalToolTextResultForLlmContentImage: + def from_dict(obj: Any) -> 'ExternalToolTextResultForLlmContentImage': assert isinstance(obj, dict) data = from_str(obj.get("data")) mime_type = from_str(obj.get("mimeType")) @@ -6999,7 +6985,7 @@ class ExternalToolTextResultForLlmContentResource: """Content block type discriminator""" @staticmethod - def from_dict(obj: Any) -> ExternalToolTextResultForLlmContentResource: + def from_dict(obj: Any) -> 'ExternalToolTextResultForLlmContentResource': assert isinstance(obj, dict) resource = (lambda x: from_union([EmbeddedTextResourceContents.from_dict, EmbeddedBlobResourceContents.from_dict], x))(obj.get("resource")) return ExternalToolTextResultForLlmContentResource(resource) @@ -7028,7 +7014,7 @@ class ExternalToolTextResultForLlmContentTerminal: """Process exit code, if the command has completed""" @staticmethod - def from_dict(obj: Any) -> ExternalToolTextResultForLlmContentTerminal: + def from_dict(obj: Any) -> 'ExternalToolTextResultForLlmContentTerminal': assert isinstance(obj, dict) text = from_str(obj.get("text")) cwd = from_union([from_str, from_none], obj.get("cwd")) @@ -7057,7 +7043,7 @@ class ExternalToolTextResultForLlmContentText: """Content block type discriminator""" @staticmethod - def from_dict(obj: Any) -> ExternalToolTextResultForLlmContentText: + def from_dict(obj: Any) -> 'ExternalToolTextResultForLlmContentText': assert isinstance(obj, dict) text = from_str(obj.get("text")) return ExternalToolTextResultForLlmContentText(text) @@ -7091,7 +7077,7 @@ class SlashCommandTextResult: """ @staticmethod - def from_dict(obj: Any) -> SlashCommandTextResult: + def from_dict(obj: Any) -> 'SlashCommandTextResult': assert isinstance(obj, dict) text = from_str(obj.get("text")) markdown = from_union([from_bool, from_none], obj.get("markdown")) @@ -7135,7 +7121,7 @@ class HistoryCompactResult: """ @staticmethod - def from_dict(obj: Any) -> HistoryCompactResult: + def from_dict(obj: Any) -> 'HistoryCompactResult': assert isinstance(obj, dict) messages_removed = from_int(obj.get("messagesRemoved")) success = from_bool(obj.get("success")) @@ -7168,7 +7154,7 @@ class InstalledPluginSourceGithub: ref: str | None = None @staticmethod - def from_dict(obj: Any) -> InstalledPluginSourceGithub: + def from_dict(obj: Any) -> 'InstalledPluginSourceGithub': assert isinstance(obj, dict) repo = from_str(obj.get("repo")) source = FluffySource(obj.get("source")) @@ -7199,7 +7185,7 @@ class SessionInstalledPluginSourceGithub: ref: str | None = None @staticmethod - def from_dict(obj: Any) -> SessionInstalledPluginSourceGithub: + def from_dict(obj: Any) -> 'SessionInstalledPluginSourceGithub': assert isinstance(obj, dict) repo = from_str(obj.get("repo")) source = FluffySource(obj.get("source")) @@ -7227,7 +7213,7 @@ class InstalledPluginSourceLocal: """Constant value. Always "local".""" @staticmethod - def from_dict(obj: Any) -> InstalledPluginSourceLocal: + def from_dict(obj: Any) -> 'InstalledPluginSourceLocal': assert isinstance(obj, dict) path = from_str(obj.get("path")) source = TentacledSource(obj.get("source")) @@ -7249,7 +7235,7 @@ class SessionInstalledPluginSourceLocal: """Constant value. Always "local".""" @staticmethod - def from_dict(obj: Any) -> SessionInstalledPluginSourceLocal: + def from_dict(obj: Any) -> 'SessionInstalledPluginSourceLocal': assert isinstance(obj, dict) path = from_str(obj.get("path")) source = TentacledSource(obj.get("source")) @@ -7274,7 +7260,7 @@ class InstalledPluginSourceURL: ref: str | None = None @staticmethod - def from_dict(obj: Any) -> InstalledPluginSourceURL: + def from_dict(obj: Any) -> 'InstalledPluginSourceURL': assert isinstance(obj, dict) source = StickySource(obj.get("source")) url = from_str(obj.get("url")) @@ -7305,7 +7291,7 @@ class SessionInstalledPluginSourceURL: ref: str | None = None @staticmethod - def from_dict(obj: Any) -> SessionInstalledPluginSourceURL: + def from_dict(obj: Any) -> 'SessionInstalledPluginSourceURL': assert isinstance(obj, dict) source = StickySource(obj.get("source")) url = from_str(obj.get("url")) @@ -7357,7 +7343,7 @@ class InstructionsSources: """Short description (body after frontmatter) for use in instruction tables""" @staticmethod - def from_dict(obj: Any) -> InstructionsSources: + def from_dict(obj: Any) -> 'InstructionsSources': assert isinstance(obj, dict) content = from_str(obj.get("content")) id = from_str(obj.get("id")) @@ -7413,7 +7399,7 @@ class LogRequest: """Optional URL the user can open in their browser for more details""" @staticmethod - def from_dict(obj: Any) -> LogRequest: + def from_dict(obj: Any) -> 'LogRequest': assert isinstance(obj, dict) message = from_str(obj.get("message")) ephemeral = from_union([from_bool, from_none], obj.get("ephemeral")) @@ -7494,7 +7480,7 @@ class MCPServerConfig: """URL of the remote MCP server endpoint.""" @staticmethod - def from_dict(obj: Any) -> MCPServerConfig: + def from_dict(obj: Any) -> 'MCPServerConfig': assert isinstance(obj, dict) args = from_union([lambda x: from_list(from_str, x), from_none], obj.get("args")) command = from_union([from_str, from_none], obj.get("command")) @@ -7587,7 +7573,7 @@ class MCPServerConfigHTTP: """Remote transport type. Defaults to "http" when omitted.""" @staticmethod - def from_dict(obj: Any) -> MCPServerConfigHTTP: + def from_dict(obj: Any) -> 'MCPServerConfigHTTP': assert isinstance(obj, dict) url = from_str(obj.get("url")) auth = from_union([MCPServerConfigHTTPAuth.from_dict, from_none], obj.get("auth")) @@ -7647,7 +7633,7 @@ class MCPSamplingExecutionResult: """ @staticmethod - def from_dict(obj: Any) -> MCPSamplingExecutionResult: + def from_dict(obj: Any) -> 'MCPSamplingExecutionResult': assert isinstance(obj, dict) action = MCPSamplingExecutionAction(obj.get("action")) error = from_union([from_str, from_none], obj.get("error")) @@ -7672,7 +7658,7 @@ class MCPServerList: """Configured MCP servers""" @staticmethod - def from_dict(obj: Any) -> MCPServerList: + def from_dict(obj: Any) -> 'MCPServerList': assert isinstance(obj, dict) servers = from_list(MCPServer.from_dict, obj.get("servers")) return MCPServerList(servers) @@ -7696,7 +7682,7 @@ class MCPSetEnvValueModeParams: """ @staticmethod - def from_dict(obj: Any) -> MCPSetEnvValueModeParams: + def from_dict(obj: Any) -> 'MCPSetEnvValueModeParams': assert isinstance(obj, dict) mode = MCPSetEnvValueModeDetails(obj.get("mode")) return MCPSetEnvValueModeParams(mode) @@ -7715,7 +7701,7 @@ class MCPSetEnvValueModeResult: """Mode recorded on the session after the update""" @staticmethod - def from_dict(obj: Any) -> MCPSetEnvValueModeResult: + def from_dict(obj: Any) -> 'MCPSetEnvValueModeResult': assert isinstance(obj, dict) mode = MCPSetEnvValueModeDetails(obj.get("mode")) return MCPSetEnvValueModeResult(mode) @@ -7736,7 +7722,7 @@ class MetadataContextInfoResult: """ @staticmethod - def from_dict(obj: Any) -> MetadataContextInfoResult: + def from_dict(obj: Any) -> 'MetadataContextInfoResult': assert isinstance(obj, dict) context_info = from_union([SessionContextInfo.from_dict, from_none], obj.get("contextInfo")) return MetadataContextInfoResult(context_info) @@ -7779,7 +7765,7 @@ class SessionWorkingDirectoryContext: """Raw host string from the git remote URL (e.g. "github.com", "dev.azure.com")""" @staticmethod - def from_dict(obj: Any) -> SessionWorkingDirectoryContext: + def from_dict(obj: Any) -> 'SessionWorkingDirectoryContext': assert isinstance(obj, dict) cwd = from_str(obj.get("cwd")) base_commit = from_union([from_str, from_none], obj.get("baseCommit")) @@ -7834,7 +7820,7 @@ class SessionContext: """Repository slug in `owner/name` form, when known""" @staticmethod - def from_dict(obj: Any) -> SessionContext: + def from_dict(obj: Any) -> 'SessionContext': assert isinstance(obj, dict) cwd = from_str(obj.get("cwd")) branch = from_union([from_str, from_none], obj.get("branch")) @@ -7878,7 +7864,7 @@ class Workspace: user_named: bool | None = None @staticmethod - def from_dict(obj: Any) -> Workspace: + def from_dict(obj: Any) -> 'Workspace': assert isinstance(obj, dict) id = from_str(obj.get("id")) branch = from_union([from_str, from_none], obj.get("branch")) @@ -7955,7 +7941,7 @@ class MetadataSnapshotRemoteMetadata: """ @staticmethod - def from_dict(obj: Any) -> MetadataSnapshotRemoteMetadata: + def from_dict(obj: Any) -> 'MetadataSnapshotRemoteMetadata': assert isinstance(obj, dict) repository = MetadataSnapshotRemoteMetadataRepository.from_dict(obj.get("repository")) pull_request_number = from_union([from_int, from_none], obj.get("pullRequestNumber")) @@ -7985,7 +7971,7 @@ class ModelBilling: """Token-level pricing information for this model""" @staticmethod - def from_dict(obj: Any) -> ModelBilling: + def from_dict(obj: Any) -> 'ModelBilling': assert isinstance(obj, dict) multiplier = from_union([from_float, from_none], obj.get("multiplier")) token_prices = from_union([ModelBillingTokenPrices.from_dict, from_none], obj.get("tokenPrices")) @@ -8016,7 +8002,7 @@ class ModelCapabilitiesLimits: """Vision-specific limits""" @staticmethod - def from_dict(obj: Any) -> ModelCapabilitiesLimits: + def from_dict(obj: Any) -> 'ModelCapabilitiesLimits': assert isinstance(obj, dict) max_context_window_tokens = from_union([from_int, from_none], obj.get("max_context_window_tokens")) max_output_tokens = from_union([from_int, from_none], obj.get("max_output_tokens")) @@ -8047,7 +8033,7 @@ class ModelPolicy: """Usage terms or conditions for this model""" @staticmethod - def from_dict(obj: Any) -> ModelPolicy: + def from_dict(obj: Any) -> 'ModelPolicy': assert isinstance(obj, dict) state = ModelPolicyState(obj.get("state")) terms = from_union([from_str, from_none], obj.get("terms")) @@ -8078,7 +8064,7 @@ class ModelCapabilitiesOverrideLimits: """Vision-specific limits""" @staticmethod - def from_dict(obj: Any) -> ModelCapabilitiesOverrideLimits: + def from_dict(obj: Any) -> 'ModelCapabilitiesOverrideLimits': assert isinstance(obj, dict) max_context_window_tokens = from_union([from_int, from_none], obj.get("max_context_window_tokens")) max_output_tokens = from_union([from_int, from_none], obj.get("max_output_tokens")) @@ -8111,7 +8097,7 @@ class PendingPermissionRequestList: """ @staticmethod - def from_dict(obj: Any) -> PendingPermissionRequestList: + def from_dict(obj: Any) -> 'PendingPermissionRequestList': assert isinstance(obj, dict) items = from_list(PendingPermissionRequest.from_dict, obj.get("items")) return PendingPermissionRequestList(items) @@ -8136,7 +8122,7 @@ class PermissionDecisionApproveForLocation: """Location key (git root or cwd) to persist the approval to""" @staticmethod - def from_dict(obj: Any) -> PermissionDecisionApproveForLocation: + def from_dict(obj: Any) -> 'PermissionDecisionApproveForLocation': assert isinstance(obj, dict) approval = _load_PermissionDecisionApproveForLocationApproval(obj.get("approval")) location_key = from_str(obj.get("locationKey")) @@ -8161,7 +8147,7 @@ class PermissionDecisionApproveForLocationApprovalCommands: """Approval scoped to specific command identifiers.""" @staticmethod - def from_dict(obj: Any) -> PermissionDecisionApproveForLocationApprovalCommands: + def from_dict(obj: Any) -> 'PermissionDecisionApproveForLocationApprovalCommands': assert isinstance(obj, dict) command_identifiers = from_list(from_str, obj.get("commandIdentifiers")) return PermissionDecisionApproveForLocationApprovalCommands(command_identifiers) @@ -8184,7 +8170,7 @@ class PermissionDecisionApproveForSessionApprovalCommands: """Approval scoped to specific command identifiers.""" @staticmethod - def from_dict(obj: Any) -> PermissionDecisionApproveForSessionApprovalCommands: + def from_dict(obj: Any) -> 'PermissionDecisionApproveForSessionApprovalCommands': assert isinstance(obj, dict) command_identifiers = from_list(from_str, obj.get("commandIdentifiers")) return PermissionDecisionApproveForSessionApprovalCommands(command_identifiers) @@ -8207,7 +8193,7 @@ class PermissionsLocationsAddToolApprovalDetailsCommands: """Approval scoped to specific command identifiers.""" @staticmethod - def from_dict(obj: Any) -> PermissionsLocationsAddToolApprovalDetailsCommands: + def from_dict(obj: Any) -> 'PermissionsLocationsAddToolApprovalDetailsCommands': assert isinstance(obj, dict) command_identifiers = from_list(from_str, obj.get("commandIdentifiers")) return PermissionsLocationsAddToolApprovalDetailsCommands(command_identifiers) @@ -8230,7 +8216,7 @@ class PermissionDecisionApproveForLocationApprovalCustomTool: """Custom tool name.""" @staticmethod - def from_dict(obj: Any) -> PermissionDecisionApproveForLocationApprovalCustomTool: + def from_dict(obj: Any) -> 'PermissionDecisionApproveForLocationApprovalCustomTool': assert isinstance(obj, dict) tool_name = from_str(obj.get("toolName")) return PermissionDecisionApproveForLocationApprovalCustomTool(tool_name) @@ -8253,7 +8239,7 @@ class PermissionDecisionApproveForSessionApprovalCustomTool: """Custom tool name.""" @staticmethod - def from_dict(obj: Any) -> PermissionDecisionApproveForSessionApprovalCustomTool: + def from_dict(obj: Any) -> 'PermissionDecisionApproveForSessionApprovalCustomTool': assert isinstance(obj, dict) tool_name = from_str(obj.get("toolName")) return PermissionDecisionApproveForSessionApprovalCustomTool(tool_name) @@ -8276,7 +8262,7 @@ class PermissionsLocationsAddToolApprovalDetailsCustomTool: """Custom tool name.""" @staticmethod - def from_dict(obj: Any) -> PermissionsLocationsAddToolApprovalDetailsCustomTool: + def from_dict(obj: Any) -> 'PermissionsLocationsAddToolApprovalDetailsCustomTool': assert isinstance(obj, dict) tool_name = from_str(obj.get("toolName")) return PermissionsLocationsAddToolApprovalDetailsCustomTool(tool_name) @@ -8301,7 +8287,7 @@ class PermissionDecisionApproveForLocationApprovalExtensionManagement: """ @staticmethod - def from_dict(obj: Any) -> PermissionDecisionApproveForLocationApprovalExtensionManagement: + def from_dict(obj: Any) -> 'PermissionDecisionApproveForLocationApprovalExtensionManagement': assert isinstance(obj, dict) operation = from_union([from_str, from_none], obj.get("operation")) return PermissionDecisionApproveForLocationApprovalExtensionManagement(operation) @@ -8327,7 +8313,7 @@ class PermissionDecisionApproveForSessionApprovalExtensionManagement: """ @staticmethod - def from_dict(obj: Any) -> PermissionDecisionApproveForSessionApprovalExtensionManagement: + def from_dict(obj: Any) -> 'PermissionDecisionApproveForSessionApprovalExtensionManagement': assert isinstance(obj, dict) operation = from_union([from_str, from_none], obj.get("operation")) return PermissionDecisionApproveForSessionApprovalExtensionManagement(operation) @@ -8353,7 +8339,7 @@ class PermissionsLocationsAddToolApprovalDetailsExtensionManagement: """ @staticmethod - def from_dict(obj: Any) -> PermissionsLocationsAddToolApprovalDetailsExtensionManagement: + def from_dict(obj: Any) -> 'PermissionsLocationsAddToolApprovalDetailsExtensionManagement': assert isinstance(obj, dict) operation = from_union([from_str, from_none], obj.get("operation")) return PermissionsLocationsAddToolApprovalDetailsExtensionManagement(operation) @@ -8380,7 +8366,7 @@ class PermissionDecisionApproveForLocationApprovalMCP: """MCP tool name, or null to cover every tool on the server.""" @staticmethod - def from_dict(obj: Any) -> PermissionDecisionApproveForLocationApprovalMCP: + def from_dict(obj: Any) -> 'PermissionDecisionApproveForLocationApprovalMCP': assert isinstance(obj, dict) server_name = from_str(obj.get("serverName")) tool_name = from_union([from_none, from_str], obj.get("toolName")) @@ -8408,7 +8394,7 @@ class PermissionDecisionApproveForSessionApprovalMCP: """MCP tool name, or null to cover every tool on the server.""" @staticmethod - def from_dict(obj: Any) -> PermissionDecisionApproveForSessionApprovalMCP: + def from_dict(obj: Any) -> 'PermissionDecisionApproveForSessionApprovalMCP': assert isinstance(obj, dict) server_name = from_str(obj.get("serverName")) tool_name = from_union([from_none, from_str], obj.get("toolName")) @@ -8436,7 +8422,7 @@ class PermissionsLocationsAddToolApprovalDetailsMCP: """MCP tool name, or null to cover every tool on the server.""" @staticmethod - def from_dict(obj: Any) -> PermissionsLocationsAddToolApprovalDetailsMCP: + def from_dict(obj: Any) -> 'PermissionsLocationsAddToolApprovalDetailsMCP': assert isinstance(obj, dict) server_name = from_str(obj.get("serverName")) tool_name = from_union([from_none, from_str], obj.get("toolName")) @@ -8461,7 +8447,7 @@ class PermissionDecisionApproveForLocationApprovalMCPSampling: """MCP server name.""" @staticmethod - def from_dict(obj: Any) -> PermissionDecisionApproveForLocationApprovalMCPSampling: + def from_dict(obj: Any) -> 'PermissionDecisionApproveForLocationApprovalMCPSampling': assert isinstance(obj, dict) server_name = from_str(obj.get("serverName")) return PermissionDecisionApproveForLocationApprovalMCPSampling(server_name) @@ -8484,7 +8470,7 @@ class PermissionDecisionApproveForSessionApprovalMCPSampling: """MCP server name.""" @staticmethod - def from_dict(obj: Any) -> PermissionDecisionApproveForSessionApprovalMCPSampling: + def from_dict(obj: Any) -> 'PermissionDecisionApproveForSessionApprovalMCPSampling': assert isinstance(obj, dict) server_name = from_str(obj.get("serverName")) return PermissionDecisionApproveForSessionApprovalMCPSampling(server_name) @@ -8507,7 +8493,7 @@ class PermissionsLocationsAddToolApprovalDetailsMCPSampling: """MCP server name.""" @staticmethod - def from_dict(obj: Any) -> PermissionsLocationsAddToolApprovalDetailsMCPSampling: + def from_dict(obj: Any) -> 'PermissionsLocationsAddToolApprovalDetailsMCPSampling': assert isinstance(obj, dict) server_name = from_str(obj.get("serverName")) return PermissionsLocationsAddToolApprovalDetailsMCPSampling(server_name) @@ -8527,7 +8513,7 @@ class PermissionDecisionApproveForLocationApprovalMemory: """Approval covering writes to long-term memory.""" @staticmethod - def from_dict(obj: Any) -> PermissionDecisionApproveForLocationApprovalMemory: + def from_dict(obj: Any) -> 'PermissionDecisionApproveForLocationApprovalMemory': assert isinstance(obj, dict) return PermissionDecisionApproveForLocationApprovalMemory() @@ -8545,7 +8531,7 @@ class PermissionDecisionApproveForSessionApprovalMemory: """Approval covering writes to long-term memory.""" @staticmethod - def from_dict(obj: Any) -> PermissionDecisionApproveForSessionApprovalMemory: + def from_dict(obj: Any) -> 'PermissionDecisionApproveForSessionApprovalMemory': assert isinstance(obj, dict) return PermissionDecisionApproveForSessionApprovalMemory() @@ -8563,7 +8549,7 @@ class PermissionsLocationsAddToolApprovalDetailsMemory: """Approval covering writes to long-term memory.""" @staticmethod - def from_dict(obj: Any) -> PermissionsLocationsAddToolApprovalDetailsMemory: + def from_dict(obj: Any) -> 'PermissionsLocationsAddToolApprovalDetailsMemory': assert isinstance(obj, dict) return PermissionsLocationsAddToolApprovalDetailsMemory() @@ -8581,7 +8567,7 @@ class PermissionDecisionApproveForLocationApprovalRead: """Approval covering read-only filesystem operations.""" @staticmethod - def from_dict(obj: Any) -> PermissionDecisionApproveForLocationApprovalRead: + def from_dict(obj: Any) -> 'PermissionDecisionApproveForLocationApprovalRead': assert isinstance(obj, dict) return PermissionDecisionApproveForLocationApprovalRead() @@ -8599,7 +8585,7 @@ class PermissionDecisionApproveForSessionApprovalRead: """Approval covering read-only filesystem operations.""" @staticmethod - def from_dict(obj: Any) -> PermissionDecisionApproveForSessionApprovalRead: + def from_dict(obj: Any) -> 'PermissionDecisionApproveForSessionApprovalRead': assert isinstance(obj, dict) return PermissionDecisionApproveForSessionApprovalRead() @@ -8617,7 +8603,7 @@ class PermissionsLocationsAddToolApprovalDetailsRead: """Approval covering read-only filesystem operations.""" @staticmethod - def from_dict(obj: Any) -> PermissionsLocationsAddToolApprovalDetailsRead: + def from_dict(obj: Any) -> 'PermissionsLocationsAddToolApprovalDetailsRead': assert isinstance(obj, dict) return PermissionsLocationsAddToolApprovalDetailsRead() @@ -8635,7 +8621,7 @@ class PermissionDecisionApproveForLocationApprovalWrite: """Approval covering filesystem write operations.""" @staticmethod - def from_dict(obj: Any) -> PermissionDecisionApproveForLocationApprovalWrite: + def from_dict(obj: Any) -> 'PermissionDecisionApproveForLocationApprovalWrite': assert isinstance(obj, dict) return PermissionDecisionApproveForLocationApprovalWrite() @@ -8653,7 +8639,7 @@ class PermissionDecisionApproveForSessionApprovalWrite: """Approval covering filesystem write operations.""" @staticmethod - def from_dict(obj: Any) -> PermissionDecisionApproveForSessionApprovalWrite: + def from_dict(obj: Any) -> 'PermissionDecisionApproveForSessionApprovalWrite': assert isinstance(obj, dict) return PermissionDecisionApproveForSessionApprovalWrite() @@ -8671,7 +8657,7 @@ class PermissionsLocationsAddToolApprovalDetailsWrite: """Approval covering filesystem write operations.""" @staticmethod - def from_dict(obj: Any) -> PermissionsLocationsAddToolApprovalDetailsWrite: + def from_dict(obj: Any) -> 'PermissionsLocationsAddToolApprovalDetailsWrite': assert isinstance(obj, dict) return PermissionsLocationsAddToolApprovalDetailsWrite() @@ -8695,7 +8681,7 @@ class PermissionDecisionApproveForSession: """URL domain to approve for the rest of the session (URL prompts only)""" @staticmethod - def from_dict(obj: Any) -> PermissionDecisionApproveForSession: + def from_dict(obj: Any) -> 'PermissionDecisionApproveForSession': assert isinstance(obj, dict) approval = from_union([_load_PermissionDecisionApproveForSessionApproval, from_none], obj.get("approval")) domain = from_union([from_str, from_none], obj.get("domain")) @@ -8719,7 +8705,7 @@ class PermissionDecisionApproveOnce: """Approve this single request only""" @staticmethod - def from_dict(obj: Any) -> PermissionDecisionApproveOnce: + def from_dict(obj: Any) -> 'PermissionDecisionApproveOnce': assert isinstance(obj, dict) return PermissionDecisionApproveOnce() @@ -8740,7 +8726,7 @@ class PermissionDecisionApprovePermanently: """Approve and persist across sessions (URL prompts only)""" @staticmethod - def from_dict(obj: Any) -> PermissionDecisionApprovePermanently: + def from_dict(obj: Any) -> 'PermissionDecisionApprovePermanently': assert isinstance(obj, dict) domain = from_str(obj.get("domain")) return PermissionDecisionApprovePermanently(domain) @@ -8760,7 +8746,7 @@ class PermissionDecisionApproved: """The permission request was approved""" @staticmethod - def from_dict(obj: Any) -> PermissionDecisionApproved: + def from_dict(obj: Any) -> 'PermissionDecisionApproved': assert isinstance(obj, dict) return PermissionDecisionApproved() @@ -8784,7 +8770,7 @@ class PermissionDecisionApprovedForLocation: """The location key (git root or cwd) to persist the approval to""" @staticmethod - def from_dict(obj: Any) -> PermissionDecisionApprovedForLocation: + def from_dict(obj: Any) -> 'PermissionDecisionApprovedForLocation': assert isinstance(obj, dict) approval = UserToolSessionApproval.from_dict(obj.get("approval")) location_key = from_str(obj.get("locationKey")) @@ -8809,7 +8795,7 @@ class PermissionDecisionApprovedForSession: """Approved and remembered for the rest of the session""" @staticmethod - def from_dict(obj: Any) -> PermissionDecisionApprovedForSession: + def from_dict(obj: Any) -> 'PermissionDecisionApprovedForSession': assert isinstance(obj, dict) approval = UserToolSessionApproval.from_dict(obj.get("approval")) return PermissionDecisionApprovedForSession(approval) @@ -8832,7 +8818,7 @@ class PermissionDecisionCancelled: """Optional explanation of why the request was cancelled""" @staticmethod - def from_dict(obj: Any) -> PermissionDecisionCancelled: + def from_dict(obj: Any) -> 'PermissionDecisionCancelled': assert isinstance(obj, dict) reason = from_union([from_str, from_none], obj.get("reason")) return PermissionDecisionCancelled(reason) @@ -8859,7 +8845,7 @@ class PermissionDecisionDeniedByContentExclusionPolicy: """File path that triggered the exclusion""" @staticmethod - def from_dict(obj: Any) -> PermissionDecisionDeniedByContentExclusionPolicy: + def from_dict(obj: Any) -> 'PermissionDecisionDeniedByContentExclusionPolicy': assert isinstance(obj, dict) message = from_str(obj.get("message")) path = from_str(obj.get("path")) @@ -8887,7 +8873,7 @@ class PermissionDecisionDeniedByPermissionRequestHook: """Optional message from the hook explaining the denial""" @staticmethod - def from_dict(obj: Any) -> PermissionDecisionDeniedByPermissionRequestHook: + def from_dict(obj: Any) -> 'PermissionDecisionDeniedByPermissionRequestHook': assert isinstance(obj, dict) interrupt = from_union([from_bool, from_none], obj.get("interrupt")) message = from_union([from_str, from_none], obj.get("message")) @@ -8914,7 +8900,7 @@ class PermissionDecisionDeniedByRules: """Rules that denied the request""" @staticmethod - def from_dict(obj: Any) -> PermissionDecisionDeniedByRules: + def from_dict(obj: Any) -> 'PermissionDecisionDeniedByRules': assert isinstance(obj, dict) rules = from_list(PermissionRule.from_dict, obj.get("rules")) return PermissionDecisionDeniedByRules(rules) @@ -8940,7 +8926,7 @@ class PermissionDecisionDeniedInteractivelyByUser: """Whether to force-reject the current agent turn""" @staticmethod - def from_dict(obj: Any) -> PermissionDecisionDeniedInteractivelyByUser: + def from_dict(obj: Any) -> 'PermissionDecisionDeniedInteractivelyByUser': assert isinstance(obj, dict) feedback = from_union([from_str, from_none], obj.get("feedback")) force_reject = from_union([from_bool, from_none], obj.get("forceReject")) @@ -8964,7 +8950,7 @@ class PermissionDecisionDeniedNoApprovalRuleAndCouldNotRequestFromUser: """Denied because no approval rule matched and user confirmation was unavailable""" @staticmethod - def from_dict(obj: Any) -> PermissionDecisionDeniedNoApprovalRuleAndCouldNotRequestFromUser: + def from_dict(obj: Any) -> 'PermissionDecisionDeniedNoApprovalRuleAndCouldNotRequestFromUser': assert isinstance(obj, dict) return PermissionDecisionDeniedNoApprovalRuleAndCouldNotRequestFromUser() @@ -8985,7 +8971,7 @@ class PermissionDecisionReject: """Optional feedback explaining the rejection""" @staticmethod - def from_dict(obj: Any) -> PermissionDecisionReject: + def from_dict(obj: Any) -> 'PermissionDecisionReject': assert isinstance(obj, dict) feedback = from_union([from_str, from_none], obj.get("feedback")) return PermissionDecisionReject(feedback) @@ -9006,7 +8992,7 @@ class PermissionDecisionUserNotAvailable: """No user is available to confirm the request""" @staticmethod - def from_dict(obj: Any) -> PermissionDecisionUserNotAvailable: + def from_dict(obj: Any) -> 'PermissionDecisionUserNotAvailable': assert isinstance(obj, dict) return PermissionDecisionUserNotAvailable() @@ -9039,7 +9025,7 @@ class PermissionLocationApplyResult: """Whether the location is a git repo or directory""" @staticmethod - def from_dict(obj: Any) -> PermissionLocationApplyResult: + def from_dict(obj: Any) -> 'PermissionLocationApplyResult': assert isinstance(obj, dict) applied_directory_count = from_int(obj.get("appliedDirectoryCount")) applied_rule_count = from_int(obj.get("appliedRuleCount")) @@ -9071,7 +9057,7 @@ class PermissionLocationResolveResult: """Whether the location is a git repo or directory""" @staticmethod - def from_dict(obj: Any) -> PermissionLocationResolveResult: + def from_dict(obj: Any) -> 'PermissionLocationResolveResult': assert isinstance(obj, dict) location_key = from_str(obj.get("locationKey")) location_type = PermissionLocationType(obj.get("locationType")) @@ -9096,7 +9082,7 @@ class PermissionsConfigureAdditionalContentExclusionPolicyRule: if_none_match: list[str] | None = None @staticmethod - def from_dict(obj: Any) -> PermissionsConfigureAdditionalContentExclusionPolicyRule: + def from_dict(obj: Any) -> 'PermissionsConfigureAdditionalContentExclusionPolicyRule': assert isinstance(obj, dict) paths = from_list(from_str, obj.get("paths")) source = PermissionsConfigureAdditionalContentExclusionPolicyRuleSource.from_dict(obj.get("source")) @@ -9136,7 +9122,7 @@ class PermissionsModifyRulesParams: """ @staticmethod - def from_dict(obj: Any) -> PermissionsModifyRulesParams: + def from_dict(obj: Any) -> 'PermissionsModifyRulesParams': assert isinstance(obj, dict) scope = PermissionsModifyRulesScope(obj.get("scope")) add = from_union([lambda x: from_list(PermissionRule.from_dict, x), from_none], obj.get("add")) @@ -9164,7 +9150,7 @@ class PluginList: """Installed plugins""" @staticmethod - def from_dict(obj: Any) -> PluginList: + def from_dict(obj: Any) -> 'PluginList': assert isinstance(obj, dict) plugins = from_list(Plugin.from_dict, obj.get("plugins")) return PluginList(plugins) @@ -9186,7 +9172,7 @@ class QueuePendingItems: """Whether this item is a queued user message or a queued slash command / model change""" @staticmethod - def from_dict(obj: Any) -> QueuePendingItems: + def from_dict(obj: Any) -> 'QueuePendingItems': assert isinstance(obj, dict) display_text = from_str(obj.get("displayText")) kind = QueuePendingItemsKind(obj.get("kind")) @@ -9210,7 +9196,7 @@ class RemoteEnableRequest: """ @staticmethod - def from_dict(obj: Any) -> RemoteEnableRequest: + def from_dict(obj: Any) -> 'RemoteEnableRequest': assert isinstance(obj, dict) mode = from_union([RemoteSessionMode, from_none], obj.get("mode")) return RemoteEnableRequest(mode) @@ -9230,7 +9216,7 @@ class ScheduleList: """Active scheduled prompts, ordered by id.""" @staticmethod - def from_dict(obj: Any) -> ScheduleList: + def from_dict(obj: Any) -> 'ScheduleList': assert isinstance(obj, dict) entries = from_list(ScheduleEntry.from_dict, obj.get("entries")) return ScheduleList(entries) @@ -9249,7 +9235,7 @@ class ScheduleStopResult: """The removed entry, or omitted if no entry matched.""" @staticmethod - def from_dict(obj: Any) -> ScheduleStopResult: + def from_dict(obj: Any) -> 'ScheduleStopResult': assert isinstance(obj, dict) entry = from_union([ScheduleEntry.from_dict, from_none], obj.get("entry")) return ScheduleStopResult(entry) @@ -9272,7 +9258,7 @@ class SendAttachmentSelectionDetails: """Start position of the selection""" @staticmethod - def from_dict(obj: Any) -> SendAttachmentSelectionDetails: + def from_dict(obj: Any) -> 'SendAttachmentSelectionDetails': assert isinstance(obj, dict) end = SendAttachmentSelectionDetailsEnd.from_dict(obj.get("end")) start = SendAttachmentSelectionDetailsStart.from_dict(obj.get("start")) @@ -9302,7 +9288,7 @@ class SendAttachmentBlob: """User-facing display name for the attachment""" @staticmethod - def from_dict(obj: Any) -> SendAttachmentBlob: + def from_dict(obj: Any) -> 'SendAttachmentBlob': assert isinstance(obj, dict) data = from_str(obj.get("data")) mime_type = from_str(obj.get("mimeType")) @@ -9336,7 +9322,7 @@ class SendAttachmentFile: """Optional line range to scope the attachment to a specific section of the file""" @staticmethod - def from_dict(obj: Any) -> SendAttachmentFile: + def from_dict(obj: Any) -> 'SendAttachmentFile': assert isinstance(obj, dict) display_name = from_str(obj.get("displayName")) path = from_str(obj.get("path")) @@ -9376,7 +9362,7 @@ class SendAttachmentGithubReference: """URL to the referenced item on GitHub""" @staticmethod - def from_dict(obj: Any) -> SendAttachmentGithubReference: + def from_dict(obj: Any) -> 'SendAttachmentGithubReference': assert isinstance(obj, dict) number = from_int(obj.get("number")) reference_type = SendAttachmentGithubReferenceTypeEnum(obj.get("referenceType")) @@ -9451,7 +9437,7 @@ class SendRequest: """ @staticmethod - def from_dict(obj: Any) -> SendRequest: + def from_dict(obj: Any) -> 'SendRequest': assert isinstance(obj, dict) prompt = from_str(obj.get("prompt")) agent_mode = from_union([SendAgentMode, from_none], obj.get("agentMode")) @@ -9505,7 +9491,7 @@ class ServerSkillList: """All discovered skills across all sources""" @staticmethod - def from_dict(obj: Any) -> ServerSkillList: + def from_dict(obj: Any) -> 'ServerSkillList': assert isinstance(obj, dict) skills = from_list(ServerSkill.from_dict, obj.get("skills")) return ServerSkillList(skills) @@ -9526,7 +9512,7 @@ class SessionFSError: """Free-form detail about the error, for logging/diagnostics""" @staticmethod - def from_dict(obj: Any) -> SessionFSError: + def from_dict(obj: Any) -> 'SessionFSError': assert isinstance(obj, dict) code = SessionFSErrorCode(obj.get("code")) message = from_union([from_str, from_none], obj.get("message")) @@ -9550,7 +9536,7 @@ class SessionFSReaddirWithTypesEntry: """Entry type""" @staticmethod - def from_dict(obj: Any) -> SessionFSReaddirWithTypesEntry: + def from_dict(obj: Any) -> 'SessionFSReaddirWithTypesEntry': assert isinstance(obj, dict) name = from_str(obj.get("name")) type = SessionFSReaddirWithTypesEntryType(obj.get("type")) @@ -9580,7 +9566,7 @@ class SessionFSSetProviderRequest: """Optional capabilities declared by the provider""" @staticmethod - def from_dict(obj: Any) -> SessionFSSetProviderRequest: + def from_dict(obj: Any) -> 'SessionFSSetProviderRequest': assert isinstance(obj, dict) conventions = SessionFSSetProviderConventions(obj.get("conventions")) initial_cwd = from_str(obj.get("initialCwd")) @@ -9616,7 +9602,7 @@ class SessionFSSqliteQueryRequest: """Optional named bind parameters""" @staticmethod - def from_dict(obj: Any) -> SessionFSSqliteQueryRequest: + def from_dict(obj: Any) -> 'SessionFSSqliteQueryRequest': assert isinstance(obj, dict) query = from_str(obj.get("query")) query_type = SessionFSSqliteQueryType(obj.get("queryType")) @@ -9648,7 +9634,7 @@ class SessionsListRequest: """ @staticmethod - def from_dict(obj: Any) -> SessionsListRequest: + def from_dict(obj: Any) -> 'SessionsListRequest': assert isinstance(obj, dict) filter = from_union([SessionListFilter.from_dict, from_none], obj.get("filter")) metadata_limit = from_union([from_int, from_none], obj.get("metadataLimit")) @@ -9674,7 +9660,7 @@ class ShellKillRequest: """Signal to send (default: SIGTERM)""" @staticmethod - def from_dict(obj: Any) -> ShellKillRequest: + def from_dict(obj: Any) -> 'ShellKillRequest': assert isinstance(obj, dict) process_id = from_str(obj.get("processId")) signal = from_union([ShellKillSignal, from_none], obj.get("signal")) @@ -9734,7 +9720,7 @@ class AgentInfo: """ @staticmethod - def from_dict(obj: Any) -> AgentInfo: + def from_dict(obj: Any) -> 'AgentInfo': assert isinstance(obj, dict) description = from_str(obj.get("description")) display_name = from_str(obj.get("displayName")) @@ -9780,7 +9766,7 @@ class SkillList: """Available skills""" @staticmethod - def from_dict(obj: Any) -> SkillList: + def from_dict(obj: Any) -> 'SkillList': assert isinstance(obj, dict) skills = from_list(Skill.from_dict, obj.get("skills")) return SkillList(skills) @@ -9798,7 +9784,7 @@ class SkillsConfigSetDisabledSkillsRequest: """List of skill names to disable""" @staticmethod - def from_dict(obj: Any) -> SkillsConfigSetDisabledSkillsRequest: + def from_dict(obj: Any) -> 'SkillsConfigSetDisabledSkillsRequest': assert isinstance(obj, dict) disabled_skills = from_list(from_str, obj.get("disabledSkills")) return SkillsConfigSetDisabledSkillsRequest(disabled_skills) @@ -9817,7 +9803,7 @@ class SkillsGetInvokedResult: """Skills invoked during this session, ordered by invocation time (most recent last)""" @staticmethod - def from_dict(obj: Any) -> SkillsGetInvokedResult: + def from_dict(obj: Any) -> 'SkillsGetInvokedResult': assert isinstance(obj, dict) skills = from_list(SkillsInvokedSkill.from_dict, obj.get("skills")) return SkillsGetInvokedResult(skills) @@ -9850,7 +9836,7 @@ class SlashCommandAgentPromptResult: """ @staticmethod - def from_dict(obj: Any) -> SlashCommandAgentPromptResult: + def from_dict(obj: Any) -> 'SlashCommandAgentPromptResult': assert isinstance(obj, dict) display_prompt = from_str(obj.get("displayPrompt")) prompt = from_str(obj.get("prompt")) @@ -9886,7 +9872,7 @@ class SlashCommandCompletedResult: """ @staticmethod - def from_dict(obj: Any) -> SlashCommandCompletedResult: + def from_dict(obj: Any) -> 'SlashCommandCompletedResult': assert isinstance(obj, dict) message = from_union([from_str, from_none], obj.get("message")) runtime_settings_changed = from_union([from_bool, from_none], obj.get("runtimeSettingsChanged")) @@ -9924,7 +9910,7 @@ class SlashCommandSelectSubcommandResult: """ @staticmethod - def from_dict(obj: Any) -> SlashCommandSelectSubcommandResult: + def from_dict(obj: Any) -> 'SlashCommandSelectSubcommandResult': assert isinstance(obj, dict) command = from_str(obj.get("command")) options = from_list(SlashCommandSelectSubcommandOption.from_dict, obj.get("options")) @@ -9957,7 +9943,7 @@ class TaskAgentProgress: """The most recent intent reported by the agent""" @staticmethod - def from_dict(obj: Any) -> TaskAgentProgress: + def from_dict(obj: Any) -> 'TaskAgentProgress': assert isinstance(obj, dict) recent_activity = from_list(TaskProgressLine.from_dict, obj.get("recentActivity")) type = TaskAgentInfoType(obj.get("type")) @@ -10015,7 +10001,7 @@ class TaskShellInfo: """Process ID when available""" @staticmethod - def from_dict(obj: Any) -> TaskShellInfo: + def from_dict(obj: Any) -> 'TaskShellInfo': assert isinstance(obj, dict) attachment_mode = TaskShellInfoAttachmentMode(obj.get("attachmentMode")) command = from_str(obj.get("command")) @@ -10066,7 +10052,7 @@ class TaskShellProgress: """Process ID when available""" @staticmethod - def from_dict(obj: Any) -> TaskShellProgress: + def from_dict(obj: Any) -> 'TaskShellProgress': assert isinstance(obj, dict) recent_output = from_str(obj.get("recentOutput")) type = TaskShellInfoType(obj.get("type")) @@ -10093,7 +10079,7 @@ class PermissionLocationAddToolApprovalParams: """Location key (git root or cwd) to persist the approval to""" @staticmethod - def from_dict(obj: Any) -> PermissionLocationAddToolApprovalParams: + def from_dict(obj: Any) -> 'PermissionLocationAddToolApprovalParams': assert isinstance(obj, dict) approval = _load_PermissionsLocationsAddToolApprovalDetails(obj.get("approval")) location_key = from_str(obj.get("locationKey")) @@ -10113,7 +10099,7 @@ class ToolList: """List of available built-in tools with metadata""" @staticmethod - def from_dict(obj: Any) -> ToolList: + def from_dict(obj: Any) -> 'ToolList': assert isinstance(obj, dict) tools = from_list(Tool.from_dict, obj.get("tools")) return ToolList(tools) @@ -10137,7 +10123,7 @@ class UIHandlePendingAutoModeSwitchRequest: """ @staticmethod - def from_dict(obj: Any) -> UIHandlePendingAutoModeSwitchRequest: + def from_dict(obj: Any) -> 'UIHandlePendingAutoModeSwitchRequest': assert isinstance(obj, dict) request_id = from_str(obj.get("requestId")) response = UIAutoModeSwitchResponse(obj.get("response")) @@ -10158,7 +10144,7 @@ class UIElicitationArrayAnyOfFieldItems: """Selectable options, each with a value and a display label.""" @staticmethod - def from_dict(obj: Any) -> UIElicitationArrayAnyOfFieldItems: + def from_dict(obj: Any) -> 'UIElicitationArrayAnyOfFieldItems': assert isinstance(obj, dict) any_of = from_list(UIElicitationArrayAnyOfFieldItemsAnyOf.from_dict, obj.get("anyOf")) return UIElicitationArrayAnyOfFieldItems(any_of) @@ -10180,7 +10166,7 @@ class UIElicitationArrayEnumFieldItems: """Type discriminator. Always "string".""" @staticmethod - def from_dict(obj: Any) -> UIElicitationArrayEnumFieldItems: + def from_dict(obj: Any) -> 'UIElicitationArrayEnumFieldItems': assert isinstance(obj, dict) enum = from_list(from_str, obj.get("enum")) type = UIElicitationArrayEnumFieldItemsType(obj.get("type")) @@ -10206,7 +10192,7 @@ class UIElicitationArrayFieldItems: """Selectable options, each with a value and a display label.""" @staticmethod - def from_dict(obj: Any) -> UIElicitationArrayFieldItems: + def from_dict(obj: Any) -> 'UIElicitationArrayFieldItems': assert isinstance(obj, dict) enum = from_union([lambda x: from_list(from_str, x), from_none], obj.get("enum")) type = from_union([UIElicitationArrayEnumFieldItemsType, from_none], obj.get("type")) @@ -10247,7 +10233,7 @@ class UIElicitationStringEnumField: """Human-readable label for the field.""" @staticmethod - def from_dict(obj: Any) -> UIElicitationStringEnumField: + def from_dict(obj: Any) -> 'UIElicitationStringEnumField': assert isinstance(obj, dict) enum = from_list(from_str, obj.get("enum")) type = UIElicitationArrayEnumFieldItemsType(obj.get("type")) @@ -10298,7 +10284,7 @@ class UIElicitationSchemaPropertyString: """Human-readable label for the field.""" @staticmethod - def from_dict(obj: Any) -> UIElicitationSchemaPropertyString: + def from_dict(obj: Any) -> 'UIElicitationSchemaPropertyString': assert isinstance(obj, dict) type = UIElicitationArrayEnumFieldItemsType(obj.get("type")) default = from_union([from_str, from_none], obj.get("default")) @@ -10347,7 +10333,7 @@ class UIElicitationStringOneOfField: """Human-readable label for the field.""" @staticmethod - def from_dict(obj: Any) -> UIElicitationStringOneOfField: + def from_dict(obj: Any) -> 'UIElicitationStringOneOfField': assert isinstance(obj, dict) one_of = from_list(UIElicitationStringOneOfFieldOneOf.from_dict, obj.get("oneOf")) type = UIElicitationArrayEnumFieldItemsType(obj.get("type")) @@ -10380,7 +10366,7 @@ class UIElicitationResponse: """The form values submitted by the user (present when action is 'accept')""" @staticmethod - def from_dict(obj: Any) -> UIElicitationResponse: + def from_dict(obj: Any) -> 'UIElicitationResponse': assert isinstance(obj, dict) action = UIElicitationResponseAction(obj.get("action")) content = from_union([lambda x: from_dict(lambda x: from_union([from_float, from_bool, lambda x: from_list(from_str, x), from_str], x), x), from_none], obj.get("content")) @@ -10411,7 +10397,7 @@ class UIElicitationSchemaPropertyBoolean: """Human-readable label for the field.""" @staticmethod - def from_dict(obj: Any) -> UIElicitationSchemaPropertyBoolean: + def from_dict(obj: Any) -> 'UIElicitationSchemaPropertyBoolean': assert isinstance(obj, dict) type = UIElicitationSchemaPropertyBooleanType(obj.get("type")) default = from_union([from_bool, from_none], obj.get("default")) @@ -10454,7 +10440,7 @@ class UIElicitationSchemaPropertyNumber: """Human-readable label for the field.""" @staticmethod - def from_dict(obj: Any) -> UIElicitationSchemaPropertyNumber: + def from_dict(obj: Any) -> 'UIElicitationSchemaPropertyNumber': assert isinstance(obj, dict) type = UIElicitationSchemaPropertyNumberType(obj.get("type")) default = from_union([from_float, from_none], obj.get("default")) @@ -10499,7 +10485,7 @@ class UIExitPlanModeResponse: """ @staticmethod - def from_dict(obj: Any) -> UIExitPlanModeResponse: + def from_dict(obj: Any) -> 'UIExitPlanModeResponse': assert isinstance(obj, dict) approved = from_bool(obj.get("approved")) auto_approve_edits = from_union([from_bool, from_none], obj.get("autoApproveEdits")) @@ -10530,7 +10516,7 @@ class UIHandlePendingUserInputRequest: """Schema for the `UIUserInputResponse` type.""" @staticmethod - def from_dict(obj: Any) -> UIHandlePendingUserInputRequest: + def from_dict(obj: Any) -> 'UIHandlePendingUserInputRequest': assert isinstance(obj, dict) request_id = from_str(obj.get("requestId")) response = UIUserInputResponse.from_dict(obj.get("response")) @@ -10560,7 +10546,7 @@ class UsageMetricsModelMetric: """Accumulated nano-AI units cost for this model""" @staticmethod - def from_dict(obj: Any) -> UsageMetricsModelMetric: + def from_dict(obj: Any) -> 'UsageMetricsModelMetric': assert isinstance(obj, dict) requests = UsageMetricsModelMetricRequests.from_dict(obj.get("requests")) usage = UsageMetricsModelMetricUsage.from_dict(obj.get("usage")) @@ -10589,7 +10575,7 @@ class WorkspacesSaveLargePasteResult: """ @staticmethod - def from_dict(obj: Any) -> WorkspacesSaveLargePasteResult: + def from_dict(obj: Any) -> 'WorkspacesSaveLargePasteResult': assert isinstance(obj, dict) saved = from_union([Saved.from_dict, from_none], obj.get("saved")) return WorkspacesSaveLargePasteResult(saved) @@ -10627,7 +10613,7 @@ class SlashCommandInfo: """Optional unstructured input hint""" @staticmethod - def from_dict(obj: Any) -> SlashCommandInfo: + def from_dict(obj: Any) -> 'SlashCommandInfo': assert isinstance(obj, dict) allow_during_agent_execution = from_bool(obj.get("allowDuringAgentExecution")) description = from_str(obj.get("description")) @@ -10664,7 +10650,7 @@ class RemoteSessionConnectionResult: """SDK session ID for the connected remote session.""" @staticmethod - def from_dict(obj: Any) -> RemoteSessionConnectionResult: + def from_dict(obj: Any) -> 'RemoteSessionConnectionResult': assert isinstance(obj, dict) metadata = ConnectedRemoteSessionMetadata.from_dict(obj.get("metadata")) session_id = from_str(obj.get("sessionId")) @@ -10712,7 +10698,7 @@ class CopilotUserResponse: token_based_billing: bool | None = None @staticmethod - def from_dict(obj: Any) -> CopilotUserResponse: + def from_dict(obj: Any) -> 'CopilotUserResponse': assert isinstance(obj, dict) access_type_sku = from_union([from_str, from_none], obj.get("access_type_sku")) analytics_tracking_id = from_union([from_str, from_none], obj.get("analytics_tracking_id")) @@ -10797,7 +10783,7 @@ class MCPDiscoverResult: """MCP servers discovered from all sources""" @staticmethod - def from_dict(obj: Any) -> MCPDiscoverResult: + def from_dict(obj: Any) -> 'MCPDiscoverResult': assert isinstance(obj, dict) servers = from_list(DiscoveredMCPServer.from_dict, obj.get("servers")) return MCPDiscoverResult(servers) @@ -10816,7 +10802,7 @@ class ExtensionList: """Discovered extensions and their current status""" @staticmethod - def from_dict(obj: Any) -> ExtensionList: + def from_dict(obj: Any) -> 'ExtensionList': assert isinstance(obj, dict) extensions = from_list(Extension.from_dict, obj.get("extensions")) return ExtensionList(extensions) @@ -10839,7 +10825,7 @@ class PermissionDecisionApproveForLocationApprovalExtensionPermissionAccess: """Approval covering an extension's request to access a permission-gated capability.""" @staticmethod - def from_dict(obj: Any) -> PermissionDecisionApproveForLocationApprovalExtensionPermissionAccess: + def from_dict(obj: Any) -> 'PermissionDecisionApproveForLocationApprovalExtensionPermissionAccess': assert isinstance(obj, dict) extension_name = from_str(obj.get("extensionName")) return PermissionDecisionApproveForLocationApprovalExtensionPermissionAccess(extension_name) @@ -10863,7 +10849,7 @@ class PermissionDecisionApproveForSessionApprovalExtensionPermissionAccess: """Approval covering an extension's request to access a permission-gated capability.""" @staticmethod - def from_dict(obj: Any) -> PermissionDecisionApproveForSessionApprovalExtensionPermissionAccess: + def from_dict(obj: Any) -> 'PermissionDecisionApproveForSessionApprovalExtensionPermissionAccess': assert isinstance(obj, dict) extension_name = from_str(obj.get("extensionName")) return PermissionDecisionApproveForSessionApprovalExtensionPermissionAccess(extension_name) @@ -10886,7 +10872,7 @@ class PermissionsLocationsAddToolApprovalDetailsExtensionPermissionAccess: """Approval covering an extension's request to access a permission-gated capability.""" @staticmethod - def from_dict(obj: Any) -> PermissionsLocationsAddToolApprovalDetailsExtensionPermissionAccess: + def from_dict(obj: Any) -> 'PermissionsLocationsAddToolApprovalDetailsExtensionPermissionAccess': assert isinstance(obj, dict) extension_name = from_str(obj.get("extensionName")) return PermissionsLocationsAddToolApprovalDetailsExtensionPermissionAccess(extension_name) @@ -10925,7 +10911,7 @@ class ExternalToolTextResultForLlm: """Optional tool-specific telemetry""" @staticmethod - def from_dict(obj: Any) -> ExternalToolTextResultForLlm: + def from_dict(obj: Any) -> 'ExternalToolTextResultForLlm': assert isinstance(obj, dict) text_result_for_llm = from_str(obj.get("textResultForLlm")) binary_results_for_llm = from_union([lambda x: from_list(ExternalToolTextResultForLlmBinaryResultsForLlm.from_dict, x), from_none], obj.get("binaryResultsForLlm")) @@ -10983,7 +10969,7 @@ class ExternalToolTextResultForLlmContentResourceLink: """Human-readable display title for the resource""" @staticmethod - def from_dict(obj: Any) -> ExternalToolTextResultForLlmContentResourceLink: + def from_dict(obj: Any) -> 'ExternalToolTextResultForLlmContentResourceLink': assert isinstance(obj, dict) name = from_str(obj.get("name")) uri = from_str(obj.get("uri")) @@ -11033,7 +11019,7 @@ class InstalledPluginSource: url: str | None = None @staticmethod - def from_dict(obj: Any) -> InstalledPluginSource: + def from_dict(obj: Any) -> 'InstalledPluginSource': assert isinstance(obj, dict) source = PurpleSource(obj.get("source")) path = from_union([from_str, from_none], obj.get("path")) @@ -11077,7 +11063,7 @@ class SessionInstalledPluginSource: url: str | None = None @staticmethod - def from_dict(obj: Any) -> SessionInstalledPluginSource: + def from_dict(obj: Any) -> 'SessionInstalledPluginSource': assert isinstance(obj, dict) source = PurpleSource(obj.get("source")) path = from_union([from_str, from_none], obj.get("path")) @@ -11108,7 +11094,7 @@ class InstructionsGetSourcesResult: """Instruction sources for the session""" @staticmethod - def from_dict(obj: Any) -> InstructionsGetSourcesResult: + def from_dict(obj: Any) -> 'InstructionsGetSourcesResult': assert isinstance(obj, dict) sources = from_list(InstructionsSources.from_dict, obj.get("sources")) return InstructionsGetSourcesResult(sources) @@ -11129,7 +11115,7 @@ class MCPConfigAddRequest: """Unique name for the MCP server""" @staticmethod - def from_dict(obj: Any) -> MCPConfigAddRequest: + def from_dict(obj: Any) -> 'MCPConfigAddRequest': assert isinstance(obj, dict) config = MCPServerConfig.from_dict(obj.get("config")) name = from_str(obj.get("name")) @@ -11149,7 +11135,7 @@ class MCPConfigList: """All MCP servers from user config, keyed by name""" @staticmethod - def from_dict(obj: Any) -> MCPConfigList: + def from_dict(obj: Any) -> 'MCPConfigList': assert isinstance(obj, dict) servers = from_dict(MCPServerConfig.from_dict, obj.get("servers")) return MCPConfigList(servers) @@ -11170,7 +11156,7 @@ class MCPConfigUpdateRequest: """Name of the MCP server to update""" @staticmethod - def from_dict(obj: Any) -> MCPConfigUpdateRequest: + def from_dict(obj: Any) -> 'MCPConfigUpdateRequest': assert isinstance(obj, dict) config = MCPServerConfig.from_dict(obj.get("config")) name = from_str(obj.get("name")) @@ -11193,7 +11179,7 @@ class MetadataRecordContextChangeRequest: """ @staticmethod - def from_dict(obj: Any) -> MetadataRecordContextChangeRequest: + def from_dict(obj: Any) -> 'MetadataRecordContextChangeRequest': assert isinstance(obj, dict) context = SessionWorkingDirectoryContext.from_dict(obj.get("context")) return MetadataRecordContextChangeRequest(context) @@ -11234,7 +11220,7 @@ class SessionMetadata: """Short summary of the session, when one has been derived""" @staticmethod - def from_dict(obj: Any) -> SessionMetadata: + def from_dict(obj: Any) -> 'SessionMetadata': assert isinstance(obj, dict) is_remote = from_bool(obj.get("isRemote")) modified_time = from_str(obj.get("modifiedTime")) @@ -11273,7 +11259,7 @@ class SessionsGetLastForContextRequest: """ @staticmethod - def from_dict(obj: Any) -> SessionsGetLastForContextRequest: + def from_dict(obj: Any) -> 'SessionsGetLastForContextRequest': assert isinstance(obj, dict) context = from_union([SessionContext.from_dict, from_none], obj.get("context")) return SessionsGetLastForContextRequest(context) @@ -11311,7 +11297,7 @@ class PermissionPathsConfig: """ @staticmethod - def from_dict(obj: Any) -> PermissionPathsConfig: + def from_dict(obj: Any) -> 'PermissionPathsConfig': assert isinstance(obj, dict) additional_directories = from_union([lambda x: from_list(from_str, x), from_none], obj.get("additionalDirectories")) include_temp_directory = from_union([from_bool, from_none], obj.get("includeTempDirectory")) @@ -11364,7 +11350,7 @@ class WorkspaceSummary: """ISO 8601 timestamp when the workspace was last updated""" @staticmethod - def from_dict(obj: Any) -> WorkspaceSummary: + def from_dict(obj: Any) -> 'WorkspaceSummary': assert isinstance(obj, dict) id = from_str(obj.get("id")) branch = from_union([from_str, from_none], obj.get("branch")) @@ -11412,7 +11398,7 @@ class WorkspacesGetWorkspaceResult: """Current workspace metadata, or null if not available""" @staticmethod - def from_dict(obj: Any) -> WorkspacesGetWorkspaceResult: + def from_dict(obj: Any) -> 'WorkspacesGetWorkspaceResult': assert isinstance(obj, dict) path = from_union([from_str, from_none], obj.get("path")) workspace = from_union([Workspace.from_dict, from_none], obj.get("workspace")) @@ -11434,7 +11420,7 @@ class WorkspacesListCheckpointsResult: """Workspace checkpoints in chronological order. Empty when workspace is not enabled.""" @staticmethod - def from_dict(obj: Any) -> WorkspacesListCheckpointsResult: + def from_dict(obj: Any) -> 'WorkspacesListCheckpointsResult': assert isinstance(obj, dict) checkpoints = from_list(WorkspacesCheckpoints.from_dict, obj.get("checkpoints")) return WorkspacesListCheckpointsResult(checkpoints) @@ -11456,7 +11442,7 @@ class ModelCapabilitiesOverride: """Feature flags indicating what the model supports""" @staticmethod - def from_dict(obj: Any) -> ModelCapabilitiesOverride: + def from_dict(obj: Any) -> 'ModelCapabilitiesOverride': assert isinstance(obj, dict) limits = from_union([ModelCapabilitiesOverrideLimits.from_dict, from_none], obj.get("limits")) supports = from_union([ModelCapabilitiesOverrideSupports.from_dict, from_none], obj.get("supports")) @@ -11483,7 +11469,7 @@ class PermissionsConfigureAdditionalContentExclusionPolicy: """ @staticmethod - def from_dict(obj: Any) -> PermissionsConfigureAdditionalContentExclusionPolicy: + def from_dict(obj: Any) -> 'PermissionsConfigureAdditionalContentExclusionPolicy': assert isinstance(obj, dict) last_updated_at = from_union([from_float, from_str], obj.get("last_updated_at")) rules = from_list(PermissionsConfigureAdditionalContentExclusionPolicyRule.from_dict, obj.get("rules")) @@ -11512,7 +11498,7 @@ class QueuePendingItemsResult: """ @staticmethod - def from_dict(obj: Any) -> QueuePendingItemsResult: + def from_dict(obj: Any) -> 'QueuePendingItemsResult': assert isinstance(obj, dict) items = from_list(QueuePendingItems.from_dict, obj.get("items")) steering_messages = from_list(from_str, obj.get("steeringMessages")) @@ -11545,7 +11531,7 @@ class SendAttachmentSelection: """Attachment type discriminator""" @staticmethod - def from_dict(obj: Any) -> SendAttachmentSelection: + def from_dict(obj: Any) -> 'SendAttachmentSelection': assert isinstance(obj, dict) display_name = from_str(obj.get("displayName")) file_path = from_str(obj.get("filePath")) @@ -11573,7 +11559,7 @@ class SessionFSReadFileResult: """Describes a filesystem error.""" @staticmethod - def from_dict(obj: Any) -> SessionFSReadFileResult: + def from_dict(obj: Any) -> 'SessionFSReadFileResult': assert isinstance(obj, dict) content = from_str(obj.get("content")) error = from_union([SessionFSError.from_dict, from_none], obj.get("error")) @@ -11597,7 +11583,7 @@ class SessionFSReaddirResult: """Describes a filesystem error.""" @staticmethod - def from_dict(obj: Any) -> SessionFSReaddirResult: + def from_dict(obj: Any) -> 'SessionFSReaddirResult': assert isinstance(obj, dict) entries = from_list(from_str, obj.get("entries")) error = from_union([SessionFSError.from_dict, from_none], obj.get("error")) @@ -11631,7 +11617,7 @@ class SessionFSSqliteQueryResult: """SQLite last_insert_rowid() value for INSERT.""" @staticmethod - def from_dict(obj: Any) -> SessionFSSqliteQueryResult: + def from_dict(obj: Any) -> 'SessionFSSqliteQueryResult': assert isinstance(obj, dict) columns = from_list(from_str, obj.get("columns")) rows = from_list(lambda x: from_dict(lambda x: x, x), obj.get("rows")) @@ -11674,7 +11660,7 @@ class SessionFSStatResult: """Describes a filesystem error.""" @staticmethod - def from_dict(obj: Any) -> SessionFSStatResult: + def from_dict(obj: Any) -> 'SessionFSStatResult': assert isinstance(obj, dict) birthtime = from_datetime(obj.get("birthtime")) is_directory = from_bool(obj.get("isDirectory")) @@ -11707,7 +11693,7 @@ class SessionFSReaddirWithTypesResult: """Describes a filesystem error.""" @staticmethod - def from_dict(obj: Any) -> SessionFSReaddirWithTypesResult: + def from_dict(obj: Any) -> 'SessionFSReaddirWithTypesResult': assert isinstance(obj, dict) entries = from_list(SessionFSReaddirWithTypesEntry.from_dict, obj.get("entries")) error = from_union([SessionFSError.from_dict, from_none], obj.get("error")) @@ -11729,7 +11715,7 @@ class AgentGetCurrentResult: """Currently selected custom agent, or null if using the default agent""" @staticmethod - def from_dict(obj: Any) -> AgentGetCurrentResult: + def from_dict(obj: Any) -> 'AgentGetCurrentResult': assert isinstance(obj, dict) agent = from_union([AgentInfo.from_dict, from_none], obj.get("agent")) return AgentGetCurrentResult(agent) @@ -11749,7 +11735,7 @@ class AgentList: """Available custom agents""" @staticmethod - def from_dict(obj: Any) -> AgentList: + def from_dict(obj: Any) -> 'AgentList': assert isinstance(obj, dict) agents = from_list(AgentInfo.from_dict, obj.get("agents")) return AgentList(agents) @@ -11768,7 +11754,7 @@ class AgentReloadResult: """Reloaded custom agents""" @staticmethod - def from_dict(obj: Any) -> AgentReloadResult: + def from_dict(obj: Any) -> 'AgentReloadResult': assert isinstance(obj, dict) agents = from_list(AgentInfo.from_dict, obj.get("agents")) return AgentReloadResult(agents) @@ -11787,7 +11773,7 @@ class AgentSelectResult: """The newly selected custom agent""" @staticmethod - def from_dict(obj: Any) -> AgentSelectResult: + def from_dict(obj: Any) -> 'AgentSelectResult': assert isinstance(obj, dict) agent = AgentInfo.from_dict(obj.get("agent")) return AgentSelectResult(agent) @@ -11820,7 +11806,7 @@ class TaskProgress: """Recent stdout/stderr lines from the running shell command""" @staticmethod - def from_dict(obj: Any) -> TaskProgress: + def from_dict(obj: Any) -> 'TaskProgress': assert isinstance(obj, dict) type = TaskInfoType(obj.get("type")) latest_intent = from_union([from_str, from_none], obj.get("latestIntent")) @@ -11869,7 +11855,7 @@ class UIElicitationArrayAnyOfField: """Human-readable label for the field.""" @staticmethod - def from_dict(obj: Any) -> UIElicitationArrayAnyOfField: + def from_dict(obj: Any) -> 'UIElicitationArrayAnyOfField': assert isinstance(obj, dict) items = UIElicitationArrayAnyOfFieldItems.from_dict(obj.get("items")) type = UIElicitationArrayAnyOfFieldType(obj.get("type")) @@ -11923,7 +11909,7 @@ class UIElicitationArrayEnumField: """Human-readable label for the field.""" @staticmethod - def from_dict(obj: Any) -> UIElicitationArrayEnumField: + def from_dict(obj: Any) -> 'UIElicitationArrayEnumField': assert isinstance(obj, dict) items = UIElicitationArrayEnumFieldItems.from_dict(obj.get("items")) type = UIElicitationArrayAnyOfFieldType(obj.get("type")) @@ -12025,7 +12011,7 @@ class UIElicitationSchemaProperty: """Minimum allowed value (inclusive).""" @staticmethod - def from_dict(obj: Any) -> UIElicitationSchemaProperty: + def from_dict(obj: Any) -> 'UIElicitationSchemaProperty': assert isinstance(obj, dict) type = UIElicitationSchemaPropertyType(obj.get("type")) default = from_union([from_float, from_bool, lambda x: from_list(from_str, x), from_str, from_none], obj.get("default")) @@ -12090,7 +12076,7 @@ class UIHandlePendingElicitationRequest: """The elicitation response (accept with form values, decline, or cancel)""" @staticmethod - def from_dict(obj: Any) -> UIHandlePendingElicitationRequest: + def from_dict(obj: Any) -> 'UIHandlePendingElicitationRequest': assert isinstance(obj, dict) request_id = from_str(obj.get("requestId")) result = UIElicitationResponse.from_dict(obj.get("result")) @@ -12114,7 +12100,7 @@ class UIHandlePendingExitPlanModeRequest: """Schema for the `UIExitPlanModeResponse` type.""" @staticmethod - def from_dict(obj: Any) -> UIHandlePendingExitPlanModeRequest: + def from_dict(obj: Any) -> 'UIHandlePendingExitPlanModeRequest': assert isinstance(obj, dict) request_id = from_str(obj.get("requestId")) response = UIExitPlanModeResponse.from_dict(obj.get("response")) @@ -12167,7 +12153,7 @@ class UsageGetMetricsResult: """Session-wide accumulated nano-AI units cost""" @staticmethod - def from_dict(obj: Any) -> UsageGetMetricsResult: + def from_dict(obj: Any) -> 'UsageGetMetricsResult': assert isinstance(obj, dict) code_changes = UsageMetricsCodeChanges.from_dict(obj.get("codeChanges")) last_call_input_tokens = from_int(obj.get("lastCallInputTokens")) @@ -12209,7 +12195,7 @@ class CommandList: """Commands available in this session""" @staticmethod - def from_dict(obj: Any) -> CommandList: + def from_dict(obj: Any) -> 'CommandList': assert isinstance(obj, dict) commands = from_list(SlashCommandInfo.from_dict, obj.get("commands")) return CommandList(commands) @@ -12240,7 +12226,7 @@ class APIKeyAuthInfo: """ @staticmethod - def from_dict(obj: Any) -> APIKeyAuthInfo: + def from_dict(obj: Any) -> 'APIKeyAuthInfo': assert isinstance(obj, dict) api_key = from_str(obj.get("apiKey")) host = from_str(obj.get("host")) @@ -12276,7 +12262,7 @@ class CopilotAPITokenAuthInfo: """ @staticmethod - def from_dict(obj: Any) -> CopilotAPITokenAuthInfo: + def from_dict(obj: Any) -> 'CopilotAPITokenAuthInfo': assert isinstance(obj, dict) host = Host(obj.get("host")) copilot_user = from_union([CopilotUserResponse.from_dict, from_none], obj.get("copilotUser")) @@ -12319,7 +12305,7 @@ class EnvAuthInfo: """ @staticmethod - def from_dict(obj: Any) -> EnvAuthInfo: + def from_dict(obj: Any) -> 'EnvAuthInfo': assert isinstance(obj, dict) env_var = from_str(obj.get("envVar")) host = from_str(obj.get("host")) @@ -12364,7 +12350,7 @@ class GhCLIAuthInfo: """ @staticmethod - def from_dict(obj: Any) -> GhCLIAuthInfo: + def from_dict(obj: Any) -> 'GhCLIAuthInfo': assert isinstance(obj, dict) host = from_str(obj.get("host")) login = from_str(obj.get("login")) @@ -12403,7 +12389,7 @@ class HMACAuthInfo: """ @staticmethod - def from_dict(obj: Any) -> HMACAuthInfo: + def from_dict(obj: Any) -> 'HMACAuthInfo': assert isinstance(obj, dict) hmac = from_str(obj.get("hmac")) host = Host(obj.get("host")) @@ -12440,7 +12426,7 @@ class TokenAuthInfo: """ @staticmethod - def from_dict(obj: Any) -> TokenAuthInfo: + def from_dict(obj: Any) -> 'TokenAuthInfo': assert isinstance(obj, dict) host = from_str(obj.get("host")) token = from_str(obj.get("token")) @@ -12478,7 +12464,7 @@ class UserAuthInfo: """ @staticmethod - def from_dict(obj: Any) -> UserAuthInfo: + def from_dict(obj: Any) -> 'UserAuthInfo': assert isinstance(obj, dict) host = from_str(obj.get("host")) login = from_str(obj.get("login")) @@ -12582,7 +12568,7 @@ class PermissionDecisionApproveForIonApproval: external_ref_marker_external_ref_user_tool_session_approval: str | None = None @staticmethod - def from_dict(obj: Any) -> PermissionDecisionApproveForIonApproval: + def from_dict(obj: Any) -> 'PermissionDecisionApproveForIonApproval': assert isinstance(obj, dict) command_identifiers = from_union([lambda x: from_list(from_str, x), from_none], obj.get("commandIdentifiers")) kind = from_union([ApprovalKind, from_none], obj.get("kind")) @@ -12627,7 +12613,7 @@ class HandlePendingToolCallRequest: """Tool call result (string or expanded result object)""" @staticmethod - def from_dict(obj: Any) -> HandlePendingToolCallRequest: + def from_dict(obj: Any) -> 'HandlePendingToolCallRequest': assert isinstance(obj, dict) request_id = from_str(obj.get("requestId")) error = from_union([from_str, from_none], obj.get("error")) @@ -12670,7 +12656,7 @@ class InstalledPlugin: """Version installed (if available)""" @staticmethod - def from_dict(obj: Any) -> InstalledPlugin: + def from_dict(obj: Any) -> 'InstalledPlugin': assert isinstance(obj, dict) enabled = from_bool(obj.get("enabled")) installed_at = from_str(obj.get("installed_at")) @@ -12722,7 +12708,7 @@ class SessionInstalledPlugin: """Installed version, if known""" @staticmethod - def from_dict(obj: Any) -> SessionInstalledPlugin: + def from_dict(obj: Any) -> 'SessionInstalledPlugin': assert isinstance(obj, dict) enabled = from_bool(obj.get("enabled")) installed_at = from_str(obj.get("installed_at")) @@ -12756,7 +12742,7 @@ class SessionEnrichMetadataResult: """Same records, with summary and context backfilled""" @staticmethod - def from_dict(obj: Any) -> SessionEnrichMetadataResult: + def from_dict(obj: Any) -> 'SessionEnrichMetadataResult': assert isinstance(obj, dict) sessions = from_list(SessionMetadata.from_dict, obj.get("sessions")) return SessionEnrichMetadataResult(sessions) @@ -12775,7 +12761,7 @@ class SessionList: """Sessions ordered most-recently-modified first""" @staticmethod - def from_dict(obj: Any) -> SessionList: + def from_dict(obj: Any) -> 'SessionList': assert isinstance(obj, dict) sessions = from_list(SessionMetadata.from_dict, obj.get("sessions")) return SessionList(sessions) @@ -12796,7 +12782,7 @@ class SessionsEnrichMetadataRequest: """ @staticmethod - def from_dict(obj: Any) -> SessionsEnrichMetadataRequest: + def from_dict(obj: Any) -> 'SessionsEnrichMetadataRequest': assert isinstance(obj, dict) sessions = from_list(SessionMetadata.from_dict, obj.get("sessions")) return SessionsEnrichMetadataRequest(sessions) @@ -12863,7 +12849,7 @@ class SessionMetadataSnapshot: """ @staticmethod - def from_dict(obj: Any) -> SessionMetadataSnapshot: + def from_dict(obj: Any) -> 'SessionMetadataSnapshot': assert isinstance(obj, dict) already_in_use = from_bool(obj.get("alreadyInUse")) current_mode = MetadataSnapshotCurrentMode(obj.get("currentMode")) @@ -12936,7 +12922,7 @@ class PermissionsConfigureParams: """ @staticmethod - def from_dict(obj: Any) -> PermissionsConfigureParams: + def from_dict(obj: Any) -> 'PermissionsConfigureParams': assert isinstance(obj, dict) additional_content_exclusion_policies = from_union([lambda x: from_list(PermissionsConfigureAdditionalContentExclusionPolicy.from_dict, x), from_none], obj.get("additionalContentExclusionPolicies")) approve_all_read_permission_requests = from_union([from_bool, from_none], obj.get("approveAllReadPermissionRequests")) @@ -12973,7 +12959,7 @@ class TasksGetProgressResult: """ @staticmethod - def from_dict(obj: Any) -> TasksGetProgressResult: + def from_dict(obj: Any) -> 'TasksGetProgressResult': assert isinstance(obj, dict) progress = from_union([TaskProgress.from_dict, from_none], obj.get("progress")) return TasksGetProgressResult(progress) @@ -12999,7 +12985,7 @@ class UIElicitationSchema: """List of required field names""" @staticmethod - def from_dict(obj: Any) -> UIElicitationSchema: + def from_dict(obj: Any) -> 'UIElicitationSchema': assert isinstance(obj, dict) properties = from_dict(UIElicitationSchemaProperty.from_dict, obj.get("properties")) type = UIElicitationSchemaType(obj.get("type")) @@ -13025,7 +13011,7 @@ class SessionsSetAdditionalPluginsRequest: """ @staticmethod - def from_dict(obj: Any) -> SessionsSetAdditionalPluginsRequest: + def from_dict(obj: Any) -> 'SessionsSetAdditionalPluginsRequest': assert isinstance(obj, dict) plugins = from_list(InstalledPlugin.from_dict, obj.get("plugins")) return SessionsSetAdditionalPluginsRequest(plugins) @@ -13158,7 +13144,7 @@ class SessionUpdateOptionsParams: """Absolute working-directory path for shell tools.""" @staticmethod - def from_dict(obj: Any) -> SessionUpdateOptionsParams: + def from_dict(obj: Any) -> 'SessionUpdateOptionsParams': assert isinstance(obj, dict) additional_content_exclusion_policies = from_union([lambda x: from_list(lambda x: x, x), from_none], obj.get("additionalContentExclusionPolicies")) agent_context = from_union([from_str, from_none], obj.get("agentContext")) @@ -13286,7 +13272,7 @@ class UIElicitationRequest: """JSON Schema describing the form fields to present to the user""" @staticmethod - def from_dict(obj: Any) -> UIElicitationRequest: + def from_dict(obj: Any) -> 'UIElicitationRequest': assert isinstance(obj, dict) message = from_str(obj.get("message")) requested_schema = UIElicitationSchema.from_dict(obj.get("requestedSchema")) @@ -13322,7 +13308,7 @@ class MCPExecuteSamplingParams: """Name of the MCP server that initiated the sampling request""" @staticmethod - def from_dict(obj: Any) -> MCPExecuteSamplingParams: + def from_dict(obj: Any) -> 'MCPExecuteSamplingParams': assert isinstance(obj, dict) mcp_request_id = from_union([from_float, from_str], obj.get("mcpRequestId")) request = from_dict(lambda x: x, obj.get("request")) @@ -13355,7 +13341,7 @@ class MetadataContextInfoRequest: """ @staticmethod - def from_dict(obj: Any) -> MetadataContextInfoRequest: + def from_dict(obj: Any) -> 'MetadataContextInfoRequest': assert isinstance(obj, dict) output_token_limit = from_int(obj.get("outputTokenLimit")) prompt_token_limit = from_int(obj.get("promptTokenLimit")) @@ -13381,7 +13367,7 @@ class MetadataRecomputeContextTokensRequest: """ @staticmethod - def from_dict(obj: Any) -> MetadataRecomputeContextTokensRequest: + def from_dict(obj: Any) -> 'MetadataRecomputeContextTokensRequest': assert isinstance(obj, dict) model_id = from_str(obj.get("modelId")) return MetadataRecomputeContextTokensRequest(model_id) @@ -13402,7 +13388,7 @@ class ModelCapabilities: """Feature flags indicating what the model supports""" @staticmethod - def from_dict(obj: Any) -> ModelCapabilities: + def from_dict(obj: Any) -> 'ModelCapabilities': assert isinstance(obj, dict) limits = from_union([ModelCapabilitiesLimits.from_dict, from_none], obj.get("limits")) supports = from_union([ModelCapabilitiesSupports.from_dict, from_none], obj.get("supports")) @@ -13455,7 +13441,7 @@ class Model: """Supported reasoning effort levels (only present if model supports reasoning effort)""" @staticmethod - def from_dict(obj: Any) -> Model: + def from_dict(obj: Any) -> 'Model': assert isinstance(obj, dict) capabilities = ModelCapabilities.from_dict(obj.get("capabilities")) id = from_str(obj.get("id")) @@ -13496,7 +13482,7 @@ class ModelList: """List of available models with full metadata""" @staticmethod - def from_dict(obj: Any) -> ModelList: + def from_dict(obj: Any) -> 'ModelList': assert isinstance(obj, dict) models = from_list(Model.from_dict, obj.get("models")) return ModelList(models) @@ -13524,7 +13510,7 @@ class ModelSwitchToRequest: """Reasoning summary mode to request for supported model clients""" @staticmethod - def from_dict(obj: Any) -> ModelSwitchToRequest: + def from_dict(obj: Any) -> 'ModelSwitchToRequest': assert isinstance(obj, dict) model_id = from_str(obj.get("modelId")) model_capabilities = from_union([ModelCapabilitiesOverride.from_dict, from_none], obj.get("modelCapabilities")) @@ -13564,7 +13550,7 @@ class PermissionsSetApproveAllRequest: """Optional source for allow-all telemetry. Defaults to `rpc` when omitted for SDK callers.""" @staticmethod - def from_dict(obj: Any) -> PermissionsSetApproveAllRequest: + def from_dict(obj: Any) -> 'PermissionsSetApproveAllRequest': assert isinstance(obj, dict) enabled = from_bool(obj.get("enabled")) source = from_union([PermissionsSetApproveAllSource, from_none], obj.get("source")) @@ -13639,7 +13625,7 @@ class TaskAgentInfo: """Result text from the task when available""" @staticmethod - def from_dict(obj: Any) -> TaskAgentInfo: + def from_dict(obj: Any) -> 'TaskAgentInfo': assert isinstance(obj, dict) agent_type = from_str(obj.get("agentType")) description = from_str(obj.get("description")) @@ -14203,7 +14189,7 @@ class RPC: workspace_summary: WorkspaceSummary | None = None @staticmethod - def from_dict(obj: Any) -> RPC: + def from_dict(obj: Any) -> 'RPC': assert isinstance(obj, dict) abort_request = AbortRequest.from_dict(obj.get("AbortRequest")) abort_result = AbortResult.from_dict(obj.get("AbortResult")) @@ -15234,7 +15220,7 @@ def rpc_to_dict(x: RPC) -> Any: # The new auth credentials to install on the session. When omitted or `undefined`, the call is a no-op and the session's existing credentials are preserved. The runtime stores the value verbatim and uses it for outbound model/API requests; it does NOT re-validate or re-fetch the associated Copilot user response. Several variants carry secret material; treat this method's params as containing secrets at rest and in transit. AuthInfo = HMACAuthInfo | EnvAuthInfo | TokenAuthInfo | CopilotAPITokenAuthInfo | UserAuthInfo | GhCLIAuthInfo | APIKeyAuthInfo -def _load_AuthInfo(obj: Any) -> AuthInfo: +def _load_AuthInfo(obj: Any) -> "AuthInfo": assert isinstance(obj, dict) kind = obj.get("type") match kind: @@ -15250,7 +15236,7 @@ def _load_AuthInfo(obj: Any) -> AuthInfo: # A content block within a tool result, which may be text, terminal output, image, audio, or a resource ExternalToolTextResultForLlmContent = ExternalToolTextResultForLlmContentText | ExternalToolTextResultForLlmContentTerminal | ExternalToolTextResultForLlmContentImage | ExternalToolTextResultForLlmContentAudio | ExternalToolTextResultForLlmContentResourceLink | ExternalToolTextResultForLlmContentResource -def _load_ExternalToolTextResultForLlmContent(obj: Any) -> ExternalToolTextResultForLlmContent: +def _load_ExternalToolTextResultForLlmContent(obj: Any) -> "ExternalToolTextResultForLlmContent": assert isinstance(obj, dict) kind = obj.get("type") match kind: @@ -15265,7 +15251,7 @@ def _load_ExternalToolTextResultForLlmContent(obj: Any) -> ExternalToolTextResul # The client's response to the pending permission prompt PermissionDecision = PermissionDecisionApproveOnce | PermissionDecisionApproveForSession | PermissionDecisionApproveForLocation | PermissionDecisionApprovePermanently | PermissionDecisionReject | PermissionDecisionUserNotAvailable | PermissionDecisionApproved | PermissionDecisionApprovedForSession | PermissionDecisionApprovedForLocation | PermissionDecisionCancelled | PermissionDecisionDeniedByRules | PermissionDecisionDeniedNoApprovalRuleAndCouldNotRequestFromUser | PermissionDecisionDeniedInteractivelyByUser | PermissionDecisionDeniedByContentExclusionPolicy | PermissionDecisionDeniedByPermissionRequestHook -def _load_PermissionDecision(obj: Any) -> PermissionDecision: +def _load_PermissionDecision(obj: Any) -> "PermissionDecision": assert isinstance(obj, dict) kind = obj.get("kind") match kind: @@ -15289,7 +15275,7 @@ def _load_PermissionDecision(obj: Any) -> PermissionDecision: # Approval to persist for this location PermissionDecisionApproveForLocationApproval = PermissionDecisionApproveForLocationApprovalCommands | PermissionDecisionApproveForLocationApprovalRead | PermissionDecisionApproveForLocationApprovalWrite | PermissionDecisionApproveForLocationApprovalMCP | PermissionDecisionApproveForLocationApprovalMCPSampling | PermissionDecisionApproveForLocationApprovalMemory | PermissionDecisionApproveForLocationApprovalCustomTool | PermissionDecisionApproveForLocationApprovalExtensionManagement | PermissionDecisionApproveForLocationApprovalExtensionPermissionAccess -def _load_PermissionDecisionApproveForLocationApproval(obj: Any) -> PermissionDecisionApproveForLocationApproval: +def _load_PermissionDecisionApproveForLocationApproval(obj: Any) -> "PermissionDecisionApproveForLocationApproval": assert isinstance(obj, dict) kind = obj.get("kind") match kind: @@ -15307,7 +15293,7 @@ def _load_PermissionDecisionApproveForLocationApproval(obj: Any) -> PermissionDe # Session-scoped approval to remember (tool prompts only; omitted for path/url prompts) PermissionDecisionApproveForSessionApproval = PermissionDecisionApproveForSessionApprovalCommands | PermissionDecisionApproveForSessionApprovalRead | PermissionDecisionApproveForSessionApprovalWrite | PermissionDecisionApproveForSessionApprovalMCP | PermissionDecisionApproveForSessionApprovalMCPSampling | PermissionDecisionApproveForSessionApprovalMemory | PermissionDecisionApproveForSessionApprovalCustomTool | PermissionDecisionApproveForSessionApprovalExtensionManagement | PermissionDecisionApproveForSessionApprovalExtensionPermissionAccess -def _load_PermissionDecisionApproveForSessionApproval(obj: Any) -> PermissionDecisionApproveForSessionApproval: +def _load_PermissionDecisionApproveForSessionApproval(obj: Any) -> "PermissionDecisionApproveForSessionApproval": assert isinstance(obj, dict) kind = obj.get("kind") match kind: @@ -15325,7 +15311,7 @@ def _load_PermissionDecisionApproveForSessionApproval(obj: Any) -> PermissionDec # Tool approval to persist and apply PermissionsLocationsAddToolApprovalDetails = PermissionsLocationsAddToolApprovalDetailsCommands | PermissionsLocationsAddToolApprovalDetailsRead | PermissionsLocationsAddToolApprovalDetailsWrite | PermissionsLocationsAddToolApprovalDetailsMCP | PermissionsLocationsAddToolApprovalDetailsMCPSampling | PermissionsLocationsAddToolApprovalDetailsMemory | PermissionsLocationsAddToolApprovalDetailsCustomTool | PermissionsLocationsAddToolApprovalDetailsExtensionManagement | PermissionsLocationsAddToolApprovalDetailsExtensionPermissionAccess -def _load_PermissionsLocationsAddToolApprovalDetails(obj: Any) -> PermissionsLocationsAddToolApprovalDetails: +def _load_PermissionsLocationsAddToolApprovalDetails(obj: Any) -> "PermissionsLocationsAddToolApprovalDetails": assert isinstance(obj, dict) kind = obj.get("kind") match kind: @@ -15343,7 +15329,7 @@ def _load_PermissionsLocationsAddToolApprovalDetails(obj: Any) -> PermissionsLoc # Result of the queued command execution. QueuedCommandResult = QueuedCommandHandled | QueuedCommandNotHandled -def _load_QueuedCommandResult(obj: Any) -> QueuedCommandResult: +def _load_QueuedCommandResult(obj: Any) -> "QueuedCommandResult": assert isinstance(obj, dict) kind = obj.get("handled") match kind: @@ -15354,7 +15340,7 @@ def _load_QueuedCommandResult(obj: Any) -> QueuedCommandResult: # A user message attachment — a file, directory, code selection, blob, or GitHub reference SendAttachment = SendAttachmentFile | SendAttachmentDirectory | SendAttachmentSelection | SendAttachmentGithubReference | SendAttachmentBlob -def _load_SendAttachment(obj: Any) -> SendAttachment: +def _load_SendAttachment(obj: Any) -> "SendAttachment": assert isinstance(obj, dict) kind = obj.get("type") match kind: @@ -15368,7 +15354,7 @@ def _load_SendAttachment(obj: Any) -> SendAttachment: # Result of invoking the slash command (text output, prompt to send to the agent, or completion). SlashCommandInvocationResult = SlashCommandTextResult | SlashCommandAgentPromptResult | SlashCommandCompletedResult | SlashCommandSelectSubcommandResult -def _load_SlashCommandInvocationResult(obj: Any) -> SlashCommandInvocationResult: +def _load_SlashCommandInvocationResult(obj: Any) -> "SlashCommandInvocationResult": assert isinstance(obj, dict) kind = obj.get("kind") match kind: @@ -15381,7 +15367,7 @@ def _load_SlashCommandInvocationResult(obj: Any) -> SlashCommandInvocationResult # Schema for the `TaskInfo` type. TaskInfo = TaskAgentInfo | TaskShellInfo -def _load_TaskInfo(obj: Any) -> TaskInfo: +def _load_TaskInfo(obj: Any) -> "TaskInfo": assert isinstance(obj, dict) kind = obj.get("type") match kind: @@ -15431,7 +15417,7 @@ def _patch_model_capabilities(data: dict) -> dict: class ServerModelsApi: - def __init__(self, client: JsonRpcClient): + def __init__(self, client: "JsonRpcClient"): self._client = client async def list(self, params: ModelsListRequest, *, timeout: float | None = None) -> ModelList: @@ -15441,7 +15427,7 @@ async def list(self, params: ModelsListRequest, *, timeout: float | None = None) class ServerToolsApi: - def __init__(self, client: JsonRpcClient): + def __init__(self, client: "JsonRpcClient"): self._client = client async def list(self, params: ToolsListRequest, *, timeout: float | None = None) -> ToolList: @@ -15451,7 +15437,7 @@ async def list(self, params: ToolsListRequest, *, timeout: float | None = None) class ServerAccountApi: - def __init__(self, client: JsonRpcClient): + def __init__(self, client: "JsonRpcClient"): self._client = client async def get_quota(self, params: AccountGetQuotaRequest, *, timeout: float | None = None) -> AccountGetQuotaResult: @@ -15461,7 +15447,7 @@ async def get_quota(self, params: AccountGetQuotaRequest, *, timeout: float | No class ServerSecretsApi: - def __init__(self, client: JsonRpcClient): + def __init__(self, client: "JsonRpcClient"): self._client = client async def add_filter_values(self, params: SecretsAddFilterValuesRequest, *, timeout: float | None = None) -> SecretsAddFilterValuesResult: @@ -15471,7 +15457,7 @@ async def add_filter_values(self, params: SecretsAddFilterValuesRequest, *, time class ServerMcpConfigApi: - def __init__(self, client: JsonRpcClient): + def __init__(self, client: "JsonRpcClient"): self._client = client async def list(self, *, timeout: float | None = None) -> MCPConfigList: @@ -15505,7 +15491,7 @@ async def disable(self, params: MCPConfigDisableRequest, *, timeout: float | Non class ServerMcpApi: - def __init__(self, client: JsonRpcClient): + def __init__(self, client: "JsonRpcClient"): self._client = client self.config = ServerMcpConfigApi(client) @@ -15516,7 +15502,7 @@ async def discover(self, params: MCPDiscoverRequest, *, timeout: float | None = class ServerSkillsConfigApi: - def __init__(self, client: JsonRpcClient): + def __init__(self, client: "JsonRpcClient"): self._client = client async def set_disabled_skills(self, params: SkillsConfigSetDisabledSkillsRequest, *, timeout: float | None = None) -> None: @@ -15526,7 +15512,7 @@ async def set_disabled_skills(self, params: SkillsConfigSetDisabledSkillsRequest class ServerSkillsApi: - def __init__(self, client: JsonRpcClient): + def __init__(self, client: "JsonRpcClient"): self._client = client self.config = ServerSkillsConfigApi(client) @@ -15537,7 +15523,7 @@ async def discover(self, params: SkillsDiscoverRequest, *, timeout: float | None class ServerSessionFsApi: - def __init__(self, client: JsonRpcClient): + def __init__(self, client: "JsonRpcClient"): self._client = client async def set_provider(self, params: SessionFSSetProviderRequest, *, timeout: float | None = None) -> SessionFSSetProviderResult: @@ -15548,7 +15534,7 @@ async def set_provider(self, params: SessionFSSetProviderRequest, *, timeout: fl # Experimental: this API group is experimental and may change or be removed. class ServerSessionsApi: - def __init__(self, client: JsonRpcClient): + def __init__(self, client: "JsonRpcClient"): self._client = client async def fork(self, params: SessionsForkRequest, *, timeout: float | None = None) -> SessionsForkResult: @@ -15648,7 +15634,7 @@ async def set_additional_plugins(self, params: SessionsSetAdditionalPluginsReque class ServerRpc: """Typed server-scoped RPC methods.""" - def __init__(self, client: JsonRpcClient): + def __init__(self, client: "JsonRpcClient"): self._client = client self.models = ServerModelsApi(client) self.tools = ServerToolsApi(client) @@ -15667,7 +15653,7 @@ async def ping(self, params: PingRequest, *, timeout: float | None = None) -> Pi class _InternalServerRpc: """Internal SDK server-scoped RPC methods. Not part of the public API.""" - def __init__(self, client: JsonRpcClient): + def __init__(self, client: "JsonRpcClient"): self._client = client async def connect(self, params: ConnectRequest, *, timeout: float | None = None) -> ConnectResult: @@ -15678,7 +15664,7 @@ async def connect(self, params: ConnectRequest, *, timeout: float | None = None) # Experimental: this API group is experimental and may change or be removed. class AuthApi: - def __init__(self, client: JsonRpcClient, session_id: str): + def __init__(self, client: "JsonRpcClient", session_id: str): self._client = client self._session_id = session_id @@ -15695,7 +15681,7 @@ async def set_credentials(self, params: SessionSetCredentialsParams, *, timeout: # Experimental: this API group is experimental and may change or be removed. class ModelApi: - def __init__(self, client: JsonRpcClient, session_id: str): + def __init__(self, client: "JsonRpcClient", session_id: str): self._client = client self._session_id = session_id @@ -15718,7 +15704,7 @@ async def set_reasoning_effort(self, params: ModelSetReasoningEffortRequest, *, # Experimental: this API group is experimental and may change or be removed. class ModeApi: - def __init__(self, client: JsonRpcClient, session_id: str): + def __init__(self, client: "JsonRpcClient", session_id: str): self._client = client self._session_id = session_id @@ -15735,7 +15721,7 @@ async def set(self, params: ModeSetRequest, *, timeout: float | None = None) -> # Experimental: this API group is experimental and may change or be removed. class NameApi: - def __init__(self, client: JsonRpcClient, session_id: str): + def __init__(self, client: "JsonRpcClient", session_id: str): self._client = client self._session_id = session_id @@ -15758,7 +15744,7 @@ async def set_auto(self, params: NameSetAutoRequest, *, timeout: float | None = # Experimental: this API group is experimental and may change or be removed. class PlanApi: - def __init__(self, client: JsonRpcClient, session_id: str): + def __init__(self, client: "JsonRpcClient", session_id: str): self._client = client self._session_id = session_id @@ -15779,7 +15765,7 @@ async def delete(self, *, timeout: float | None = None) -> None: # Experimental: this API group is experimental and may change or be removed. class WorkspacesApi: - def __init__(self, client: JsonRpcClient, session_id: str): + def __init__(self, client: "JsonRpcClient", session_id: str): self._client = client self._session_id = session_id @@ -15822,7 +15808,7 @@ async def save_large_paste(self, params: WorkspacesSaveLargePasteRequest, *, tim # Experimental: this API group is experimental and may change or be removed. class InstructionsApi: - def __init__(self, client: JsonRpcClient, session_id: str): + def __init__(self, client: "JsonRpcClient", session_id: str): self._client = client self._session_id = session_id @@ -15833,7 +15819,7 @@ async def get_sources(self, *, timeout: float | None = None) -> InstructionsGetS # Experimental: this API group is experimental and may change or be removed. class FleetApi: - def __init__(self, client: JsonRpcClient, session_id: str): + def __init__(self, client: "JsonRpcClient", session_id: str): self._client = client self._session_id = session_id @@ -15846,7 +15832,7 @@ async def start(self, params: FleetStartRequest, *, timeout: float | None = None # Experimental: this API group is experimental and may change or be removed. class AgentApi: - def __init__(self, client: JsonRpcClient, session_id: str): + def __init__(self, client: "JsonRpcClient", session_id: str): self._client = client self._session_id = session_id @@ -15875,7 +15861,7 @@ async def reload(self, *, timeout: float | None = None) -> AgentReloadResult: # Experimental: this API group is experimental and may change or be removed. class TasksApi: - def __init__(self, client: JsonRpcClient, session_id: str): + def __init__(self, client: "JsonRpcClient", session_id: str): self._client = client self._session_id = session_id @@ -15938,7 +15924,7 @@ async def send_message(self, params: TasksSendMessageRequest, *, timeout: float # Experimental: this API group is experimental and may change or be removed. class SkillsApi: - def __init__(self, client: JsonRpcClient, session_id: str): + def __init__(self, client: "JsonRpcClient", session_id: str): self._client = client self._session_id = session_id @@ -15973,7 +15959,7 @@ async def ensure_loaded(self, *, timeout: float | None = None) -> None: # Experimental: this API group is experimental and may change or be removed. class McpOauthApi: - def __init__(self, client: JsonRpcClient, session_id: str): + def __init__(self, client: "JsonRpcClient", session_id: str): self._client = client self._session_id = session_id @@ -15986,7 +15972,7 @@ async def login(self, params: MCPOauthLoginRequest, *, timeout: float | None = N # Experimental: this API group is experimental and may change or be removed. class McpApi: - def __init__(self, client: JsonRpcClient, session_id: str): + def __init__(self, client: "JsonRpcClient", session_id: str): self._client = client self._session_id = session_id self.oauth = McpOauthApi(client, session_id) @@ -16036,7 +16022,7 @@ async def remove_git_hub(self, *, timeout: float | None = None) -> MCPRemoveGitH # Experimental: this API group is experimental and may change or be removed. class PluginsApi: - def __init__(self, client: JsonRpcClient, session_id: str): + def __init__(self, client: "JsonRpcClient", session_id: str): self._client = client self._session_id = session_id @@ -16047,7 +16033,7 @@ async def list(self, *, timeout: float | None = None) -> PluginList: # Experimental: this API group is experimental and may change or be removed. class OptionsApi: - def __init__(self, client: JsonRpcClient, session_id: str): + def __init__(self, client: "JsonRpcClient", session_id: str): self._client = client self._session_id = session_id @@ -16060,7 +16046,7 @@ async def update(self, params: SessionUpdateOptionsParams, *, timeout: float | N # Experimental: this API group is experimental and may change or be removed. class LspApi: - def __init__(self, client: JsonRpcClient, session_id: str): + def __init__(self, client: "JsonRpcClient", session_id: str): self._client = client self._session_id = session_id @@ -16073,7 +16059,7 @@ async def initialize(self, params: LspInitializeRequest, *, timeout: float | Non # Experimental: this API group is experimental and may change or be removed. class ExtensionsApi: - def __init__(self, client: JsonRpcClient, session_id: str): + def __init__(self, client: "JsonRpcClient", session_id: str): self._client = client self._session_id = session_id @@ -16100,7 +16086,7 @@ async def reload(self, *, timeout: float | None = None) -> None: # Experimental: this API group is experimental and may change or be removed. class ToolsApi: - def __init__(self, client: JsonRpcClient, session_id: str): + def __init__(self, client: "JsonRpcClient", session_id: str): self._client = client self._session_id = session_id @@ -16117,7 +16103,7 @@ async def initialize_and_validate(self, *, timeout: float | None = None) -> Tool # Experimental: this API group is experimental and may change or be removed. class CommandsApi: - def __init__(self, client: JsonRpcClient, session_id: str): + def __init__(self, client: "JsonRpcClient", session_id: str): self._client = client self._session_id = session_id @@ -16160,7 +16146,7 @@ async def respond_to_queued_command(self, params: CommandsRespondToQueuedCommand # Experimental: this API group is experimental and may change or be removed. class TelemetryApi: - def __init__(self, client: JsonRpcClient, session_id: str): + def __init__(self, client: "JsonRpcClient", session_id: str): self._client = client self._session_id = session_id @@ -16173,7 +16159,7 @@ async def set_feature_overrides(self, params: TelemetrySetFeatureOverridesReques # Experimental: this API group is experimental and may change or be removed. class UiApi: - def __init__(self, client: JsonRpcClient, session_id: str): + def __init__(self, client: "JsonRpcClient", session_id: str): self._client = client self._session_id = session_id @@ -16226,7 +16212,7 @@ async def unregister_direct_auto_mode_switch_handler(self, params: UIUnregisterD # Experimental: this API group is experimental and may change or be removed. class PermissionsPathsApi: - def __init__(self, client: JsonRpcClient, session_id: str): + def __init__(self, client: "JsonRpcClient", session_id: str): self._client = client self._session_id = session_id @@ -16261,7 +16247,7 @@ async def is_path_within_workspace(self, params: PermissionPathsWorkspaceCheckPa # Experimental: this API group is experimental and may change or be removed. class PermissionsLocationsApi: - def __init__(self, client: JsonRpcClient, session_id: str): + def __init__(self, client: "JsonRpcClient", session_id: str): self._client = client self._session_id = session_id @@ -16286,7 +16272,7 @@ async def add_tool_approval(self, params: PermissionLocationAddToolApprovalParam # Experimental: this API group is experimental and may change or be removed. class PermissionsFolderTrustApi: - def __init__(self, client: JsonRpcClient, session_id: str): + def __init__(self, client: "JsonRpcClient", session_id: str): self._client = client self._session_id = session_id @@ -16305,7 +16291,7 @@ async def add_trusted(self, params: FolderTrustAddParams, *, timeout: float | No # Experimental: this API group is experimental and may change or be removed. class PermissionsUrlsApi: - def __init__(self, client: JsonRpcClient, session_id: str): + def __init__(self, client: "JsonRpcClient", session_id: str): self._client = client self._session_id = session_id @@ -16318,7 +16304,7 @@ async def set_unrestricted_mode(self, params: PermissionUrlsSetUnrestrictedModeP # Experimental: this API group is experimental and may change or be removed. class PermissionsApi: - def __init__(self, client: JsonRpcClient, session_id: str): + def __init__(self, client: "JsonRpcClient", session_id: str): self._client = client self._session_id = session_id self.paths = PermissionsPathsApi(client, session_id) @@ -16373,7 +16359,7 @@ async def notify_prompt_shown(self, params: PermissionPromptShownNotification, * # Experimental: this API group is experimental and may change or be removed. class MetadataApi: - def __init__(self, client: JsonRpcClient, session_id: str): + def __init__(self, client: "JsonRpcClient", session_id: str): self._client = client self._session_id = session_id @@ -16412,7 +16398,7 @@ async def recompute_context_tokens(self, params: MetadataRecomputeContextTokensR # Experimental: this API group is experimental and may change or be removed. class ShellApi: - def __init__(self, client: JsonRpcClient, session_id: str): + def __init__(self, client: "JsonRpcClient", session_id: str): self._client = client self._session_id = session_id @@ -16431,7 +16417,7 @@ async def kill(self, params: ShellKillRequest, *, timeout: float | None = None) # Experimental: this API group is experimental and may change or be removed. class HistoryApi: - def __init__(self, client: JsonRpcClient, session_id: str): + def __init__(self, client: "JsonRpcClient", session_id: str): self._client = client self._session_id = session_id @@ -16462,7 +16448,7 @@ async def summarize_for_handoff(self, *, timeout: float | None = None) -> Histor # Experimental: this API group is experimental and may change or be removed. class QueueApi: - def __init__(self, client: JsonRpcClient, session_id: str): + def __init__(self, client: "JsonRpcClient", session_id: str): self._client = client self._session_id = session_id @@ -16481,7 +16467,7 @@ async def clear(self, *, timeout: float | None = None) -> None: # Experimental: this API group is experimental and may change or be removed. class EventLogApi: - def __init__(self, client: JsonRpcClient, session_id: str): + def __init__(self, client: "JsonRpcClient", session_id: str): self._client = client self._session_id = session_id @@ -16510,7 +16496,7 @@ async def release_interest(self, params: ReleaseEventInterestParams, *, timeout: # Experimental: this API group is experimental and may change or be removed. class UsageApi: - def __init__(self, client: JsonRpcClient, session_id: str): + def __init__(self, client: "JsonRpcClient", session_id: str): self._client = client self._session_id = session_id @@ -16521,7 +16507,7 @@ async def get_metrics(self, *, timeout: float | None = None) -> UsageGetMetricsR # Experimental: this API group is experimental and may change or be removed. class RemoteApi: - def __init__(self, client: JsonRpcClient, session_id: str): + def __init__(self, client: "JsonRpcClient", session_id: str): self._client = client self._session_id = session_id @@ -16544,7 +16530,7 @@ async def notify_steerable_changed(self, params: RemoteNotifySteerableChangedReq # Experimental: this API group is experimental and may change or be removed. class ScheduleApi: - def __init__(self, client: JsonRpcClient, session_id: str): + def __init__(self, client: "JsonRpcClient", session_id: str): self._client = client self._session_id = session_id @@ -16561,7 +16547,7 @@ async def stop(self, params: ScheduleStopRequest, *, timeout: float | None = Non class SessionRpc: """Typed session-scoped RPC methods.""" - def __init__(self, client: JsonRpcClient, session_id: str): + def __init__(self, client: "JsonRpcClient", session_id: str): self._client = client self._session_id = session_id self.auth = AuthApi(client, session_id) @@ -16666,7 +16652,7 @@ class ClientSessionApiHandlers: session_fs: SessionFsHandler | None = None def register_client_session_api_handlers( - client: JsonRpcClient, + client: "JsonRpcClient", get_handlers: Callable[[str], ClientSessionApiHandlers], ) -> None: """Register client-session request handlers on a JSON-RPC connection.""" diff --git a/python/copilot/generated/session_events.py b/python/copilot/generated/session_events.py index c0bd58f3f..4b72621df 100644 --- a/python/copilot/generated/session_events.py +++ b/python/copilot/generated/session_events.py @@ -205,7 +205,7 @@ class SessionEventType(Enum): UNKNOWN = "unknown" @classmethod - def _missing_(cls, value: object) -> SessionEventType: + def _missing_(cls, value: object) -> "SessionEventType": return cls.UNKNOWN @@ -214,7 +214,7 @@ class RawSessionEventData: raw: Any @staticmethod - def from_dict(obj: Any) -> RawSessionEventData: + def from_dict(obj: Any) -> "RawSessionEventData": return RawSessionEventData(obj) def to_dict(self) -> Any: @@ -269,7 +269,7 @@ def __init__(self, **kwargs: Any): setattr(self, key, value) @staticmethod - def from_dict(obj: Any) -> Data: + def from_dict(obj: Any) -> "Data": assert isinstance(obj, dict) return Data(**{_compat_to_python_key(key): _compat_from_json_value(value) for key, value in obj.items()}) @@ -283,7 +283,7 @@ class AbortData: reason: AbortReason @staticmethod - def from_dict(obj: Any) -> AbortData: + def from_dict(obj: Any) -> "AbortData": assert isinstance(obj, dict) reason = parse_enum(AbortReason, obj.get("reason")) return AbortData( @@ -302,7 +302,7 @@ class AssistantIntentData: intent: str @staticmethod - def from_dict(obj: Any) -> AssistantIntentData: + def from_dict(obj: Any) -> "AssistantIntentData": assert isinstance(obj, dict) intent = from_str(obj.get("intent")) return AssistantIntentData( @@ -336,7 +336,7 @@ class AssistantMessageData: turn_id: str | None = None @staticmethod - def from_dict(obj: Any) -> AssistantMessageData: + def from_dict(obj: Any) -> "AssistantMessageData": assert isinstance(obj, dict) content = from_str(obj.get("content")) message_id = from_str(obj.get("messageId")) @@ -413,7 +413,7 @@ class AssistantMessageDeltaData: parent_tool_call_id: str | None = None @staticmethod - def from_dict(obj: Any) -> AssistantMessageDeltaData: + def from_dict(obj: Any) -> "AssistantMessageDeltaData": assert isinstance(obj, dict) delta_content = from_str(obj.get("deltaContent")) message_id = from_str(obj.get("messageId")) @@ -440,7 +440,7 @@ class AssistantMessageStartData: phase: str | None = None @staticmethod - def from_dict(obj: Any) -> AssistantMessageStartData: + def from_dict(obj: Any) -> "AssistantMessageStartData": assert isinstance(obj, dict) message_id = from_str(obj.get("messageId")) phase = from_union([from_none, from_str], obj.get("phase")) @@ -470,7 +470,7 @@ class AssistantMessageToolRequest: type: AssistantMessageToolRequestType | None = None @staticmethod - def from_dict(obj: Any) -> AssistantMessageToolRequest: + def from_dict(obj: Any) -> "AssistantMessageToolRequest": assert isinstance(obj, dict) name = from_str(obj.get("name")) tool_call_id = from_str(obj.get("toolCallId")) @@ -517,7 +517,7 @@ class AssistantReasoningData: reasoning_id: str @staticmethod - def from_dict(obj: Any) -> AssistantReasoningData: + def from_dict(obj: Any) -> "AssistantReasoningData": assert isinstance(obj, dict) content = from_str(obj.get("content")) reasoning_id = from_str(obj.get("reasoningId")) @@ -540,7 +540,7 @@ class AssistantReasoningDeltaData: reasoning_id: str @staticmethod - def from_dict(obj: Any) -> AssistantReasoningDeltaData: + def from_dict(obj: Any) -> "AssistantReasoningDeltaData": assert isinstance(obj, dict) delta_content = from_str(obj.get("deltaContent")) reasoning_id = from_str(obj.get("reasoningId")) @@ -562,7 +562,7 @@ class AssistantStreamingDeltaData: total_response_size_bytes: int @staticmethod - def from_dict(obj: Any) -> AssistantStreamingDeltaData: + def from_dict(obj: Any) -> "AssistantStreamingDeltaData": assert isinstance(obj, dict) total_response_size_bytes = from_int(obj.get("totalResponseSizeBytes")) return AssistantStreamingDeltaData( @@ -581,7 +581,7 @@ class AssistantTurnEndData: turn_id: str @staticmethod - def from_dict(obj: Any) -> AssistantTurnEndData: + def from_dict(obj: Any) -> "AssistantTurnEndData": assert isinstance(obj, dict) turn_id = from_str(obj.get("turnId")) return AssistantTurnEndData( @@ -601,7 +601,7 @@ class AssistantTurnStartData: interaction_id: str | None = None @staticmethod - def from_dict(obj: Any) -> AssistantTurnStartData: + def from_dict(obj: Any) -> "AssistantTurnStartData": assert isinstance(obj, dict) turn_id = from_str(obj.get("turnId")) interaction_id = from_union([from_none, from_str], obj.get("interactionId")) @@ -625,7 +625,7 @@ class AssistantUsageCopilotUsage: total_nano_aiu: float @staticmethod - def from_dict(obj: Any) -> AssistantUsageCopilotUsage: + def from_dict(obj: Any) -> "AssistantUsageCopilotUsage": assert isinstance(obj, dict) token_details = from_list(AssistantUsageCopilotUsageTokenDetail.from_dict, obj.get("tokenDetails")) total_nano_aiu = from_float(obj.get("totalNanoAiu")) @@ -650,7 +650,7 @@ class AssistantUsageCopilotUsageTokenDetail: token_type: str @staticmethod - def from_dict(obj: Any) -> AssistantUsageCopilotUsageTokenDetail: + def from_dict(obj: Any) -> "AssistantUsageCopilotUsageTokenDetail": assert isinstance(obj, dict) batch_size = from_int(obj.get("batchSize")) cost_per_batch = from_int(obj.get("costPerBatch")) @@ -696,7 +696,7 @@ class AssistantUsageData: time_to_first_token: timedelta | None = None @staticmethod - def from_dict(obj: Any) -> AssistantUsageData: + def from_dict(obj: Any) -> "AssistantUsageData": assert isinstance(obj, dict) model = from_str(obj.get("model")) api_call_id = from_union([from_none, from_str], obj.get("apiCallId")) @@ -790,7 +790,7 @@ class AssistantUsageQuotaSnapshot: reset_date: datetime | None = None @staticmethod - def from_dict(obj: Any) -> AssistantUsageQuotaSnapshot: + def from_dict(obj: Any) -> "AssistantUsageQuotaSnapshot": assert isinstance(obj, dict) entitlement_requests = from_int(obj.get("entitlementRequests")) is_unlimited_entitlement = from_bool(obj.get("isUnlimitedEntitlement")) @@ -832,7 +832,7 @@ class AutoModeSwitchCompletedData: response: AutoModeSwitchResponse @staticmethod - def from_dict(obj: Any) -> AutoModeSwitchCompletedData: + def from_dict(obj: Any) -> "AutoModeSwitchCompletedData": assert isinstance(obj, dict) request_id = from_str(obj.get("requestId")) response = parse_enum(AutoModeSwitchResponse, obj.get("response")) @@ -856,7 +856,7 @@ class AutoModeSwitchRequestedData: retry_after_seconds: int | None = None @staticmethod - def from_dict(obj: Any) -> AutoModeSwitchRequestedData: + def from_dict(obj: Any) -> "AutoModeSwitchRequestedData": assert isinstance(obj, dict) request_id = from_str(obj.get("requestId")) error_code = from_union([from_none, from_str], obj.get("errorCode")) @@ -883,7 +883,7 @@ class CapabilitiesChangedData: ui: CapabilitiesChangedUI | None = None @staticmethod - def from_dict(obj: Any) -> CapabilitiesChangedData: + def from_dict(obj: Any) -> "CapabilitiesChangedData": assert isinstance(obj, dict) ui = from_union([from_none, CapabilitiesChangedUI.from_dict], obj.get("ui")) return CapabilitiesChangedData( @@ -903,7 +903,7 @@ class CapabilitiesChangedUI: elicitation: bool | None = None @staticmethod - def from_dict(obj: Any) -> CapabilitiesChangedUI: + def from_dict(obj: Any) -> "CapabilitiesChangedUI": assert isinstance(obj, dict) elicitation = from_union([from_none, from_bool], obj.get("elicitation")) return CapabilitiesChangedUI( @@ -923,7 +923,7 @@ class CommandCompletedData: request_id: str @staticmethod - def from_dict(obj: Any) -> CommandCompletedData: + def from_dict(obj: Any) -> "CommandCompletedData": assert isinstance(obj, dict) request_id = from_str(obj.get("requestId")) return CommandCompletedData( @@ -945,7 +945,7 @@ class CommandExecuteData: request_id: str @staticmethod - def from_dict(obj: Any) -> CommandExecuteData: + def from_dict(obj: Any) -> "CommandExecuteData": assert isinstance(obj, dict) args = from_str(obj.get("args")) command = from_str(obj.get("command")) @@ -974,7 +974,7 @@ class CommandQueuedData: request_id: str @staticmethod - def from_dict(obj: Any) -> CommandQueuedData: + def from_dict(obj: Any) -> "CommandQueuedData": assert isinstance(obj, dict) command = from_str(obj.get("command")) request_id = from_str(obj.get("requestId")) @@ -997,7 +997,7 @@ class CommandsChangedCommand: description: str | None = None @staticmethod - def from_dict(obj: Any) -> CommandsChangedCommand: + def from_dict(obj: Any) -> "CommandsChangedCommand": assert isinstance(obj, dict) name = from_str(obj.get("name")) description = from_union([from_none, from_str], obj.get("description")) @@ -1020,7 +1020,7 @@ class CommandsChangedData: commands: list[CommandsChangedCommand] @staticmethod - def from_dict(obj: Any) -> CommandsChangedData: + def from_dict(obj: Any) -> "CommandsChangedData": assert isinstance(obj, dict) commands = from_list(CommandsChangedCommand.from_dict, obj.get("commands")) return CommandsChangedData( @@ -1045,7 +1045,7 @@ class CompactionCompleteCompactionTokensUsed: output_tokens: int | None = None @staticmethod - def from_dict(obj: Any) -> CompactionCompleteCompactionTokensUsed: + def from_dict(obj: Any) -> "CompactionCompleteCompactionTokensUsed": assert isinstance(obj, dict) cache_read_tokens = from_union([from_none, from_int], obj.get("cacheReadTokens")) cache_write_tokens = from_union([from_none, from_int], obj.get("cacheWriteTokens")) @@ -1090,7 +1090,7 @@ class CompactionCompleteCompactionTokensUsedCopilotUsage: total_nano_aiu: float @staticmethod - def from_dict(obj: Any) -> CompactionCompleteCompactionTokensUsedCopilotUsage: + def from_dict(obj: Any) -> "CompactionCompleteCompactionTokensUsedCopilotUsage": assert isinstance(obj, dict) token_details = from_list(CompactionCompleteCompactionTokensUsedCopilotUsageTokenDetail.from_dict, obj.get("tokenDetails")) total_nano_aiu = from_float(obj.get("totalNanoAiu")) @@ -1115,7 +1115,7 @@ class CompactionCompleteCompactionTokensUsedCopilotUsageTokenDetail: token_type: str @staticmethod - def from_dict(obj: Any) -> CompactionCompleteCompactionTokensUsedCopilotUsageTokenDetail: + def from_dict(obj: Any) -> "CompactionCompleteCompactionTokensUsedCopilotUsageTokenDetail": assert isinstance(obj, dict) batch_size = from_int(obj.get("batchSize")) cost_per_batch = from_int(obj.get("costPerBatch")) @@ -1150,7 +1150,7 @@ class CustomAgentsUpdatedAgent: model: str | None = None @staticmethod - def from_dict(obj: Any) -> CustomAgentsUpdatedAgent: + def from_dict(obj: Any) -> "CustomAgentsUpdatedAgent": assert isinstance(obj, dict) description = from_str(obj.get("description")) display_name = from_str(obj.get("displayName")) @@ -1193,7 +1193,7 @@ class ElicitationCompletedData: content: dict[str, Any] | None = None @staticmethod - def from_dict(obj: Any) -> ElicitationCompletedData: + def from_dict(obj: Any) -> "ElicitationCompletedData": assert isinstance(obj, dict) request_id = from_str(obj.get("requestId")) action = from_union([from_none, lambda x: parse_enum(ElicitationCompletedAction, x)], obj.get("action")) @@ -1226,7 +1226,7 @@ class ElicitationRequestedData: url: str | None = None @staticmethod - def from_dict(obj: Any) -> ElicitationRequestedData: + def from_dict(obj: Any) -> "ElicitationRequestedData": assert isinstance(obj, dict) message = from_str(obj.get("message")) request_id = from_str(obj.get("requestId")) @@ -1270,7 +1270,7 @@ class ElicitationRequestedSchema: required: list[str] | None = None @staticmethod - def from_dict(obj: Any) -> ElicitationRequestedSchema: + def from_dict(obj: Any) -> "ElicitationRequestedSchema": assert isinstance(obj, dict) properties = from_dict(lambda x: x, obj.get("properties")) type = from_str(obj.get("type")) @@ -1298,7 +1298,7 @@ class EmbeddedBlobResourceContents: mime_type: str | None = None @staticmethod - def from_dict(obj: Any) -> EmbeddedBlobResourceContents: + def from_dict(obj: Any) -> "EmbeddedBlobResourceContents": assert isinstance(obj, dict) blob = from_str(obj.get("blob")) uri = from_str(obj.get("uri")) @@ -1326,7 +1326,7 @@ class EmbeddedTextResourceContents: mime_type: str | None = None @staticmethod - def from_dict(obj: Any) -> EmbeddedTextResourceContents: + def from_dict(obj: Any) -> "EmbeddedTextResourceContents": assert isinstance(obj, dict) text = from_str(obj.get("text")) uri = from_str(obj.get("uri")) @@ -1356,7 +1356,7 @@ class ExitPlanModeCompletedData: selected_action: ExitPlanModeAction | None = None @staticmethod - def from_dict(obj: Any) -> ExitPlanModeCompletedData: + def from_dict(obj: Any) -> "ExitPlanModeCompletedData": assert isinstance(obj, dict) request_id = from_str(obj.get("requestId")) approved = from_union([from_none, from_bool], obj.get("approved")) @@ -1395,7 +1395,7 @@ class ExitPlanModeRequestedData: summary: str @staticmethod - def from_dict(obj: Any) -> ExitPlanModeRequestedData: + def from_dict(obj: Any) -> "ExitPlanModeRequestedData": assert isinstance(obj, dict) actions = from_list(lambda x: parse_enum(ExitPlanModeAction, x), obj.get("actions")) plan_content = from_str(obj.get("planContent")) @@ -1429,7 +1429,7 @@ class ExtensionsLoadedExtension: status: ExtensionsLoadedExtensionStatus @staticmethod - def from_dict(obj: Any) -> ExtensionsLoadedExtension: + def from_dict(obj: Any) -> "ExtensionsLoadedExtension": assert isinstance(obj, dict) id = from_str(obj.get("id")) name = from_str(obj.get("name")) @@ -1457,7 +1457,7 @@ class ExternalToolCompletedData: request_id: str @staticmethod - def from_dict(obj: Any) -> ExternalToolCompletedData: + def from_dict(obj: Any) -> "ExternalToolCompletedData": assert isinstance(obj, dict) request_id = from_str(obj.get("requestId")) return ExternalToolCompletedData( @@ -1482,7 +1482,7 @@ class ExternalToolRequestedData: tracestate: str | None = None @staticmethod - def from_dict(obj: Any) -> ExternalToolRequestedData: + def from_dict(obj: Any) -> "ExternalToolRequestedData": assert isinstance(obj, dict) request_id = from_str(obj.get("requestId")) session_id = from_str(obj.get("sessionId")) @@ -1524,7 +1524,7 @@ class HandoffRepository: branch: str | None = None @staticmethod - def from_dict(obj: Any) -> HandoffRepository: + def from_dict(obj: Any) -> "HandoffRepository": assert isinstance(obj, dict) name = from_str(obj.get("name")) owner = from_str(obj.get("owner")) @@ -1554,7 +1554,7 @@ class HookEndData: output: Any = None @staticmethod - def from_dict(obj: Any) -> HookEndData: + def from_dict(obj: Any) -> "HookEndData": assert isinstance(obj, dict) hook_invocation_id = from_str(obj.get("hookInvocationId")) hook_type = from_str(obj.get("hookType")) @@ -1588,7 +1588,7 @@ class HookEndError: stack: str | None = None @staticmethod - def from_dict(obj: Any) -> HookEndError: + def from_dict(obj: Any) -> "HookEndError": assert isinstance(obj, dict) message = from_str(obj.get("message")) stack = from_union([from_none, from_str], obj.get("stack")) @@ -1613,7 +1613,7 @@ class HookStartData: input: Any = None @staticmethod - def from_dict(obj: Any) -> HookStartData: + def from_dict(obj: Any) -> "HookStartData": assert isinstance(obj, dict) hook_invocation_id = from_str(obj.get("hookInvocationId")) hook_type = from_str(obj.get("hookType")) @@ -1639,7 +1639,7 @@ class McpOauthCompletedData: request_id: str @staticmethod - def from_dict(obj: Any) -> McpOauthCompletedData: + def from_dict(obj: Any) -> "McpOauthCompletedData": assert isinstance(obj, dict) request_id = from_str(obj.get("requestId")) return McpOauthCompletedData( @@ -1661,7 +1661,7 @@ class McpOauthRequiredData: static_client_config: McpOauthRequiredStaticClientConfig | None = None @staticmethod - def from_dict(obj: Any) -> McpOauthRequiredData: + def from_dict(obj: Any) -> "McpOauthRequiredData": assert isinstance(obj, dict) request_id = from_str(obj.get("requestId")) server_name = from_str(obj.get("serverName")) @@ -1692,7 +1692,7 @@ class McpOauthRequiredStaticClientConfig: public_client: bool | None = None @staticmethod - def from_dict(obj: Any) -> McpOauthRequiredStaticClientConfig: + def from_dict(obj: Any) -> "McpOauthRequiredStaticClientConfig": assert isinstance(obj, dict) client_id = from_str(obj.get("clientId")) grant_type = from_union([from_none, from_str], obj.get("grantType")) @@ -1722,7 +1722,7 @@ class McpServersLoadedServer: source: McpServerSource | None = None @staticmethod - def from_dict(obj: Any) -> McpServersLoadedServer: + def from_dict(obj: Any) -> "McpServersLoadedServer": assert isinstance(obj, dict) name = from_str(obj.get("name")) status = parse_enum(McpServerStatus, obj.get("status")) @@ -1759,7 +1759,7 @@ class ModelCallFailureData: status_code: int | None = None @staticmethod - def from_dict(obj: Any) -> ModelCallFailureData: + def from_dict(obj: Any) -> "ModelCallFailureData": assert isinstance(obj, dict) source = parse_enum(ModelCallFailureSource, obj.get("source")) api_call_id = from_union([from_none, from_str], obj.get("apiCallId")) @@ -1804,7 +1804,7 @@ def to_dict(self) -> dict: class PendingMessagesModifiedData: "Empty payload; the event signals that the pending message queue has changed" @staticmethod - def from_dict(obj: Any) -> PendingMessagesModifiedData: + def from_dict(obj: Any) -> "PendingMessagesModifiedData": assert isinstance(obj, dict) return PendingMessagesModifiedData() @@ -1818,7 +1818,7 @@ class PermissionApproved: kind: ClassVar[str] = "approved" @staticmethod - def from_dict(obj: Any) -> PermissionApproved: + def from_dict(obj: Any) -> "PermissionApproved": assert isinstance(obj, dict) return PermissionApproved( ) @@ -1837,7 +1837,7 @@ class PermissionApprovedForLocation: location_key: str @staticmethod - def from_dict(obj: Any) -> PermissionApprovedForLocation: + def from_dict(obj: Any) -> "PermissionApprovedForLocation": assert isinstance(obj, dict) approval = _load_UserToolSessionApproval(obj.get("approval")) location_key = from_str(obj.get("locationKey")) @@ -1861,7 +1861,7 @@ class PermissionApprovedForSession: kind: ClassVar[str] = "approved-for-session" @staticmethod - def from_dict(obj: Any) -> PermissionApprovedForSession: + def from_dict(obj: Any) -> "PermissionApprovedForSession": assert isinstance(obj, dict) approval = _load_UserToolSessionApproval(obj.get("approval")) return PermissionApprovedForSession( @@ -1882,7 +1882,7 @@ class PermissionCancelled: reason: str | None = None @staticmethod - def from_dict(obj: Any) -> PermissionCancelled: + def from_dict(obj: Any) -> "PermissionCancelled": assert isinstance(obj, dict) reason = from_union([from_none, from_str], obj.get("reason")) return PermissionCancelled( @@ -1905,7 +1905,7 @@ class PermissionCompletedData: tool_call_id: str | None = None @staticmethod - def from_dict(obj: Any) -> PermissionCompletedData: + def from_dict(obj: Any) -> "PermissionCompletedData": assert isinstance(obj, dict) request_id = from_str(obj.get("requestId")) result = _load_PermissionResult(obj.get("result")) @@ -1933,7 +1933,7 @@ class PermissionDeniedByContentExclusionPolicy: path: str @staticmethod - def from_dict(obj: Any) -> PermissionDeniedByContentExclusionPolicy: + def from_dict(obj: Any) -> "PermissionDeniedByContentExclusionPolicy": assert isinstance(obj, dict) message = from_str(obj.get("message")) path = from_str(obj.get("path")) @@ -1958,7 +1958,7 @@ class PermissionDeniedByPermissionRequestHook: message: str | None = None @staticmethod - def from_dict(obj: Any) -> PermissionDeniedByPermissionRequestHook: + def from_dict(obj: Any) -> "PermissionDeniedByPermissionRequestHook": assert isinstance(obj, dict) interrupt = from_union([from_none, from_bool], obj.get("interrupt")) message = from_union([from_none, from_str], obj.get("message")) @@ -1984,7 +1984,7 @@ class PermissionDeniedByRules: rules: list[PermissionRule] @staticmethod - def from_dict(obj: Any) -> PermissionDeniedByRules: + def from_dict(obj: Any) -> "PermissionDeniedByRules": assert isinstance(obj, dict) rules = from_list(PermissionRule.from_dict, obj.get("rules")) return PermissionDeniedByRules( @@ -2006,7 +2006,7 @@ class PermissionDeniedInteractivelyByUser: force_reject: bool | None = None @staticmethod - def from_dict(obj: Any) -> PermissionDeniedInteractivelyByUser: + def from_dict(obj: Any) -> "PermissionDeniedInteractivelyByUser": assert isinstance(obj, dict) feedback = from_union([from_none, from_str], obj.get("feedback")) force_reject = from_union([from_none, from_bool], obj.get("forceReject")) @@ -2031,7 +2031,7 @@ class PermissionDeniedNoApprovalRuleAndCouldNotRequestFromUser: kind: ClassVar[str] = "denied-no-approval-rule-and-could-not-request-from-user" @staticmethod - def from_dict(obj: Any) -> PermissionDeniedNoApprovalRuleAndCouldNotRequestFromUser: + def from_dict(obj: Any) -> "PermissionDeniedNoApprovalRuleAndCouldNotRequestFromUser": assert isinstance(obj, dict) return PermissionDeniedNoApprovalRuleAndCouldNotRequestFromUser( ) @@ -2054,7 +2054,7 @@ class PermissionPromptRequestCommands: warning: str | None = None @staticmethod - def from_dict(obj: Any) -> PermissionPromptRequestCommands: + def from_dict(obj: Any) -> "PermissionPromptRequestCommands": assert isinstance(obj, dict) can_offer_session_approval = from_bool(obj.get("canOfferSessionApproval")) command_identifiers = from_list(from_str, obj.get("commandIdentifiers")) @@ -2095,7 +2095,7 @@ class PermissionPromptRequestCustomTool: tool_call_id: str | None = None @staticmethod - def from_dict(obj: Any) -> PermissionPromptRequestCustomTool: + def from_dict(obj: Any) -> "PermissionPromptRequestCustomTool": assert isinstance(obj, dict) tool_description = from_str(obj.get("toolDescription")) tool_name = from_str(obj.get("toolName")) @@ -2129,7 +2129,7 @@ class PermissionPromptRequestExtensionManagement: tool_call_id: str | None = None @staticmethod - def from_dict(obj: Any) -> PermissionPromptRequestExtensionManagement: + def from_dict(obj: Any) -> "PermissionPromptRequestExtensionManagement": assert isinstance(obj, dict) operation = from_str(obj.get("operation")) extension_name = from_union([from_none, from_str], obj.get("extensionName")) @@ -2160,7 +2160,7 @@ class PermissionPromptRequestExtensionPermissionAccess: tool_call_id: str | None = None @staticmethod - def from_dict(obj: Any) -> PermissionPromptRequestExtensionPermissionAccess: + def from_dict(obj: Any) -> "PermissionPromptRequestExtensionPermissionAccess": assert isinstance(obj, dict) capabilities = from_list(from_str, obj.get("capabilities")) extension_name = from_str(obj.get("extensionName")) @@ -2191,7 +2191,7 @@ class PermissionPromptRequestHook: tool_call_id: str | None = None @staticmethod - def from_dict(obj: Any) -> PermissionPromptRequestHook: + def from_dict(obj: Any) -> "PermissionPromptRequestHook": assert isinstance(obj, dict) tool_name = from_str(obj.get("toolName")) hook_message = from_union([from_none, from_str], obj.get("hookMessage")) @@ -2228,7 +2228,7 @@ class PermissionPromptRequestMcp: tool_call_id: str | None = None @staticmethod - def from_dict(obj: Any) -> PermissionPromptRequestMcp: + def from_dict(obj: Any) -> "PermissionPromptRequestMcp": assert isinstance(obj, dict) server_name = from_str(obj.get("serverName")) tool_name = from_str(obj.get("toolName")) @@ -2269,7 +2269,7 @@ class PermissionPromptRequestMemory: tool_call_id: str | None = None @staticmethod - def from_dict(obj: Any) -> PermissionPromptRequestMemory: + def from_dict(obj: Any) -> "PermissionPromptRequestMemory": assert isinstance(obj, dict) fact = from_str(obj.get("fact")) action = from_union([from_none, lambda x: parse_enum(PermissionRequestMemoryAction, x)], obj.get("action")) @@ -2316,7 +2316,7 @@ class PermissionPromptRequestPath: tool_call_id: str | None = None @staticmethod - def from_dict(obj: Any) -> PermissionPromptRequestPath: + def from_dict(obj: Any) -> "PermissionPromptRequestPath": assert isinstance(obj, dict) access_kind = parse_enum(PermissionPromptRequestPathAccessKind, obj.get("accessKind")) paths = from_list(from_str, obj.get("paths")) @@ -2346,7 +2346,7 @@ class PermissionPromptRequestRead: tool_call_id: str | None = None @staticmethod - def from_dict(obj: Any) -> PermissionPromptRequestRead: + def from_dict(obj: Any) -> "PermissionPromptRequestRead": assert isinstance(obj, dict) intention = from_str(obj.get("intention")) path = from_str(obj.get("path")) @@ -2376,7 +2376,7 @@ class PermissionPromptRequestUrl: tool_call_id: str | None = None @staticmethod - def from_dict(obj: Any) -> PermissionPromptRequestUrl: + def from_dict(obj: Any) -> "PermissionPromptRequestUrl": assert isinstance(obj, dict) intention = from_str(obj.get("intention")) url = from_str(obj.get("url")) @@ -2409,7 +2409,7 @@ class PermissionPromptRequestWrite: tool_call_id: str | None = None @staticmethod - def from_dict(obj: Any) -> PermissionPromptRequestWrite: + def from_dict(obj: Any) -> "PermissionPromptRequestWrite": assert isinstance(obj, dict) can_offer_session_approval = from_bool(obj.get("canOfferSessionApproval")) diff = from_str(obj.get("diff")) @@ -2450,7 +2450,7 @@ class PermissionRequestCustomTool: tool_call_id: str | None = None @staticmethod - def from_dict(obj: Any) -> PermissionRequestCustomTool: + def from_dict(obj: Any) -> "PermissionRequestCustomTool": assert isinstance(obj, dict) tool_description = from_str(obj.get("toolDescription")) tool_name = from_str(obj.get("toolName")) @@ -2484,7 +2484,7 @@ class PermissionRequestExtensionManagement: tool_call_id: str | None = None @staticmethod - def from_dict(obj: Any) -> PermissionRequestExtensionManagement: + def from_dict(obj: Any) -> "PermissionRequestExtensionManagement": assert isinstance(obj, dict) operation = from_str(obj.get("operation")) extension_name = from_union([from_none, from_str], obj.get("extensionName")) @@ -2515,7 +2515,7 @@ class PermissionRequestExtensionPermissionAccess: tool_call_id: str | None = None @staticmethod - def from_dict(obj: Any) -> PermissionRequestExtensionPermissionAccess: + def from_dict(obj: Any) -> "PermissionRequestExtensionPermissionAccess": assert isinstance(obj, dict) capabilities = from_list(from_str, obj.get("capabilities")) extension_name = from_str(obj.get("extensionName")) @@ -2546,7 +2546,7 @@ class PermissionRequestHook: tool_call_id: str | None = None @staticmethod - def from_dict(obj: Any) -> PermissionRequestHook: + def from_dict(obj: Any) -> "PermissionRequestHook": assert isinstance(obj, dict) tool_name = from_str(obj.get("toolName")) hook_message = from_union([from_none, from_str], obj.get("hookMessage")) @@ -2584,7 +2584,7 @@ class PermissionRequestMcp: tool_call_id: str | None = None @staticmethod - def from_dict(obj: Any) -> PermissionRequestMcp: + def from_dict(obj: Any) -> "PermissionRequestMcp": assert isinstance(obj, dict) read_only = from_bool(obj.get("readOnly")) server_name = from_str(obj.get("serverName")) @@ -2628,7 +2628,7 @@ class PermissionRequestMemory: tool_call_id: str | None = None @staticmethod - def from_dict(obj: Any) -> PermissionRequestMemory: + def from_dict(obj: Any) -> "PermissionRequestMemory": assert isinstance(obj, dict) fact = from_str(obj.get("fact")) action = from_union([from_none, lambda x: parse_enum(PermissionRequestMemoryAction, x)], obj.get("action")) @@ -2675,7 +2675,7 @@ class PermissionRequestRead: tool_call_id: str | None = None @staticmethod - def from_dict(obj: Any) -> PermissionRequestRead: + def from_dict(obj: Any) -> "PermissionRequestRead": assert isinstance(obj, dict) intention = from_str(obj.get("intention")) path = from_str(obj.get("path")) @@ -2711,7 +2711,7 @@ class PermissionRequestShell: warning: str | None = None @staticmethod - def from_dict(obj: Any) -> PermissionRequestShell: + def from_dict(obj: Any) -> "PermissionRequestShell": assert isinstance(obj, dict) can_offer_session_approval = from_bool(obj.get("canOfferSessionApproval")) commands = from_list(PermissionRequestShellCommand.from_dict, obj.get("commands")) @@ -2758,7 +2758,7 @@ class PermissionRequestShellCommand: read_only: bool @staticmethod - def from_dict(obj: Any) -> PermissionRequestShellCommand: + def from_dict(obj: Any) -> "PermissionRequestShellCommand": assert isinstance(obj, dict) identifier = from_str(obj.get("identifier")) read_only = from_bool(obj.get("readOnly")) @@ -2780,7 +2780,7 @@ class PermissionRequestShellPossibleUrl: url: str @staticmethod - def from_dict(obj: Any) -> PermissionRequestShellPossibleUrl: + def from_dict(obj: Any) -> "PermissionRequestShellPossibleUrl": assert isinstance(obj, dict) url = from_str(obj.get("url")) return PermissionRequestShellPossibleUrl( @@ -2802,7 +2802,7 @@ class PermissionRequestUrl: tool_call_id: str | None = None @staticmethod - def from_dict(obj: Any) -> PermissionRequestUrl: + def from_dict(obj: Any) -> "PermissionRequestUrl": assert isinstance(obj, dict) intention = from_str(obj.get("intention")) url = from_str(obj.get("url")) @@ -2835,7 +2835,7 @@ class PermissionRequestWrite: tool_call_id: str | None = None @staticmethod - def from_dict(obj: Any) -> PermissionRequestWrite: + def from_dict(obj: Any) -> "PermissionRequestWrite": assert isinstance(obj, dict) can_offer_session_approval = from_bool(obj.get("canOfferSessionApproval")) diff = from_str(obj.get("diff")) @@ -2875,7 +2875,7 @@ class PermissionRequestedData: resolved_by_hook: bool | None = None @staticmethod - def from_dict(obj: Any) -> PermissionRequestedData: + def from_dict(obj: Any) -> "PermissionRequestedData": assert isinstance(obj, dict) permission_request = _load_PermissionRequest(obj.get("permissionRequest")) request_id = from_str(obj.get("requestId")) @@ -2906,7 +2906,7 @@ class PermissionRule: kind: str @staticmethod - def from_dict(obj: Any) -> PermissionRule: + def from_dict(obj: Any) -> "PermissionRule": assert isinstance(obj, dict) argument = from_union([from_none, from_str], obj.get("argument")) kind = from_str(obj.get("kind")) @@ -2928,7 +2928,7 @@ class SamplingCompletedData: request_id: str @staticmethod - def from_dict(obj: Any) -> SamplingCompletedData: + def from_dict(obj: Any) -> "SamplingCompletedData": assert isinstance(obj, dict) request_id = from_str(obj.get("requestId")) return SamplingCompletedData( @@ -2949,7 +2949,7 @@ class SamplingRequestedData: server_name: str @staticmethod - def from_dict(obj: Any) -> SamplingRequestedData: + def from_dict(obj: Any) -> "SamplingRequestedData": assert isinstance(obj, dict) mcp_request_id = obj.get("mcpRequestId") request_id = from_str(obj.get("requestId")) @@ -2972,7 +2972,7 @@ def to_dict(self) -> dict: class SessionBackgroundTasksChangedData: "Schema for the `BackgroundTasksChangedData` type." @staticmethod - def from_dict(obj: Any) -> SessionBackgroundTasksChangedData: + def from_dict(obj: Any) -> "SessionBackgroundTasksChangedData": assert isinstance(obj, dict) return SessionBackgroundTasksChangedData() @@ -3001,7 +3001,7 @@ class SessionCompactionCompleteData: tool_definitions_tokens: int | None = None @staticmethod - def from_dict(obj: Any) -> SessionCompactionCompleteData: + def from_dict(obj: Any) -> "SessionCompactionCompleteData": assert isinstance(obj, dict) success = from_bool(obj.get("success")) checkpoint_number = from_union([from_none, from_int], obj.get("checkpointNumber")) @@ -3082,7 +3082,7 @@ class SessionCompactionStartData: tool_definitions_tokens: int | None = None @staticmethod - def from_dict(obj: Any) -> SessionCompactionStartData: + def from_dict(obj: Any) -> "SessionCompactionStartData": assert isinstance(obj, dict) conversation_tokens = from_union([from_none, from_int], obj.get("conversationTokens")) system_tokens = from_union([from_none, from_int], obj.get("systemTokens")) @@ -3117,7 +3117,7 @@ class SessionContextChangedData: repository_host: str | None = None @staticmethod - def from_dict(obj: Any) -> SessionContextChangedData: + def from_dict(obj: Any) -> "SessionContextChangedData": assert isinstance(obj, dict) cwd = from_str(obj.get("cwd")) base_commit = from_union([from_none, from_str], obj.get("baseCommit")) @@ -3166,7 +3166,7 @@ class SessionCustomAgentsUpdatedData: warnings: list[str] @staticmethod - def from_dict(obj: Any) -> SessionCustomAgentsUpdatedData: + def from_dict(obj: Any) -> "SessionCustomAgentsUpdatedData": assert isinstance(obj, dict) agents = from_list(CustomAgentsUpdatedAgent.from_dict, obj.get("agents")) errors = from_list(from_str, obj.get("errors")) @@ -3195,7 +3195,7 @@ class SessionCustomNotificationData: version: int | None = None @staticmethod - def from_dict(obj: Any) -> SessionCustomNotificationData: + def from_dict(obj: Any) -> "SessionCustomNotificationData": assert isinstance(obj, dict) name = from_str(obj.get("name")) payload = obj.get("payload") @@ -3235,7 +3235,7 @@ class SessionErrorData: url: str | None = None @staticmethod - def from_dict(obj: Any) -> SessionErrorData: + def from_dict(obj: Any) -> "SessionErrorData": assert isinstance(obj, dict) error_type = from_str(obj.get("errorType")) message = from_str(obj.get("message")) @@ -3281,7 +3281,7 @@ class SessionExtensionsLoadedData: extensions: list[ExtensionsLoadedExtension] @staticmethod - def from_dict(obj: Any) -> SessionExtensionsLoadedData: + def from_dict(obj: Any) -> "SessionExtensionsLoadedData": assert isinstance(obj, dict) extensions = from_list(ExtensionsLoadedExtension.from_dict, obj.get("extensions")) return SessionExtensionsLoadedData( @@ -3306,7 +3306,7 @@ class SessionHandoffData: summary: str | None = None @staticmethod - def from_dict(obj: Any) -> SessionHandoffData: + def from_dict(obj: Any) -> "SessionHandoffData": assert isinstance(obj, dict) handoff_time = from_datetime(obj.get("handoffTime")) source_type = parse_enum(HandoffSourceType, obj.get("sourceType")) @@ -3348,7 +3348,7 @@ class SessionIdleData: aborted: bool | None = None @staticmethod - def from_dict(obj: Any) -> SessionIdleData: + def from_dict(obj: Any) -> "SessionIdleData": assert isinstance(obj, dict) aborted = from_union([from_none, from_bool], obj.get("aborted")) return SessionIdleData( @@ -3371,7 +3371,7 @@ class SessionInfoData: url: str | None = None @staticmethod - def from_dict(obj: Any) -> SessionInfoData: + def from_dict(obj: Any) -> "SessionInfoData": assert isinstance(obj, dict) info_type = from_str(obj.get("infoType")) message = from_str(obj.get("message")) @@ -3402,7 +3402,7 @@ class SessionMcpServerStatusChangedData: status: McpServerStatus @staticmethod - def from_dict(obj: Any) -> SessionMcpServerStatusChangedData: + def from_dict(obj: Any) -> "SessionMcpServerStatusChangedData": assert isinstance(obj, dict) server_name = from_str(obj.get("serverName")) status = parse_enum(McpServerStatus, obj.get("status")) @@ -3424,7 +3424,7 @@ class SessionMcpServersLoadedData: servers: list[McpServersLoadedServer] @staticmethod - def from_dict(obj: Any) -> SessionMcpServersLoadedData: + def from_dict(obj: Any) -> "SessionMcpServersLoadedData": assert isinstance(obj, dict) servers = from_list(McpServersLoadedServer.from_dict, obj.get("servers")) return SessionMcpServersLoadedData( @@ -3444,7 +3444,7 @@ class SessionModeChangedData: previous_mode: SessionMode @staticmethod - def from_dict(obj: Any) -> SessionModeChangedData: + def from_dict(obj: Any) -> "SessionModeChangedData": assert isinstance(obj, dict) new_mode = parse_enum(SessionMode, obj.get("newMode")) previous_mode = parse_enum(SessionMode, obj.get("previousMode")) @@ -3472,7 +3472,7 @@ class SessionModelChangeData: reasoning_summary: ReasoningSummary | None = None @staticmethod - def from_dict(obj: Any) -> SessionModelChangeData: + def from_dict(obj: Any) -> "SessionModelChangeData": assert isinstance(obj, dict) new_model = from_str(obj.get("newModel")) cause = from_union([from_none, from_str], obj.get("cause")) @@ -3515,7 +3515,7 @@ class SessionPlanChangedData: operation: PlanChangedOperation @staticmethod - def from_dict(obj: Any) -> SessionPlanChangedData: + def from_dict(obj: Any) -> "SessionPlanChangedData": assert isinstance(obj, dict) operation = parse_enum(PlanChangedOperation, obj.get("operation")) return SessionPlanChangedData( @@ -3534,7 +3534,7 @@ class SessionRemoteSteerableChangedData: remote_steerable: bool @staticmethod - def from_dict(obj: Any) -> SessionRemoteSteerableChangedData: + def from_dict(obj: Any) -> "SessionRemoteSteerableChangedData": assert isinstance(obj, dict) remote_steerable = from_bool(obj.get("remoteSteerable")) return SessionRemoteSteerableChangedData( @@ -3562,7 +3562,7 @@ class SessionResumeData: session_was_active: bool | None = None @staticmethod - def from_dict(obj: Any) -> SessionResumeData: + def from_dict(obj: Any) -> "SessionResumeData": assert isinstance(obj, dict) event_count = from_int(obj.get("eventCount")) resume_time = from_datetime(obj.get("resumeTime")) @@ -3616,7 +3616,7 @@ class SessionScheduleCancelledData: id: int @staticmethod - def from_dict(obj: Any) -> SessionScheduleCancelledData: + def from_dict(obj: Any) -> "SessionScheduleCancelledData": assert isinstance(obj, dict) id = from_int(obj.get("id")) return SessionScheduleCancelledData( @@ -3639,7 +3639,7 @@ class SessionScheduleCreatedData: recurring: bool | None = None @staticmethod - def from_dict(obj: Any) -> SessionScheduleCreatedData: + def from_dict(obj: Any) -> "SessionScheduleCreatedData": assert isinstance(obj, dict) id = from_int(obj.get("id")) interval = from_timedelta(obj.get("intervalMs")) @@ -3685,7 +3685,7 @@ class SessionShutdownData: total_premium_requests: float | None = None @staticmethod - def from_dict(obj: Any) -> SessionShutdownData: + def from_dict(obj: Any) -> "SessionShutdownData": assert isinstance(obj, dict) code_changes = ShutdownCodeChanges.from_dict(obj.get("codeChanges")) model_metrics = from_dict(ShutdownModelMetric.from_dict, obj.get("modelMetrics")) @@ -3752,7 +3752,7 @@ class SessionSkillsLoadedData: skills: list[SkillsLoadedSkill] @staticmethod - def from_dict(obj: Any) -> SessionSkillsLoadedData: + def from_dict(obj: Any) -> "SessionSkillsLoadedData": assert isinstance(obj, dict) skills = from_list(SkillsLoadedSkill.from_dict, obj.get("skills")) return SessionSkillsLoadedData( @@ -3772,7 +3772,7 @@ class SessionSnapshotRewindData: up_to_event_id: str @staticmethod - def from_dict(obj: Any) -> SessionSnapshotRewindData: + def from_dict(obj: Any) -> "SessionSnapshotRewindData": assert isinstance(obj, dict) events_removed = from_int(obj.get("eventsRemoved")) up_to_event_id = from_str(obj.get("upToEventId")) @@ -3805,7 +3805,7 @@ class SessionStartData: selected_model: str | None = None @staticmethod - def from_dict(obj: Any) -> SessionStartData: + def from_dict(obj: Any) -> "SessionStartData": assert isinstance(obj, dict) copilot_version = from_str(obj.get("copilotVersion")) producer = from_str(obj.get("producer")) @@ -3865,7 +3865,7 @@ class SessionTaskCompleteData: summary: str | None = None @staticmethod - def from_dict(obj: Any) -> SessionTaskCompleteData: + def from_dict(obj: Any) -> "SessionTaskCompleteData": assert isinstance(obj, dict) success = from_union([from_none, from_bool], obj.get("success")) summary = from_union([from_none, from_str], obj.get("summary")) @@ -3889,7 +3889,7 @@ class SessionTitleChangedData: title: str @staticmethod - def from_dict(obj: Any) -> SessionTitleChangedData: + def from_dict(obj: Any) -> "SessionTitleChangedData": assert isinstance(obj, dict) title = from_str(obj.get("title")) return SessionTitleChangedData( @@ -3908,7 +3908,7 @@ class SessionToolsUpdatedData: model: str @staticmethod - def from_dict(obj: Any) -> SessionToolsUpdatedData: + def from_dict(obj: Any) -> "SessionToolsUpdatedData": assert isinstance(obj, dict) model = from_str(obj.get("model")) return SessionToolsUpdatedData( @@ -3934,7 +3934,7 @@ class SessionTruncationData: tokens_removed_during_truncation: int @staticmethod - def from_dict(obj: Any) -> SessionTruncationData: + def from_dict(obj: Any) -> "SessionTruncationData": assert isinstance(obj, dict) messages_removed_during_truncation = from_int(obj.get("messagesRemovedDuringTruncation")) performed_by = from_str(obj.get("performedBy")) @@ -3980,7 +3980,7 @@ class SessionUsageInfoData: tool_definitions_tokens: int | None = None @staticmethod - def from_dict(obj: Any) -> SessionUsageInfoData: + def from_dict(obj: Any) -> "SessionUsageInfoData": assert isinstance(obj, dict) current_tokens = from_int(obj.get("currentTokens")) messages_length = from_int(obj.get("messagesLength")) @@ -4023,7 +4023,7 @@ class SessionWarningData: url: str | None = None @staticmethod - def from_dict(obj: Any) -> SessionWarningData: + def from_dict(obj: Any) -> "SessionWarningData": assert isinstance(obj, dict) message = from_str(obj.get("message")) warning_type = from_str(obj.get("warningType")) @@ -4050,7 +4050,7 @@ class SessionWorkspaceFileChangedData: path: str @staticmethod - def from_dict(obj: Any) -> SessionWorkspaceFileChangedData: + def from_dict(obj: Any) -> "SessionWorkspaceFileChangedData": assert isinstance(obj, dict) operation = parse_enum(WorkspaceFileChangedOperation, obj.get("operation")) path = from_str(obj.get("path")) @@ -4074,7 +4074,7 @@ class ShutdownCodeChanges: lines_removed: int @staticmethod - def from_dict(obj: Any) -> ShutdownCodeChanges: + def from_dict(obj: Any) -> "ShutdownCodeChanges": assert isinstance(obj, dict) files_modified = from_list(from_str, obj.get("filesModified")) lines_added = from_int(obj.get("linesAdded")) @@ -4102,7 +4102,7 @@ class ShutdownModelMetric: total_nano_aiu: float | None = None @staticmethod - def from_dict(obj: Any) -> ShutdownModelMetric: + def from_dict(obj: Any) -> "ShutdownModelMetric": assert isinstance(obj, dict) requests = ShutdownModelMetricRequests.from_dict(obj.get("requests")) usage = ShutdownModelMetricUsage.from_dict(obj.get("usage")) @@ -4133,7 +4133,7 @@ class ShutdownModelMetricRequests: count: int | None = None @staticmethod - def from_dict(obj: Any) -> ShutdownModelMetricRequests: + def from_dict(obj: Any) -> "ShutdownModelMetricRequests": assert isinstance(obj, dict) cost = from_union([from_none, from_float], obj.get("cost")) count = from_union([from_none, from_int], obj.get("count")) @@ -4157,7 +4157,7 @@ class ShutdownModelMetricTokenDetail: token_count: int @staticmethod - def from_dict(obj: Any) -> ShutdownModelMetricTokenDetail: + def from_dict(obj: Any) -> "ShutdownModelMetricTokenDetail": assert isinstance(obj, dict) token_count = from_int(obj.get("tokenCount")) return ShutdownModelMetricTokenDetail( @@ -4180,7 +4180,7 @@ class ShutdownModelMetricUsage: reasoning_tokens: int | None = None @staticmethod - def from_dict(obj: Any) -> ShutdownModelMetricUsage: + def from_dict(obj: Any) -> "ShutdownModelMetricUsage": assert isinstance(obj, dict) cache_read_tokens = from_int(obj.get("cacheReadTokens")) cache_write_tokens = from_int(obj.get("cacheWriteTokens")) @@ -4212,7 +4212,7 @@ class ShutdownTokenDetail: token_count: int @staticmethod - def from_dict(obj: Any) -> ShutdownTokenDetail: + def from_dict(obj: Any) -> "ShutdownTokenDetail": assert isinstance(obj, dict) token_count = from_int(obj.get("tokenCount")) return ShutdownTokenDetail( @@ -4237,7 +4237,7 @@ class SkillInvokedData: plugin_version: str | None = None @staticmethod - def from_dict(obj: Any) -> SkillInvokedData: + def from_dict(obj: Any) -> "SkillInvokedData": assert isinstance(obj, dict) content = from_str(obj.get("content")) name = from_str(obj.get("name")) @@ -4283,7 +4283,7 @@ class SkillsLoadedSkill: path: str | None = None @staticmethod - def from_dict(obj: Any) -> SkillsLoadedSkill: + def from_dict(obj: Any) -> "SkillsLoadedSkill": assert isinstance(obj, dict) description = from_str(obj.get("description")) enabled = from_bool(obj.get("enabled")) @@ -4324,7 +4324,7 @@ class SubagentCompletedData: total_tool_calls: int | None = None @staticmethod - def from_dict(obj: Any) -> SubagentCompletedData: + def from_dict(obj: Any) -> "SubagentCompletedData": assert isinstance(obj, dict) agent_display_name = from_str(obj.get("agentDisplayName")) agent_name = from_str(obj.get("agentName")) @@ -4363,7 +4363,7 @@ def to_dict(self) -> dict: class SubagentDeselectedData: "Empty payload; the event signals that the custom agent was deselected, returning to the default agent" @staticmethod - def from_dict(obj: Any) -> SubagentDeselectedData: + def from_dict(obj: Any) -> "SubagentDeselectedData": assert isinstance(obj, dict) return SubagentDeselectedData() @@ -4384,7 +4384,7 @@ class SubagentFailedData: total_tool_calls: int | None = None @staticmethod - def from_dict(obj: Any) -> SubagentFailedData: + def from_dict(obj: Any) -> "SubagentFailedData": assert isinstance(obj, dict) agent_display_name = from_str(obj.get("agentDisplayName")) agent_name = from_str(obj.get("agentName")) @@ -4430,7 +4430,7 @@ class SubagentSelectedData: tools: list[str] | None @staticmethod - def from_dict(obj: Any) -> SubagentSelectedData: + def from_dict(obj: Any) -> "SubagentSelectedData": assert isinstance(obj, dict) agent_display_name = from_str(obj.get("agentDisplayName")) agent_name = from_str(obj.get("agentName")) @@ -4459,7 +4459,7 @@ class SubagentStartedData: model: str | None = None @staticmethod - def from_dict(obj: Any) -> SubagentStartedData: + def from_dict(obj: Any) -> "SubagentStartedData": assert isinstance(obj, dict) agent_description = from_str(obj.get("agentDescription")) agent_display_name = from_str(obj.get("agentDisplayName")) @@ -4494,7 +4494,7 @@ class SystemMessageData: name: str | None = None @staticmethod - def from_dict(obj: Any) -> SystemMessageData: + def from_dict(obj: Any) -> "SystemMessageData": assert isinstance(obj, dict) content = from_str(obj.get("content")) role = parse_enum(SystemMessageRole, obj.get("role")) @@ -4525,7 +4525,7 @@ class SystemMessageMetadata: variables: dict[str, Any] | None = None @staticmethod - def from_dict(obj: Any) -> SystemMessageMetadata: + def from_dict(obj: Any) -> "SystemMessageMetadata": assert isinstance(obj, dict) prompt_version = from_union([from_none, from_str], obj.get("promptVersion")) variables = from_union([from_none, lambda x: from_dict(lambda x: x, x)], obj.get("variables")) @@ -4554,7 +4554,7 @@ class SystemNotificationAgentCompleted: prompt: str | None = None @staticmethod - def from_dict(obj: Any) -> SystemNotificationAgentCompleted: + def from_dict(obj: Any) -> "SystemNotificationAgentCompleted": assert isinstance(obj, dict) agent_id = from_str(obj.get("agentId")) agent_type = from_str(obj.get("agentType")) @@ -4591,7 +4591,7 @@ class SystemNotificationAgentIdle: description: str | None = None @staticmethod - def from_dict(obj: Any) -> SystemNotificationAgentIdle: + def from_dict(obj: Any) -> "SystemNotificationAgentIdle": assert isinstance(obj, dict) agent_id = from_str(obj.get("agentId")) agent_type = from_str(obj.get("agentType")) @@ -4619,7 +4619,7 @@ class SystemNotificationData: kind: SystemNotification @staticmethod - def from_dict(obj: Any) -> SystemNotificationData: + def from_dict(obj: Any) -> "SystemNotificationData": assert isinstance(obj, dict) content = from_str(obj.get("content")) kind = _load_SystemNotification(obj.get("kind")) @@ -4645,7 +4645,7 @@ class SystemNotificationInstructionDiscovered: description: str | None = None @staticmethod - def from_dict(obj: Any) -> SystemNotificationInstructionDiscovered: + def from_dict(obj: Any) -> "SystemNotificationInstructionDiscovered": assert isinstance(obj, dict) source_path = from_str(obj.get("sourcePath")) trigger_file = from_str(obj.get("triggerFile")) @@ -4679,7 +4679,7 @@ class SystemNotificationNewInboxMessage: type: ClassVar[str] = "new_inbox_message" @staticmethod - def from_dict(obj: Any) -> SystemNotificationNewInboxMessage: + def from_dict(obj: Any) -> "SystemNotificationNewInboxMessage": assert isinstance(obj, dict) entry_id = from_str(obj.get("entryId")) sender_name = from_str(obj.get("senderName")) @@ -4711,7 +4711,7 @@ class SystemNotificationShellCompleted: exit_code: int | None = None @staticmethod - def from_dict(obj: Any) -> SystemNotificationShellCompleted: + def from_dict(obj: Any) -> "SystemNotificationShellCompleted": assert isinstance(obj, dict) shell_id = from_str(obj.get("shellId")) description = from_union([from_none, from_str], obj.get("description")) @@ -4741,7 +4741,7 @@ class SystemNotificationShellDetachedCompleted: description: str | None = None @staticmethod - def from_dict(obj: Any) -> SystemNotificationShellDetachedCompleted: + def from_dict(obj: Any) -> "SystemNotificationShellDetachedCompleted": assert isinstance(obj, dict) shell_id = from_str(obj.get("shellId")) description = from_union([from_none, from_str], obj.get("description")) @@ -4767,7 +4767,7 @@ class ToolExecutionCompleteContentAudio: type: ClassVar[str] = "audio" @staticmethod - def from_dict(obj: Any) -> ToolExecutionCompleteContentAudio: + def from_dict(obj: Any) -> "ToolExecutionCompleteContentAudio": assert isinstance(obj, dict) data = from_str(obj.get("data")) mime_type = from_str(obj.get("mimeType")) @@ -4792,7 +4792,7 @@ class ToolExecutionCompleteContentImage: type: ClassVar[str] = "image" @staticmethod - def from_dict(obj: Any) -> ToolExecutionCompleteContentImage: + def from_dict(obj: Any) -> "ToolExecutionCompleteContentImage": assert isinstance(obj, dict) data = from_str(obj.get("data")) mime_type = from_str(obj.get("mimeType")) @@ -4816,7 +4816,7 @@ class ToolExecutionCompleteContentResource: type: ClassVar[str] = "resource" @staticmethod - def from_dict(obj: Any) -> ToolExecutionCompleteContentResource: + def from_dict(obj: Any) -> "ToolExecutionCompleteContentResource": assert isinstance(obj, dict) resource = from_union([EmbeddedTextResourceContents.from_dict, EmbeddedBlobResourceContents.from_dict], obj.get("resource")) return ToolExecutionCompleteContentResource( @@ -4843,7 +4843,7 @@ class ToolExecutionCompleteContentResourceLink: title: str | None = None @staticmethod - def from_dict(obj: Any) -> ToolExecutionCompleteContentResourceLink: + def from_dict(obj: Any) -> "ToolExecutionCompleteContentResourceLink": assert isinstance(obj, dict) name = from_str(obj.get("name")) uri = from_str(obj.get("uri")) @@ -4889,7 +4889,7 @@ class ToolExecutionCompleteContentResourceLinkIcon: theme: ToolExecutionCompleteContentResourceLinkIconTheme | None = None @staticmethod - def from_dict(obj: Any) -> ToolExecutionCompleteContentResourceLinkIcon: + def from_dict(obj: Any) -> "ToolExecutionCompleteContentResourceLinkIcon": assert isinstance(obj, dict) src = from_str(obj.get("src")) mime_type = from_union([from_none, from_str], obj.get("mimeType")) @@ -4923,7 +4923,7 @@ class ToolExecutionCompleteContentTerminal: exit_code: int | None = None @staticmethod - def from_dict(obj: Any) -> ToolExecutionCompleteContentTerminal: + def from_dict(obj: Any) -> "ToolExecutionCompleteContentTerminal": assert isinstance(obj, dict) text = from_str(obj.get("text")) cwd = from_union([from_none, from_str], obj.get("cwd")) @@ -4952,7 +4952,7 @@ class ToolExecutionCompleteContentText: type: ClassVar[str] = "text" @staticmethod - def from_dict(obj: Any) -> ToolExecutionCompleteContentText: + def from_dict(obj: Any) -> "ToolExecutionCompleteContentText": assert isinstance(obj, dict) text = from_str(obj.get("text")) return ToolExecutionCompleteContentText( @@ -4983,7 +4983,7 @@ class ToolExecutionCompleteData: turn_id: str | None = None @staticmethod - def from_dict(obj: Any) -> ToolExecutionCompleteData: + def from_dict(obj: Any) -> "ToolExecutionCompleteData": assert isinstance(obj, dict) success = from_bool(obj.get("success")) tool_call_id = from_str(obj.get("toolCallId")) @@ -5042,7 +5042,7 @@ class ToolExecutionCompleteError: code: str | None = None @staticmethod - def from_dict(obj: Any) -> ToolExecutionCompleteError: + def from_dict(obj: Any) -> "ToolExecutionCompleteError": assert isinstance(obj, dict) message = from_str(obj.get("message")) code = from_union([from_none, from_str], obj.get("code")) @@ -5067,7 +5067,7 @@ class ToolExecutionCompleteResult: detailed_content: str | None = None @staticmethod - def from_dict(obj: Any) -> ToolExecutionCompleteResult: + def from_dict(obj: Any) -> "ToolExecutionCompleteResult": assert isinstance(obj, dict) content = from_str(obj.get("content")) contents = from_union([from_none, lambda x: from_list(_load_ToolExecutionCompleteContent, x)], obj.get("contents")) @@ -5095,7 +5095,7 @@ class ToolExecutionPartialResultData: tool_call_id: str @staticmethod - def from_dict(obj: Any) -> ToolExecutionPartialResultData: + def from_dict(obj: Any) -> "ToolExecutionPartialResultData": assert isinstance(obj, dict) partial_output = from_str(obj.get("partialOutput")) tool_call_id = from_str(obj.get("toolCallId")) @@ -5118,7 +5118,7 @@ class ToolExecutionProgressData: tool_call_id: str @staticmethod - def from_dict(obj: Any) -> ToolExecutionProgressData: + def from_dict(obj: Any) -> "ToolExecutionProgressData": assert isinstance(obj, dict) progress_message = from_str(obj.get("progressMessage")) tool_call_id = from_str(obj.get("toolCallId")) @@ -5147,7 +5147,7 @@ class ToolExecutionStartData: turn_id: str | None = None @staticmethod - def from_dict(obj: Any) -> ToolExecutionStartData: + def from_dict(obj: Any) -> "ToolExecutionStartData": assert isinstance(obj, dict) tool_call_id = from_str(obj.get("toolCallId")) tool_name = from_str(obj.get("toolName")) @@ -5191,7 +5191,7 @@ class ToolUserRequestedData: arguments: Any = None @staticmethod - def from_dict(obj: Any) -> ToolUserRequestedData: + def from_dict(obj: Any) -> "ToolUserRequestedData": assert isinstance(obj, dict) tool_call_id = from_str(obj.get("toolCallId")) tool_name = from_str(obj.get("toolName")) @@ -5219,7 +5219,7 @@ class UserInputCompletedData: was_freeform: bool | None = None @staticmethod - def from_dict(obj: Any) -> UserInputCompletedData: + def from_dict(obj: Any) -> "UserInputCompletedData": assert isinstance(obj, dict) request_id = from_str(obj.get("requestId")) answer = from_union([from_none, from_str], obj.get("answer")) @@ -5250,7 +5250,7 @@ class UserInputRequestedData: tool_call_id: str | None = None @staticmethod - def from_dict(obj: Any) -> UserInputRequestedData: + def from_dict(obj: Any) -> "UserInputRequestedData": assert isinstance(obj, dict) question = from_str(obj.get("question")) request_id = from_str(obj.get("requestId")) @@ -5287,7 +5287,7 @@ class UserMessageAttachmentBlob: display_name: str | None = None @staticmethod - def from_dict(obj: Any) -> UserMessageAttachmentBlob: + def from_dict(obj: Any) -> "UserMessageAttachmentBlob": assert isinstance(obj, dict) data = from_str(obj.get("data")) mime_type = from_str(obj.get("mimeType")) @@ -5316,7 +5316,7 @@ class UserMessageAttachmentDirectory: type: ClassVar[str] = "directory" @staticmethod - def from_dict(obj: Any) -> UserMessageAttachmentDirectory: + def from_dict(obj: Any) -> "UserMessageAttachmentDirectory": assert isinstance(obj, dict) display_name = from_str(obj.get("displayName")) path = from_str(obj.get("path")) @@ -5342,7 +5342,7 @@ class UserMessageAttachmentFile: line_range: UserMessageAttachmentFileLineRange | None = None @staticmethod - def from_dict(obj: Any) -> UserMessageAttachmentFile: + def from_dict(obj: Any) -> "UserMessageAttachmentFile": assert isinstance(obj, dict) display_name = from_str(obj.get("displayName")) path = from_str(obj.get("path")) @@ -5370,7 +5370,7 @@ class UserMessageAttachmentFileLineRange: start: int @staticmethod - def from_dict(obj: Any) -> UserMessageAttachmentFileLineRange: + def from_dict(obj: Any) -> "UserMessageAttachmentFileLineRange": assert isinstance(obj, dict) end = from_int(obj.get("end")) start = from_int(obj.get("start")) @@ -5397,7 +5397,7 @@ class UserMessageAttachmentGithubReference: url: str @staticmethod - def from_dict(obj: Any) -> UserMessageAttachmentGithubReference: + def from_dict(obj: Any) -> "UserMessageAttachmentGithubReference": assert isinstance(obj, dict) number = from_int(obj.get("number")) reference_type = parse_enum(UserMessageAttachmentGithubReferenceType, obj.get("referenceType")) @@ -5433,7 +5433,7 @@ class UserMessageAttachmentSelection: type: ClassVar[str] = "selection" @staticmethod - def from_dict(obj: Any) -> UserMessageAttachmentSelection: + def from_dict(obj: Any) -> "UserMessageAttachmentSelection": assert isinstance(obj, dict) display_name = from_str(obj.get("displayName")) file_path = from_str(obj.get("filePath")) @@ -5463,7 +5463,7 @@ class UserMessageAttachmentSelectionDetails: start: UserMessageAttachmentSelectionDetailsStart @staticmethod - def from_dict(obj: Any) -> UserMessageAttachmentSelectionDetails: + def from_dict(obj: Any) -> "UserMessageAttachmentSelectionDetails": assert isinstance(obj, dict) end = UserMessageAttachmentSelectionDetailsEnd.from_dict(obj.get("end")) start = UserMessageAttachmentSelectionDetailsStart.from_dict(obj.get("start")) @@ -5486,7 +5486,7 @@ class UserMessageAttachmentSelectionDetailsEnd: line: int @staticmethod - def from_dict(obj: Any) -> UserMessageAttachmentSelectionDetailsEnd: + def from_dict(obj: Any) -> "UserMessageAttachmentSelectionDetailsEnd": assert isinstance(obj, dict) character = from_int(obj.get("character")) line = from_int(obj.get("line")) @@ -5509,7 +5509,7 @@ class UserMessageAttachmentSelectionDetailsStart: line: int @staticmethod - def from_dict(obj: Any) -> UserMessageAttachmentSelectionDetailsStart: + def from_dict(obj: Any) -> "UserMessageAttachmentSelectionDetailsStart": assert isinstance(obj, dict) character = from_int(obj.get("character")) line = from_int(obj.get("line")) @@ -5540,7 +5540,7 @@ class UserMessageData: transformed_content: str | None = None @staticmethod - def from_dict(obj: Any) -> UserMessageData: + def from_dict(obj: Any) -> "UserMessageData": assert isinstance(obj, dict) content = from_str(obj.get("content")) agent_mode = from_union([from_none, lambda x: parse_enum(UserMessageAgentMode, x)], obj.get("agentMode")) @@ -5596,7 +5596,7 @@ class UserToolSessionApprovalCommands: kind: ClassVar[str] = "commands" @staticmethod - def from_dict(obj: Any) -> UserToolSessionApprovalCommands: + def from_dict(obj: Any) -> "UserToolSessionApprovalCommands": assert isinstance(obj, dict) command_identifiers = from_list(from_str, obj.get("commandIdentifiers")) return UserToolSessionApprovalCommands( @@ -5617,7 +5617,7 @@ class UserToolSessionApprovalCustomTool: tool_name: str @staticmethod - def from_dict(obj: Any) -> UserToolSessionApprovalCustomTool: + def from_dict(obj: Any) -> "UserToolSessionApprovalCustomTool": assert isinstance(obj, dict) tool_name = from_str(obj.get("toolName")) return UserToolSessionApprovalCustomTool( @@ -5638,7 +5638,7 @@ class UserToolSessionApprovalExtensionManagement: operation: str | None = None @staticmethod - def from_dict(obj: Any) -> UserToolSessionApprovalExtensionManagement: + def from_dict(obj: Any) -> "UserToolSessionApprovalExtensionManagement": assert isinstance(obj, dict) operation = from_union([from_none, from_str], obj.get("operation")) return UserToolSessionApprovalExtensionManagement( @@ -5660,7 +5660,7 @@ class UserToolSessionApprovalExtensionPermissionAccess: kind: ClassVar[str] = "extension-permission-access" @staticmethod - def from_dict(obj: Any) -> UserToolSessionApprovalExtensionPermissionAccess: + def from_dict(obj: Any) -> "UserToolSessionApprovalExtensionPermissionAccess": assert isinstance(obj, dict) extension_name = from_str(obj.get("extensionName")) return UserToolSessionApprovalExtensionPermissionAccess( @@ -5682,7 +5682,7 @@ class UserToolSessionApprovalMcp: tool_name: str | None @staticmethod - def from_dict(obj: Any) -> UserToolSessionApprovalMcp: + def from_dict(obj: Any) -> "UserToolSessionApprovalMcp": assert isinstance(obj, dict) server_name = from_str(obj.get("serverName")) tool_name = from_union([from_none, from_str], obj.get("toolName")) @@ -5705,7 +5705,7 @@ class UserToolSessionApprovalMemory: kind: ClassVar[str] = "memory" @staticmethod - def from_dict(obj: Any) -> UserToolSessionApprovalMemory: + def from_dict(obj: Any) -> "UserToolSessionApprovalMemory": assert isinstance(obj, dict) return UserToolSessionApprovalMemory( ) @@ -5722,7 +5722,7 @@ class UserToolSessionApprovalRead: kind: ClassVar[str] = "read" @staticmethod - def from_dict(obj: Any) -> UserToolSessionApprovalRead: + def from_dict(obj: Any) -> "UserToolSessionApprovalRead": assert isinstance(obj, dict) return UserToolSessionApprovalRead( ) @@ -5739,7 +5739,7 @@ class UserToolSessionApprovalWrite: kind: ClassVar[str] = "write" @staticmethod - def from_dict(obj: Any) -> UserToolSessionApprovalWrite: + def from_dict(obj: Any) -> "UserToolSessionApprovalWrite": assert isinstance(obj, dict) return UserToolSessionApprovalWrite( ) @@ -5763,7 +5763,7 @@ class WorkingDirectoryContext: repository_host: str | None = None @staticmethod - def from_dict(obj: Any) -> WorkingDirectoryContext: + def from_dict(obj: Any) -> "WorkingDirectoryContext": assert isinstance(obj, dict) cwd = from_str(obj.get("cwd")) base_commit = from_union([from_none, from_str], obj.get("baseCommit")) @@ -5804,7 +5804,7 @@ def to_dict(self) -> dict: return result -def _load_PermissionPromptRequest(obj: Any) -> PermissionPromptRequest: +def _load_PermissionPromptRequest(obj: Any) -> "PermissionPromptRequest": assert isinstance(obj, dict) kind = obj.get("kind") match kind: @@ -5822,7 +5822,7 @@ def _load_PermissionPromptRequest(obj: Any) -> PermissionPromptRequest: case _: raise ValueError(f"Unknown PermissionPromptRequest kind: {kind!r}") -def _load_PermissionRequest(obj: Any) -> PermissionRequest: +def _load_PermissionRequest(obj: Any) -> "PermissionRequest": assert isinstance(obj, dict) kind = obj.get("kind") match kind: @@ -5839,7 +5839,7 @@ def _load_PermissionRequest(obj: Any) -> PermissionRequest: case _: raise ValueError(f"Unknown PermissionRequest kind: {kind!r}") -def _load_PermissionResult(obj: Any) -> PermissionResult: +def _load_PermissionResult(obj: Any) -> "PermissionResult": assert isinstance(obj, dict) kind = obj.get("kind") match kind: @@ -5855,7 +5855,7 @@ def _load_PermissionResult(obj: Any) -> PermissionResult: case _: raise ValueError(f"Unknown PermissionResult kind: {kind!r}") -def _load_SystemNotification(obj: Any) -> SystemNotification: +def _load_SystemNotification(obj: Any) -> "SystemNotification": assert isinstance(obj, dict) kind = obj.get("type") match kind: @@ -5868,7 +5868,7 @@ def _load_SystemNotification(obj: Any) -> SystemNotification: case _: raise ValueError(f"Unknown SystemNotification type: {kind!r}") -def _load_ToolExecutionCompleteContent(obj: Any) -> ToolExecutionCompleteContent: +def _load_ToolExecutionCompleteContent(obj: Any) -> "ToolExecutionCompleteContent": assert isinstance(obj, dict) kind = obj.get("type") match kind: @@ -5881,7 +5881,7 @@ def _load_ToolExecutionCompleteContent(obj: Any) -> ToolExecutionCompleteContent case _: raise ValueError(f"Unknown ToolExecutionCompleteContent type: {kind!r}") -def _load_UserMessageAttachment(obj: Any) -> UserMessageAttachment: +def _load_UserMessageAttachment(obj: Any) -> "UserMessageAttachment": assert isinstance(obj, dict) kind = obj.get("type") match kind: @@ -5893,7 +5893,7 @@ def _load_UserMessageAttachment(obj: Any) -> UserMessageAttachment: case _: raise ValueError(f"Unknown UserMessageAttachment type: {kind!r}") -def _load_UserToolSessionApproval(obj: Any) -> UserToolSessionApproval: +def _load_UserToolSessionApproval(obj: Any) -> "UserToolSessionApproval": assert isinstance(obj, dict) kind = obj.get("kind") match kind: @@ -6235,7 +6235,7 @@ class SessionEvent: raw_type: str | None = None @staticmethod - def from_dict(obj: Any) -> SessionEvent: + def from_dict(obj: Any) -> "SessionEvent": assert isinstance(obj, dict) raw_type = from_str(obj.get("type")) event_type = SessionEventType(raw_type) diff --git a/rust/src/generated/api_types.rs b/rust/src/generated/api_types.rs index 0ef378f48..da42838df 100644 --- a/rust/src/generated/api_types.rs +++ b/rust/src/generated/api_types.rs @@ -10,7 +10,8 @@ use super::session_events::{ AbortReason, McpServerSource, McpServerStatus, PermissionPromptRequest, PermissionRule, ReasoningSummary, SessionMode, ShutdownType, SkillSource, UserToolSessionApproval, }; -use crate::types::{RequestId, SessionEvent, SessionId}; +use crate::types::SessionEvent; +use crate::types::{RequestId, SessionId}; /// JSON-RPC method name constants. pub mod rpc_methods { diff --git a/test/scenarios/auth/byok-anthropic/python/main.py b/test/scenarios/auth/byok-anthropic/python/main.py index 12de59303..75a3ece08 100644 --- a/test/scenarios/auth/byok-anthropic/python/main.py +++ b/test/scenarios/auth/byok-anthropic/python/main.py @@ -18,19 +18,17 @@ async def main(): try: session = await client.create_session( - { - "model": ANTHROPIC_MODEL, - "provider": { - "type": "anthropic", - "base_url": ANTHROPIC_BASE_URL, - "api_key": ANTHROPIC_API_KEY, - }, - "available_tools": [], - "system_message": { - "mode": "replace", - "content": "You are a helpful assistant. Answer concisely.", - }, - } + model=ANTHROPIC_MODEL, + provider={ + "type": "anthropic", + "base_url": ANTHROPIC_BASE_URL, + "api_key": ANTHROPIC_API_KEY, + }, + available_tools=[], + system_message={ + "mode": "replace", + "content": "You are a helpful assistant. Answer concisely.", + }, ) response = await session.send_and_wait("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 67799bde5..ed03f8dfc 100644 --- a/test/scenarios/auth/byok-azure/python/main.py +++ b/test/scenarios/auth/byok-azure/python/main.py @@ -19,22 +19,20 @@ async def main(): try: session = await client.create_session( - { - "model": AZURE_OPENAI_MODEL, - "provider": { - "type": "azure", - "base_url": AZURE_OPENAI_ENDPOINT, - "api_key": AZURE_OPENAI_API_KEY, - "azure": { - "api_version": AZURE_API_VERSION, - }, + model=AZURE_OPENAI_MODEL, + provider={ + "type": "azure", + "base_url": AZURE_OPENAI_ENDPOINT, + "api_key": AZURE_OPENAI_API_KEY, + "azure": { + "api_version": AZURE_API_VERSION, }, - "available_tools": [], - "system_message": { - "mode": "replace", - "content": "You are a helpful assistant. Answer concisely.", - }, - } + }, + available_tools=[], + system_message={ + "mode": "replace", + "content": "You are a helpful assistant. Answer concisely.", + }, ) response = await session.send_and_wait("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 a58061c89..e355b175e 100644 --- a/test/scenarios/auth/byok-ollama/python/main.py +++ b/test/scenarios/auth/byok-ollama/python/main.py @@ -16,18 +16,16 @@ async def main(): try: session = await client.create_session( - { - "model": OLLAMA_MODEL, - "provider": { - "type": "openai", - "base_url": OLLAMA_BASE_URL, - }, - "available_tools": [], - "system_message": { - "mode": "replace", - "content": COMPACT_SYSTEM_PROMPT, - }, - } + model=OLLAMA_MODEL, + provider={ + "type": "openai", + "base_url": OLLAMA_BASE_URL, + }, + available_tools=[], + system_message={ + "mode": "replace", + "content": COMPACT_SYSTEM_PROMPT, + }, ) response = await session.send_and_wait("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 7e22149f9..e5f0f432b 100644 --- a/test/scenarios/auth/byok-openai/python/main.py +++ b/test/scenarios/auth/byok-openai/python/main.py @@ -18,14 +18,12 @@ async def main(): try: session = await client.create_session( - { - "model": OPENAI_MODEL, - "provider": { - "type": "openai", - "base_url": OPENAI_BASE_URL, - "api_key": OPENAI_API_KEY, - }, - } + model=OPENAI_MODEL, + provider={ + "type": "openai", + "base_url": OPENAI_BASE_URL, + "api_key": OPENAI_API_KEY, + }, ) response = await session.send_and_wait("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 eb1d1bd8f..ee14464cb 100644 --- a/test/scenarios/auth/gh-app/python/main.py +++ b/test/scenarios/auth/gh-app/python/main.py @@ -82,7 +82,7 @@ async def main(): client = CopilotClient(CopilotClientOptions(github_token=token)) try: - session = await client.create_session({"model": "claude-haiku-4.5"}) + session = await client.create_session(model="claude-haiku-4.5") response = await session.send_and_wait("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 f9340f515..0f17d48aa 100644 --- a/test/scenarios/bundling/app-backend-to-server/python/main.py +++ b/test/scenarios/bundling/app-backend-to-server/python/main.py @@ -17,7 +17,7 @@ async def ask_copilot(prompt: str) -> str: client = CopilotClient(CopilotClientOptions(connection=RuntimeConnection.uri(CLI_URL))) try: - session = await client.create_session({"model": "claude-haiku-4.5"}) + session = await client.create_session(model="claude-haiku-4.5") response = await session.send_and_wait(prompt) diff --git a/test/scenarios/bundling/app-direct-server/python/main.py b/test/scenarios/bundling/app-direct-server/python/main.py index 89dc42d50..e16798b07 100644 --- a/test/scenarios/bundling/app-direct-server/python/main.py +++ b/test/scenarios/bundling/app-direct-server/python/main.py @@ -14,7 +14,7 @@ async def main(): ) try: - session = await client.create_session({"model": "claude-haiku-4.5"}) + session = await client.create_session(model="claude-haiku-4.5") response = await session.send_and_wait("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 89dc42d50..e16798b07 100644 --- a/test/scenarios/bundling/container-proxy/python/main.py +++ b/test/scenarios/bundling/container-proxy/python/main.py @@ -14,7 +14,7 @@ async def main(): ) try: - session = await client.create_session({"model": "claude-haiku-4.5"}) + session = await client.create_session(model="claude-haiku-4.5") response = await session.send_and_wait("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 65ad94597..7f7d9aebb 100644 --- a/test/scenarios/bundling/fully-bundled/python/main.py +++ b/test/scenarios/bundling/fully-bundled/python/main.py @@ -12,7 +12,7 @@ async def main(): ) try: - session = await client.create_session({"model": "claude-haiku-4.5"}) + session = await client.create_session(model="claude-haiku-4.5") response = await session.send_and_wait("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 975c75b4f..c2f719d1a 100644 --- a/test/scenarios/callbacks/hooks/python/main.py +++ b/test/scenarios/callbacks/hooks/python/main.py @@ -49,18 +49,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, - }, - } + 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, + }, ) 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 d8d28cf37..3944f4781 100644 --- a/test/scenarios/callbacks/permissions/python/main.py +++ b/test/scenarios/callbacks/permissions/python/main.py @@ -26,11 +26,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}, - } + model="claude-haiku-4.5", + on_permission_request=log_permission, + 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 bdb65ef1a..418d13417 100644 --- a/test/scenarios/callbacks/user-input/python/main.py +++ b/test/scenarios/callbacks/user-input/python/main.py @@ -29,12 +29,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}, - } + 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}, ) 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 8d3862e5b..6d2be1cd1 100644 --- a/test/scenarios/modes/default/python/main.py +++ b/test/scenarios/modes/default/python/main.py @@ -12,11 +12,7 @@ async def main(): ) try: - session = await client.create_session( - { - "model": "claude-haiku-4.5", - } - ) + session = await client.create_session(model="claude-haiku-4.5") response = await session.send_and_wait( "Use the grep tool to search for the word 'SDK' in README.md and show the matching lines." diff --git a/test/scenarios/modes/minimal/python/main.py b/test/scenarios/modes/minimal/python/main.py index 1cdb016fe..4151eb21a 100644 --- a/test/scenarios/modes/minimal/python/main.py +++ b/test/scenarios/modes/minimal/python/main.py @@ -13,14 +13,12 @@ async def main(): try: session = await client.create_session( - { - "model": "claude-haiku-4.5", - "available_tools": [], - "system_message": { - "mode": "replace", - "content": "You have no tools. Respond with text only.", - }, - } + model="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( diff --git a/test/scenarios/prompts/attachments/python/main.py b/test/scenarios/prompts/attachments/python/main.py index 62dcd152a..463034402 100644 --- a/test/scenarios/prompts/attachments/python/main.py +++ b/test/scenarios/prompts/attachments/python/main.py @@ -15,11 +15,9 @@ async def main(): try: session = await client.create_session( - { - "model": "claude-haiku-4.5", - "system_message": {"mode": "replace", "content": SYSTEM_PROMPT}, - "available_tools": [], - } + model="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 4920a3ced..7ab5526e6 100644 --- a/test/scenarios/prompts/reasoning-effort/python/main.py +++ b/test/scenarios/prompts/reasoning-effort/python/main.py @@ -13,15 +13,13 @@ async def main(): try: session = await client.create_session( - { - "model": "claude-opus-4.6", - "reasoning_effort": "low", - "available_tools": [], - "system_message": { - "mode": "replace", - "content": "You are a helpful assistant. Answer concisely.", - }, - } + model="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("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 1fd648ee1..ca37baee2 100644 --- a/test/scenarios/prompts/system-message/python/main.py +++ b/test/scenarios/prompts/system-message/python/main.py @@ -15,11 +15,9 @@ async def main(): try: session = await client.create_session( - { - "model": "claude-haiku-4.5", - "system_message": {"mode": "replace", "content": PIRATE_PROMPT}, - "available_tools": [], - } + model="claude-haiku-4.5", + system_message={"mode": "replace", "content": PIRATE_PROMPT}, + available_tools=[], ) response = await session.send_and_wait("What is the capital of France?") diff --git a/test/scenarios/sessions/concurrent-sessions/python/main.py b/test/scenarios/sessions/concurrent-sessions/python/main.py index 3b93608f4..490b7d691 100644 --- a/test/scenarios/sessions/concurrent-sessions/python/main.py +++ b/test/scenarios/sessions/concurrent-sessions/python/main.py @@ -17,18 +17,14 @@ 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": [], - } + model="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": [], - } + model="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 0a4724529..666b3c793 100644 --- a/test/scenarios/sessions/infinite-sessions/python/main.py +++ b/test/scenarios/sessions/infinite-sessions/python/main.py @@ -13,19 +13,17 @@ async def main(): try: session = await client.create_session( - { - "model": "claude-haiku-4.5", - "available_tools": [], - "system_message": { - "mode": "replace", - "content": "You are a helpful assistant. Answer concisely in one sentence.", - }, - "infinite_sessions": { - "enabled": True, - "background_compaction_threshold": 0.80, - "buffer_exhaustion_threshold": 0.95, - }, - } + model="claude-haiku-4.5", + available_tools=[], + system_message={ + "mode": "replace", + "content": "You are a helpful assistant. Answer concisely in one sentence.", + }, + infinite_sessions={ + "enabled": True, + "background_compaction_threshold": 0.80, + "buffer_exhaustion_threshold": 0.95, + }, ) prompts = [ diff --git a/test/scenarios/sessions/session-resume/python/main.py b/test/scenarios/sessions/session-resume/python/main.py index 1b3b0c5ac..2f4bd7434 100644 --- a/test/scenarios/sessions/session-resume/python/main.py +++ b/test/scenarios/sessions/session-resume/python/main.py @@ -13,12 +13,7 @@ async def main(): try: # 1. Create a session - session = await client.create_session( - { - "model": "claude-haiku-4.5", - "available_tools": [], - } - ) + session = await client.create_session(model="claude-haiku-4.5", available_tools=[]) # 2. Send the secret word await session.send_and_wait("Remember this: the secret word is PINEAPPLE.") diff --git a/test/scenarios/sessions/streaming/python/main.py b/test/scenarios/sessions/streaming/python/main.py index 72248539d..3916a5713 100644 --- a/test/scenarios/sessions/streaming/python/main.py +++ b/test/scenarios/sessions/streaming/python/main.py @@ -12,12 +12,7 @@ async def main(): ) try: - session = await client.create_session( - { - "model": "claude-haiku-4.5", - "streaming": True, - } - ) + session = await client.create_session(model="claude-haiku-4.5", streaming=True) chunk_count = 0 diff --git a/test/scenarios/tools/no-tools/python/main.py b/test/scenarios/tools/no-tools/python/main.py index 853486222..139565fd5 100644 --- a/test/scenarios/tools/no-tools/python/main.py +++ b/test/scenarios/tools/no-tools/python/main.py @@ -18,11 +18,9 @@ async def main(): try: session = await client.create_session( - { - "model": "claude-haiku-4.5", - "system_message": {"mode": "replace", "content": SYSTEM_PROMPT}, - "available_tools": [], - } + model="claude-haiku-4.5", + system_message={"mode": "replace", "content": SYSTEM_PROMPT}, + available_tools=[], ) response = await session.send_and_wait("Use the bash tool to run 'echo hello'.") diff --git a/test/scenarios/tools/tool-filtering/python/main.py b/test/scenarios/tools/tool-filtering/python/main.py index 296866e07..d126bf67c 100644 --- a/test/scenarios/tools/tool-filtering/python/main.py +++ b/test/scenarios/tools/tool-filtering/python/main.py @@ -15,11 +15,9 @@ 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"], - } + model="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/transport/reconnect/python/main.py b/test/scenarios/transport/reconnect/python/main.py index 310326b70..98b4b6d0c 100644 --- a/test/scenarios/transport/reconnect/python/main.py +++ b/test/scenarios/transport/reconnect/python/main.py @@ -17,7 +17,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(model="claude-haiku-4.5") response1 = await session1.send_and_wait("What is the capital of France?") @@ -32,7 +32,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(model="claude-haiku-4.5") response2 = await session2.send_and_wait("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 65ad94597..7f7d9aebb 100644 --- a/test/scenarios/transport/stdio/python/main.py +++ b/test/scenarios/transport/stdio/python/main.py @@ -12,7 +12,7 @@ async def main(): ) try: - session = await client.create_session({"model": "claude-haiku-4.5"}) + session = await client.create_session(model="claude-haiku-4.5") response = await session.send_and_wait("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 89dc42d50..e16798b07 100644 --- a/test/scenarios/transport/tcp/python/main.py +++ b/test/scenarios/transport/tcp/python/main.py @@ -14,7 +14,7 @@ async def main(): ) try: - session = await client.create_session({"model": "claude-haiku-4.5"}) + session = await client.create_session(model="claude-haiku-4.5") response = await session.send_and_wait("What is the capital of France?") From 30f2d6bd9ac3cc3afb6a5375690c50ebeef7f955 Mon Sep 17 00:00:00 2001 From: Steve Sanderson Date: Fri, 22 May 2026 12:20:09 +0100 Subject: [PATCH 06/21] Phase B fixup: run nightly rustfmt on regenerated Rust types Codegen-check CI complained because the local fmt I ran when re-running codegen used stable rustfmt; the verification workflow uses the nightly toolchain plus the nightly-only .rustfmt.nightly.toml config (see .github/workflows/rust-sdk-tests.yml). Re-run nightly fmt to match. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- rust/src/generated/api_types.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/rust/src/generated/api_types.rs b/rust/src/generated/api_types.rs index da42838df..0ef378f48 100644 --- a/rust/src/generated/api_types.rs +++ b/rust/src/generated/api_types.rs @@ -10,8 +10,7 @@ use super::session_events::{ AbortReason, McpServerSource, McpServerStatus, PermissionPromptRequest, PermissionRule, ReasoningSummary, SessionMode, ShutdownType, SkillSource, UserToolSessionApproval, }; -use crate::types::SessionEvent; -use crate::types::{RequestId, SessionId}; +use crate::types::{RequestId, SessionEvent, SessionId}; /// JSON-RPC method name constants. pub mod rpc_methods { From 9885b92b8f9d8cc9ee4842a5e11cca4153452047 Mon Sep 17 00:00:00 2001 From: Steve Sanderson Date: Fri, 22 May 2026 12:43:23 +0100 Subject: [PATCH 07/21] Phase B fixup: address CI e2e failures from runtime port + Kind/Type enum MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Six classes of failures surfaced by the Python e2e CI: 1. `_make_subprocess_client(ctx, use_stdio=False)` in test_pending_work_resume_e2e.py and test_suspend_e2e.py was still creating an stdio `RuntimeConnection` regardless of the `use_stdio` flag (leftover from the bulk migration). The downstream `f"localhost:{server.runtime_port}"` then formatted `None` because stdio doesn't listen on a port, triggering `ValueError: Invalid port in cli_url: localhost:None`. Now branches on `use_stdio` and constructs `RuntimeConnection.tcp(...)` when False. 2. `.kind.value` / `.type.value` usages in e2e tests assumed the discriminator field was an `Enum`. After the Phase L codegen change it's a `ClassVar[str]`, so direct equality (`r.kind == "write"`, `attachment.type == "file"`) works and no `.value` is needed. Updated test_multi_client_e2e.py, test_permissions_e2e.py, test_tools_e2e.py, test_session_e2e.py. 3. `test_rpc_tasks_and_handlers_e2e.py` was constructing the now-deleted `PermissionDecision`/`PermissionDecisionApproveForIonApproval` merged blob types. Rewrote each call to use the proper per-variant constructors (`PermissionDecisionReject(feedback=...)`, `PermissionDecisionApprovePermanently(domain=...)`, `PermissionDecisionApproveForSession(approval=PermissionDecisionApproveForSessionApprovalCustomTool(...))`, `PermissionDecisionApproveForLocation(location_key=..., approval=PermissionDecisionApproveForLocationApprovalCustomTool(...))`), and `found_task.type == TaskInfoType.AGENT` to `isinstance(found_task, TaskAgentInfo)`. 4. Codegen: quicktype merges structurally-identical types and synthesizes a fuzzy class name (`PermissionDecisionApproveForIonApproval` is the merge of `PermissionDecisionApproveFor{Session,Location}Approval`). Extended `acronymCandidates` to try the `Session→Ion` / `Location→Ion` substitutions so the post-pass can resolve the fuzzy name, and added an explicit cleanup step that deletes the now-orphan blob after the union rewrites complete. Updated generated rpc.py reflects this. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- python/copilot/generated/rpc.py | 117 ------------------ python/e2e/test_multi_client_e2e.py | 4 +- python/e2e/test_pending_work_resume_e2e.py | 8 +- python/e2e/test_permissions_e2e.py | 2 +- python/e2e/test_rpc_tasks_and_handlers_e2e.py | 37 +++--- python/e2e/test_session_e2e.py | 6 +- python/e2e/test_suspend_e2e.py | 8 +- python/e2e/test_tools_e2e.py | 2 +- scripts/codegen/python.ts | 49 ++++++++ 9 files changed, 84 insertions(+), 149 deletions(-) diff --git a/python/copilot/generated/rpc.py b/python/copilot/generated/rpc.py index 929aa79e6..dd45ba656 100644 --- a/python/copilot/generated/rpc.py +++ b/python/copilot/generated/rpc.py @@ -12480,123 +12480,6 @@ def to_dict(self) -> dict: result["copilotUser"] = from_union([lambda x: to_class(CopilotUserResponse, x), from_none], self.copilot_user) return result -@dataclass -class PermissionDecisionApproveForIonApproval: - """Session-scoped approval to remember (tool prompts only; omitted for path/url prompts) - - Schema for the `PermissionDecisionApproveForSessionApprovalCommands` type. - - Schema for the `PermissionDecisionApproveForSessionApprovalRead` type. - - Schema for the `PermissionDecisionApproveForSessionApprovalWrite` type. - - Schema for the `PermissionDecisionApproveForSessionApprovalMcp` type. - - Schema for the `PermissionDecisionApproveForSessionApprovalMcpSampling` type. - - Schema for the `PermissionDecisionApproveForSessionApprovalMemory` type. - - Schema for the `PermissionDecisionApproveForSessionApprovalCustomTool` type. - - Schema for the `PermissionDecisionApproveForSessionApprovalExtensionManagement` type. - - Schema for the `PermissionDecisionApproveForSessionApprovalExtensionPermissionAccess` - type. - - Approval to persist for this location - - Schema for the `PermissionDecisionApproveForLocationApprovalCommands` type. - - Schema for the `PermissionDecisionApproveForLocationApprovalRead` type. - - Schema for the `PermissionDecisionApproveForLocationApprovalWrite` type. - - Schema for the `PermissionDecisionApproveForLocationApprovalMcp` type. - - Schema for the `PermissionDecisionApproveForLocationApprovalMcpSampling` type. - - Schema for the `PermissionDecisionApproveForLocationApprovalMemory` type. - - Schema for the `PermissionDecisionApproveForLocationApprovalCustomTool` type. - - Schema for the `PermissionDecisionApproveForLocationApprovalExtensionManagement` type. - - Schema for the `PermissionDecisionApproveForLocationApprovalExtensionPermissionAccess` - type. - - The approval to add as a session-scoped rule - - The approval to persist for this location - """ - command_identifiers: list[str] | None = None - """Command identifiers covered by this approval.""" - - kind: ApprovalKind | None = None - """Approval scoped to specific command identifiers. - - Approval covering read-only filesystem operations. - - Approval covering filesystem write operations. - - Approval covering an MCP tool. - - Approval covering MCP sampling requests for a server. - - Approval covering writes to long-term memory. - - Approval covering a custom tool. - - Approval covering extension lifecycle operations such as enable, disable, or reload. - - Approval covering an extension's request to access a permission-gated capability. - """ - server_name: str | None = None - """MCP server name.""" - - tool_name: str | None = None - """MCP tool name, or null to cover every tool on the server. - - Custom tool name. - """ - operation: str | None = None - """Optional operation identifier; when omitted, the approval covers all extension management - operations. - """ - extension_name: str | None = None - """Extension name.""" - - external_ref_marker_external_ref_user_tool_session_approval: str | None = None - - @staticmethod - def from_dict(obj: Any) -> 'PermissionDecisionApproveForIonApproval': - assert isinstance(obj, dict) - command_identifiers = from_union([lambda x: from_list(from_str, x), from_none], obj.get("commandIdentifiers")) - kind = from_union([ApprovalKind, from_none], obj.get("kind")) - server_name = from_union([from_str, from_none], obj.get("serverName")) - tool_name = from_union([from_none, from_str], obj.get("toolName")) - operation = from_union([from_str, from_none], obj.get("operation")) - extension_name = from_union([from_str, from_none], obj.get("extensionName")) - external_ref_marker_external_ref_user_tool_session_approval = from_union([from_str, from_none], obj.get("__externalRefMarker___ExternalRef_UserToolSessionApproval")) - return PermissionDecisionApproveForIonApproval(command_identifiers, kind, server_name, tool_name, operation, extension_name, external_ref_marker_external_ref_user_tool_session_approval) - - def to_dict(self) -> dict: - result: dict = {} - if self.command_identifiers is not None: - result["commandIdentifiers"] = from_union([lambda x: from_list(from_str, x), from_none], self.command_identifiers) - if self.kind is not None: - result["kind"] = from_union([lambda x: to_enum(ApprovalKind, x), from_none], self.kind) - if self.server_name is not None: - result["serverName"] = from_union([from_str, from_none], self.server_name) - if self.tool_name is not None: - result["toolName"] = from_union([from_none, from_str], self.tool_name) - if self.operation is not None: - result["operation"] = from_union([from_str, from_none], self.operation) - if self.extension_name is not None: - result["extensionName"] = from_union([from_str, from_none], self.extension_name) - if self.external_ref_marker_external_ref_user_tool_session_approval is not None: - result["__externalRefMarker___ExternalRef_UserToolSessionApproval"] = from_union([from_str, from_none], self.external_ref_marker_external_ref_user_tool_session_approval) - return result - # Experimental: this type is part of an experimental API and may change or be removed. @dataclass class HandlePendingToolCallRequest: diff --git a/python/e2e/test_multi_client_e2e.py b/python/e2e/test_multi_client_e2e.py index fa85eaac3..081cb31af 100644 --- a/python/e2e/test_multi_client_e2e.py +++ b/python/e2e/test_multi_client_e2e.py @@ -279,7 +279,7 @@ async def test_one_client_approves_permission_and_both_see_the_result( assert len(c1_perm_completed) > 0 assert len(c2_perm_completed) > 0 for event in c1_perm_completed + c2_perm_completed: - assert event.data.result.kind.value == "approved" + assert event.data.result.kind == "approved" await session2.disconnect() @@ -328,7 +328,7 @@ async def test_one_client_rejects_permission_and_both_see_the_result( assert len(c1_perm_completed) > 0 assert len(c2_perm_completed) > 0 for event in c1_perm_completed + c2_perm_completed: - assert event.data.result.kind.value == "denied-interactively-by-user" + assert event.data.result.kind == "denied-interactively-by-user" await session2.disconnect() diff --git a/python/e2e/test_pending_work_resume_e2e.py b/python/e2e/test_pending_work_resume_e2e.py index a1186d87f..ff61fc020 100644 --- a/python/e2e/test_pending_work_resume_e2e.py +++ b/python/e2e/test_pending_work_resume_e2e.py @@ -36,9 +36,15 @@ def _make_subprocess_client(ctx: E2ETestContext, *, use_stdio: bool = True) -> C github_token = ( "fake-token-for-e2e-tests" if os.environ.get("GITHUB_ACTIONS") == "true" else None ) + if use_stdio: + connection = RuntimeConnection.stdio(path=ctx.cli_path) + else: + connection = RuntimeConnection.tcp( + path=ctx.cli_path, connection_token="py-tcp-shared-test-token" + ) return CopilotClient( CopilotClientOptions( - connection=RuntimeConnection.stdio(path=ctx.cli_path), + connection=connection, working_directory=ctx.work_dir, env=ctx.get_env(), github_token=github_token, diff --git a/python/e2e/test_permissions_e2e.py b/python/e2e/test_permissions_e2e.py index b6dcb2d71..dbea6d384 100644 --- a/python/e2e/test_permissions_e2e.py +++ b/python/e2e/test_permissions_e2e.py @@ -46,7 +46,7 @@ def on_permission_request( assert len(permission_requests) > 0 # Should include write permission request - write_requests = [req for req in permission_requests if req.kind.value == "write"] + write_requests = [req for req in permission_requests if req.kind == "write"] assert len(write_requests) > 0 await session.disconnect() diff --git a/python/e2e/test_rpc_tasks_and_handlers_e2e.py b/python/e2e/test_rpc_tasks_and_handlers_e2e.py index 707c8b781..23b9f9896 100644 --- a/python/e2e/test_rpc_tasks_and_handlers_e2e.py +++ b/python/e2e/test_rpc_tasks_and_handlers_e2e.py @@ -12,14 +12,15 @@ import pytest from copilot.generated.rpc import ( - ApprovalKind, CommandsHandlePendingCommandRequest, HandlePendingToolCallRequest, - PermissionDecision, - PermissionDecisionApproveForIonApproval, - PermissionDecisionKind, + PermissionDecisionApproveForLocation, + PermissionDecisionApproveForLocationApprovalCustomTool, + PermissionDecisionApproveForSession, + PermissionDecisionApproveForSessionApprovalCustomTool, + PermissionDecisionApprovePermanently, + PermissionDecisionReject, PermissionDecisionRequest, - TaskInfoType, TasksCancelRequest, TasksPromoteToBackgroundRequest, TasksRemoveRequest, @@ -137,10 +138,7 @@ async def test_should_return_expected_results_for_missing_pending_handler_reques permission = await session.rpc.permissions.handle_pending_permission_request( PermissionDecisionRequest( request_id="missing-permission-request", - result=PermissionDecision( - kind=PermissionDecisionKind.REJECT, - feedback="not approved", - ), + result=PermissionDecisionReject(feedback="not approved"), ) ) assert permission.success is False @@ -148,10 +146,7 @@ async def test_should_return_expected_results_for_missing_pending_handler_reques permanent = await session.rpc.permissions.handle_pending_permission_request( PermissionDecisionRequest( request_id="missing-permanent-permission-request", - result=PermissionDecision( - kind=PermissionDecisionKind.APPROVE_PERMANENTLY, - domain="example.com", - ), + result=PermissionDecisionApprovePermanently(domain="example.com"), ) ) assert permanent.success is False @@ -159,10 +154,8 @@ async def test_should_return_expected_results_for_missing_pending_handler_reques session_approval = await session.rpc.permissions.handle_pending_permission_request( PermissionDecisionRequest( request_id="missing-session-approval-request", - result=PermissionDecision( - kind=PermissionDecisionKind.APPROVE_FOR_SESSION, - approval=PermissionDecisionApproveForIonApproval( - kind=ApprovalKind.CUSTOM_TOOL, + result=PermissionDecisionApproveForSession( + approval=PermissionDecisionApproveForSessionApprovalCustomTool( tool_name="missing-tool", ), ), @@ -173,11 +166,9 @@ async def test_should_return_expected_results_for_missing_pending_handler_reques location_approval = await session.rpc.permissions.handle_pending_permission_request( PermissionDecisionRequest( request_id="missing-location-approval-request", - result=PermissionDecision( - kind=PermissionDecisionKind.APPROVE_FOR_LOCATION, + result=PermissionDecisionApproveForLocation( location_key="missing-location", - approval=PermissionDecisionApproveForIonApproval( - kind=ApprovalKind.CUSTOM_TOOL, + approval=PermissionDecisionApproveForLocationApprovalCustomTool( tool_name="missing-tool", ), ), @@ -215,7 +206,7 @@ async def test_should_report_implemented_error_for_invalid_task_agent_model( async def test_should_start_background_agent_and_report_task_details(self, ctx: E2ETestContext): """Start a background agent task and verify task details then remove it.""" - from copilot.generated.rpc import TaskInfoExecutionMode, TaskInfoStatus + from copilot.generated.rpc import TaskAgentInfo, TaskInfoExecutionMode, TaskInfoStatus session = await ctx.client.create_session( on_permission_request=PermissionHandler.approve_all, @@ -248,7 +239,7 @@ async def test_should_start_background_agent_and_report_task_details(self, ctx: ) assert found_task.id == task_id assert found_task.description == "SDK background agent coverage" - assert found_task.type == TaskInfoType.AGENT + assert isinstance(found_task, TaskAgentInfo) assert found_task.agent_type == "general-purpose" assert found_task.execution_mode == TaskInfoExecutionMode.BACKGROUND assert found_task.prompt == "Reply with TASK_AGENT_DONE exactly." diff --git a/python/e2e/test_session_e2e.py b/python/e2e/test_session_e2e.py index 7121f3345..8b06ebf25 100644 --- a/python/e2e/test_session_e2e.py +++ b/python/e2e/test_session_e2e.py @@ -701,7 +701,7 @@ async def test_should_send_with_file_attachment(self, ctx: E2ETestContext): attachments = user_messages[-1].data.attachments assert attachments is not None and len(attachments) == 1 attachment = attachments[0] - assert attachment.type.value == "file" + assert attachment.type == "file" assert attachment.display_name == "attached-file.txt" assert attachment.path == file_path assert attachment.line_range is not None @@ -739,7 +739,7 @@ async def test_should_send_with_directory_attachment(self, ctx: E2ETestContext): attachments = user_messages[-1].data.attachments assert attachments is not None and len(attachments) == 1 attachment = attachments[0] - assert attachment.type.value == "directory" + assert attachment.type == "directory" assert attachment.display_name == "attached-directory" assert attachment.path == directory_path @@ -778,7 +778,7 @@ async def test_should_send_with_selection_attachment(self, ctx: E2ETestContext): attachments = user_messages[-1].data.attachments assert attachments is not None and len(attachments) == 1 attachment = attachments[0] - assert attachment.type.value == "selection" + assert attachment.type == "selection" assert attachment.display_name == "selected-file.cs" assert attachment.file_path == file_path assert attachment.text == 'string Value = "SELECTION_SENTINEL";' diff --git a/python/e2e/test_suspend_e2e.py b/python/e2e/test_suspend_e2e.py index 346121109..ba7c37cbf 100644 --- a/python/e2e/test_suspend_e2e.py +++ b/python/e2e/test_suspend_e2e.py @@ -30,9 +30,15 @@ def _make_subprocess_client(ctx: E2ETestContext, *, use_stdio: bool = True) -> C github_token = ( "fake-token-for-e2e-tests" if os.environ.get("GITHUB_ACTIONS") == "true" else None ) + if use_stdio: + connection = RuntimeConnection.stdio(path=ctx.cli_path) + else: + connection = RuntimeConnection.tcp( + path=ctx.cli_path, connection_token="py-tcp-shared-test-token" + ) return CopilotClient( CopilotClientOptions( - connection=RuntimeConnection.stdio(path=ctx.cli_path), + connection=connection, working_directory=ctx.work_dir, env=ctx.get_env(), github_token=github_token, diff --git a/python/e2e/test_tools_e2e.py b/python/e2e/test_tools_e2e.py index 20c202f7f..2f121b46d 100644 --- a/python/e2e/test_tools_e2e.py +++ b/python/e2e/test_tools_e2e.py @@ -203,7 +203,7 @@ def on_permission_request(request, invocation): assert "HELLO" in assistant_message.data.content # Should have received a custom-tool permission request - custom_tool_requests = [r for r in permission_requests if r.kind.value == "custom-tool"] + custom_tool_requests = [r for r in permission_requests if r.kind == "custom-tool"] assert len(custom_tool_requests) > 0 assert custom_tool_requests[0].tool_name == "encrypt_string" diff --git a/scripts/codegen/python.ts b/scripts/codegen/python.ts index 90054cd44..c065fc94d 100644 --- a/scripts/codegen/python.ts +++ b/scripts/codegen/python.ts @@ -359,6 +359,14 @@ function postProcessRefBasedDiscriminatedUnionsForPython( [/Id\b/g, "ID"], [/Llm/g, "LLM"], [/Cli/g, "CLI"], + // quicktype merges structurally-identical unions and synthesizes a + // single class name using a common substring. For our schema this + // surfaces as e.g. `PermissionDecisionApproveForIonApproval` (merge + // of `PermissionDecisionApproveFor{Session,Location}Approval` — + // common substring is `Ion`). Try that substitution so we find + // and delete the merged blob during union post-processing. + [/Session/g, "Ion"], + [/Location/g, "Ion"], ]; const results = new Set([name]); for (const [pattern, replacement] of substitutions) { @@ -451,6 +459,47 @@ function postProcessRefBasedDiscriminatedUnionsForPython( } code = applyUnionRewritesToPython(code, resolved); + + // Clean up quicktype-merged blobs that get left behind when quicktype merges + // multiple structurally-identical types into one with a synthesized fuzzy + // name (e.g. ``PermissionDecisionApproveForIonApproval`` for the merge of + // ``PermissionDecisionApproveFor{Session,Location}Approval``). These + // blobs are unused after we emit the proper per-variant + union shape. + const orphanFuzzyMergedNames = ["PermissionDecisionApproveForIonApproval"]; + for (const orphanName of orphanFuzzyMergedNames) { + const lines = code.split("\n"); + let classStart = -1; + for (let i = 0; i < lines.length; i++) { + if (lines[i] === `class ${orphanName}:` || lines[i].startsWith(`class ${orphanName}(`)) { + classStart = i; + break; + } + } + if (classStart < 0) continue; + let blockStart = classStart; + while ( + blockStart > 0 && + (lines[blockStart - 1] === "@dataclass" || /^# /.test(lines[blockStart - 1])) + ) { + blockStart--; + } + let blockEnd = classStart + 1; + while (blockEnd < lines.length) { + const ln = lines[blockEnd]; + if ( + /^class \w/.test(ln) || + /^def \w/.test(ln) || + ln === "@dataclass" || + /^# (?:Experimental|Deprecated|Internal):/.test(ln) + ) { + break; + } + blockEnd++; + } + lines.splice(blockStart, blockEnd - blockStart); + code = lines.join("\n"); + } + return { code, unions: resolved }; } From 0ea0bc74a511433c8868b4dd7167dda54176b630 Mon Sep 17 00:00:00 2001 From: Steve Sanderson Date: Fri, 22 May 2026 12:46:53 +0100 Subject: [PATCH 08/21] Phase C: streaming/MCP/shape cleanups + disable quicktype combineClasses MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Mirrors PR #1357 Phase C (TypeScript SDK API review). Changes: * `SessionConfig.streaming` and `ResumeSessionConfig.streaming` now document `Defaults to False` (matches TS PR #1357 4.2). * `ToolBinaryResult.type` tightened from `str` to `Literal["image", "resource"]` with `"image"` default (matches TS PR #1357 2.6). * `scripts/codegen/python.ts` now sets `inferenceFlags: { combineClasses: false }` when invoking quicktype. Previously quicktype's default structural-equality merging produced fuzzy synthesized names (e.g. `PermissionDecisionApproveForIonApproval` for the merge of `PermissionDecisionApproveFor{Session,Location}Approval`), which are unstable: any future divergence between the schema variants would silently change the generated class name. We rely on the schema's named definitions and resolve structural unions explicitly via `postProcessRefBasedDiscriminatedUnionsForPython`, so the merging is also redundant. Removed the `Session→Ion` / `Location→Ion` acronym hack we needed when the merging was on. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- python/copilot/generated/rpc.py | 117 ++++++++++++++++++++++++++++++++ python/copilot/session.py | 7 +- python/copilot/tools.py | 2 +- scripts/codegen/python.ts | 57 +++------------- 4 files changed, 130 insertions(+), 53 deletions(-) diff --git a/python/copilot/generated/rpc.py b/python/copilot/generated/rpc.py index dd45ba656..929aa79e6 100644 --- a/python/copilot/generated/rpc.py +++ b/python/copilot/generated/rpc.py @@ -12480,6 +12480,123 @@ def to_dict(self) -> dict: result["copilotUser"] = from_union([lambda x: to_class(CopilotUserResponse, x), from_none], self.copilot_user) return result +@dataclass +class PermissionDecisionApproveForIonApproval: + """Session-scoped approval to remember (tool prompts only; omitted for path/url prompts) + + Schema for the `PermissionDecisionApproveForSessionApprovalCommands` type. + + Schema for the `PermissionDecisionApproveForSessionApprovalRead` type. + + Schema for the `PermissionDecisionApproveForSessionApprovalWrite` type. + + Schema for the `PermissionDecisionApproveForSessionApprovalMcp` type. + + Schema for the `PermissionDecisionApproveForSessionApprovalMcpSampling` type. + + Schema for the `PermissionDecisionApproveForSessionApprovalMemory` type. + + Schema for the `PermissionDecisionApproveForSessionApprovalCustomTool` type. + + Schema for the `PermissionDecisionApproveForSessionApprovalExtensionManagement` type. + + Schema for the `PermissionDecisionApproveForSessionApprovalExtensionPermissionAccess` + type. + + Approval to persist for this location + + Schema for the `PermissionDecisionApproveForLocationApprovalCommands` type. + + Schema for the `PermissionDecisionApproveForLocationApprovalRead` type. + + Schema for the `PermissionDecisionApproveForLocationApprovalWrite` type. + + Schema for the `PermissionDecisionApproveForLocationApprovalMcp` type. + + Schema for the `PermissionDecisionApproveForLocationApprovalMcpSampling` type. + + Schema for the `PermissionDecisionApproveForLocationApprovalMemory` type. + + Schema for the `PermissionDecisionApproveForLocationApprovalCustomTool` type. + + Schema for the `PermissionDecisionApproveForLocationApprovalExtensionManagement` type. + + Schema for the `PermissionDecisionApproveForLocationApprovalExtensionPermissionAccess` + type. + + The approval to add as a session-scoped rule + + The approval to persist for this location + """ + command_identifiers: list[str] | None = None + """Command identifiers covered by this approval.""" + + kind: ApprovalKind | None = None + """Approval scoped to specific command identifiers. + + Approval covering read-only filesystem operations. + + Approval covering filesystem write operations. + + Approval covering an MCP tool. + + Approval covering MCP sampling requests for a server. + + Approval covering writes to long-term memory. + + Approval covering a custom tool. + + Approval covering extension lifecycle operations such as enable, disable, or reload. + + Approval covering an extension's request to access a permission-gated capability. + """ + server_name: str | None = None + """MCP server name.""" + + tool_name: str | None = None + """MCP tool name, or null to cover every tool on the server. + + Custom tool name. + """ + operation: str | None = None + """Optional operation identifier; when omitted, the approval covers all extension management + operations. + """ + extension_name: str | None = None + """Extension name.""" + + external_ref_marker_external_ref_user_tool_session_approval: str | None = None + + @staticmethod + def from_dict(obj: Any) -> 'PermissionDecisionApproveForIonApproval': + assert isinstance(obj, dict) + command_identifiers = from_union([lambda x: from_list(from_str, x), from_none], obj.get("commandIdentifiers")) + kind = from_union([ApprovalKind, from_none], obj.get("kind")) + server_name = from_union([from_str, from_none], obj.get("serverName")) + tool_name = from_union([from_none, from_str], obj.get("toolName")) + operation = from_union([from_str, from_none], obj.get("operation")) + extension_name = from_union([from_str, from_none], obj.get("extensionName")) + external_ref_marker_external_ref_user_tool_session_approval = from_union([from_str, from_none], obj.get("__externalRefMarker___ExternalRef_UserToolSessionApproval")) + return PermissionDecisionApproveForIonApproval(command_identifiers, kind, server_name, tool_name, operation, extension_name, external_ref_marker_external_ref_user_tool_session_approval) + + def to_dict(self) -> dict: + result: dict = {} + if self.command_identifiers is not None: + result["commandIdentifiers"] = from_union([lambda x: from_list(from_str, x), from_none], self.command_identifiers) + if self.kind is not None: + result["kind"] = from_union([lambda x: to_enum(ApprovalKind, x), from_none], self.kind) + if self.server_name is not None: + result["serverName"] = from_union([from_str, from_none], self.server_name) + if self.tool_name is not None: + result["toolName"] = from_union([from_none, from_str], self.tool_name) + if self.operation is not None: + result["operation"] = from_union([from_str, from_none], self.operation) + if self.extension_name is not None: + result["extensionName"] = from_union([from_str, from_none], self.extension_name) + if self.external_ref_marker_external_ref_user_tool_session_approval is not None: + result["__externalRefMarker___ExternalRef_UserToolSessionApproval"] = from_union([from_str, from_none], self.external_ref_marker_external_ref_user_tool_session_approval) + return result + # Experimental: this type is part of an experimental API and may change or be removed. @dataclass class HandlePendingToolCallRequest: diff --git a/python/copilot/session.py b/python/copilot/session.py index 8ef6a9ce8..2ec2bc0af 100644 --- a/python/copilot/session.py +++ b/python/copilot/session.py @@ -980,9 +980,10 @@ class SessionConfig(TypedDict, total=False): # session telemetry is always disabled regardless of this setting. # This is independent of the client OpenTelemetry configuration. enable_session_telemetry: bool - # Enable streaming of assistant message and reasoning chunks + # 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 + # with delta_content are sent as the response is generated. + # Defaults to False. streaming: bool # Include sub-agent streaming events in the event stream. When True, streaming # delta events from sub-agents (e.g., assistant.message_delta, @@ -1071,7 +1072,7 @@ class ResumeSessionConfig(TypedDict, total=False): working_directory: str # Override the default configuration directory location. config_dir: str - # Enable streaming of assistant message chunks + # Enable streaming of assistant message chunks. Defaults to False. streaming: bool # Include sub-agent streaming events in the event stream. When True, streaming # delta events from sub-agents (e.g., assistant.message_delta, diff --git a/python/copilot/tools.py b/python/copilot/tools.py index 3f8eb9c1b..c6a29dc61 100644 --- a/python/copilot/tools.py +++ b/python/copilot/tools.py @@ -24,7 +24,7 @@ class ToolBinaryResult: data: str = "" mime_type: str = "" - type: str = "" + type: Literal["image", "resource"] = "image" description: str = "" diff --git a/scripts/codegen/python.ts b/scripts/codegen/python.ts index c065fc94d..eae6f09b4 100644 --- a/scripts/codegen/python.ts +++ b/scripts/codegen/python.ts @@ -359,14 +359,6 @@ function postProcessRefBasedDiscriminatedUnionsForPython( [/Id\b/g, "ID"], [/Llm/g, "LLM"], [/Cli/g, "CLI"], - // quicktype merges structurally-identical unions and synthesizes a - // single class name using a common substring. For our schema this - // surfaces as e.g. `PermissionDecisionApproveForIonApproval` (merge - // of `PermissionDecisionApproveFor{Session,Location}Approval` — - // common substring is `Ion`). Try that substitution so we find - // and delete the merged blob during union post-processing. - [/Session/g, "Ion"], - [/Location/g, "Ion"], ]; const results = new Set([name]); for (const [pattern, replacement] of substitutions) { @@ -459,47 +451,6 @@ function postProcessRefBasedDiscriminatedUnionsForPython( } code = applyUnionRewritesToPython(code, resolved); - - // Clean up quicktype-merged blobs that get left behind when quicktype merges - // multiple structurally-identical types into one with a synthesized fuzzy - // name (e.g. ``PermissionDecisionApproveForIonApproval`` for the merge of - // ``PermissionDecisionApproveFor{Session,Location}Approval``). These - // blobs are unused after we emit the proper per-variant + union shape. - const orphanFuzzyMergedNames = ["PermissionDecisionApproveForIonApproval"]; - for (const orphanName of orphanFuzzyMergedNames) { - const lines = code.split("\n"); - let classStart = -1; - for (let i = 0; i < lines.length; i++) { - if (lines[i] === `class ${orphanName}:` || lines[i].startsWith(`class ${orphanName}(`)) { - classStart = i; - break; - } - } - if (classStart < 0) continue; - let blockStart = classStart; - while ( - blockStart > 0 && - (lines[blockStart - 1] === "@dataclass" || /^# /.test(lines[blockStart - 1])) - ) { - blockStart--; - } - let blockEnd = classStart + 1; - while (blockEnd < lines.length) { - const ln = lines[blockEnd]; - if ( - /^class \w/.test(ln) || - /^def \w/.test(ln) || - ln === "@dataclass" || - /^# (?:Experimental|Deprecated|Internal):/.test(ln) - ) { - break; - } - blockEnd++; - } - lines.splice(blockStart, blockEnd - blockStart); - code = lines.join("\n"); - } - return { code, unions: resolved }; } @@ -2789,6 +2740,14 @@ async function generateRpc(schemaPath?: string, sessionEventsSchema?: JSONSchema inputData, lang: "python", rendererOptions: { "python-version": "3.7" }, + // Disable quicktype's structural-equality merging of class types. + // It produces fuzzy synthesized names (e.g. ``PermissionDecisionApproveForIonApproval`` + // as the merge of ``PermissionDecisionApproveFor{Session,Location}Approval``) which + // are unstable: any future divergence between the variants would silently change + // the generated class name. We rely on the schema's named definitions and resolve + // structural unions via :func:`postProcessRefBasedDiscriminatedUnionsForPython`, + // so the merging is also redundant. + inferenceFlags: { combineClasses: false }, }); let typesCode = qtResult.lines.join("\n"); From 9d63384e1bea3171055a91acbdc25c659d4d145b Mon Sep 17 00:00:00 2001 From: Steve Sanderson Date: Fri, 22 May 2026 12:52:03 +0100 Subject: [PATCH 09/21] Phase D + E: lifecycle polymorphic union + datetime timestamps MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Mirrors PR #1357 Phases D + E (TypeScript SDK API review). Phase D — Session lifecycle event polymorphic hierarchy: * Split the flat `SessionLifecycleEvent` dataclass into a `SessionLifecycleEventBase` + five concrete variant dataclasses (`SessionCreatedEvent`, `SessionDeletedEvent`, `SessionUpdatedEvent`, `SessionForegroundEvent`, `SessionBackgroundEvent`). `SessionLifecycleEvent` is now a `Union` type alias over the five variants; pattern-match on the variant class to branch on the event kind. Each variant exposes `type: ClassVar[Literal["..."]]`, so existing `event.type == "session.created"` consumer code continues to work. * `SessionLifecycleEvent.from_dict` becomes a module-private `_session_lifecycle_event_from_dict` factory that switches on the wire `type` and constructs the matching variant. Phase D / E — Timestamps as `datetime`: * `SessionMetadata.startTime` / `modifiedTime` now `datetime` (previously ISO-8601 `str`). `to_dict` round-trips back to ISO strings. * `SessionLifecycleEventMetadata.startTime` / `modifiedTime` likewise. * All `Pre/PostToolUseHookInput`, `PreMcpToolCallHookInput`, `UserPromptSubmittedHookInput`, `SessionStartHookInput`, `SessionEndHookInput`, `ErrorOccurredHookInput` declare `timestamp: datetime`. `CopilotSession._handle_hooks_invoke` converts the wire epoch-milliseconds integer into a timezone-aware `datetime` at the dispatch boundary (same place the existing `cwd → workingDirectory` remap lives). Tests updated in lock-step: `test_session_e2e.py` asserts the new `datetime` types on `SessionMetadata`. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- python/copilot/client.py | 122 ++++++++++++++++++++++++++------- python/copilot/session.py | 30 +++++--- python/e2e/test_session_e2e.py | 13 ++-- 3 files changed, 123 insertions(+), 42 deletions(-) diff --git a/python/copilot/client.py b/python/copilot/client.py index 0eca03e55..2923f44ed 100644 --- a/python/copilot/client.py +++ b/python/copilot/client.py @@ -30,7 +30,7 @@ from datetime import UTC, datetime from pathlib import Path from types import TracebackType -from typing import Any, Literal, TypedDict, cast, overload +from typing import Any, ClassVar, Literal, TypedDict, cast, overload from ._diagnostics import log_timing from ._jsonrpc import JsonRpcClient, JsonRpcError, ProcessExitedError @@ -812,8 +812,8 @@ class SessionMetadata: """Metadata about a session""" sessionId: str # Session identifier - startTime: str # ISO 8601 timestamp when session was created - modifiedTime: str # ISO 8601 timestamp when session was last modified + startTime: datetime # Timestamp when session was created + modifiedTime: datetime # Timestamp when session was last modified isRemote: bool # Whether the session is remote summary: str | None = None # Optional summary of the session context: SessionContext | None = None # Working directory context @@ -835,8 +835,8 @@ def from_dict(obj: Any) -> SessionMetadata: context = SessionContext.from_dict(context_dict) if context_dict else None return SessionMetadata( sessionId=str(sessionId), - startTime=str(startTime), - modifiedTime=str(modifiedTime), + startTime=_parse_session_timestamp(startTime), + modifiedTime=_parse_session_timestamp(modifiedTime), isRemote=bool(isRemote), summary=summary, context=context, @@ -845,8 +845,8 @@ def from_dict(obj: Any) -> SessionMetadata: def to_dict(self) -> dict: result: dict = {} result["sessionId"] = self.sessionId - result["startTime"] = self.startTime - result["modifiedTime"] = self.modifiedTime + result["startTime"] = self.startTime.isoformat() + result["modifiedTime"] = self.modifiedTime.isoformat() result["isRemote"] = self.isRemote if self.summary is not None: result["summary"] = self.summary @@ -855,6 +855,18 @@ def to_dict(self) -> dict: return result +def _parse_session_timestamp(value: Any) -> datetime: + """Parse a wire-format timestamp into ``datetime``. + + Accepts either an ISO-8601 string (server-sent JSON) or an existing + ``datetime`` (round-tripped from a previous parse). Returns the value + as-is if it's already a ``datetime``. + """ + if isinstance(value, datetime): + return value + return from_datetime(value) + + # ============================================================================ # Session Lifecycle Types (for TUI+server mode) # ============================================================================ @@ -872,37 +884,95 @@ def to_dict(self) -> dict: class SessionLifecycleEventMetadata: """Metadata for session lifecycle events.""" - startTime: str - modifiedTime: str + startTime: datetime + modifiedTime: datetime summary: str | None = None @staticmethod def from_dict(data: dict) -> SessionLifecycleEventMetadata: return SessionLifecycleEventMetadata( - startTime=data.get("startTime", ""), - modifiedTime=data.get("modifiedTime", ""), + startTime=_parse_session_timestamp(data.get("startTime", "")), + modifiedTime=_parse_session_timestamp(data.get("modifiedTime", "")), summary=data.get("summary"), ) @dataclass -class SessionLifecycleEvent: - """Session lifecycle event notification.""" +class SessionLifecycleEventBase: + """Base for session lifecycle event variants. + + Construct concrete variants directly (e.g. :class:`SessionCreatedEvent`, + :class:`SessionDeletedEvent`); pattern-match on the variant class to + branch on the event kind. + """ - type: SessionLifecycleEventType sessionId: str metadata: SessionLifecycleEventMetadata | None = None - @staticmethod - def from_dict(data: dict) -> SessionLifecycleEvent: - metadata = None - if "metadata" in data and data["metadata"]: - metadata = SessionLifecycleEventMetadata.from_dict(data["metadata"]) - return SessionLifecycleEvent( - type=data.get("type", "session.updated"), - sessionId=data.get("sessionId", ""), - metadata=metadata, - ) + +@dataclass +class SessionCreatedEvent(SessionLifecycleEventBase): + """Emitted when a session is created.""" + + type: ClassVar[Literal["session.created"]] = "session.created" + + +@dataclass +class SessionDeletedEvent(SessionLifecycleEventBase): + """Emitted when a session is deleted.""" + + type: ClassVar[Literal["session.deleted"]] = "session.deleted" + + +@dataclass +class SessionUpdatedEvent(SessionLifecycleEventBase): + """Emitted when a session is updated (summary/title/etc. changed).""" + + type: ClassVar[Literal["session.updated"]] = "session.updated" + + +@dataclass +class SessionForegroundEvent(SessionLifecycleEventBase): + """Emitted when a session moves to the foreground (TUI+server mode).""" + + type: ClassVar[Literal["session.foreground"]] = "session.foreground" + + +@dataclass +class SessionBackgroundEvent(SessionLifecycleEventBase): + """Emitted when a session moves to the background (TUI+server mode).""" + + type: ClassVar[Literal["session.background"]] = "session.background" + + +SessionLifecycleEvent = ( + SessionCreatedEvent + | SessionDeletedEvent + | SessionUpdatedEvent + | SessionForegroundEvent + | SessionBackgroundEvent +) + + +def _session_lifecycle_event_from_dict(data: dict) -> SessionLifecycleEvent: + """Construct the correct :class:`SessionLifecycleEvent` variant from a wire dict.""" + metadata = None + if "metadata" in data and data["metadata"]: + metadata = SessionLifecycleEventMetadata.from_dict(data["metadata"]) + session_id = data.get("sessionId", "") + match data.get("type"): + case "session.created": + return SessionCreatedEvent(sessionId=session_id, metadata=metadata) + case "session.deleted": + return SessionDeletedEvent(sessionId=session_id, metadata=metadata) + case "session.foreground": + return SessionForegroundEvent(sessionId=session_id, metadata=metadata) + case "session.background": + return SessionBackgroundEvent(sessionId=session_id, metadata=metadata) + case _: + # Default to ``session.updated`` for unknown event types so consumers + # keep working across server upgrades. + return SessionUpdatedEvent(sessionId=session_id, metadata=metadata) SessionLifecycleHandler = Callable[[SessionLifecycleEvent], None] @@ -2922,7 +2992,7 @@ def handle_notification(method: str, params: dict): session._dispatch_event(event) elif method == "session.lifecycle": # Handle session lifecycle events - lifecycle_event = SessionLifecycleEvent.from_dict(params) + lifecycle_event = _session_lifecycle_event_from_dict(params) self._dispatch_lifecycle_event(lifecycle_event) self._client.set_notification_handler(handle_notification) @@ -3047,7 +3117,7 @@ def handle_notification(method: str, params: dict): session._dispatch_event(event) elif method == "session.lifecycle": # Handle session lifecycle events - lifecycle_event = SessionLifecycleEvent.from_dict(params) + lifecycle_event = _session_lifecycle_event_from_dict(params) self._dispatch_lifecycle_event(lifecycle_event) self._client.set_notification_handler(handle_notification) diff --git a/python/copilot/session.py b/python/copilot/session.py index 2ec2bc0af..ca9c478fd 100644 --- a/python/copilot/session.py +++ b/python/copilot/session.py @@ -18,6 +18,7 @@ import time from collections.abc import Awaitable, Callable from dataclasses import dataclass +from datetime import UTC, datetime from types import TracebackType from typing import TYPE_CHECKING, Any, Literal, NotRequired, Required, TypedDict, cast @@ -627,7 +628,7 @@ class PreToolUseHookInput(TypedDict): """Input for pre-tool-use hook""" sessionId: str - timestamp: int + timestamp: datetime workingDirectory: str toolName: str toolArgs: Any @@ -653,7 +654,7 @@ class PreMcpToolCallHookInput(TypedDict): """Input for pre-MCP-tool-call hook""" sessionId: str - timestamp: int + timestamp: datetime workingDirectory: str serverName: str toolName: str @@ -684,7 +685,7 @@ class PostToolUseHookInput(TypedDict): """Input for post-tool-use hook""" sessionId: str - timestamp: int + timestamp: datetime workingDirectory: str toolName: str toolArgs: Any @@ -709,7 +710,7 @@ class UserPromptSubmittedHookInput(TypedDict): """Input for user-prompt-submitted hook""" sessionId: str - timestamp: int + timestamp: datetime workingDirectory: str prompt: str @@ -732,7 +733,7 @@ class SessionStartHookInput(TypedDict): """Input for session-start hook""" sessionId: str - timestamp: int + timestamp: datetime workingDirectory: str source: Literal["startup", "resume", "new"] initialPrompt: NotRequired[str] @@ -755,7 +756,7 @@ class SessionEndHookInput(TypedDict): """Input for session-end hook""" sessionId: str - timestamp: int + timestamp: datetime workingDirectory: str reason: Literal["complete", "error", "abort", "timeout", "user_exit"] finalMessage: NotRequired[str] @@ -780,7 +781,7 @@ class ErrorOccurredHookInput(TypedDict): """Input for error-occurred hook""" sessionId: str - timestamp: int + timestamp: datetime workingDirectory: str error: str errorContext: Literal["model_call", "tool_execution", "system", "user_input"] @@ -2229,9 +2230,18 @@ async def _handle_hooks_invoke(self, hook_type: str, input_data: Any) -> Any: try: handler_start = time.perf_counter() - # Remap wire key "cwd" to public API key "workingDirectory" - if "cwd" in input_data: - input_data = {**input_data, "workingDirectory": input_data.pop("cwd")} + # Normalize input from the wire format: + # - Remap wire key "cwd" to public API key "workingDirectory". + # - Convert "timestamp" from epoch milliseconds to ``datetime`` so + # hook handlers see a timezone-aware ``datetime`` rather than a + # raw integer (matches TS PR #1357 Phase E). + transformed = dict(input_data) + if "cwd" in transformed: + transformed["workingDirectory"] = transformed.pop("cwd") + timestamp = transformed.get("timestamp") + if isinstance(timestamp, (int, float)): + transformed["timestamp"] = datetime.fromtimestamp(timestamp / 1000, tz=UTC) + input_data = transformed result = handler(input_data, {"session_id": self.session_id}) if inspect.isawaitable(result): result = await result diff --git a/python/e2e/test_session_e2e.py b/python/e2e/test_session_e2e.py index 8b06ebf25..1f1241218 100644 --- a/python/e2e/test_session_e2e.py +++ b/python/e2e/test_session_e2e.py @@ -2,6 +2,7 @@ import base64 import os +from datetime import datetime import pytest @@ -306,8 +307,8 @@ async def test_should_list_sessions(self, ctx: E2ETestContext): assert hasattr(session_data, "isRemote") # summary is optional assert isinstance(session_data.sessionId, str) - assert isinstance(session_data.startTime, str) - assert isinstance(session_data.modifiedTime, str) + assert isinstance(session_data.startTime, datetime) + assert isinstance(session_data.modifiedTime, datetime) assert isinstance(session_data.isRemote, bool) # Verify context field is present @@ -365,8 +366,8 @@ async def test_should_get_session_metadata(self, ctx: E2ETestContext): metadata = await ctx.client.get_session_metadata(session.session_id) assert metadata is not None assert metadata.sessionId == session.session_id - assert isinstance(metadata.startTime, str) - assert isinstance(metadata.modifiedTime, str) + assert isinstance(metadata.startTime, datetime) + assert isinstance(metadata.modifiedTime, datetime) assert isinstance(metadata.isRemote, bool) # Verify context field is present @@ -854,8 +855,8 @@ async def test_should_get_session_metadata_by_id(self, ctx: E2ETestContext): await asyncio.sleep(0.1) assert metadata is not None assert metadata.sessionId == session.session_id - assert isinstance(metadata.startTime, str) and metadata.startTime - assert isinstance(metadata.modifiedTime, str) and metadata.modifiedTime + assert isinstance(metadata.startTime, datetime) + assert isinstance(metadata.modifiedTime, datetime) not_found = await ctx.client.get_session_metadata("non-existent-session-id") assert not_found is None From 24cafdef2db7b3f947a0ec17b265b50bd3befc0a Mon Sep 17 00:00:00 2001 From: Steve Sanderson Date: Fri, 22 May 2026 12:55:28 +0100 Subject: [PATCH 10/21] Phase F: internals cleanup + expand public API surface MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Mirrors PR #1357 Phase K + 4.1 (TypeScript SDK API review). Cleanup: * Remove the deprecated `CopilotSession.destroy()` method. Only `disconnect()` remains (matches TS PR #1357 4.1). * Rename `NO_RESULT_PERMISSION_V2_ERROR` → `_NO_RESULT_PERMISSION_V2_ERROR` and `MIN_PROTOCOL_VERSION` → `_MIN_PROTOCOL_VERSION` (module-private constants; Python equivalent of TS `@internal` + `stripInternal`). Public API audit vs C# / TypeScript: * `copilot/__init__.py` now re-exports the full set of types that match the TS public surface from `nodejs/src/index.ts`. Notable additions: - Lifecycle event hierarchy: `SessionCreatedEvent`, `SessionDeletedEvent`, `SessionUpdatedEvent`, `SessionForegroundEvent`, `SessionBackgroundEvent`, `SessionLifecycleEvent`, `SessionLifecycleEventBase`, `SessionLifecycleEventMetadata`, `SessionLifecycleEventType`, `SessionLifecycleHandler` - Session metadata / context: `SessionContext`, `SessionListFilter`, `SessionMetadata` - Generated `PermissionRequest`, `SessionEvent`, `SessionEventType` ( consumers shouldn't have to reach into `copilot.generated.*`) - `PermissionHandler`, `PermissionRequestResult`, `PermissionNoResult`, `UserInputHandler`, `UserInputRequest`, `UserInputResponse` - MCP server configs: `MCPHTTPServerConfig`, `MCPServerConfig`, `MCPStdioServerConfig` - Session config: `SessionConfig`, `ResumeSessionConfig`, `SessionHooks`, `SystemMessageConfig`, `InfiniteSessionConfig` - All hook handler / input / output TypedDicts: `PreToolUseHandler` & friends across the six hook lifecycle points - Model* completion (added `ModelLimits`, `ModelSupports`, `ModelVisionLimits`, `ModelBilling`, `ModelPolicy`, `ModelCapabilities`) - `ConnectionState`, `LogLevel`, `PingResponse`, `GetStatusResponse`, `GetAuthStatusResponse`, `StopError` - `ToolResultType`, `SessionEventHandler` `__all__` is now alphabetically sorted. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- python/copilot/__init__.py | 142 +++++++++++++++++++++++++++++++++++-- python/copilot/client.py | 14 ++-- python/copilot/session.py | 20 ------ 3 files changed, 145 insertions(+), 31 deletions(-) diff --git a/python/copilot/__init__.py b/python/copilot/__init__.py index 30385a802..430da882c 100644 --- a/python/copilot/__init__.py +++ b/python/copilot/__init__.py @@ -8,19 +8,50 @@ ChildProcessRuntimeConnection, CloudSessionOptions, CloudSessionRepository, + ConnectionState, CopilotClient, CopilotClientOptions, + GetAuthStatusResponse, + GetStatusResponse, + LogLevel, + ModelBilling, + ModelCapabilities, ModelCapabilitiesOverride, + ModelInfo, + ModelLimits, ModelLimitsOverride, + ModelPolicy, + ModelSupports, ModelSupportsOverride, + ModelVisionLimits, ModelVisionLimitsOverride, + PingResponse, RemoteSessionMode, RuntimeConnection, + SessionBackgroundEvent, + SessionContext, + SessionCreatedEvent, + SessionDeletedEvent, + SessionForegroundEvent, + SessionLifecycleEvent, + SessionLifecycleEventBase, + SessionLifecycleEventMetadata, + SessionLifecycleEventType, + SessionLifecycleHandler, + SessionListFilter, + SessionMetadata, + SessionUpdatedEvent, StdioRuntimeConnection, + StopError, TcpRuntimeConnection, TelemetryConfig, UriRuntimeConnection, ) +from .generated.session_events import ( + PermissionRequest, + SessionEvent, + SessionEventType, +) from .session import ( AutoModeSwitchHandler, AutoModeSwitchRequest, @@ -33,16 +64,52 @@ ElicitationHandler, ElicitationParams, ElicitationResult, + ErrorOccurredHandler, + ErrorOccurredHookInput, + ErrorOccurredHookOutput, ExitPlanModeHandler, ExitPlanModeRequest, ExitPlanModeResult, + InfiniteSessionConfig, InputOptions, + MCPHTTPServerConfig, + MCPServerConfig, + MCPStdioServerConfig, + PermissionHandler, + PermissionNoResult, + PermissionRequestResult, + PostToolUseHandler, + PostToolUseHookInput, + PostToolUseHookOutput, + PreMcpToolCallHandler, + PreMcpToolCallHookInput, + PreMcpToolCallHookOutput, + PreToolUseHandler, + PreToolUseHookInput, + PreToolUseHookOutput, ProviderConfig, + ResumeSessionConfig, SessionCapabilities, + SessionConfig, + SessionEndHandler, + SessionEndHookInput, + SessionEndHookOutput, + SessionEventHandler, SessionFsCapabilities, SessionFsConfig, + SessionHooks, + SessionStartHandler, + SessionStartHookInput, + SessionStartHookOutput, SessionUiApi, SessionUiCapabilities, + SystemMessageConfig, + UserInputHandler, + UserInputRequest, + UserInputResponse, + UserPromptSubmittedHandler, + UserPromptSubmittedHookInput, + UserPromptSubmittedHookOutput, ) from .session_fs_provider import ( SessionFsFileInfo, @@ -56,6 +123,7 @@ ToolBinaryResult, ToolInvocation, ToolResult, + ToolResultType, convert_mcp_call_tool_result, define_tool, ) @@ -63,51 +131,117 @@ __version__ = "0.1.0" __all__ = [ - "CommandContext", "AutoModeSwitchHandler", "AutoModeSwitchRequest", "AutoModeSwitchResponse", "ChildProcessRuntimeConnection", - "CommandDefinition", "CloudSessionOptions", "CloudSessionRepository", + "CommandContext", + "CommandDefinition", + "ConnectionState", "CopilotClient", "CopilotClientOptions", "CopilotSession", "CreateSessionFsHandler", + "ElicitationContext", "ElicitationHandler", "ElicitationParams", - "ElicitationContext", "ElicitationResult", + "ErrorOccurredHandler", + "ErrorOccurredHookInput", + "ErrorOccurredHookOutput", "ExitPlanModeHandler", "ExitPlanModeRequest", "ExitPlanModeResult", + "GetAuthStatusResponse", + "GetStatusResponse", + "InfiniteSessionConfig", "InputOptions", + "LogLevel", + "MCPHTTPServerConfig", + "MCPServerConfig", + "MCPStdioServerConfig", + "ModelBilling", + "ModelCapabilities", "ModelCapabilitiesOverride", + "ModelInfo", + "ModelLimits", "ModelLimitsOverride", + "ModelPolicy", + "ModelSupports", "ModelSupportsOverride", + "ModelVisionLimits", "ModelVisionLimitsOverride", + "PermissionHandler", + "PermissionNoResult", + "PermissionRequest", + "PermissionRequestResult", + "PingResponse", + "PostToolUseHandler", + "PostToolUseHookInput", + "PostToolUseHookOutput", + "PreMcpToolCallHandler", + "PreMcpToolCallHookInput", + "PreMcpToolCallHookOutput", + "PreToolUseHandler", + "PreToolUseHookInput", + "PreToolUseHookOutput", "ProviderConfig", "RemoteSessionMode", + "ResumeSessionConfig", "RuntimeConnection", + "SessionBackgroundEvent", "SessionCapabilities", + "SessionConfig", + "SessionContext", + "SessionCreatedEvent", + "SessionDeletedEvent", + "SessionEndHandler", + "SessionEndHookInput", + "SessionEndHookOutput", + "SessionEvent", + "SessionEventHandler", + "SessionEventType", + "SessionForegroundEvent", "SessionFsCapabilities", "SessionFsConfig", "SessionFsFileInfo", "SessionFsProvider", "SessionFsSqliteProvider", "SessionFsSqliteQueryResult", - "create_session_fs_adapter", + "SessionHooks", + "SessionLifecycleEvent", + "SessionLifecycleEventBase", + "SessionLifecycleEventMetadata", + "SessionLifecycleEventType", + "SessionLifecycleHandler", + "SessionListFilter", + "SessionMetadata", + "SessionStartHandler", + "SessionStartHookInput", + "SessionStartHookOutput", "SessionUiApi", "SessionUiCapabilities", + "SessionUpdatedEvent", "StdioRuntimeConnection", + "StopError", + "SystemMessageConfig", "TcpRuntimeConnection", "TelemetryConfig", "Tool", "ToolBinaryResult", "ToolInvocation", "ToolResult", + "ToolResultType", "UriRuntimeConnection", + "UserInputHandler", + "UserInputRequest", + "UserInputResponse", + "UserPromptSubmittedHandler", + "UserPromptSubmittedHookInput", + "UserPromptSubmittedHookOutput", "convert_mcp_call_tool_result", + "create_session_fs_adapter", "define_tool", ] diff --git a/python/copilot/client.py b/python/copilot/client.py index 2923f44ed..7f70ff31d 100644 --- a/python/copilot/client.py +++ b/python/copilot/client.py @@ -979,13 +979,13 @@ def _session_lifecycle_event_from_dict(data: dict) -> SessionLifecycleEvent: HandlerUnsubcribe = Callable[[], None] -NO_RESULT_PERMISSION_V2_ERROR = ( +_NO_RESULT_PERMISSION_V2_ERROR = ( "Permission handlers cannot return 'no-result' when connected to a protocol v2 server." ) # Minimum protocol version this SDK can communicate with. # Servers reporting a version below this are rejected. -MIN_PROTOCOL_VERSION = 2 +_MIN_PROTOCOL_VERSION = 2 def _get_bundled_cli_path() -> str | None: @@ -2664,15 +2664,15 @@ async def _verify_protocol_version(self) -> None: if server_version is None: raise RuntimeError( "SDK protocol version mismatch: " - f"SDK supports versions {MIN_PROTOCOL_VERSION}-{max_version}" + f"SDK supports versions {_MIN_PROTOCOL_VERSION}-{max_version}" ", but server does not report a protocol version. " "Please update your server to ensure compatibility." ) - if server_version < MIN_PROTOCOL_VERSION or server_version > max_version: + if server_version < _MIN_PROTOCOL_VERSION or server_version > max_version: raise RuntimeError( "SDK protocol version mismatch: " - f"SDK supports versions {MIN_PROTOCOL_VERSION}-{max_version}" + f"SDK supports versions {_MIN_PROTOCOL_VERSION}-{max_version}" f", but server reports version {server_version}. " "Please update your SDK or server to ensure compatibility." ) @@ -3364,10 +3364,10 @@ async def _handle_permission_request_v2(self, params: dict) -> dict: perm_request = _load_PermissionRequest(permission_request) result = await session._handle_permission_request(perm_request) if isinstance(result, PermissionNoResult): - raise ValueError(NO_RESULT_PERMISSION_V2_ERROR) + raise ValueError(_NO_RESULT_PERMISSION_V2_ERROR) return {"result": result.to_dict()} except ValueError as exc: - if str(exc) == NO_RESULT_PERMISSION_V2_ERROR: + if str(exc) == _NO_RESULT_PERMISSION_V2_ERROR: raise return {"result": PermissionDecisionUserNotAvailable().to_dict()} except Exception: # pylint: disable=broad-except diff --git a/python/copilot/session.py b/python/copilot/session.py index ca9c478fd..2ba27547c 100644 --- a/python/copilot/session.py +++ b/python/copilot/session.py @@ -2338,26 +2338,6 @@ async def disconnect(self) -> None: with self._auto_mode_switch_handler_lock: self._auto_mode_switch_handler = None - async def destroy(self) -> None: - """ - .. deprecated:: - Use :meth:`disconnect` instead. This method will be removed in a future release. - - Disconnect this session and release all in-memory resources. - Session data on disk is preserved for later resumption. - - Raises: - Exception: If the connection fails. - """ - import warnings - - warnings.warn( - "destroy() is deprecated, use disconnect() instead", - DeprecationWarning, - stacklevel=2, - ) - await self.disconnect() - async def __aenter__(self) -> CopilotSession: """Enable use as an async context manager.""" return self From 7531acaf627e4487aa66fb52962d0618b7879b79 Mon Sep 17 00:00:00 2001 From: Steve Sanderson Date: Fri, 22 May 2026 13:00:17 +0100 Subject: [PATCH 11/21] Phase G: snake_case fix-up on public dataclass fields MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Mirrors PR #1357 phase B + api_review_python.md item B.9 (TypeScript SDK API review). Public Python dataclasses no longer expose camelCase attribute names; the wire JSON keys are unchanged. Renames (Python attribute → wire JSON key): * `PingResponse.protocolVersion` → `protocol_version` (wire: `protocolVersion`) * `SessionContext.gitRoot` → `git_root` (wire: `gitRoot`) * `SessionListFilter.gitRoot` → `git_root` (wire: `gitRoot`) * `SessionMetadata.sessionId` → `session_id` (wire: `sessionId`) * `SessionMetadata.startTime` → `start_time` (wire: `startTime`) * `SessionMetadata.modifiedTime` → `modified_time` (wire: `modifiedTime`) * `SessionMetadata.isRemote` → `is_remote` (wire: `isRemote`) * `SessionLifecycleEventMetadata.startTime` → `start_time` * `SessionLifecycleEventMetadata.modifiedTime` → `modified_time` * `SessionLifecycleEventBase.sessionId` → `session_id` Hook input TypedDicts (`PreToolUseHookInput`, `PreMcpToolCallHookInput`, etc.) intentionally keep camelCase keys — those are wire-format dicts delivered straight to handlers, not Python attributes. Tests + docs updated. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- python/README.md | 4 +- python/copilot/client.py | 92 ++++++++++++------------- python/e2e/test_client_e2e.py | 4 +- python/e2e/test_client_lifecycle_e2e.py | 14 ++-- python/e2e/test_client_options_e2e.py | 2 +- python/e2e/test_session_e2e.py | 30 ++++---- python/e2e/test_subagent_hooks_e2e.py | 2 +- 7 files changed, 74 insertions(+), 74 deletions(-) diff --git a/python/README.md b/python/README.md index 6d6cb2cba..2f95c1f3c 100644 --- a/python/README.md +++ b/python/README.md @@ -199,12 +199,12 @@ await client.set_foreground_session_id("session-123") # Subscribe to all lifecycle events def on_lifecycle(event): - print(f"{event.type}: {event.sessionId}") + print(f"{event.type}: {event.session_id}") unsubscribe = client.on_lifecycle(on_lifecycle) # Subscribe to specific event type -unsubscribe = client.on_lifecycle("session.foreground", lambda e: print(f"Foreground: {e.sessionId}")) +unsubscribe = client.on_lifecycle("session.foreground", lambda e: print(f"Foreground: {e.session_id}")) # Later, to stop receiving events: unsubscribe() diff --git a/python/copilot/client.py b/python/copilot/client.py index 7f70ff31d..8035acbc5 100644 --- a/python/copilot/client.py +++ b/python/copilot/client.py @@ -351,32 +351,32 @@ class PingResponse: """Response from ping""" message: str # Echo message with "pong: " prefix - timestamp: datetime # ISO 8601 timestamp when the ping was processed - protocolVersion: int # Protocol version for SDK compatibility + timestamp: datetime # Timestamp when the ping was processed + protocol_version: int # Protocol version for SDK compatibility @staticmethod def from_dict(obj: Any) -> PingResponse: assert isinstance(obj, dict) message = obj.get("message") timestamp = obj.get("timestamp") - protocolVersion = obj.get("protocolVersion") - if message is None or timestamp is None or protocolVersion is None: + protocol_version = obj.get("protocolVersion") + if message is None or timestamp is None or protocol_version is None: raise ValueError( f"Missing required fields in PingResponse: message={message}, " - f"timestamp={timestamp}, protocolVersion={protocolVersion}" + f"timestamp={timestamp}, protocolVersion={protocol_version}" ) timestamp_value = ( datetime.fromtimestamp(timestamp / 1000, tz=UTC) if isinstance(timestamp, (int, float)) else from_datetime(timestamp) ) - return PingResponse(str(message), timestamp_value, int(protocolVersion)) + return PingResponse(str(message), timestamp_value, int(protocol_version)) def to_dict(self) -> dict: result: dict = {} result["message"] = self.message result["timestamp"] = self.timestamp.isoformat() - result["protocolVersion"] = self.protocolVersion + result["protocolVersion"] = self.protocol_version return result @@ -757,7 +757,7 @@ class SessionContext: """Working directory context for a session""" working_directory: str # Working directory where the session was created - gitRoot: str | None = None # Git repository root (if in a git repo) + git_root: str | None = None # Git repository root (if in a git repo) repository: str | None = None # GitHub repository in "owner/repo" format branch: str | None = None # Current git branch @@ -769,15 +769,15 @@ def from_dict(obj: Any) -> SessionContext: raise ValueError("Missing required field 'cwd' in SessionContext") return SessionContext( working_directory=str(cwd), - gitRoot=obj.get("gitRoot"), + git_root=obj.get("gitRoot"), repository=obj.get("repository"), branch=obj.get("branch"), ) def to_dict(self) -> dict: result: dict = {"cwd": self.working_directory} - if self.gitRoot is not None: - result["gitRoot"] = self.gitRoot + if self.git_root is not None: + result["gitRoot"] = self.git_root if self.repository is not None: result["repository"] = self.repository if self.branch is not None: @@ -790,7 +790,7 @@ class SessionListFilter: """Filter options for listing sessions""" working_directory: str | None = None # Filter by exact working directory match - gitRoot: str | None = None # Filter by git root + git_root: str | None = None # Filter by git root repository: str | None = None # Filter by repository (owner/repo format) branch: str | None = None # Filter by branch @@ -798,8 +798,8 @@ def to_dict(self) -> dict: result: dict = {} if self.working_directory is not None: result["cwd"] = self.working_directory - if self.gitRoot is not None: - result["gitRoot"] = self.gitRoot + if self.git_root is not None: + result["gitRoot"] = self.git_root if self.repository is not None: result["repository"] = self.repository if self.branch is not None: @@ -811,43 +811,43 @@ def to_dict(self) -> dict: class SessionMetadata: """Metadata about a session""" - sessionId: str # Session identifier - startTime: datetime # Timestamp when session was created - modifiedTime: datetime # Timestamp when session was last modified - isRemote: bool # Whether the session is remote + session_id: str # Session identifier + start_time: datetime # Timestamp when session was created + modified_time: datetime # Timestamp when session was last modified + is_remote: bool # Whether the session is remote summary: str | None = None # Optional summary of the session context: SessionContext | None = None # Working directory context @staticmethod def from_dict(obj: Any) -> SessionMetadata: assert isinstance(obj, dict) - sessionId = obj.get("sessionId") - startTime = obj.get("startTime") - modifiedTime = obj.get("modifiedTime") - isRemote = obj.get("isRemote") - if sessionId is None or startTime is None or modifiedTime is None or isRemote is None: + session_id = obj.get("sessionId") + start_time = obj.get("startTime") + modified_time = obj.get("modifiedTime") + is_remote = obj.get("isRemote") + if session_id is None or start_time is None or modified_time is None or is_remote is None: raise ValueError( - f"Missing required fields in SessionMetadata: sessionId={sessionId}, " - f"startTime={startTime}, modifiedTime={modifiedTime}, isRemote={isRemote}" + f"Missing required fields in SessionMetadata: sessionId={session_id}, " + f"startTime={start_time}, modifiedTime={modified_time}, isRemote={is_remote}" ) summary = obj.get("summary") context_dict = obj.get("context") context = SessionContext.from_dict(context_dict) if context_dict else None return SessionMetadata( - sessionId=str(sessionId), - startTime=_parse_session_timestamp(startTime), - modifiedTime=_parse_session_timestamp(modifiedTime), - isRemote=bool(isRemote), + session_id=str(session_id), + start_time=_parse_session_timestamp(start_time), + modified_time=_parse_session_timestamp(modified_time), + is_remote=bool(is_remote), summary=summary, context=context, ) def to_dict(self) -> dict: result: dict = {} - result["sessionId"] = self.sessionId - result["startTime"] = self.startTime.isoformat() - result["modifiedTime"] = self.modifiedTime.isoformat() - result["isRemote"] = self.isRemote + result["sessionId"] = self.session_id + result["startTime"] = self.start_time.isoformat() + result["modifiedTime"] = self.modified_time.isoformat() + result["isRemote"] = self.is_remote if self.summary is not None: result["summary"] = self.summary if self.context is not None: @@ -884,15 +884,15 @@ def _parse_session_timestamp(value: Any) -> datetime: class SessionLifecycleEventMetadata: """Metadata for session lifecycle events.""" - startTime: datetime - modifiedTime: datetime + start_time: datetime + modified_time: datetime summary: str | None = None @staticmethod def from_dict(data: dict) -> SessionLifecycleEventMetadata: return SessionLifecycleEventMetadata( - startTime=_parse_session_timestamp(data.get("startTime", "")), - modifiedTime=_parse_session_timestamp(data.get("modifiedTime", "")), + start_time=_parse_session_timestamp(data.get("startTime", "")), + modified_time=_parse_session_timestamp(data.get("modifiedTime", "")), summary=data.get("summary"), ) @@ -906,7 +906,7 @@ class SessionLifecycleEventBase: branch on the event kind. """ - sessionId: str + session_id: str metadata: SessionLifecycleEventMetadata | None = None @@ -962,17 +962,17 @@ def _session_lifecycle_event_from_dict(data: dict) -> SessionLifecycleEvent: session_id = data.get("sessionId", "") match data.get("type"): case "session.created": - return SessionCreatedEvent(sessionId=session_id, metadata=metadata) + return SessionCreatedEvent(session_id=session_id, metadata=metadata) case "session.deleted": - return SessionDeletedEvent(sessionId=session_id, metadata=metadata) + return SessionDeletedEvent(session_id=session_id, metadata=metadata) case "session.foreground": - return SessionForegroundEvent(sessionId=session_id, metadata=metadata) + return SessionForegroundEvent(session_id=session_id, metadata=metadata) case "session.background": - return SessionBackgroundEvent(sessionId=session_id, metadata=metadata) + return SessionBackgroundEvent(session_id=session_id, metadata=metadata) case _: # Default to ``session.updated`` for unknown event types so consumers # keep working across server upgrades. - return SessionUpdatedEvent(sessionId=session_id, metadata=metadata) + return SessionUpdatedEvent(session_id=session_id, metadata=metadata) SessionLifecycleHandler = Callable[[SessionLifecycleEvent], None] @@ -2419,7 +2419,7 @@ async def get_session_metadata(self, session_id: str) -> SessionMetadata | None: Example: >>> metadata = await client.get_session_metadata("session-123") >>> if metadata: - ... print(f"Session started at: {metadata.startTime}") + ... print(f"Session started at: {metadata.start_time}") """ if not self._client: raise RuntimeError("Client not connected") @@ -2573,11 +2573,11 @@ def on_lifecycle( Example: >>> # Subscribe to specific event type >>> unsubscribe = client.on_lifecycle( - ... "session.foreground", lambda e: print(e.sessionId) + ... "session.foreground", lambda e: print(e.session_id) ... ) >>> >>> # Subscribe to all events - >>> unsubscribe = client.on_lifecycle(lambda e: print(f"{e.type}: {e.sessionId}")) + >>> unsubscribe = client.on_lifecycle(lambda e: print(f"{e.type}: {e.session_id}")) >>> >>> # Later, to stop receiving events: >>> unsubscribe() diff --git a/python/e2e/test_client_e2e.py b/python/e2e/test_client_e2e.py index 34444081d..8cff89c78 100644 --- a/python/e2e/test_client_e2e.py +++ b/python/e2e/test_client_e2e.py @@ -105,8 +105,8 @@ async def test_should_get_status_with_version_and_protocol_info(self): assert hasattr(status, "version") assert isinstance(status.version, str) assert hasattr(status, "protocolVersion") - assert isinstance(status.protocolVersion, int) - assert status.protocolVersion >= 1 + assert isinstance(status.protocol_version, int) + assert status.protocol_version >= 1 await client.stop() finally: diff --git a/python/e2e/test_client_lifecycle_e2e.py b/python/e2e/test_client_lifecycle_e2e.py index b35ff176a..29c88cdd2 100644 --- a/python/e2e/test_client_lifecycle_e2e.py +++ b/python/e2e/test_client_lifecycle_e2e.py @@ -118,7 +118,7 @@ def handler(event): try: event = await asyncio.wait_for(created, 10.0) assert event.type == "session.created" - assert event.sessionId == session.session_id + assert event.session_id == session.session_id finally: await session.disconnect() finally: @@ -140,7 +140,7 @@ def handler(event): try: event = await asyncio.wait_for(created, 10.0) assert event.type == "session.created" - assert event.sessionId == session.session_id + assert event.session_id == session.session_id finally: await session.disconnect() finally: @@ -170,7 +170,7 @@ def disposed_handler(_event): ) try: event = await asyncio.wait_for(active_event, 10.0) - assert event.sessionId == session.session_id + assert event.session_id == session.session_id assert unsubscribed_count == 0, "Disposed handler should not have fired" finally: await session.disconnect() @@ -206,7 +206,7 @@ async def test_should_receive_session_updated_lifecycle_event_for_non_ephemeral_ def handler(event): if ( event.type == "session.updated" - and event.sessionId == session.session_id + and event.session_id == session.session_id and not updated.done() ): updated.set_result(event) @@ -216,7 +216,7 @@ def handler(event): await session.rpc.mode.set(ModeSetRequest(mode=SessionMode.PLAN)) event = await asyncio.wait_for(updated, timeout=15.0) assert event.type == "session.updated" - assert event.sessionId == session.session_id + assert event.session_id == session.session_id finally: unsubscribe() await session.disconnect() @@ -241,7 +241,7 @@ async def test_should_receive_session_deleted_lifecycle_event_when_deleted( def handler(event): if ( event.type == "session.deleted" - and event.sessionId == session_id + and event.session_id == session_id and not deleted.done() ): deleted.set_result(event) @@ -253,6 +253,6 @@ def handler(event): event = await asyncio.wait_for(deleted, timeout=15.0) assert event.type == "session.deleted" - assert event.sessionId == session_id + assert event.session_id == session_id finally: unsubscribe() diff --git a/python/e2e/test_client_options_e2e.py b/python/e2e/test_client_options_e2e.py index f18764b83..be42755fb 100644 --- a/python/e2e/test_client_options_e2e.py +++ b/python/e2e/test_client_options_e2e.py @@ -142,7 +142,7 @@ def _get_available_port() -> int: return; } if (message.method === "session.create") { - const sessionId = message.params?.sessionId ?? "fake-session"; + const sessionId = message.params?.session_id ?? "fake-session"; writeResponse(message.id, { sessionId, workspacePath: null, capabilities: null }); return; } diff --git a/python/e2e/test_session_e2e.py b/python/e2e/test_session_e2e.py index 1f1241218..268372bb0 100644 --- a/python/e2e/test_session_e2e.py +++ b/python/e2e/test_session_e2e.py @@ -295,7 +295,7 @@ async def test_should_list_sessions(self, ctx: E2ETestContext): sessions = await ctx.client.list_sessions() assert isinstance(sessions, list) - session_ids = [s.sessionId for s in sessions] + session_ids = [s.session_id for s in sessions] assert session1.session_id in session_ids assert session2.session_id in session_ids @@ -306,10 +306,10 @@ async def test_should_list_sessions(self, ctx: E2ETestContext): assert hasattr(session_data, "modifiedTime") assert hasattr(session_data, "isRemote") # summary is optional - assert isinstance(session_data.sessionId, str) - assert isinstance(session_data.startTime, datetime) - assert isinstance(session_data.modifiedTime, datetime) - assert isinstance(session_data.isRemote, bool) + assert isinstance(session_data.session_id, str) + assert isinstance(session_data.start_time, datetime) + assert isinstance(session_data.modified_time, datetime) + assert isinstance(session_data.is_remote, bool) # Verify context field is present for session_data in sessions: @@ -333,7 +333,7 @@ async def test_should_delete_session(self, ctx: E2ETestContext): # Verify session exists in the list sessions = await ctx.client.list_sessions() - session_ids = [s.sessionId for s in sessions] + session_ids = [s.session_id for s in sessions] assert session_id in session_ids # Delete the session @@ -341,7 +341,7 @@ async def test_should_delete_session(self, ctx: E2ETestContext): # Verify session no longer exists in the list sessions_after = await ctx.client.list_sessions() - session_ids_after = [s.sessionId for s in sessions_after] + session_ids_after = [s.session_id for s in sessions_after] assert session_id not in session_ids_after # Verify we cannot resume the deleted session @@ -365,10 +365,10 @@ async def test_should_get_session_metadata(self, ctx: E2ETestContext): # Get metadata for the session we just created metadata = await ctx.client.get_session_metadata(session.session_id) assert metadata is not None - assert metadata.sessionId == session.session_id - assert isinstance(metadata.startTime, datetime) - assert isinstance(metadata.modifiedTime, datetime) - assert isinstance(metadata.isRemote, bool) + assert metadata.session_id == session.session_id + assert isinstance(metadata.start_time, datetime) + assert isinstance(metadata.modified_time, datetime) + assert isinstance(metadata.is_remote, bool) # Verify context field is present if metadata.context is not None: @@ -822,7 +822,7 @@ async def test_should_list_sessions_with_context(self, ctx: E2ETestContext): our_session = None for _ in range(50): sessions = await ctx.client.list_sessions() - our_session = next((s for s in sessions if s.sessionId == session.session_id), None) + our_session = next((s for s in sessions if s.session_id == session.session_id), None) if our_session is not None: break await asyncio.sleep(0.1) @@ -854,9 +854,9 @@ async def test_should_get_session_metadata_by_id(self, ctx: E2ETestContext): break await asyncio.sleep(0.1) assert metadata is not None - assert metadata.sessionId == session.session_id - assert isinstance(metadata.startTime, datetime) - assert isinstance(metadata.modifiedTime, datetime) + assert metadata.session_id == session.session_id + assert isinstance(metadata.start_time, datetime) + assert isinstance(metadata.modified_time, datetime) not_found = await ctx.client.get_session_metadata("non-existent-session-id") assert not_found is None diff --git a/python/e2e/test_subagent_hooks_e2e.py b/python/e2e/test_subagent_hooks_e2e.py index da1dba506..f501663ec 100644 --- a/python/e2e/test_subagent_hooks_e2e.py +++ b/python/e2e/test_subagent_hooks_e2e.py @@ -85,7 +85,7 @@ async def on_post_tool_use(input_data, invocation): assert len(view_pre) > 0, "preToolUse should fire for the sub-agent's 'view' tool call" assert len(view_post) > 0, "postToolUse should fire for the sub-agent's 'view' tool call" - # input.sessionId distinguishes parent from sub-agent + # input.session_id distinguishes parent from sub-agent assert view_pre[0]["sessionId"] != task_pre[0]["sessionId"], ( "Sub-agent tool hooks should have a different sessionId than parent tool hooks" ) From b23e8dc64af3b118d6891870684b432e966697c7 Mon Sep 17 00:00:00 2001 From: Steve Sanderson Date: Fri, 22 May 2026 13:02:04 +0100 Subject: [PATCH 12/21] Phase H: extract SessionConfigBase TypedDict Mirrors PR #1357 Phase G (TypeScript SDK API review) and addresses api_review_python.md item B.5. Hoists the ~30 fields shared between `SessionConfig` and `ResumeSessionConfig` into a new `SessionConfigBase(TypedDict, total=False)`. `SessionConfig` now only adds `session_id` (create-only) and `ResumeSessionConfig` only adds `disable_resume` + `continue_pending_work` (resume-only). Both inherit `SessionConfigBase`. `SessionConfigBase` is re-exported from `copilot.__init__` to match the TS public API. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- python/copilot/__init__.py | 2 + python/copilot/session.py | 107 +++++++++---------------------------- 2 files changed, 28 insertions(+), 81 deletions(-) diff --git a/python/copilot/__init__.py b/python/copilot/__init__.py index 430da882c..c491dc8fb 100644 --- a/python/copilot/__init__.py +++ b/python/copilot/__init__.py @@ -91,6 +91,7 @@ ResumeSessionConfig, SessionCapabilities, SessionConfig, + SessionConfigBase, SessionEndHandler, SessionEndHookInput, SessionEndHookOutput, @@ -194,6 +195,7 @@ "SessionBackgroundEvent", "SessionCapabilities", "SessionConfig", + "SessionConfigBase", "SessionContext", "SessionCreatedEvent", "SessionDeletedEvent", diff --git a/python/copilot/session.py b/python/copilot/session.py index 2ba27547c..9c650bbcd 100644 --- a/python/copilot/session.py +++ b/python/copilot/session.py @@ -944,10 +944,13 @@ class ProviderConfig(TypedDict, total=False): max_output_tokens: int -class SessionConfig(TypedDict, total=False): - """Configuration for creating a session""" +class SessionConfigBase(TypedDict, total=False): + """Shared configuration fields between :class:`SessionConfig` and + :class:`ResumeSessionConfig`. + + See those classes for the create-only / resume-only fields. + """ - 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 @@ -1018,9 +1021,10 @@ class SessionConfig(TypedDict, total=False): # Set to {"enabled": False} to disable. infinite_sessions: InfiniteSessionConfig # Optional event handler that is registered on the session before the - # session.create RPC is issued, ensuring early events (e.g. session.start) - # are delivered. Equivalent to calling session.on(handler) immediately - # after creation, but executes earlier in the lifecycle so no events are missed. + # session.create / session.resume RPC is issued, ensuring early events + # (e.g. session.start) are delivered. Equivalent to calling session.on(handler) + # immediately after creation, but executes earlier in the lifecycle so no + # events are missed. on_event: Callable[[SessionEvent], None] # Slash commands to register with the session. # When the CLI has a TUI, each command appears as /name for the user to invoke. @@ -1036,69 +1040,23 @@ class SessionConfig(TypedDict, total=False): create_session_fs_handler: CreateSessionFsHandler -class ResumeSessionConfig(TypedDict, total=False): - """Configuration for resuming a session""" +class SessionConfig(SessionConfigBase, total=False): + """Configuration for creating a session. + + Inherits all shared fields from :class:`SessionConfigBase`; only the + create-specific fields appear here. + """ + + session_id: str # Optional custom session ID + + +class ResumeSessionConfig(SessionConfigBase, total=False): + """Configuration for resuming a session. + + Inherits all shared fields from :class:`SessionConfigBase`; only the + resume-specific fields appear here. + """ - # Client name to identify the application using the SDK. - # Included in the User-Agent header for API requests. - client_name: str - # Model to use for this session. Can change the model when resuming. - model: str - tools: list[Tool] - system_message: SystemMessageConfig # System message configuration - # List of tool names to allow. When specified, only these tools will be available. - # Applies to the full merged tool catalog (built-in, MCP, and custom tools - # registered via tools=). Takes precedence over excluded_tools. - available_tools: list[str] - # List of tool names to disable. Applies to all tools including custom tools - # registered via tools=. Ignored if available_tools is set. - excluded_tools: list[str] - provider: ProviderConfig - # Enables or disables internal session telemetry for this session. When False, - # disables session telemetry. When omitted (the default) or True, telemetry is enabled for - # GitHub-authenticated sessions. When a custom provider (BYOK) is configured, - # session telemetry is always disabled regardless of this setting. - # This is independent of the client OpenTelemetry configuration. - enable_session_telemetry: bool - # Reasoning effort level for models that support it. - reasoning_effort: ReasoningEffort - # Optional handler for permission requests from the server. When omitted, - # requests are surfaced as events and left pending for manual resolution. - on_permission_request: _PermissionHandlerFn | None - # Handler for user input requestsfrom 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 - # Override the default configuration directory location. - config_dir: str - # Enable streaming of assistant message chunks. Defaults to False. - streaming: bool - # Include sub-agent streaming events in the event stream. When True, streaming - # delta events from sub-agents (e.g., assistant.message_delta, - # assistant.reasoning_delta, assistant.streaming_delta with agentId set) are - # forwarded to this connection. When False, only non-streaming sub-agent events - # and subagent.* lifecycle events are forwarded; streaming deltas from sub-agents - # are suppressed. Defaults to True. - include_sub_agent_streaming_events: bool - # MCP server configurations for the session - mcp_servers: dict[str, MCPServerConfig] - # Custom agent configurations for the session - custom_agents: list[CustomAgentConfig] - # Configuration for the default agent. - default_agent: DefaultAgentConfig - # Name of the custom agent to activate when the session starts. - # Must match the name of one of the agents in custom_agents. - agent: str - # Directories to load skills from - skill_directories: list[str] - # Additional directories to search for custom instruction files. - instruction_directories: list[str] - # List of skill names to disable - disabled_skills: list[str] - # Infinite session configuration for persistent workspaces and automatic compaction. - infinite_sessions: InfiniteSessionConfig # When True, skips emitting the session.resume event. # Useful for reconnecting to a session without triggering resume-related side effects. disable_resume: bool @@ -1111,19 +1069,6 @@ class ResumeSessionConfig(TypedDict, total=False): # calls, the consumer is expected to supply the result via the corresponding # low-level RPC method. continue_pending_work: bool - # Optional event handler registered before the session.resume RPC is issued, - # ensuring early events are delivered. See SessionConfig.on_event. - on_event: Callable[[SessionEvent], None] - # Slash commands to register with the session. - commands: list[CommandDefinition] - # Handler for elicitation requests from the server. - on_elicitation_request: ElicitationHandler - # Handler for exit-plan-mode requests from the server. - on_exit_plan_mode_request: ExitPlanModeHandler - # Handler for auto-mode-switch requests from the server. - on_auto_mode_switch_request: AutoModeSwitchHandler - # Handler factory for session-scoped sessionFs operations. - create_session_fs_handler: CreateSessionFsHandler SessionEventHandler = Callable[[SessionEvent], None] From b80af48a961809c7bd950051036ebeccaa73ef23 Mon Sep 17 00:00:00 2001 From: Steve Sanderson Date: Fri, 22 May 2026 13:15:18 +0100 Subject: [PATCH 13/21] Phase I: update README + docs for new Python SDK API Mirrors PR #1357 Phase K (TypeScript SDK API review): every Python code snippet under \docs/\ and the \python/README.md\ now uses the post-Phase-B/F/G/H public API: * `CopilotClient()` / `CopilotClient(CopilotClientOptions(connection=RuntimeConnection.uri/stdio/tcp(...), ...))` in place of `SubprocessConfig` / `ExternalServerConfig` * `PermissionDecisionApproveOnce()`, `PermissionDecisionReject(feedback=...)`, `PermissionDecisionUserNotAvailable()`, `PermissionNoResult()` in place of `PermissionRequestResult(kind=...)` * `base_directory` (was `copilot_home`) * `enable_remote_sessions` (was `remote`) * `on_exit_plan_mode_request` / `on_auto_mode_switch_request` * `session.get_events()` (was `get_messages`) * `client.on_lifecycle(...)` (was `client.on`) * `max_prompt_tokens` (was `max_input_tokens`) * snake_case dataclass attribute accesses (\session_id\, \start_time\, \modified_time\, \is_remote\, \git_root\, \protocol_version\) TypeScript / Go / C# / Rust / Java snippets in the same files are untouched. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- docs/features/custom-agents.md | 5 +- docs/features/hooks.md | 21 ++++---- docs/features/image-input.md | 10 ++-- docs/features/remote-sessions.md | 4 +- docs/features/skills.md | 5 +- docs/features/steering-and-queueing.md | 12 ++--- docs/getting-started.md | 17 ++++--- docs/observability/opentelemetry.md | 4 +- docs/setup/backend-services.md | 6 ++- docs/troubleshooting/debugging.md | 6 +-- python/README.md | 66 ++++++++++++++------------ 11 files changed, 78 insertions(+), 78 deletions(-) diff --git a/docs/features/custom-agents.md b/docs/features/custom-agents.md index 3d93f7589..716e80583 100644 --- a/docs/features/custom-agents.md +++ b/docs/features/custom-agents.md @@ -64,14 +64,13 @@ const session = await client.createSession({ Python ```python -from copilot import CopilotClient -from copilot.session import PermissionRequestResult +from copilot import CopilotClient, PermissionDecisionApproveOnce client = CopilotClient() await client.start() session = await client.create_session( - on_permission_request=lambda req, inv: PermissionRequestResult(kind="approve-once"), + on_permission_request=lambda req, inv: PermissionDecisionApproveOnce(), model="gpt-4.1", custom_agents=[ { diff --git a/docs/features/hooks.md b/docs/features/hooks.md index c88c6e605..6ba554a1e 100644 --- a/docs/features/hooks.md +++ b/docs/features/hooks.md @@ -60,14 +60,13 @@ const session = await client.createSession({ Python ```python -from copilot import CopilotClient -from copilot.session import PermissionRequestResult +from copilot import CopilotClient, PermissionDecisionApproveOnce client = CopilotClient() await client.start() session = await client.create_session( - on_permission_request=lambda req, inv: PermissionRequestResult(kind="approve-once"), + on_permission_request=lambda req, inv: PermissionDecisionApproveOnce(), hooks={ "on_session_start": on_session_start, "on_pre_tool_use": on_pre_tool_use, @@ -262,7 +261,7 @@ const session = await client.createSession({ Python ```python -from copilot.session import PermissionRequestResult +from copilot import PermissionDecisionApproveOnce READ_ONLY_TOOLS = ["read_file", "glob", "grep", "view"] @@ -276,7 +275,7 @@ async def on_pre_tool_use(input_data, invocation): return {"permissionDecision": "allow"} session = await client.create_session( - on_permission_request=lambda req, inv: PermissionRequestResult(kind="approve-once"), + on_permission_request=lambda req, inv: PermissionDecisionApproveOnce(), hooks={"on_pre_tool_use": on_pre_tool_use}, ) ``` @@ -578,7 +577,7 @@ const session = await client.createSession({ ```python import json, aiofiles -from copilot.session import PermissionRequestResult +from copilot import PermissionDecisionApproveOnce audit_log = [] @@ -630,7 +629,7 @@ async def on_session_end(input_data, invocation): return None session = await client.create_session( - on_permission_request=lambda req, inv: PermissionRequestResult(kind="approve-once"), + on_permission_request=lambda req, inv: PermissionDecisionApproveOnce(), hooks={ "on_session_start": on_session_start, "on_user_prompt_submitted": on_user_prompt_submitted, @@ -709,7 +708,7 @@ const session = await client.createSession({ ```python import subprocess -from copilot.session import PermissionRequestResult +from copilot import PermissionDecisionApproveOnce async def on_session_end(input_data, invocation): sid = invocation["session_id"][:8] @@ -728,7 +727,7 @@ async def on_error_occurred(input_data, invocation): return None session = await client.create_session( - on_permission_request=lambda req, inv: PermissionRequestResult(kind="approve-once"), + on_permission_request=lambda req, inv: PermissionDecisionApproveOnce(), hooks={ "on_session_end": on_session_end, "on_error_occurred": on_error_occurred, @@ -932,7 +931,7 @@ const session = await client.createSession({ Python ```python -from copilot.session import PermissionRequestResult +from copilot import PermissionDecisionApproveOnce session_metrics = {} @@ -963,7 +962,7 @@ async def on_session_end(input_data, invocation): return None session = await client.create_session( - on_permission_request=lambda req, inv: PermissionRequestResult(kind="approve-once"), + on_permission_request=lambda req, inv: PermissionDecisionApproveOnce(), hooks={ "on_session_start": on_session_start, "on_user_prompt_submitted": on_user_prompt_submitted, diff --git a/docs/features/image-input.md b/docs/features/image-input.md index 4aa564558..6f743f1fa 100644 --- a/docs/features/image-input.md +++ b/docs/features/image-input.md @@ -68,14 +68,13 @@ await session.send({ Python ```python -from copilot import CopilotClient -from copilot.session import PermissionRequestResult +from copilot import CopilotClient, PermissionDecisionApproveOnce client = CopilotClient() await client.start() session = await client.create_session( - on_permission_request=lambda req, inv: PermissionRequestResult(kind="approve-once"), + on_permission_request=lambda req, inv: PermissionDecisionApproveOnce(), model="gpt-4.1", ) @@ -286,14 +285,13 @@ await session.send({ Python ```python -from copilot import CopilotClient -from copilot.session import PermissionRequestResult +from copilot import CopilotClient, PermissionDecisionApproveOnce client = CopilotClient() await client.start() session = await client.create_session( - on_permission_request=lambda req, inv: PermissionRequestResult(kind="approve-once"), + on_permission_request=lambda req, inv: PermissionDecisionApproveOnce(), model="gpt-4.1", ) diff --git a/docs/features/remote-sessions.md b/docs/features/remote-sessions.md index 391bb762d..ade22da15 100644 --- a/docs/features/remote-sessions.md +++ b/docs/features/remote-sessions.md @@ -38,9 +38,9 @@ session.on("session.info", (event) => { ```python -from copilot import CopilotClient, SubprocessConfig +from copilot import CopilotClient, CopilotClientOptions -client = CopilotClient(SubprocessConfig(remote=True)) +client = CopilotClient(CopilotClientOptions(enable_remote_sessions=True)) session = await client.create_session( working_directory="/path/to/github-repo", on_permission_request=lambda req: {"allowed": True}, diff --git a/docs/features/skills.md b/docs/features/skills.md index 516c11762..05c5a97a9 100644 --- a/docs/features/skills.md +++ b/docs/features/skills.md @@ -42,15 +42,14 @@ await session.sendAndWait({ prompt: "Review this code for security issues" }); Python ```python -from copilot import CopilotClient -from copilot.session import PermissionRequestResult +from copilot import CopilotClient, PermissionDecisionApproveOnce async def main(): client = CopilotClient() await client.start() session = await client.create_session( - on_permission_request=lambda req, inv: PermissionRequestResult(kind="approve-once"), + on_permission_request=lambda req, inv: PermissionDecisionApproveOnce(), model="gpt-4.1", skill_directories=[ "./skills/code-review", diff --git a/docs/features/steering-and-queueing.md b/docs/features/steering-and-queueing.md index ce4f4fba2..237457585 100644 --- a/docs/features/steering-and-queueing.md +++ b/docs/features/steering-and-queueing.md @@ -69,15 +69,14 @@ await session.send({ Python ```python -from copilot import CopilotClient -from copilot.session import PermissionRequestResult +from copilot import CopilotClient, PermissionDecisionApproveOnce async def main(): client = CopilotClient() await client.start() session = await client.create_session( - on_permission_request=lambda req, inv: PermissionRequestResult(kind="approve-once"), + on_permission_request=lambda req, inv: PermissionDecisionApproveOnce(), model="gpt-4.1", ) @@ -261,15 +260,14 @@ await session.send({ Python ```python -from copilot import CopilotClient -from copilot.session import PermissionRequestResult +from copilot import CopilotClient, PermissionDecisionApproveOnce async def main(): client = CopilotClient() await client.start() session = await client.create_session( - on_permission_request=lambda req, inv: PermissionRequestResult(kind="approve-once"), + on_permission_request=lambda req, inv: PermissionDecisionApproveOnce(), model="gpt-4.1", ) @@ -502,7 +500,7 @@ await session.send({ ```python session = await client.create_session( - on_permission_request=lambda req, inv: PermissionRequestResult(kind="approve-once"), + on_permission_request=lambda req, inv: PermissionDecisionApproveOnce(), model="gpt-4.1", ) diff --git a/docs/getting-started.md b/docs/getting-started.md index 0d5e5887e..2e9aa19ea 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -665,13 +665,12 @@ unsubscribeIdle(); ```python -from copilot import CopilotClient +from copilot import CopilotClient, PermissionDecisionApproveOnce from copilot.generated.session_events import SessionEvent, SessionEventType -from copilot.session import PermissionRequestResult client = CopilotClient() -session = await client.create_session(on_permission_request=lambda req, inv: PermissionRequestResult(kind="approve-once")) +session = await client.create_session(on_permission_request=lambda req, inv: PermissionDecisionApproveOnce()) # Subscribe to all events unsubscribe = session.on(lambda event: print(f"Event: {event.type}")) @@ -1968,12 +1967,12 @@ const session = await client.createSession({ onPermissionRequest: approveAll }); Python ```python -from copilot import CopilotClient +from copilot import CopilotClient, CopilotClientOptions, RuntimeConnection from copilot.session import PermissionHandler -client = CopilotClient({ - "cli_url": "localhost:4321" -}) +client = CopilotClient(CopilotClientOptions( + connection=RuntimeConnection.uri("localhost:4321"), +)) await client.start() # Use the client normally @@ -2138,9 +2137,9 @@ Optional peer dependency: `@opentelemetry/api` ```python -from copilot import CopilotClient, SubprocessConfig +from copilot import CopilotClient, CopilotClientOptions -client = CopilotClient(SubprocessConfig( +client = CopilotClient(CopilotClientOptions( telemetry={ "otlp_endpoint": "http://localhost:4318", }, diff --git a/docs/observability/opentelemetry.md b/docs/observability/opentelemetry.md index 42a5c6e96..94888aaf1 100644 --- a/docs/observability/opentelemetry.md +++ b/docs/observability/opentelemetry.md @@ -27,9 +27,9 @@ const client = new CopilotClient({ ```python -from copilot import CopilotClient, SubprocessConfig +from copilot import CopilotClient, CopilotClientOptions -client = CopilotClient(SubprocessConfig( +client = CopilotClient(CopilotClientOptions( telemetry={ "otlp_endpoint": "http://localhost:4318", }, diff --git a/docs/setup/backend-services.md b/docs/setup/backend-services.md index 0552ee36e..88960d782 100644 --- a/docs/setup/backend-services.md +++ b/docs/setup/backend-services.md @@ -144,10 +144,12 @@ res.json({ content: response?.data.content }); Python ```python -from copilot import CopilotClient, ExternalServerConfig +from copilot import CopilotClient, CopilotClientOptions, RuntimeConnection from copilot.session import PermissionHandler -client = CopilotClient(ExternalServerConfig(url="localhost:4321")) +client = CopilotClient(CopilotClientOptions( + connection=RuntimeConnection.uri("localhost:4321"), +)) await client.start() session = await client.create_session(on_permission_request=PermissionHandler.approve_all, model="gpt-4.1", session_id=f"user-{user_id}-{int(time.time())}") diff --git a/docs/troubleshooting/debugging.md b/docs/troubleshooting/debugging.md index 3beadb99f..3f0a9344e 100644 --- a/docs/troubleshooting/debugging.md +++ b/docs/troubleshooting/debugging.md @@ -32,9 +32,9 @@ const client = new CopilotClient({ Python ```python -from copilot import CopilotClient +from copilot import CopilotClient, CopilotClientOptions -client = CopilotClient({"log_level": "debug"}) +client = CopilotClient(CopilotClientOptions(log_level="debug")) ``` @@ -131,7 +131,7 @@ const client = new CopilotClient({ ``` > [!NOTE] -> Python SDK logging configuration is limited. For advanced logging, run the CLI manually with `--log-dir` and connect via `cli_url`. +> Python SDK logging configuration is limited. For advanced logging, run the CLI manually with `--log-dir` and connect via `RuntimeConnection.uri(...)`. diff --git a/python/README.md b/python/README.md index 2f95c1f3c..fe64cc484 100644 --- a/python/README.md +++ b/python/README.md @@ -579,31 +579,27 @@ session = await client.create_session( Provide your own function to inspect each request and apply custom logic (sync or async): ```python -from copilot.session import PermissionRequestResult -from copilot.generated.session_events import PermissionRequest +from copilot import ( + PermissionDecisionApproveOnce, + PermissionDecisionReject, + PermissionRequest, + PermissionRequestResult, + PermissionRequestShell, +) + def on_permission_request( request: PermissionRequest, invocation: dict ) -> PermissionRequestResult: - # request.kind — what type of operation is being requested: - # "shell" — executing a shell command - # "write" — writing or editing a file - # "read" — reading a file - # "mcp" — calling an MCP tool - # "custom-tool" — calling one of your registered tools - # "url" — fetching a URL - # "memory" — accessing or updating session/workspace memory - # "hook" — invoking a registered hook - # request.tool_call_id — the tool call that triggered this request - # request.tool_name — name of the tool (for custom-tool / mcp) - # request.file_name — file being written (for write) - # request.full_command_text — full shell command (for shell) - - if request.kind.value == "shell": - # Deny shell commands - return PermissionRequestResult(kind="reject") - - return PermissionRequestResult(kind="approve-once") + # ``PermissionRequest`` is a discriminated union — pattern-match on + # the variant class to access the per-kind fields. + match request: + case PermissionRequestShell(full_command_text=cmd): + # Deny shell commands + return PermissionDecisionReject(feedback=f"Shell denied: {cmd}") + case _: + return PermissionDecisionApproveOnce() + session = await client.create_session( on_permission_request=on_permission_request, @@ -619,19 +615,29 @@ async def on_permission_request( ) -> PermissionRequestResult: # Simulate an async approval check (e.g., prompting a user over a network) await asyncio.sleep(0) - return PermissionRequestResult(kind="approve-once") + return PermissionDecisionApproveOnce() ``` ### Permission Result Kinds -The handler must return a `PermissionRequestResult` with one of the kinds declared by the `PermissionRequestResultKind` type. Approval decisions are present-tense — they describe the decision to apply, not the past-tense outcome reported back on `permission.completed` session events. - -| `kind` value | Meaning | -| ---------------------- | ------------------------------------------------------------------------------------------- | -| `"approve-once"` | Allow this single request | -| `"reject"` | Deny the request | -| `"user-not-available"` | Deny the request because no user is available to confirm it (the default) | -| `"no-result"` | Leave the request unanswered (only valid with protocol v1; rejected by protocol v2 servers) | +The handler returns a ``PermissionRequestResult``, which is an alias for +``PermissionDecision | PermissionNoResult`` (the generated wire-level +union of every decision variant, plus a small sentinel for v1 servers). +Approval decisions are present-tense — they describe the decision to +apply, not the past-tense outcome reported back on `permission.completed` +session events. + +| Variant | Meaning | +| --------------------------------------------- | ------------------------------------------------------------------------------------------- | +| `PermissionDecisionApproveOnce()` | Allow this single request | +| `PermissionDecisionReject(feedback="…")` | Deny the request (optional feedback string forwarded to the LLM) | +| `PermissionDecisionUserNotAvailable()` | Deny the request because no user is available to confirm it (the default) | +| `PermissionNoResult()` | Leave the request unanswered (only valid with protocol v1; rejected by protocol v2 servers) | + +Several richer variants (``PermissionDecisionApproveForSession``, +``PermissionDecisionApproveForLocation``, ``PermissionDecisionApprovePermanently``, +…) are available for granting longer-lived approvals; see the generated +``copilot.generated.rpc`` module for the full list. ### Resuming Sessions From 2c6bedd1bf348cdb529aee8c624679a3e7178ce5 Mon Sep 17 00:00:00 2001 From: Steve Sanderson Date: Fri, 22 May 2026 13:24:09 +0100 Subject: [PATCH 14/21] Phase F/G fixup: fix new ty errors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two new ty errors surfaced by Phase F/G changes: 1. `client.py:2660` accessed `ping_result.protocolVersion` — the field was renamed to `protocol_version` in Phase G but this call site was missed. 2. `session.py:_handle_hooks_invoke` lost the static link between the normalized hook input dict and the per-hook-type TypedDict variant after the Phase E wire-key remap + timestamp conversion, breaking the typed Union dispatch. Cast the normalized dict back to `Any` so the call-site picks the correct Union variant at type-check time. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- python/copilot/client.py | 2 +- python/copilot/session.py | 7 +++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/python/copilot/client.py b/python/copilot/client.py index 8035acbc5..85b417a39 100644 --- a/python/copilot/client.py +++ b/python/copilot/client.py @@ -2657,7 +2657,7 @@ async def _verify_protocol_version(self) -> None: # is silently dropped — the legacy server can't enforce one. used_fallback_ping = True ping_result = await self.ping() - server_version = ping_result.protocolVersion + server_version = ping_result.protocol_version else: raise diff --git a/python/copilot/session.py b/python/copilot/session.py index 9c650bbcd..1da8e59ab 100644 --- a/python/copilot/session.py +++ b/python/copilot/session.py @@ -2180,13 +2180,16 @@ async def _handle_hooks_invoke(self, hook_type: str, input_data: Any) -> Any: # - Convert "timestamp" from epoch milliseconds to ``datetime`` so # hook handlers see a timezone-aware ``datetime`` rather than a # raw integer (matches TS PR #1357 Phase E). - transformed = dict(input_data) + transformed: dict[str, Any] = dict(input_data) if "cwd" in transformed: transformed["workingDirectory"] = transformed.pop("cwd") timestamp = transformed.get("timestamp") if isinstance(timestamp, (int, float)): transformed["timestamp"] = datetime.fromtimestamp(timestamp / 1000, tz=UTC) - input_data = transformed + # Each per-hook-type TypedDict is structurally compatible with the + # normalized dict; cast to ``Any`` so ty doesn't try to narrow the + # specific TypedDict variant from the runtime ``dict``. + input_data = cast(Any, transformed) result = handler(input_data, {"session_id": self.session_id}) if inspect.isawaitable(result): result = await result From 15a466ab1b52dcc57376eddc1f089d51063f7497 Mon Sep 17 00:00:00 2001 From: Steve Sanderson Date: Fri, 22 May 2026 13:37:15 +0100 Subject: [PATCH 15/21] E2E test fixups for renamed APIs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix six E2E test failures surfaced by the macOS Python 3.11 CI job: 1. test_should_get_null_last_session_id_before_any_sessions_exist: the runtime tracks 'last session id' as a persistent value; if a prior test in the class created any session, the assertion `result is None` fails. Clear leftover sessions before reading the value (matches the .NET fix in commit 5d55127c). 2. test_should_get_status_with_version_and_protocol_info: the assertion was checking `hasattr(status, "protocolVersion")` (camelCase) but asserting against `status.protocol_version` (snake_case). Phase G renamed the field to snake_case; update the hasattr check to match. 3. test_should_emit_session_lifecycle_events: `getattr(e, "sessionId", ...)` still used camelCase after Phase G's rename to `session_id`. 4. test_should_propagate_process_options_to_spawned_cli: my earlier rename sweep mistakenly changed `COPILOT_HOME` env-var references in the testharness and fake CLI script to `base_directory` (the new option *name*). `COPILOT_HOME` is a wire-format env var owned by the CLI and must remain unchanged — only the SDK option name was renamed. Restore `COPILOT_HOME` everywhere it's used as an env var. 5. test_should_set_meta_via_premcptoolcall_hook: assertion compared the hook input timestamp against `0` (int), which now raises `TypeError: '>' not supported between instances of 'datetime.datetime' and 'int'` after Phase E converted hook timestamps to `datetime`. Replace with `isinstance(..., datetime)`. 6. test_should_list_sessions: `hasattr(session_data, "sessionId")` etc. still used camelCase after Phase G renamed `SessionMetadata` fields to snake_case. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- python/e2e/test_client_api_e2e.py | 7 +++++++ python/e2e/test_client_e2e.py | 2 +- python/e2e/test_client_lifecycle_e2e.py | 2 +- python/e2e/test_client_options_e2e.py | 4 ++-- python/e2e/test_commands_e2e.py | 4 ++-- python/e2e/test_connection_token.py | 2 +- python/e2e/test_multi_client_e2e.py | 4 ++-- python/e2e/test_pre_mcp_tool_call_hook_e2e.py | 3 ++- python/e2e/test_session_e2e.py | 8 ++++---- python/e2e/test_ui_elicitation_multi_client_e2e.py | 10 +++++----- python/e2e/testharness/context.py | 2 +- 11 files changed, 28 insertions(+), 20 deletions(-) diff --git a/python/e2e/test_client_api_e2e.py b/python/e2e/test_client_api_e2e.py index 1699bb8cf..217352817 100644 --- a/python/e2e/test_client_api_e2e.py +++ b/python/e2e/test_client_api_e2e.py @@ -44,6 +44,13 @@ async def test_should_get_null_last_session_id_before_any_sessions_exist( self, ctx: E2ETestContext ): await ctx.client.start() + + # Other tests in this class create sessions, and pytest doesn't + # guarantee test execution order. Clear any leftover sessions so this + # test sees a genuinely empty state regardless of order. + for existing in await ctx.client.list_sessions(): + await ctx.client.delete_session(existing.session_id) + result = await ctx.client.get_last_session_id() assert result is None diff --git a/python/e2e/test_client_e2e.py b/python/e2e/test_client_e2e.py index 8cff89c78..31906a2df 100644 --- a/python/e2e/test_client_e2e.py +++ b/python/e2e/test_client_e2e.py @@ -104,7 +104,7 @@ async def test_should_get_status_with_version_and_protocol_info(self): status = await client.get_status() assert hasattr(status, "version") assert isinstance(status.version, str) - assert hasattr(status, "protocolVersion") + assert hasattr(status, "protocol_version") assert isinstance(status.protocol_version, int) assert status.protocol_version >= 1 diff --git a/python/e2e/test_client_lifecycle_e2e.py b/python/e2e/test_client_lifecycle_e2e.py index 29c88cdd2..e706df7c9 100644 --- a/python/e2e/test_client_lifecycle_e2e.py +++ b/python/e2e/test_client_lifecycle_e2e.py @@ -93,7 +93,7 @@ async def test_should_emit_session_lifecycle_events(self, ctx: E2ETestContext): await _wait_for_condition( lambda: any( - getattr(e, "sessionId", None) == session.session_id for e in events + getattr(e, "session_id", None) == session.session_id for e in events ), timeout=10.0, ) diff --git a/python/e2e/test_client_options_e2e.py b/python/e2e/test_client_options_e2e.py index be42755fb..ec779d697 100644 --- a/python/e2e/test_client_options_e2e.py +++ b/python/e2e/test_client_options_e2e.py @@ -88,7 +88,7 @@ def _get_available_port() -> int: cwd: process.cwd(), requests, env: { - COPILOT_HOME: process.env.base_directory, + COPILOT_HOME: process.env.COPILOT_HOME, COPILOT_SDK_AUTH_TOKEN: process.env.COPILOT_SDK_AUTH_TOKEN, COPILOT_OTEL_ENABLED: process.env.COPILOT_OTEL_ENABLED, OTEL_EXPORTER_OTLP_ENDPOINT: process.env.OTEL_EXPORTER_OTLP_ENDPOINT, @@ -239,7 +239,7 @@ async def test_should_propagate_process_options_to_spawned_cli(self, ctx: E2ETes cli_path=cli_path, base_directory=copilot_home_from_option, cli_args=["--capture-file", capture_path], - env={**ctx.get_env(), "base_directory": copilot_home_from_env}, + env={**ctx.get_env(), "COPILOT_HOME": copilot_home_from_env}, github_token="process-option-token", log_level="debug", session_idle_timeout_seconds=17, diff --git a/python/e2e/test_commands_e2e.py b/python/e2e/test_commands_e2e.py index b5f2bfa2f..115cdebca 100644 --- a/python/e2e/test_commands_e2e.py +++ b/python/e2e/test_commands_e2e.py @@ -25,7 +25,7 @@ # --------------------------------------------------------------------------- -# Multi-client context (TCP mode) — same pattern as test_multi_client.py +# Multi-client context (TCP mode) — same pattern as test_multi_client.py # --------------------------------------------------------------------------- @@ -121,7 +121,7 @@ def _get_env(self) -> dict: env.update( { "COPILOT_API_URL": self.proxy_url, - "base_directory": self.home_dir, + "COPILOT_HOME": self.home_dir, "XDG_CONFIG_HOME": self.home_dir, "XDG_STATE_HOME": self.home_dir, } diff --git a/python/e2e/test_connection_token.py b/python/e2e/test_connection_token.py index f08820f34..4986c6f25 100644 --- a/python/e2e/test_connection_token.py +++ b/python/e2e/test_connection_token.py @@ -78,7 +78,7 @@ def get_env(self) -> dict: env.update( { "COPILOT_API_URL": self.proxy_url, - "base_directory": self.home_dir, + "COPILOT_HOME": self.home_dir, "XDG_CONFIG_HOME": self.home_dir, "XDG_STATE_HOME": self.home_dir, } diff --git a/python/e2e/test_multi_client_e2e.py b/python/e2e/test_multi_client_e2e.py index 081cb31af..f1f79b158 100644 --- a/python/e2e/test_multi_client_e2e.py +++ b/python/e2e/test_multi_client_e2e.py @@ -135,7 +135,7 @@ def get_env(self) -> dict: env.update( { "COPILOT_API_URL": self.proxy_url, - "base_directory": self.home_dir, + "COPILOT_HOME": self.home_dir, "XDG_CONFIG_HOME": self.home_dir, "XDG_STATE_HOME": self.home_dir, } @@ -206,7 +206,7 @@ def magic_number(params: SeedParams, invocation: ToolInvocation) -> str: on_permission_request=PermissionHandler.approve_all, tools=[magic_number] ) - # Client 2 resumes with NO tools — should not overwrite client 1's tools + # Client 2 resumes with NO tools — should not overwrite client 1's tools session2 = await mctx.client2.resume_session( session1.session_id, on_permission_request=PermissionHandler.approve_all ) diff --git a/python/e2e/test_pre_mcp_tool_call_hook_e2e.py b/python/e2e/test_pre_mcp_tool_call_hook_e2e.py index 9a140dd38..c59994437 100644 --- a/python/e2e/test_pre_mcp_tool_call_hook_e2e.py +++ b/python/e2e/test_pre_mcp_tool_call_hook_e2e.py @@ -5,6 +5,7 @@ from __future__ import annotations +from datetime import datetime from pathlib import Path import pytest @@ -58,7 +59,7 @@ async def on_pre_mcp_tool_call(input_data, invocation): assert inputs[0].get("serverName") == "meta-echo" assert inputs[0].get("toolName") == "echo_meta" assert inputs[0].get("workingDirectory") - assert inputs[0].get("timestamp", 0) > 0 + assert isinstance(inputs[0].get("timestamp"), datetime) finally: await session.disconnect() diff --git a/python/e2e/test_session_e2e.py b/python/e2e/test_session_e2e.py index 268372bb0..84eb95d16 100644 --- a/python/e2e/test_session_e2e.py +++ b/python/e2e/test_session_e2e.py @@ -301,10 +301,10 @@ async def test_should_list_sessions(self, ctx: E2ETestContext): # Verify session metadata structure for session_data in sessions: - assert hasattr(session_data, "sessionId") - assert hasattr(session_data, "startTime") - assert hasattr(session_data, "modifiedTime") - assert hasattr(session_data, "isRemote") + assert hasattr(session_data, "session_id") + assert hasattr(session_data, "start_time") + assert hasattr(session_data, "modified_time") + assert hasattr(session_data, "is_remote") # summary is optional assert isinstance(session_data.session_id, str) assert isinstance(session_data.start_time, datetime) diff --git a/python/e2e/test_ui_elicitation_multi_client_e2e.py b/python/e2e/test_ui_elicitation_multi_client_e2e.py index b72890510..8d8934d6e 100644 --- a/python/e2e/test_ui_elicitation_multi_client_e2e.py +++ b/python/e2e/test_ui_elicitation_multi_client_e2e.py @@ -1,6 +1,6 @@ """E2E UI Elicitation Tests (multi-client) -Mirrors nodejs/test/e2e/ui_elicitation.test.ts — multi-client scenarios. +Mirrors nodejs/test/e2e/ui_elicitation.test.ts — multi-client scenarios. Tests: - capabilities.changed fires when second client joins with elicitation handler @@ -31,7 +31,7 @@ # --------------------------------------------------------------------------- -# Multi-client context (TCP mode) — same pattern as test_multi_client.py +# Multi-client context (TCP mode) — same pattern as test_multi_client.py # --------------------------------------------------------------------------- @@ -128,7 +128,7 @@ def _get_env(self) -> dict: env.update( { "COPILOT_API_URL": self.proxy_url, - "base_directory": self.home_dir, + "COPILOT_HOME": self.home_dir, "XDG_CONFIG_HOME": self.home_dir, "XDG_STATE_HOME": self.home_dir, } @@ -264,7 +264,7 @@ def on_event(event): unsubscribe = session1.on(on_event) - # Client 2 joins WITH elicitation handler — triggers capabilities.changed + # Client 2 joins WITH elicitation handler — triggers capabilities.changed async def handler( context: ElicitationContext, ) -> ElicitationResult: @@ -340,7 +340,7 @@ def on_disabled(event): unsub_disabled = session1.on(on_disabled) - # Force-stop client 3 — destroys the socket, triggering server-side cleanup + # Force-stop client 3 — destroys the socket, triggering server-side cleanup await client3.force_stop() await asyncio.wait_for(cap_disabled.wait(), timeout=15.0) diff --git a/python/e2e/testharness/context.py b/python/e2e/testharness/context.py index 527a0245d..f44e89f6c 100644 --- a/python/e2e/testharness/context.py +++ b/python/e2e/testharness/context.py @@ -149,7 +149,7 @@ def get_env(self) -> dict: env.update( { "COPILOT_API_URL": self.proxy_url, - "base_directory": self.home_dir, + "COPILOT_HOME": self.home_dir, "COPILOT_SDK_AUTH_TOKEN": DEFAULT_GITHUB_TOKEN, "GH_CONFIG_DIR": self.home_dir, "GH_TOKEN": DEFAULT_GITHUB_TOKEN, From b57bab87eac6d6a13ac6fecba7cc08a8a08f435a Mon Sep 17 00:00:00 2001 From: Steve Sanderson Date: Fri, 22 May 2026 13:39:20 +0100 Subject: [PATCH 16/21] Rename RuntimeConnection factories to for_stdio/for_tcp/for_uri MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Match the naming used by the TypeScript and C# SDKs (`forStdio` / `forTcp` / `forUri` and `ForStdio` / `ForTcp` / `ForUri` respectively). The plain names `stdio` / `tcp` / `uri` read like properties or constructors of those concepts; the `for_` prefix makes the factory-method semantics explicit and keeps the API parallel across languages. Renames `RuntimeConnection.stdio` → `for_stdio`, `RuntimeConnection.tcp` → `for_tcp`, `RuntimeConnection.uri` → `for_uri` along with every call-site across the SDK, unit tests, E2E tests, scenarios, README, and docs. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- docs/getting-started.md | 2 +- docs/setup/backend-services.md | 2 +- docs/troubleshooting/debugging.md | 2 +- python/README.md | 12 +- python/copilot/client.py | 21 ++-- python/e2e/test_agent_and_compact_rpc_e2e.py | 12 +- python/e2e/test_client_e2e.py | 28 ++--- python/e2e/test_client_lifecycle_e2e.py | 2 +- python/e2e/test_client_options_e2e.py | 6 +- python/e2e/test_commands_e2e.py | 4 +- python/e2e/test_connection_token.py | 8 +- python/e2e/test_multi_client_e2e.py | 6 +- python/e2e/test_pending_work_resume_e2e.py | 28 ++--- python/e2e/test_per_session_auth_e2e.py | 2 +- python/e2e/test_rpc_e2e.py | 12 +- python/e2e/test_rpc_server_e2e.py | 2 +- python/e2e/test_session_e2e.py | 2 +- python/e2e/test_session_fs_e2e.py | 6 +- python/e2e/test_session_fs_sqlite_e2e.py | 2 +- python/e2e/test_streaming_fidelity_e2e.py | 4 +- python/e2e/test_subagent_hooks_e2e.py | 2 +- python/e2e/test_suspend_e2e.py | 8 +- python/e2e/test_telemetry_e2e.py | 4 +- .../test_ui_elicitation_multi_client_e2e.py | 6 +- python/e2e/testharness/context.py | 2 +- python/test_client.py | 106 +++++++++--------- python/test_commands_and_elicitation.py | 36 +++--- python/test_telemetry.py | 2 +- .../app-backend-to-server/python/main.py | 2 +- .../bundling/app-direct-server/python/main.py | 2 +- .../bundling/container-proxy/python/main.py | 2 +- .../transport/reconnect/python/main.py | 2 +- test/scenarios/transport/tcp/python/main.py | 2 +- 33 files changed, 174 insertions(+), 165 deletions(-) diff --git a/docs/getting-started.md b/docs/getting-started.md index 2e9aa19ea..f451fa69c 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -1971,7 +1971,7 @@ from copilot import CopilotClient, CopilotClientOptions, RuntimeConnection from copilot.session import PermissionHandler client = CopilotClient(CopilotClientOptions( - connection=RuntimeConnection.uri("localhost:4321"), + connection=RuntimeConnection.for_uri("localhost:4321"), )) await client.start() diff --git a/docs/setup/backend-services.md b/docs/setup/backend-services.md index 88960d782..6dbcf1a0e 100644 --- a/docs/setup/backend-services.md +++ b/docs/setup/backend-services.md @@ -148,7 +148,7 @@ from copilot import CopilotClient, CopilotClientOptions, RuntimeConnection from copilot.session import PermissionHandler client = CopilotClient(CopilotClientOptions( - connection=RuntimeConnection.uri("localhost:4321"), + connection=RuntimeConnection.for_uri("localhost:4321"), )) await client.start() diff --git a/docs/troubleshooting/debugging.md b/docs/troubleshooting/debugging.md index 3f0a9344e..a648c067c 100644 --- a/docs/troubleshooting/debugging.md +++ b/docs/troubleshooting/debugging.md @@ -131,7 +131,7 @@ const client = new CopilotClient({ ``` > [!NOTE] -> Python SDK logging configuration is limited. For advanced logging, run the CLI manually with `--log-dir` and connect via `RuntimeConnection.uri(...)`. +> Python SDK logging configuration is limited. For advanced logging, run the CLI manually with `--log-dir` and connect via `RuntimeConnection.for_uri(...)`. diff --git a/python/README.md b/python/README.md index fe64cc484..608c9e4dd 100644 --- a/python/README.md +++ b/python/README.md @@ -137,7 +137,7 @@ from copilot import CopilotClient, CopilotClientOptions, RuntimeConnection # Connect to an existing CLI server client = CopilotClient( - CopilotClientOptions(connection=RuntimeConnection.uri("localhost:3000")) + CopilotClientOptions(connection=RuntimeConnection.for_uri("localhost:3000")) ) ``` @@ -155,8 +155,8 @@ CopilotClient( **CopilotClientOptions** — configure the client: - `connection` (RuntimeConnection | None): How to reach the runtime. Use - `RuntimeConnection.stdio(...)`, `RuntimeConnection.tcp(...)`, or - `RuntimeConnection.uri(...)`. Defaults to a stdio connection with the bundled binary. + `RuntimeConnection.for_stdio(...)`, `RuntimeConnection.for_tcp(...)`, or + `RuntimeConnection.for_uri(...)`. Defaults to a stdio connection with the bundled binary. - `working_directory` (str | None): Working directory for the CLI process (default: current dir). - `log_level` (str): Log level (default: "info"). - `env` (dict | None): Environment variables for the CLI process. @@ -168,9 +168,9 @@ CopilotClient( **RuntimeConnection variants:** -- `RuntimeConnection.stdio(path=None, args=None)` — spawn a local CLI process and talk over stdio. -- `RuntimeConnection.tcp(port=0, connection_token=None, path=None, args=None)` — spawn a local CLI in TCP mode. -- `RuntimeConnection.uri(url, connection_token=None)` — connect to an existing CLI server (e.g. `"localhost:8080"`). +- `RuntimeConnection.for_stdio(path=None, args=None)` — spawn a local CLI process and talk over stdio. +- `RuntimeConnection.for_tcp(port=0, connection_token=None, path=None, args=None)` — spawn a local CLI in TCP mode. +- `RuntimeConnection.for_uri(url, connection_token=None)` — connect to an existing CLI server (e.g. `"localhost:8080"`). **`CopilotClient.create_session()`:** diff --git a/python/copilot/client.py b/python/copilot/client.py index 85b417a39..fc64d29ed 100644 --- a/python/copilot/client.py +++ b/python/copilot/client.py @@ -165,11 +165,13 @@ class RuntimeConnection: Example: >>> CopilotClient() # default: stdio with the bundled runtime - >>> CopilotClient(CopilotClientOptions(connection=RuntimeConnection.uri("localhost:3000"))) + >>> CopilotClient( + ... CopilotClientOptions(connection=RuntimeConnection.for_uri("localhost:3000")) + ... ) """ @staticmethod - def stdio( + def for_stdio( *, path: str | None = None, args: Sequence[str] = (), @@ -187,7 +189,7 @@ def stdio( return StdioRuntimeConnection(path=path, args=tuple(args)) @staticmethod - def tcp( + def for_tcp( *, port: int = 0, connection_token: str | None = None, @@ -216,7 +218,7 @@ def tcp( ) @staticmethod - def uri(url: str, *, connection_token: str | None = None) -> UriRuntimeConnection: + def for_uri(url: str, *, connection_token: str | None = None) -> UriRuntimeConnection: """Connect to an already-running runtime at the given URL. Args: @@ -1073,7 +1075,7 @@ class CopilotClient: >>> # Or connect to an existing server >>> client = CopilotClient( - ... CopilotClientOptions(connection=RuntimeConnection.uri("localhost:3000")) + ... CopilotClientOptions(connection=RuntimeConnection.for_uri("localhost:3000")) ... ) """ @@ -1102,13 +1104,13 @@ def __init__( >>> >>> # Connect to an existing runtime >>> client = CopilotClient( - ... CopilotClientOptions(connection=RuntimeConnection.uri("localhost:3000")) + ... CopilotClientOptions(connection=RuntimeConnection.for_uri("localhost:3000")) ... ) >>> >>> # Custom runtime path with specific log level >>> client = CopilotClient( ... CopilotClientOptions( - ... connection=RuntimeConnection.stdio(path="/usr/local/bin/copilot"), + ... connection=RuntimeConnection.for_stdio(path="/usr/local/bin/copilot"), ... log_level="debug", ... ) ... ) @@ -1116,7 +1118,7 @@ def __init__( if options is None: options = CopilotClientOptions() connection = ( - options.connection if options.connection is not None else RuntimeConnection.stdio() + options.connection if options.connection is not None else RuntimeConnection.for_stdio() ) self._options: CopilotClientOptions = options @@ -1169,7 +1171,8 @@ def __init__( raise RuntimeError( "Copilot CLI not found. The bundled CLI binary is not available. " "Ensure you installed a platform-specific wheel, or set " - "RuntimeConnection.stdio(path=...) / RuntimeConnection.tcp(path=...)." + "RuntimeConnection.for_stdio(path=...) / " + "RuntimeConnection.for_tcp(path=...)." ) # Resolve use_logged_in_user default diff --git a/python/e2e/test_agent_and_compact_rpc_e2e.py b/python/e2e/test_agent_and_compact_rpc_e2e.py index 60128a9e8..1209ca1d4 100644 --- a/python/e2e/test_agent_and_compact_rpc_e2e.py +++ b/python/e2e/test_agent_and_compact_rpc_e2e.py @@ -18,7 +18,7 @@ class TestAgentSelectionRpc: async def test_should_list_available_custom_agents(self): """Test listing available custom agents via RPC.""" client = CopilotClient( - CopilotClientOptions(connection=RuntimeConnection.stdio(path=CLI_PATH)) + CopilotClientOptions(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) ) try: @@ -58,7 +58,7 @@ async def test_should_list_available_custom_agents(self): async def test_should_return_null_when_no_agent_is_selected(self): """Test getCurrent returns null when no agent is selected.""" client = CopilotClient( - CopilotClientOptions(connection=RuntimeConnection.stdio(path=CLI_PATH)) + CopilotClientOptions(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) ) try: @@ -87,7 +87,7 @@ async def test_should_return_null_when_no_agent_is_selected(self): async def test_should_select_and_get_current_agent(self): """Test selecting an agent and verifying getCurrent returns it.""" client = CopilotClient( - CopilotClientOptions(connection=RuntimeConnection.stdio(path=CLI_PATH)) + CopilotClientOptions(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) ) try: @@ -124,7 +124,7 @@ async def test_should_select_and_get_current_agent(self): async def test_should_deselect_current_agent(self): """Test deselecting the current agent.""" client = CopilotClient( - CopilotClientOptions(connection=RuntimeConnection.stdio(path=CLI_PATH)) + CopilotClientOptions(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) ) try: @@ -158,7 +158,7 @@ async def test_should_deselect_current_agent(self): async def test_should_return_empty_list_when_no_custom_agents_configured(self): """Test listing agents returns no custom agents when none configured.""" client = CopilotClient( - CopilotClientOptions(connection=RuntimeConnection.stdio(path=CLI_PATH)) + CopilotClientOptions(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) ) try: @@ -185,7 +185,7 @@ async def test_should_return_empty_list_when_no_custom_agents_configured(self): async def test_should_call_agent_reload(self): """Test reloading agents via RPC.""" client = CopilotClient( - CopilotClientOptions(connection=RuntimeConnection.stdio(path=CLI_PATH)) + CopilotClientOptions(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) ) reload_agent = { "name": f"reload-test-agent-{uuid.uuid4().hex}", diff --git a/python/e2e/test_client_e2e.py b/python/e2e/test_client_e2e.py index 31906a2df..edb736051 100644 --- a/python/e2e/test_client_e2e.py +++ b/python/e2e/test_client_e2e.py @@ -19,7 +19,7 @@ class TestClient: @pytest.mark.asyncio async def test_should_start_and_connect_to_server_using_stdio(self): client = CopilotClient( - CopilotClientOptions(connection=RuntimeConnection.stdio(path=CLI_PATH)) + CopilotClientOptions(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) ) try: @@ -38,7 +38,7 @@ async def test_should_start_and_connect_to_server_using_stdio(self): @pytest.mark.asyncio async def test_should_start_and_connect_to_server_using_tcp(self): client = CopilotClient( - CopilotClientOptions(connection=RuntimeConnection.tcp(path=CLI_PATH)) + CopilotClientOptions(connection=RuntimeConnection.for_tcp(path=CLI_PATH)) ) try: @@ -59,7 +59,7 @@ async def test_should_raise_exception_group_on_failed_cleanup(self): import asyncio client = CopilotClient( - CopilotClientOptions(connection=RuntimeConnection.stdio(path=CLI_PATH)) + CopilotClientOptions(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) ) try: @@ -85,7 +85,7 @@ async def test_should_raise_exception_group_on_failed_cleanup(self): @pytest.mark.asyncio async def test_should_force_stop_without_cleanup(self): client = CopilotClient( - CopilotClientOptions(connection=RuntimeConnection.stdio(path=CLI_PATH)) + CopilotClientOptions(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) ) await client.create_session(on_permission_request=PermissionHandler.approve_all) @@ -95,7 +95,7 @@ async def test_should_force_stop_without_cleanup(self): @pytest.mark.asyncio async def test_should_get_status_with_version_and_protocol_info(self): client = CopilotClient( - CopilotClientOptions(connection=RuntimeConnection.stdio(path=CLI_PATH)) + CopilotClientOptions(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) ) try: @@ -115,7 +115,7 @@ async def test_should_get_status_with_version_and_protocol_info(self): @pytest.mark.asyncio async def test_should_get_auth_status(self): client = CopilotClient( - CopilotClientOptions(connection=RuntimeConnection.stdio(path=CLI_PATH)) + CopilotClientOptions(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) ) try: @@ -135,7 +135,7 @@ async def test_should_get_auth_status(self): @pytest.mark.asyncio async def test_should_list_models_when_authenticated(self): client = CopilotClient( - CopilotClientOptions(connection=RuntimeConnection.stdio(path=CLI_PATH)) + CopilotClientOptions(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) ) try: @@ -165,7 +165,7 @@ async def test_should_list_models_when_authenticated(self): async def test_should_cache_models_list(self): """Test that list_models caches results to avoid rate limiting""" client = CopilotClient( - CopilotClientOptions(connection=RuntimeConnection.stdio(path=CLI_PATH)) + CopilotClientOptions(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) ) try: @@ -212,7 +212,7 @@ async def test_should_report_error_with_stderr_when_cli_fails_to_start(self): """Test that CLI startup errors include stderr output in the error message.""" client = CopilotClient( CopilotClientOptions( - connection=RuntimeConnection.stdio( + connection=RuntimeConnection.for_stdio( path=CLI_PATH, args=["--nonexistent-flag-for-testing"] ) ) @@ -247,7 +247,7 @@ async def test_should_report_error_with_stderr_when_cli_fails_to_start(self): async def test_should_not_throw_when_disposing_session_after_stopping_client(self): """Disconnecting a session after the client is stopped must not raise.""" client = CopilotClient( - CopilotClientOptions(connection=RuntimeConnection.stdio(path=CLI_PATH)) + CopilotClientOptions(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) ) try: @@ -268,7 +268,7 @@ async def test_should_not_throw_when_disposing_session_after_stopping_client(sel async def test_should_create_session_without_permission_handler(self): """`create_session` allows omitting an `on_permission_request` handler.""" client = CopilotClient( - CopilotClientOptions(connection=RuntimeConnection.stdio(path=CLI_PATH)) + CopilotClientOptions(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) ) try: @@ -285,7 +285,7 @@ async def test_should_create_session_without_permission_handler(self): async def test_should_resume_session_without_permission_handler(self): """`resume_session` allows omitting an `on_permission_request` handler.""" client = CopilotClient( - CopilotClientOptions(connection=RuntimeConnection.stdio(path=CLI_PATH)) + CopilotClientOptions(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) ) try: @@ -321,7 +321,7 @@ def on_list_models(): return custom_models client = CopilotClient( - CopilotClientOptions(connection=RuntimeConnection.stdio(path=CLI_PATH)), + CopilotClientOptions(connection=RuntimeConnection.for_stdio(path=CLI_PATH)), on_list_models=on_list_models, ) @@ -359,7 +359,7 @@ def on_list_models(): return custom_models client = CopilotClient( - CopilotClientOptions(connection=RuntimeConnection.stdio(path=CLI_PATH)), + CopilotClientOptions(connection=RuntimeConnection.for_stdio(path=CLI_PATH)), on_list_models=on_list_models, ) diff --git a/python/e2e/test_client_lifecycle_e2e.py b/python/e2e/test_client_lifecycle_e2e.py index e706df7c9..388499667 100644 --- a/python/e2e/test_client_lifecycle_e2e.py +++ b/python/e2e/test_client_lifecycle_e2e.py @@ -60,7 +60,7 @@ def _make_isolated_client(ctx: E2ETestContext) -> CopilotClient: ) return CopilotClient( CopilotClientOptions( - connection=RuntimeConnection.stdio(path=ctx.cli_path), + connection=RuntimeConnection.for_stdio(path=ctx.cli_path), working_directory=ctx.work_dir, env=ctx.get_env(), github_token=github_token, diff --git a/python/e2e/test_client_options_e2e.py b/python/e2e/test_client_options_e2e.py index ec779d697..3d7e2c272 100644 --- a/python/e2e/test_client_options_e2e.py +++ b/python/e2e/test_client_options_e2e.py @@ -5,7 +5,7 @@ Mirrors ``dotnet/test/ClientOptionsTests.cs``. The two CliUrl-conflict tests (``Should_Throw_When_GitHubToken_Used_With_CliUrl`` and ``Should_Throw_When_UseLoggedInUser_Used_With_CliUrl``) have no Python -equivalent because Python's ``RuntimeConnection.uri(...)`` does not accept +equivalent because Python's ``RuntimeConnection.for_uri(...)`` does not accept ``github_token`` / ``use_logged_in_user`` fields at all (those live on ``CopilotClientOptions``, but a Uri-connected runtime ignores them), so the conflict cannot be expressed in code and the configurations are therefore @@ -41,14 +41,14 @@ def _make_options( ) -> CopilotClientOptions: """Build a ``CopilotClientOptions`` pre-populated for the test harness.""" if use_tcp: - connection = RuntimeConnection.tcp( + connection = RuntimeConnection.for_tcp( port=port, connection_token=connection_token, path=cli_path if cli_path is not None else ctx.cli_path, args=tuple(cli_args or []), ) else: - connection = RuntimeConnection.stdio( + connection = RuntimeConnection.for_stdio( path=cli_path if cli_path is not None else ctx.cli_path, args=tuple(cli_args or []), ) diff --git a/python/e2e/test_commands_e2e.py b/python/e2e/test_commands_e2e.py index 115cdebca..305807702 100644 --- a/python/e2e/test_commands_e2e.py +++ b/python/e2e/test_commands_e2e.py @@ -56,7 +56,7 @@ async def setup(self): # Client 1 uses TCP mode so a second client can connect self._client1 = CopilotClient( CopilotClientOptions( - connection=RuntimeConnection.tcp( + connection=RuntimeConnection.for_tcp( path=self.cli_path, connection_token="py-tcp-shared-test-token" ), working_directory=self.work_dir, @@ -76,7 +76,7 @@ async def setup(self): self._client2 = CopilotClient( CopilotClientOptions( - connection=RuntimeConnection.uri( + connection=RuntimeConnection.for_uri( f"localhost:{actual_port}", connection_token="py-tcp-shared-test-token" ) ) diff --git a/python/e2e/test_connection_token.py b/python/e2e/test_connection_token.py index 4986c6f25..509486446 100644 --- a/python/e2e/test_connection_token.py +++ b/python/e2e/test_connection_token.py @@ -47,7 +47,9 @@ async def setup(self): self._client = CopilotClient( CopilotClientOptions( - connection=RuntimeConnection.tcp(path=self.cli_path, connection_token=self.token), + connection=RuntimeConnection.for_tcp( + path=self.cli_path, connection_token=self.token + ), working_directory=self.work_dir, env=self.get_env(), github_token=github_token, @@ -135,7 +137,7 @@ async def test_wrong_token_is_rejected(self, explicit_token_ctx: ConnectionToken wrong = CopilotClient( CopilotClientOptions( - connection=RuntimeConnection.uri(f"localhost:{port}", connection_token="wrong") + connection=RuntimeConnection.for_uri(f"localhost:{port}", connection_token="wrong") ) ) try: @@ -155,7 +157,7 @@ async def test_missing_token_is_rejected(self, explicit_token_ctx: ConnectionTok assert port is not None no_token = CopilotClient( - CopilotClientOptions(connection=RuntimeConnection.uri(f"localhost:{port}")) + CopilotClientOptions(connection=RuntimeConnection.for_uri(f"localhost:{port}")) ) try: with pytest.raises(Exception, match="AUTHENTICATION_FAILED"): diff --git a/python/e2e/test_multi_client_e2e.py b/python/e2e/test_multi_client_e2e.py index f1f79b158..8ebe62c18 100644 --- a/python/e2e/test_multi_client_e2e.py +++ b/python/e2e/test_multi_client_e2e.py @@ -54,7 +54,7 @@ async def setup(self): # Client 1 uses TCP mode so a second client can connect to the same server self._client1 = CopilotClient( CopilotClientOptions( - connection=RuntimeConnection.tcp( + connection=RuntimeConnection.for_tcp( path=self.cli_path, connection_token="py-tcp-shared-test-token" ), working_directory=self.work_dir, @@ -75,7 +75,7 @@ async def setup(self): self._client2 = CopilotClient( CopilotClientOptions( - connection=RuntimeConnection.uri( + connection=RuntimeConnection.for_uri( f"localhost:{actual_port}", connection_token="py-tcp-shared-test-token" ) ) @@ -430,7 +430,7 @@ def ephemeral_tool(params: InputParams, invocation: ToolInvocation) -> str: actual_port = mctx.client1.runtime_port mctx._client2 = CopilotClient( CopilotClientOptions( - connection=RuntimeConnection.uri( + connection=RuntimeConnection.for_uri( f"localhost:{actual_port}", connection_token="py-tcp-shared-test-token" ) ) diff --git a/python/e2e/test_pending_work_resume_e2e.py b/python/e2e/test_pending_work_resume_e2e.py index ff61fc020..38c071d09 100644 --- a/python/e2e/test_pending_work_resume_e2e.py +++ b/python/e2e/test_pending_work_resume_e2e.py @@ -37,9 +37,9 @@ def _make_subprocess_client(ctx: E2ETestContext, *, use_stdio: bool = True) -> C "fake-token-for-e2e-tests" if os.environ.get("GITHUB_ACTIONS") == "true" else None ) if use_stdio: - connection = RuntimeConnection.stdio(path=ctx.cli_path) + connection = RuntimeConnection.for_stdio(path=ctx.cli_path) else: - connection = RuntimeConnection.tcp( + connection = RuntimeConnection.for_tcp( path=ctx.cli_path, connection_token="py-tcp-shared-test-token" ) return CopilotClient( @@ -157,7 +157,7 @@ def original_tool_handler(args): suspended_client = CopilotClient( CopilotClientOptions( - connection=RuntimeConnection.uri( + connection=RuntimeConnection.for_uri( cli_url, connection_token="py-tcp-shared-test-token" ) ) @@ -187,7 +187,7 @@ def resumed_tool_handler(args): resumed_client = CopilotClient( CopilotClientOptions( - connection=RuntimeConnection.uri( + connection=RuntimeConnection.for_uri( cli_url, connection_token="py-tcp-shared-test-token" ) ) @@ -246,7 +246,7 @@ async def blocking_external_tool(args): suspended_client = CopilotClient( CopilotClientOptions( - connection=RuntimeConnection.uri( + connection=RuntimeConnection.for_uri( cli_url, connection_token="py-tcp-shared-test-token" ) ) @@ -271,7 +271,7 @@ async def blocking_external_tool(args): resumed_client = CopilotClient( CopilotClientOptions( - connection=RuntimeConnection.uri( + connection=RuntimeConnection.for_uri( cli_url, connection_token="py-tcp-shared-test-token" ) ) @@ -330,7 +330,7 @@ async def tool_b(args): suspended_client = CopilotClient( CopilotClientOptions( - connection=RuntimeConnection.uri( + connection=RuntimeConnection.for_uri( cli_url, connection_token="py-tcp-shared-test-token" ) ) @@ -365,7 +365,7 @@ async def tool_b(args): resumed_client = CopilotClient( CopilotClientOptions( - connection=RuntimeConnection.uri( + connection=RuntimeConnection.for_uri( cli_url, connection_token="py-tcp-shared-test-token" ) ) @@ -413,7 +413,7 @@ async def test_should_resume_successfully_when_no_pending_work_exists( first_client = CopilotClient( CopilotClientOptions( - connection=RuntimeConnection.uri( + connection=RuntimeConnection.for_uri( cli_url, connection_token="py-tcp-shared-test-token" ) ) @@ -433,7 +433,7 @@ async def test_should_resume_successfully_when_no_pending_work_exists( resumed_client = CopilotClient( CopilotClientOptions( - connection=RuntimeConnection.uri( + connection=RuntimeConnection.for_uri( cli_url, connection_token="py-tcp-shared-test-token" ) ) @@ -478,7 +478,7 @@ async def blocking_external_tool(args): suspended_client = CopilotClient( CopilotClientOptions( - connection=RuntimeConnection.uri( + connection=RuntimeConnection.for_uri( cli_url, connection_token="py-tcp-shared-test-token" ) ) @@ -503,7 +503,7 @@ async def blocking_external_tool(args): resumed_client = CopilotClient( CopilotClientOptions( - connection=RuntimeConnection.uri( + connection=RuntimeConnection.for_uri( cli_url, connection_token="py-tcp-shared-test-token" ) ) @@ -559,7 +559,7 @@ async def test_should_report_continuependingwork_true_in_resume_event( first_client = CopilotClient( CopilotClientOptions( - connection=RuntimeConnection.uri( + connection=RuntimeConnection.for_uri( cli_url, connection_token="py-tcp-shared-test-token" ) ) @@ -580,7 +580,7 @@ async def test_should_report_continuependingwork_true_in_resume_event( resumed_client = CopilotClient( CopilotClientOptions( - connection=RuntimeConnection.uri( + connection=RuntimeConnection.for_uri( cli_url, connection_token="py-tcp-shared-test-token" ) ) diff --git a/python/e2e/test_per_session_auth_e2e.py b/python/e2e/test_per_session_auth_e2e.py index 15695d831..092cb8642 100644 --- a/python/e2e/test_per_session_auth_e2e.py +++ b/python/e2e/test_per_session_auth_e2e.py @@ -98,7 +98,7 @@ async def test_should_return_unauthenticated_when_no_token_provided( env["COPILOT_DEBUG_GITHUB_API_URL"] = auth_ctx.proxy_url no_token_client = CopilotClient( CopilotClientOptions( - connection=RuntimeConnection.stdio(path=auth_ctx.cli_path), + connection=RuntimeConnection.for_stdio(path=auth_ctx.cli_path), working_directory=auth_ctx.work_dir, env=env, use_logged_in_user=False, diff --git a/python/e2e/test_rpc_e2e.py b/python/e2e/test_rpc_e2e.py index 71c5b4f56..837574526 100644 --- a/python/e2e/test_rpc_e2e.py +++ b/python/e2e/test_rpc_e2e.py @@ -16,7 +16,7 @@ class TestRpc: async def test_should_call_rpc_ping_with_typed_params(self): """Test calling rpc.ping with typed params and result""" client = CopilotClient( - CopilotClientOptions(connection=RuntimeConnection.stdio(path=CLI_PATH)) + CopilotClientOptions(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) ) try: @@ -34,7 +34,7 @@ async def test_should_call_rpc_ping_with_typed_params(self): async def test_should_call_rpc_models_list(self): """Test calling rpc.models.list with typed result""" client = CopilotClient( - CopilotClientOptions(connection=RuntimeConnection.stdio(path=CLI_PATH)) + CopilotClientOptions(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) ) try: @@ -59,7 +59,7 @@ async def test_should_call_rpc_models_list(self): async def test_should_call_rpc_account_get_quota(self): """Test calling rpc.account.getQuota when authenticated""" client = CopilotClient( - CopilotClientOptions(connection=RuntimeConnection.stdio(path=CLI_PATH)) + CopilotClientOptions(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) ) try: @@ -122,7 +122,7 @@ async def test_get_and_set_session_mode(self): from copilot.generated.rpc import ModeSetRequest, SessionMode client = CopilotClient( - CopilotClientOptions(connection=RuntimeConnection.stdio(path=CLI_PATH)) + CopilotClientOptions(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) ) try: @@ -156,7 +156,7 @@ async def test_read_update_and_delete_plan(self): from copilot.generated.rpc import PlanUpdateRequest client = CopilotClient( - CopilotClientOptions(connection=RuntimeConnection.stdio(path=CLI_PATH)) + CopilotClientOptions(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) ) try: @@ -201,7 +201,7 @@ async def test_create_list_and_read_workspace_files(self): ) client = CopilotClient( - CopilotClientOptions(connection=RuntimeConnection.stdio(path=CLI_PATH)) + CopilotClientOptions(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) ) try: diff --git a/python/e2e/test_rpc_server_e2e.py b/python/e2e/test_rpc_server_e2e.py index 742f1eb84..6180d754a 100644 --- a/python/e2e/test_rpc_server_e2e.py +++ b/python/e2e/test_rpc_server_e2e.py @@ -56,7 +56,7 @@ def _make_authed_client(ctx: E2ETestContext, token: str) -> CopilotClient: env["COPILOT_DEBUG_GITHUB_API_URL"] = ctx.proxy_url return CopilotClient( CopilotClientOptions( - connection=RuntimeConnection.stdio(path=ctx.cli_path), + connection=RuntimeConnection.for_stdio(path=ctx.cli_path), working_directory=ctx.work_dir, env=env, github_token=token, diff --git a/python/e2e/test_session_e2e.py b/python/e2e/test_session_e2e.py index 84eb95d16..fffdedf7a 100644 --- a/python/e2e/test_session_e2e.py +++ b/python/e2e/test_session_e2e.py @@ -244,7 +244,7 @@ async def test_should_resume_a_session_using_a_new_client(self, ctx: E2ETestCont ) new_client = CopilotClient( CopilotClientOptions( - connection=RuntimeConnection.stdio(path=ctx.cli_path), + connection=RuntimeConnection.for_stdio(path=ctx.cli_path), working_directory=ctx.work_dir, env=ctx.get_env(), github_token=github_token, diff --git a/python/e2e/test_session_fs_e2e.py b/python/e2e/test_session_fs_e2e.py index e066d5160..b4afb2226 100644 --- a/python/e2e/test_session_fs_e2e.py +++ b/python/e2e/test_session_fs_e2e.py @@ -51,7 +51,7 @@ async def session_fs_client(ctx: E2ETestContext): client = CopilotClient( CopilotClientOptions( - connection=RuntimeConnection.stdio(path=ctx.cli_path), + connection=RuntimeConnection.for_stdio(path=ctx.cli_path), working_directory=ctx.work_dir, env=ctx.get_env(), github_token=DEFAULT_GITHUB_TOKEN, @@ -123,7 +123,7 @@ async def test_should_load_session_data_from_fs_provider_on_resume( async def test_should_reject_setprovider_when_sessions_already_exist(self, ctx: E2ETestContext): client1 = CopilotClient( CopilotClientOptions( - connection=RuntimeConnection.tcp(path=ctx.cli_path), + connection=RuntimeConnection.for_tcp(path=ctx.cli_path), working_directory=ctx.work_dir, env=ctx.get_env(), github_token=DEFAULT_GITHUB_TOKEN, @@ -141,7 +141,7 @@ async def test_should_reject_setprovider_when_sessions_already_exist(self, ctx: client2 = CopilotClient( CopilotClientOptions( - connection=RuntimeConnection.uri(f"localhost:{actual_port}"), + connection=RuntimeConnection.for_uri(f"localhost:{actual_port}"), session_fs=SESSION_FS_CONFIG, ) ) diff --git a/python/e2e/test_session_fs_sqlite_e2e.py b/python/e2e/test_session_fs_sqlite_e2e.py index 0bd9a2216..df89939b6 100644 --- a/python/e2e/test_session_fs_sqlite_e2e.py +++ b/python/e2e/test_session_fs_sqlite_e2e.py @@ -200,7 +200,7 @@ def factory(session): async def sqlite_client(ctx: E2ETestContext): client = CopilotClient( CopilotClientOptions( - connection=RuntimeConnection.stdio(path=ctx.cli_path), + connection=RuntimeConnection.for_stdio(path=ctx.cli_path), working_directory=ctx.work_dir, env=ctx.get_env(), github_token=DEFAULT_GITHUB_TOKEN, diff --git a/python/e2e/test_streaming_fidelity_e2e.py b/python/e2e/test_streaming_fidelity_e2e.py index 88b9670d5..b389eeb45 100644 --- a/python/e2e/test_streaming_fidelity_e2e.py +++ b/python/e2e/test_streaming_fidelity_e2e.py @@ -79,7 +79,7 @@ async def test_should_produce_deltas_after_session_resume(self, ctx: E2ETestCont ) new_client = CopilotClient( CopilotClientOptions( - connection=RuntimeConnection.stdio(path=ctx.cli_path), + connection=RuntimeConnection.for_stdio(path=ctx.cli_path), working_directory=ctx.work_dir, env=ctx.get_env(), github_token=github_token, @@ -131,7 +131,7 @@ async def test_should_not_produce_deltas_after_session_resume_with_streaming_dis # Resume with streaming disabled new_client = CopilotClient( CopilotClientOptions( - connection=RuntimeConnection.stdio(path=ctx.cli_path), + connection=RuntimeConnection.for_stdio(path=ctx.cli_path), working_directory=ctx.work_dir, env=ctx.get_env(), github_token=github_token, diff --git a/python/e2e/test_subagent_hooks_e2e.py b/python/e2e/test_subagent_hooks_e2e.py index f501663ec..434ed704b 100644 --- a/python/e2e/test_subagent_hooks_e2e.py +++ b/python/e2e/test_subagent_hooks_e2e.py @@ -51,7 +51,7 @@ async def on_post_tool_use(input_data, invocation): ) client = CopilotClient( CopilotClientOptions( - connection=RuntimeConnection.stdio(path=ctx.cli_path), + connection=RuntimeConnection.for_stdio(path=ctx.cli_path), working_directory=ctx.work_dir, env=env, github_token=github_token, diff --git a/python/e2e/test_suspend_e2e.py b/python/e2e/test_suspend_e2e.py index ba7c37cbf..2aa16bba3 100644 --- a/python/e2e/test_suspend_e2e.py +++ b/python/e2e/test_suspend_e2e.py @@ -31,9 +31,9 @@ def _make_subprocess_client(ctx: E2ETestContext, *, use_stdio: bool = True) -> C "fake-token-for-e2e-tests" if os.environ.get("GITHUB_ACTIONS") == "true" else None ) if use_stdio: - connection = RuntimeConnection.stdio(path=ctx.cli_path) + connection = RuntimeConnection.for_stdio(path=ctx.cli_path) else: - connection = RuntimeConnection.tcp( + connection = RuntimeConnection.for_tcp( path=ctx.cli_path, connection_token="py-tcp-shared-test-token" ) return CopilotClient( @@ -108,7 +108,7 @@ async def test_should_allow_resume_and_continue_conversation_after_suspend( first_client = CopilotClient( CopilotClientOptions( - connection=RuntimeConnection.uri( + connection=RuntimeConnection.for_uri( cli_url, connection_token="py-tcp-shared-test-token" ) ) @@ -129,7 +129,7 @@ async def test_should_allow_resume_and_continue_conversation_after_suspend( resumed_client = CopilotClient( CopilotClientOptions( - connection=RuntimeConnection.uri( + connection=RuntimeConnection.for_uri( cli_url, connection_token="py-tcp-shared-test-token" ) ) diff --git a/python/e2e/test_telemetry_e2e.py b/python/e2e/test_telemetry_e2e.py index ddb17c899..8d37beb90 100644 --- a/python/e2e/test_telemetry_e2e.py +++ b/python/e2e/test_telemetry_e2e.py @@ -82,7 +82,7 @@ def echo(invocation: ToolInvocation) -> ToolResult: ) client = CopilotClient( CopilotClientOptions( - connection=RuntimeConnection.stdio(path=ctx.cli_path), + connection=RuntimeConnection.for_stdio(path=ctx.cli_path), working_directory=ctx.work_dir, env=ctx.get_env(), github_token=github_token, @@ -212,7 +212,7 @@ class TestSubprocessConfigTelemetry: """Mirrors CopilotClientOptions_Telemetry_DefaultsToNull.""" async def test_telemetry_defaults_to_none(self): - config = CopilotClientOptions(connection=RuntimeConnection.stdio()) + config = CopilotClientOptions(connection=RuntimeConnection.for_stdio()) assert config.telemetry is None # NOTE: CopilotClientOptions_Clone_CopiesTelemetry from the C# baseline has diff --git a/python/e2e/test_ui_elicitation_multi_client_e2e.py b/python/e2e/test_ui_elicitation_multi_client_e2e.py index 8d8934d6e..e683a63ac 100644 --- a/python/e2e/test_ui_elicitation_multi_client_e2e.py +++ b/python/e2e/test_ui_elicitation_multi_client_e2e.py @@ -63,7 +63,7 @@ async def setup(self): # Client 1 uses TCP mode so additional clients can connect self._client1 = CopilotClient( CopilotClientOptions( - connection=RuntimeConnection.tcp( + connection=RuntimeConnection.for_tcp( path=self.cli_path, connection_token="py-tcp-shared-test-token" ), working_directory=self.work_dir, @@ -83,7 +83,7 @@ async def setup(self): self._client2 = CopilotClient( CopilotClientOptions( - connection=RuntimeConnection.uri( + connection=RuntimeConnection.for_uri( f"localhost:{self._actual_port}", connection_token="py-tcp-shared-test-token" ) ) @@ -140,7 +140,7 @@ def make_external_client(self) -> CopilotClient: assert self._actual_port is not None return CopilotClient( CopilotClientOptions( - connection=RuntimeConnection.uri( + connection=RuntimeConnection.for_uri( f"localhost:{self._actual_port}", connection_token="py-tcp-shared-test-token" ) ) diff --git a/python/e2e/testharness/context.py b/python/e2e/testharness/context.py index f44e89f6c..bf1d8cfe9 100644 --- a/python/e2e/testharness/context.py +++ b/python/e2e/testharness/context.py @@ -80,7 +80,7 @@ async def setup(self, cli_args: list[str] | None = None): # Create the shared client (like Node.js/Go do) self._client = CopilotClient( CopilotClientOptions( - connection=RuntimeConnection.stdio( + connection=RuntimeConnection.for_stdio( path=self.cli_path, args=tuple(cli_args or []), ), diff --git a/python/test_client.py b/python/test_client.py index bcf1e9d84..29e1265f1 100644 --- a/python/test_client.py +++ b/python/test_client.py @@ -31,7 +31,7 @@ class TestPermissionHandlerOptional: @pytest.mark.asyncio async def test_create_session_allows_missing_permission_handler(self): client = CopilotClient( - CopilotClientOptions(connection=RuntimeConnection.stdio(path=CLI_PATH)) + CopilotClientOptions(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) ) await client.start() try: @@ -43,7 +43,7 @@ async def test_create_session_allows_missing_permission_handler(self): @pytest.mark.asyncio async def test_create_session_allows_none_permission_handler(self): client = CopilotClient( - CopilotClientOptions(connection=RuntimeConnection.stdio(path=CLI_PATH)) + CopilotClientOptions(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) ) await client.start() try: @@ -55,7 +55,7 @@ async def test_create_session_allows_none_permission_handler(self): @pytest.mark.asyncio async def test_v2_permission_adapter_rejects_no_result(self): client = CopilotClient( - CopilotClientOptions(connection=RuntimeConnection.stdio(path=CLI_PATH)) + CopilotClientOptions(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) ) await client.start() try: @@ -81,7 +81,7 @@ async def test_v2_permission_adapter_rejects_no_result(self): @pytest.mark.asyncio async def test_resume_session_allows_none_permission_handler(self): client = CopilotClient( - CopilotClientOptions(connection=RuntimeConnection.stdio(path=CLI_PATH)) + CopilotClientOptions(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) ) await client.start() try: @@ -98,7 +98,7 @@ class TestCreateSessionConfig: @pytest.mark.asyncio async def test_create_session_forwards_cloud_options(self): client = CopilotClient( - CopilotClientOptions(connection=RuntimeConnection.stdio(path=CLI_PATH)) + CopilotClientOptions(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) ) await client.start() try: @@ -135,14 +135,14 @@ async def mock_request(method, params): class TestURLParsing: def test_parse_port_only_url(self): - client = CopilotClient(CopilotClientOptions(connection=RuntimeConnection.uri("8080"))) + client = CopilotClient(CopilotClientOptions(connection=RuntimeConnection.for_uri("8080"))) assert client._runtime_port == 8080 assert client._actual_host == "localhost" assert client._is_external_server def test_parse_host_port_url(self): client = CopilotClient( - CopilotClientOptions(connection=RuntimeConnection.uri("127.0.0.1:9000")) + CopilotClientOptions(connection=RuntimeConnection.for_uri("127.0.0.1:9000")) ) assert client._runtime_port == 9000 assert client._actual_host == "127.0.0.1" @@ -150,7 +150,7 @@ def test_parse_host_port_url(self): def test_parse_http_url(self): client = CopilotClient( - CopilotClientOptions(connection=RuntimeConnection.uri("http://localhost:7000")) + CopilotClientOptions(connection=RuntimeConnection.for_uri("http://localhost:7000")) ) assert client._runtime_port == 7000 assert client._actual_host == "localhost" @@ -158,7 +158,7 @@ def test_parse_http_url(self): def test_parse_https_url(self): client = CopilotClient( - CopilotClientOptions(connection=RuntimeConnection.uri("https://example.com:443")) + CopilotClientOptions(connection=RuntimeConnection.for_uri("https://example.com:443")) ) assert client._runtime_port == 443 assert client._actual_host == "example.com" @@ -166,23 +166,27 @@ def test_parse_https_url(self): def test_invalid_url_format(self): with pytest.raises(ValueError, match="Invalid cli_url format"): - CopilotClient(CopilotClientOptions(connection=RuntimeConnection.uri("invalid-url"))) + CopilotClient(CopilotClientOptions(connection=RuntimeConnection.for_uri("invalid-url"))) def test_invalid_port_too_high(self): with pytest.raises(ValueError, match="Invalid port in cli_url"): - CopilotClient(CopilotClientOptions(connection=RuntimeConnection.uri("localhost:99999"))) + CopilotClient( + CopilotClientOptions(connection=RuntimeConnection.for_uri("localhost:99999")) + ) def test_invalid_port_zero(self): with pytest.raises(ValueError, match="Invalid port in cli_url"): - CopilotClient(CopilotClientOptions(connection=RuntimeConnection.uri("localhost:0"))) + CopilotClient(CopilotClientOptions(connection=RuntimeConnection.for_uri("localhost:0"))) def test_invalid_port_negative(self): with pytest.raises(ValueError, match="Invalid port in cli_url"): - CopilotClient(CopilotClientOptions(connection=RuntimeConnection.uri("localhost:-1"))) + CopilotClient( + CopilotClientOptions(connection=RuntimeConnection.for_uri("localhost:-1")) + ) def test_is_external_server_true(self): client = CopilotClient( - CopilotClientOptions(connection=RuntimeConnection.uri("localhost:8080")) + CopilotClientOptions(connection=RuntimeConnection.for_uri("localhost:8080")) ) assert client._is_external_server @@ -192,7 +196,7 @@ def test_missing_initial_cwd(self): with pytest.raises(ValueError, match="session_fs.initial_working_directory is required"): CopilotClient( CopilotClientOptions( - connection=RuntimeConnection.stdio(path=CLI_PATH), + connection=RuntimeConnection.for_stdio(path=CLI_PATH), log_level="error", session_fs={ "initial_working_directory": "", @@ -206,7 +210,7 @@ def test_missing_session_state_path(self): with pytest.raises(ValueError, match="session_fs.session_state_path is required"): CopilotClient( CopilotClientOptions( - connection=RuntimeConnection.stdio(path=CLI_PATH), + connection=RuntimeConnection.for_stdio(path=CLI_PATH), log_level="error", session_fs={ "initial_working_directory": "/", @@ -221,7 +225,7 @@ class TestAuthOptions: def test_accepts_github_token(self): client = CopilotClient( CopilotClientOptions( - connection=RuntimeConnection.stdio(path=CLI_PATH), + connection=RuntimeConnection.for_stdio(path=CLI_PATH), github_token="gho_test_token", log_level="error", ) @@ -232,7 +236,7 @@ def test_accepts_github_token(self): def test_default_use_logged_in_user_true_without_token(self): client = CopilotClient( CopilotClientOptions( - connection=RuntimeConnection.stdio(path=CLI_PATH), log_level="error" + connection=RuntimeConnection.for_stdio(path=CLI_PATH), log_level="error" ) ) assert isinstance(client._options.connection, StdioRuntimeConnection) @@ -241,7 +245,7 @@ def test_default_use_logged_in_user_true_without_token(self): def test_default_use_logged_in_user_false_with_token(self): client = CopilotClient( CopilotClientOptions( - connection=RuntimeConnection.stdio(path=CLI_PATH), + connection=RuntimeConnection.for_stdio(path=CLI_PATH), github_token="gho_test_token", log_level="error", ) @@ -252,7 +256,7 @@ def test_default_use_logged_in_user_false_with_token(self): def test_explicit_use_logged_in_user_true_with_token(self): client = CopilotClient( CopilotClientOptions( - connection=RuntimeConnection.stdio(path=CLI_PATH), + connection=RuntimeConnection.for_stdio(path=CLI_PATH), github_token="gho_test_token", use_logged_in_user=True, log_level="error", @@ -264,7 +268,7 @@ def test_explicit_use_logged_in_user_true_with_token(self): def test_explicit_use_logged_in_user_false_without_token(self): client = CopilotClient( CopilotClientOptions( - connection=RuntimeConnection.stdio(path=CLI_PATH), + connection=RuntimeConnection.for_stdio(path=CLI_PATH), use_logged_in_user=False, log_level="error", ) @@ -277,7 +281,7 @@ class TestSessionIdleTimeoutSeconds: def test_accepts_session_idle_timeout_seconds(self): client = CopilotClient( CopilotClientOptions( - connection=RuntimeConnection.stdio(path=CLI_PATH), + connection=RuntimeConnection.for_stdio(path=CLI_PATH), session_idle_timeout_seconds=600, log_level="error", ) @@ -288,7 +292,7 @@ def test_accepts_session_idle_timeout_seconds(self): def test_default_session_idle_timeout_seconds_is_none(self): client = CopilotClient( CopilotClientOptions( - connection=RuntimeConnection.stdio(path=CLI_PATH), log_level="error" + connection=RuntimeConnection.for_stdio(path=CLI_PATH), log_level="error" ) ) assert isinstance(client._options.connection, StdioRuntimeConnection) @@ -299,7 +303,7 @@ class TestCopilotHome: def test_accepts_copilot_home(self): client = CopilotClient( CopilotClientOptions( - connection=RuntimeConnection.stdio(path=CLI_PATH), + connection=RuntimeConnection.for_stdio(path=CLI_PATH), base_directory="/custom/copilot/home", log_level="error", ) @@ -310,7 +314,7 @@ def test_accepts_copilot_home(self): def test_default_copilot_home_is_none(self): client = CopilotClient( CopilotClientOptions( - connection=RuntimeConnection.stdio(path=CLI_PATH), log_level="error" + connection=RuntimeConnection.for_stdio(path=CLI_PATH), log_level="error" ) ) assert isinstance(client._options.connection, StdioRuntimeConnection) @@ -321,7 +325,7 @@ class TestOverridesBuiltInTool: @pytest.mark.asyncio async def test_overrides_built_in_tool_sent_in_tool_definition(self): client = CopilotClient( - CopilotClientOptions(connection=RuntimeConnection.stdio(path=CLI_PATH)) + CopilotClientOptions(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) ) await client.start() @@ -352,7 +356,7 @@ def grep(params) -> str: @pytest.mark.asyncio async def test_resume_session_sends_overrides_built_in_tool(self): client = CopilotClient( - CopilotClientOptions(connection=RuntimeConnection.stdio(path=CLI_PATH)) + CopilotClientOptions(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) ) await client.start() @@ -391,7 +395,7 @@ class TestInstructionDirectories: @pytest.mark.asyncio async def test_create_session_sends_instruction_directories(self): client = CopilotClient( - CopilotClientOptions(connection=RuntimeConnection.stdio(path=CLI_PATH)) + CopilotClientOptions(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) ) await client.start() @@ -421,7 +425,7 @@ async def mock_request(method, params): @pytest.mark.asyncio async def test_resume_session_sends_instruction_directories(self): client = CopilotClient( - CopilotClientOptions(connection=RuntimeConnection.stdio(path=CLI_PATH)) + CopilotClientOptions(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) ) await client.start() @@ -471,7 +475,7 @@ def handler(): return custom_models client = CopilotClient( - CopilotClientOptions(connection=RuntimeConnection.stdio(path=CLI_PATH)), + CopilotClientOptions(connection=RuntimeConnection.for_stdio(path=CLI_PATH)), on_list_models=handler, ) await client.start() @@ -503,7 +507,7 @@ def handler(): return custom_models client = CopilotClient( - CopilotClientOptions(connection=RuntimeConnection.stdio(path=CLI_PATH)), + CopilotClientOptions(connection=RuntimeConnection.for_stdio(path=CLI_PATH)), on_list_models=handler, ) await client.start() @@ -532,7 +536,7 @@ async def handler(): return custom_models client = CopilotClient( - CopilotClientOptions(connection=RuntimeConnection.stdio(path=CLI_PATH)), + CopilotClientOptions(connection=RuntimeConnection.for_stdio(path=CLI_PATH)), on_list_models=handler, ) await client.start() @@ -563,7 +567,7 @@ def handler(): return custom_models client = CopilotClient( - CopilotClientOptions(connection=RuntimeConnection.stdio(path=CLI_PATH)), + CopilotClientOptions(connection=RuntimeConnection.for_stdio(path=CLI_PATH)), on_list_models=handler, ) models = await client.list_models() @@ -575,7 +579,7 @@ class TestSessionConfigForwarding: @pytest.mark.asyncio async def test_create_session_forwards_client_name(self): client = CopilotClient( - CopilotClientOptions(connection=RuntimeConnection.stdio(path=CLI_PATH)) + CopilotClientOptions(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) ) await client.start() @@ -598,7 +602,7 @@ async def mock_request(method, params): @pytest.mark.asyncio async def test_resume_session_forwards_client_name(self): client = CopilotClient( - CopilotClientOptions(connection=RuntimeConnection.stdio(path=CLI_PATH)) + CopilotClientOptions(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) ) await client.start() @@ -630,7 +634,7 @@ async def mock_request(method, params): @pytest.mark.asyncio async def test_create_session_forwards_enable_session_telemetry(self): client = CopilotClient( - CopilotClientOptions(connection=RuntimeConnection.stdio(path=CLI_PATH)) + CopilotClientOptions(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) ) await client.start() @@ -654,7 +658,7 @@ async def mock_request(method, params): @pytest.mark.asyncio async def test_resume_session_forwards_enable_session_telemetry(self): client = CopilotClient( - CopilotClientOptions(connection=RuntimeConnection.stdio(path=CLI_PATH)) + CopilotClientOptions(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) ) await client.start() @@ -685,7 +689,7 @@ async def mock_request(method, params): @pytest.mark.asyncio async def test_create_session_forwards_provider_headers(self): client = CopilotClient( - CopilotClientOptions(connection=RuntimeConnection.stdio(path=CLI_PATH)) + CopilotClientOptions(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) ) await client.start() @@ -725,7 +729,7 @@ async def mock_request(method, params): @pytest.mark.asyncio async def test_resume_session_forwards_provider_headers(self): client = CopilotClient( - CopilotClientOptions(connection=RuntimeConnection.stdio(path=CLI_PATH)) + CopilotClientOptions(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) ) await client.start() @@ -770,7 +774,7 @@ async def mock_request(method, params): @pytest.mark.asyncio async def test_session_send_forwards_request_headers(self): client = CopilotClient( - CopilotClientOptions(connection=RuntimeConnection.stdio(path=CLI_PATH)) + CopilotClientOptions(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) ) await client.start() @@ -804,7 +808,7 @@ async def mock_request(method, params): @pytest.mark.asyncio async def test_create_session_forwards_agent(self): client = CopilotClient( - CopilotClientOptions(connection=RuntimeConnection.stdio(path=CLI_PATH)) + CopilotClientOptions(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) ) await client.start() @@ -829,7 +833,7 @@ async def mock_request(method, params): @pytest.mark.asyncio async def test_resume_session_forwards_agent(self): client = CopilotClient( - CopilotClientOptions(connection=RuntimeConnection.stdio(path=CLI_PATH)) + CopilotClientOptions(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) ) await client.start() @@ -861,7 +865,7 @@ async def mock_request(method, params): @pytest.mark.asyncio async def test_create_session_defaults_include_sub_agent_streaming_events_to_true(self): client = CopilotClient( - CopilotClientOptions(connection=RuntimeConnection.stdio(path=CLI_PATH)) + CopilotClientOptions(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) ) await client.start() @@ -886,7 +890,7 @@ async def test_create_session_preserves_explicit_false_include_sub_agent_streami self, ): client = CopilotClient( - CopilotClientOptions(connection=RuntimeConnection.stdio(path=CLI_PATH)) + CopilotClientOptions(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) ) await client.start() @@ -910,7 +914,7 @@ async def mock_request(method, params): @pytest.mark.asyncio async def test_resume_session_defaults_include_sub_agent_streaming_events_to_true(self): client = CopilotClient( - CopilotClientOptions(connection=RuntimeConnection.stdio(path=CLI_PATH)) + CopilotClientOptions(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) ) await client.start() @@ -942,7 +946,7 @@ async def test_resume_session_preserves_explicit_false_include_sub_agent_streami self, ): client = CopilotClient( - CopilotClientOptions(connection=RuntimeConnection.stdio(path=CLI_PATH)) + CopilotClientOptions(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) ) await client.start() @@ -973,7 +977,7 @@ async def mock_request(method, params): @pytest.mark.asyncio async def test_resume_session_forwards_continue_pending_work(self): client = CopilotClient( - CopilotClientOptions(connection=RuntimeConnection.stdio(path=CLI_PATH)) + CopilotClientOptions(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) ) await client.start() @@ -1004,7 +1008,7 @@ async def mock_request(method, params): @pytest.mark.asyncio async def test_resume_session_omits_continue_pending_work_by_default(self): client = CopilotClient( - CopilotClientOptions(connection=RuntimeConnection.stdio(path=CLI_PATH)) + CopilotClientOptions(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) ) await client.start() @@ -1034,7 +1038,7 @@ async def mock_request(method, params): @pytest.mark.asyncio async def test_set_model_sends_correct_rpc(self): client = CopilotClient( - CopilotClientOptions(connection=RuntimeConnection.stdio(path=CLI_PATH)) + CopilotClientOptions(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) ) await client.start() @@ -1064,7 +1068,7 @@ class TestCopilotClientContextManager: @pytest.mark.asyncio async def test_aenter_calls_start_and_returns_self(self): client = CopilotClient( - CopilotClientOptions(connection=RuntimeConnection.stdio(path=CLI_PATH)) + CopilotClientOptions(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) ) with patch.object(client, "start", new_callable=AsyncMock) as mock_start: result = await client.__aenter__() @@ -1074,7 +1078,7 @@ async def test_aenter_calls_start_and_returns_self(self): @pytest.mark.asyncio async def test_aexit_calls_stop(self): client = CopilotClient( - CopilotClientOptions(connection=RuntimeConnection.stdio(path=CLI_PATH)) + CopilotClientOptions(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) ) with patch.object(client, "stop", new_callable=AsyncMock) as mock_stop: await client.__aexit__(None, None, None) diff --git a/python/test_commands_and_elicitation.py b/python/test_commands_and_elicitation.py index a8eb18197..432bbf9b1 100644 --- a/python/test_commands_and_elicitation.py +++ b/python/test_commands_and_elicitation.py @@ -50,7 +50,7 @@ class TestCommands: async def test_forwards_commands_in_session_create_rpc(self): """Verifies that commands (name + description) are serialized in session.create payload.""" client = CopilotClient( - CopilotClientOptions(connection=RuntimeConnection.stdio(path=CLI_PATH)) + CopilotClientOptions(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) ) await client.start() @@ -91,7 +91,7 @@ async def mock_request(method, params): async def test_forwards_commands_in_session_resume_rpc(self): """Verifies that commands are serialized in session.resume payload.""" client = CopilotClient( - CopilotClientOptions(connection=RuntimeConnection.stdio(path=CLI_PATH)) + CopilotClientOptions(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) ) await client.start() @@ -131,7 +131,7 @@ async def mock_request(method, params): async def test_routes_command_execute_event_to_correct_handler(self): """Verifies the command dispatch works for command.execute events.""" client = CopilotClient( - CopilotClientOptions(connection=RuntimeConnection.stdio(path=CLI_PATH)) + CopilotClientOptions(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) ) await client.start() @@ -204,7 +204,7 @@ async def mock_request(method, params): async def test_sends_error_when_command_handler_throws(self): """Verifies error is sent via RPC when a command handler raises.""" client = CopilotClient( - CopilotClientOptions(connection=RuntimeConnection.stdio(path=CLI_PATH)) + CopilotClientOptions(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) ) await client.start() @@ -264,7 +264,7 @@ async def mock_request(method, params): async def test_sends_error_for_unknown_command(self): """Verifies error is sent via RPC for an unrecognized command.""" client = CopilotClient( - CopilotClientOptions(connection=RuntimeConnection.stdio(path=CLI_PATH)) + CopilotClientOptions(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) ) await client.start() @@ -327,7 +327,7 @@ class TestUiElicitation: async def test_reads_capabilities_from_session_create_response(self): """Verifies capabilities are parsed from session.create response.""" client = CopilotClient( - CopilotClientOptions(connection=RuntimeConnection.stdio(path=CLI_PATH)) + CopilotClientOptions(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) ) await client.start() @@ -353,7 +353,7 @@ async def mock_request(method, params): async def test_defaults_capabilities_when_not_injected(self): """Verifies capabilities default to empty when server returns none.""" client = CopilotClient( - CopilotClientOptions(connection=RuntimeConnection.stdio(path=CLI_PATH)) + CopilotClientOptions(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) ) await client.start() @@ -372,7 +372,7 @@ async def test_defaults_capabilities_when_not_injected(self): async def test_elicitation_throws_when_capability_is_missing(self): """Verifies that UI methods throw when elicitation is not supported.""" client = CopilotClient( - CopilotClientOptions(connection=RuntimeConnection.stdio(path=CLI_PATH)) + CopilotClientOptions(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) ) await client.start() @@ -401,7 +401,7 @@ async def test_elicitation_throws_when_capability_is_missing(self): async def test_confirm_throws_when_capability_is_missing(self): """Verifies confirm throws when elicitation is not supported.""" client = CopilotClient( - CopilotClientOptions(connection=RuntimeConnection.stdio(path=CLI_PATH)) + CopilotClientOptions(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) ) await client.start() @@ -427,7 +427,7 @@ class TestOnElicitationContext: async def test_sends_request_elicitation_flag_when_handler_provided(self): """Verifies requestElicitation=true is sent when onElicitationContext is provided.""" client = CopilotClient( - CopilotClientOptions(connection=RuntimeConnection.stdio(path=CLI_PATH)) + CopilotClientOptions(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) ) await client.start() @@ -461,7 +461,7 @@ async def elicitation_handler( async def test_does_not_send_request_elicitation_when_no_handler(self): """Verifies requestElicitation=false when no handler is provided.""" client = CopilotClient( - CopilotClientOptions(connection=RuntimeConnection.stdio(path=CLI_PATH)) + CopilotClientOptions(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) ) await client.start() @@ -491,7 +491,7 @@ async def mock_request(method, params): async def test_sends_mode_callback_flags_when_handlers_provided(self): """Verifies mode callback flags are sent when handlers are provided.""" client = CopilotClient( - CopilotClientOptions(connection=RuntimeConnection.stdio(path=CLI_PATH)) + CopilotClientOptions(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) ) await client.start() @@ -532,7 +532,7 @@ def auto_handler( async def test_sends_mode_callback_flags_on_resume_when_handlers_provided(self): """Verifies mode callback flags are sent on session.resume.""" client = CopilotClient( - CopilotClientOptions(connection=RuntimeConnection.stdio(path=CLI_PATH)) + CopilotClientOptions(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) ) await client.start() @@ -567,7 +567,7 @@ async def mock_request(method, params): async def test_dispatches_mode_callback_requests_to_registered_handlers(self): """Verifies direct mode requests are dispatched to registered handlers.""" client = CopilotClient( - CopilotClientOptions(connection=RuntimeConnection.stdio(path=CLI_PATH)) + CopilotClientOptions(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) ) await client.start() @@ -631,7 +631,7 @@ async def auto_handler( async def test_sends_cancel_when_elicitation_handler_throws(self): """Verifies auto-cancel when the elicitation handler raises.""" client = CopilotClient( - CopilotClientOptions(connection=RuntimeConnection.stdio(path=CLI_PATH)) + CopilotClientOptions(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) ) await client.start() @@ -678,7 +678,7 @@ async def mock_request(method, params): async def test_dispatches_elicitation_requested_event_to_handler(self): """Verifies that an elicitation.requested event dispatches to the handler.""" client = CopilotClient( - CopilotClientOptions(connection=RuntimeConnection.stdio(path=CLI_PATH)) + CopilotClientOptions(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) ) await client.start() @@ -741,7 +741,7 @@ async def mock_request(method, params): async def test_elicitation_handler_receives_full_schema(self): """Verifies that requestedSchema passes type, properties, and required to handler.""" client = CopilotClient( - CopilotClientOptions(connection=RuntimeConnection.stdio(path=CLI_PATH)) + CopilotClientOptions(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) ) await client.start() @@ -819,7 +819,7 @@ class TestCapabilitiesChanged: async def test_capabilities_changed_event_updates_session(self): """Verifies that a capabilities.changed event updates session capabilities.""" client = CopilotClient( - CopilotClientOptions(connection=RuntimeConnection.stdio(path=CLI_PATH)) + CopilotClientOptions(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) ) await client.start() diff --git a/python/test_telemetry.py b/python/test_telemetry.py index 81ead8cee..f123f5e8b 100644 --- a/python/test_telemetry.py +++ b/python/test_telemetry.py @@ -77,7 +77,7 @@ def test_telemetry_config_type(self): def test_telemetry_config_in_subprocess_config(self): """TelemetryConfig can be used in SubprocessConfig.""" config = CopilotClientOptions( - connection=RuntimeConnection.stdio(), + connection=RuntimeConnection.for_stdio(), telemetry={ "otlp_endpoint": "http://localhost:4318", "exporter_type": "otlp-http", 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 0f17d48aa..108ca5345 100644 --- a/test/scenarios/bundling/app-backend-to-server/python/main.py +++ b/test/scenarios/bundling/app-backend-to-server/python/main.py @@ -14,7 +14,7 @@ async def ask_copilot(prompt: str) -> str: - client = CopilotClient(CopilotClientOptions(connection=RuntimeConnection.uri(CLI_URL))) + client = CopilotClient(CopilotClientOptions(connection=RuntimeConnection.for_uri(CLI_URL))) try: session = await client.create_session(model="claude-haiku-4.5") diff --git a/test/scenarios/bundling/app-direct-server/python/main.py b/test/scenarios/bundling/app-direct-server/python/main.py index e16798b07..67ca82978 100644 --- a/test/scenarios/bundling/app-direct-server/python/main.py +++ b/test/scenarios/bundling/app-direct-server/python/main.py @@ -7,7 +7,7 @@ async def main(): client = CopilotClient( CopilotClientOptions( - connection=RuntimeConnection.uri( + connection=RuntimeConnection.for_uri( os.environ.get("COPILOT_CLI_URL", "localhost:3000"), ), ) diff --git a/test/scenarios/bundling/container-proxy/python/main.py b/test/scenarios/bundling/container-proxy/python/main.py index e16798b07..67ca82978 100644 --- a/test/scenarios/bundling/container-proxy/python/main.py +++ b/test/scenarios/bundling/container-proxy/python/main.py @@ -7,7 +7,7 @@ async def main(): client = CopilotClient( CopilotClientOptions( - connection=RuntimeConnection.uri( + connection=RuntimeConnection.for_uri( os.environ.get("COPILOT_CLI_URL", "localhost:3000"), ), ) diff --git a/test/scenarios/transport/reconnect/python/main.py b/test/scenarios/transport/reconnect/python/main.py index 98b4b6d0c..dac6abeab 100644 --- a/test/scenarios/transport/reconnect/python/main.py +++ b/test/scenarios/transport/reconnect/python/main.py @@ -8,7 +8,7 @@ async def main(): client = CopilotClient( CopilotClientOptions( - connection=RuntimeConnection.uri( + connection=RuntimeConnection.for_uri( os.environ.get("COPILOT_CLI_URL", "localhost:3000"), ), ) diff --git a/test/scenarios/transport/tcp/python/main.py b/test/scenarios/transport/tcp/python/main.py index e16798b07..67ca82978 100644 --- a/test/scenarios/transport/tcp/python/main.py +++ b/test/scenarios/transport/tcp/python/main.py @@ -7,7 +7,7 @@ async def main(): client = CopilotClient( CopilotClientOptions( - connection=RuntimeConnection.uri( + connection=RuntimeConnection.for_uri( os.environ.get("COPILOT_CLI_URL", "localhost:3000"), ), ) From 91981ead599968071b0599bbd96fd39c95105775 Mon Sep 17 00:00:00 2001 From: Steve Sanderson Date: Fri, 22 May 2026 13:42:48 +0100 Subject: [PATCH 17/21] Address CodeQL findings: overload bodies + match-to-if-chain - `CopilotClient.on_lifecycle` `@overload` stubs were using `...` as the body. CodeQL flags this as `Statement has no effect`; the rest of the codebase (`tools.py:define_tool`) already uses `pass` for overload bodies. Match the existing convention. - `_session_lifecycle_event_from_dict` used a `match` statement with a `case _:` default that returned in every arm. CodeQL still flagged it as `Explicit returns mixed with implicit (fall through) returns` (false positive on `match` analysis). Restructure as an if/return chain with a tail return, which CodeQL handles correctly. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- python/copilot/client.py | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/python/copilot/client.py b/python/copilot/client.py index fc64d29ed..c91b9eb9e 100644 --- a/python/copilot/client.py +++ b/python/copilot/client.py @@ -962,19 +962,18 @@ def _session_lifecycle_event_from_dict(data: dict) -> SessionLifecycleEvent: if "metadata" in data and data["metadata"]: metadata = SessionLifecycleEventMetadata.from_dict(data["metadata"]) session_id = data.get("sessionId", "") - match data.get("type"): - case "session.created": - return SessionCreatedEvent(session_id=session_id, metadata=metadata) - case "session.deleted": - return SessionDeletedEvent(session_id=session_id, metadata=metadata) - case "session.foreground": - return SessionForegroundEvent(session_id=session_id, metadata=metadata) - case "session.background": - return SessionBackgroundEvent(session_id=session_id, metadata=metadata) - case _: - # Default to ``session.updated`` for unknown event types so consumers - # keep working across server upgrades. - return SessionUpdatedEvent(session_id=session_id, metadata=metadata) + event_type = data.get("type") + if event_type == "session.created": + return SessionCreatedEvent(session_id=session_id, metadata=metadata) + if event_type == "session.deleted": + return SessionDeletedEvent(session_id=session_id, metadata=metadata) + if event_type == "session.foreground": + return SessionForegroundEvent(session_id=session_id, metadata=metadata) + if event_type == "session.background": + return SessionBackgroundEvent(session_id=session_id, metadata=metadata) + # Default to ``session.updated`` for unknown event types so consumers + # keep working across server upgrades. + return SessionUpdatedEvent(session_id=session_id, metadata=metadata) SessionLifecycleHandler = Callable[[SessionLifecycleEvent], None] @@ -2542,12 +2541,14 @@ async def set_foreground_session_id(self, session_id: str) -> None: raise RuntimeError(f"Failed to set foreground session: {error}") @overload - def on_lifecycle(self, handler: SessionLifecycleHandler, /) -> HandlerUnsubcribe: ... + def on_lifecycle(self, handler: SessionLifecycleHandler, /) -> HandlerUnsubcribe: + pass @overload def on_lifecycle( self, event_type: SessionLifecycleEventType, /, handler: SessionLifecycleHandler - ) -> HandlerUnsubcribe: ... + ) -> HandlerUnsubcribe: + pass def on_lifecycle( self, From 53bd275d7fce536f2c5b4e5956b124f67765ac66 Mon Sep 17 00:00:00 2001 From: Steve Sanderson Date: Fri, 22 May 2026 13:49:11 +0100 Subject: [PATCH 18/21] Rename GetStatusResponse.protocolVersion -> protocol_version Phase G missed this hand-written response dataclass (it lives in `client.py` alongside the other RPC response types). The E2E test `test_should_get_status_with_version_and_protocol_info` exposed this: the test was updated to assert `hasattr(status, 'protocol_version')` but the attribute itself still used camelCase, so the assertion failed in CI. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- python/copilot/client.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/python/copilot/client.py b/python/copilot/client.py index c91b9eb9e..2dc24ce89 100644 --- a/python/copilot/client.py +++ b/python/copilot/client.py @@ -410,24 +410,24 @@ class GetStatusResponse: """Response from status.get""" version: str # Package version (e.g., "1.0.0") - protocolVersion: int # Protocol version for SDK compatibility + protocol_version: int # Protocol version for SDK compatibility @staticmethod def from_dict(obj: Any) -> GetStatusResponse: assert isinstance(obj, dict) version = obj.get("version") - protocolVersion = obj.get("protocolVersion") - if version is None or protocolVersion is None: + protocol_version = obj.get("protocolVersion") + if version is None or protocol_version is None: raise ValueError( f"Missing required fields in GetStatusResponse: version={version}, " - f"protocolVersion={protocolVersion}" + f"protocolVersion={protocol_version}" ) - return GetStatusResponse(str(version), int(protocolVersion)) + return GetStatusResponse(str(version), int(protocol_version)) def to_dict(self) -> dict: result: dict = {} result["version"] = self.version - result["protocolVersion"] = self.protocolVersion + result["protocolVersion"] = self.protocol_version return result From 9786c53a68e43aa107088ddc92ed0dfb7ddcb973 Mon Sep 17 00:00:00 2001 From: Steve Sanderson Date: Fri, 22 May 2026 13:59:28 +0100 Subject: [PATCH 19/21] Move on_list_models into options, drop auto_start + get_state MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Align CopilotClient constructor with the TypeScript and .NET SDKs: 1. **Move `on_list_models` into `CopilotClientOptions`** — both TS (`onListModels`) and .NET (`OnListModels`) place this handler inside the options object. The Python equivalent now lives on the options dataclass instead of as a `CopilotClient.__init__` kwarg. 2. **Remove the `auto_start` option** — .NET and TypeScript do not expose an opt-out for autostart. Autostart behaviour is preserved (calling `start()` is still optional), but consumers can no longer disable it. The deleted test `test_autostart_false_requires_explicit_start` covered an option that no longer exists. 3. **Remove `CopilotClient.get_state()` and the public `ConnectionState` type** — .NET removed these in commit b00fd8c9 (Phase 4d/4e). Python now matches. The internal `_state` field is preserved (used by `start()`/`stop()` to gate behavior), but it is no longer exposed via a public accessor or type alias. TypeScript still has `getState()` — tracked as a cross-SDK follow-up to remove there too. `CopilotClient` is now just `CopilotClient()` or `CopilotClient(options)` with no kwargs. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- docs/auth/byok.md | 28 ++++++------ python/README.md | 8 +--- python/copilot/__init__.py | 2 - python/copilot/client.py | 61 ++++++++----------------- python/e2e/test_client_e2e.py | 19 ++++---- python/e2e/test_client_lifecycle_e2e.py | 7 +-- python/e2e/test_client_options_e2e.py | 30 +----------- python/test_client.py | 24 ++++++---- 8 files changed, 63 insertions(+), 116 deletions(-) diff --git a/docs/auth/byok.md b/docs/auth/byok.md index cb2f8cb90..1b153fc2f 100644 --- a/docs/auth/byok.md +++ b/docs/auth/byok.md @@ -369,21 +369,23 @@ const client = new CopilotClient({ Python ```python -from copilot import CopilotClient +from copilot import CopilotClient, CopilotClientOptions from copilot.client import ModelInfo, ModelCapabilities, ModelSupports, ModelLimits -client = CopilotClient({ - "on_list_models": lambda: [ - ModelInfo( - id="my-custom-model", - name="My Custom Model", - capabilities=ModelCapabilities( - supports=ModelSupports(vision=False, reasoning_effort=False), - limits=ModelLimits(max_context_window_tokens=128000), - ), - ) - ], -}) +client = CopilotClient( + CopilotClientOptions( + on_list_models=lambda: [ + ModelInfo( + id="my-custom-model", + name="My Custom Model", + capabilities=ModelCapabilities( + supports=ModelSupports(vision=False, reasoning_effort=False), + limits=ModelLimits(max_context_window_tokens=128000), + ), + ) + ], + ), +) ``` diff --git a/python/README.md b/python/README.md index 608c9e4dd..a2deee659 100644 --- a/python/README.md +++ b/python/README.md @@ -144,12 +144,8 @@ client = CopilotClient( **CopilotClient Constructor:** ```python -CopilotClient( - options=None, # CopilotClientOptions | None - *, - auto_start=True, # auto-start server on first use - on_list_models=None, # custom handler for list_models() -) +CopilotClient() # spawn the bundled runtime with defaults +CopilotClient(CopilotClientOptions(...)) # customise via options ``` **CopilotClientOptions** — configure the client: diff --git a/python/copilot/__init__.py b/python/copilot/__init__.py index c491dc8fb..0c351a3ed 100644 --- a/python/copilot/__init__.py +++ b/python/copilot/__init__.py @@ -8,7 +8,6 @@ ChildProcessRuntimeConnection, CloudSessionOptions, CloudSessionRepository, - ConnectionState, CopilotClient, CopilotClientOptions, GetAuthStatusResponse, @@ -140,7 +139,6 @@ "CloudSessionRepository", "CommandContext", "CommandDefinition", - "ConnectionState", "CopilotClient", "CopilotClientOptions", "CopilotSession", diff --git a/python/copilot/client.py b/python/copilot/client.py index 2dc24ce89..62f12eb96 100644 --- a/python/copilot/client.py +++ b/python/copilot/client.py @@ -81,7 +81,7 @@ # Connection Types # ============================================================================ -ConnectionState = Literal["disconnected", "connecting", "connected", "error"] +_ConnectionState = Literal["disconnected", "connecting", "connected", "error"] LogLevel = Literal["none", "error", "warning", "info", "debug", "all"] @@ -342,6 +342,14 @@ class CopilotClientOptions: accessible from GitHub web and mobile. """ + on_list_models: Callable[[], list[ModelInfo] | Awaitable[list[ModelInfo]]] | None = None + """Custom handler for :meth:`CopilotClient.list_models`. + + When provided, the handler is called instead of querying the runtime + server. Matches the ``onListModels`` / ``OnListModels`` option in the + TypeScript and .NET SDKs. + """ + # ============================================================================ # Response Types @@ -1081,21 +1089,14 @@ class CopilotClient: def __init__( self, options: CopilotClientOptions | None = None, - *, - auto_start: bool = True, - on_list_models: Callable[[], list[ModelInfo] | Awaitable[list[ModelInfo]]] | None = None, ): """ Initialize a new CopilotClient. Args: options: Client configuration. Defaults to ``CopilotClientOptions()`` - with a default :meth:`RuntimeConnection.stdio` connection using + with a default :meth:`RuntimeConnection.for_stdio` connection using the bundled runtime binary. - auto_start: Automatically start the connection on first use - (default: ``True``). - on_list_models: Custom handler for :meth:`list_models`. When provided, - the handler is called instead of querying the runtime server. Example: >>> # Default — spawns runtime using stdio with the bundled binary @@ -1122,8 +1123,7 @@ def __init__( self._options: CopilotClientOptions = options self._connection: RuntimeConnection = connection - self._auto_start = auto_start - self._on_list_models = on_list_models + self._on_list_models = options.on_list_models # Resolve connection-mode-specific state. self._actual_host: str = "localhost" @@ -1180,7 +1180,7 @@ def __init__( self._process: subprocess.Popen | None = None self._client: JsonRpcClient | None = None - self._state: ConnectionState = "disconnected" + self._state: _ConnectionState = "disconnected" self._sessions: dict[str, CopilotSession] = {} self._sessions_lock = threading.Lock() self._models_cache: list[ModelInfo] | None = None @@ -1293,18 +1293,18 @@ async def start(self) -> None: """ Start the CLI server and establish a connection. - If connecting to an already-running runtime (via :meth:`RuntimeConnection.uri`), + If connecting to an already-running runtime (via :meth:`RuntimeConnection.for_uri`), only establishes the connection. Otherwise, spawns the CLI server process and then connects. - This method is called automatically when creating a session if ``auto_start`` - is True (default). + This method is called automatically when creating a session, so most + callers do not need to call it explicitly. Raises: RuntimeError: If the server fails to start or the connection fails. Example: - >>> client = CopilotClient(auto_start=False) + >>> client = CopilotClient() >>> await client.start() >>> # Now ready to create sessions """ @@ -1551,8 +1551,8 @@ async def create_session( Create a new conversation session with the Copilot CLI. Sessions maintain conversation state, handle events, and manage tool execution. - If the client is not connected and ``auto_start`` is enabled, this will - automatically start the connection. + If the client is not yet connected, this will automatically start the + connection. Args: on_permission_request: Optional handler for permission requests. When @@ -1617,7 +1617,6 @@ async def create_session( A :class:`CopilotSession` instance for the new session. Raises: - RuntimeError: If the client is not connected and auto_start is disabled. ValueError: If ``on_permission_request`` is provided but not callable. Example: @@ -1635,10 +1634,7 @@ async def create_session( if on_permission_request is not None and not callable(on_permission_request): raise ValueError("on_permission_request must be callable when provided.") if not self._client: - if self._auto_start: - await self.start() - else: - raise RuntimeError("Client not connected. Call start() first.") + await self.start() tool_defs = [] if tools: @@ -2016,10 +2012,7 @@ async def resume_session( if on_permission_request is not None and not callable(on_permission_request): raise ValueError("on_permission_request must be callable when provided.") if not self._client: - if self._auto_start: - await self.start() - else: - raise RuntimeError("Client not connected. Call start() first.") + await self.start() tool_defs = [] if tools: @@ -2239,20 +2232,6 @@ async def resume_session( ) return session - def get_state(self) -> ConnectionState: - """ - Get the current connection state of the client. - - Returns: - The current connection state: "disconnected", "connecting", - "connected", or "error". - - Example: - >>> if client.get_state() == "connected": - ... session = await client.create_session() - """ - return self._state - async def ping(self, message: str | None = None) -> PingResponse: """ Send a ping request to the server to verify connectivity. diff --git a/python/e2e/test_client_e2e.py b/python/e2e/test_client_e2e.py index edb736051..f3d3c8d44 100644 --- a/python/e2e/test_client_e2e.py +++ b/python/e2e/test_client_e2e.py @@ -24,14 +24,12 @@ async def test_should_start_and_connect_to_server_using_stdio(self): try: await client.start() - assert client.get_state() == "connected" pong = await client.ping("test message") assert pong.message == "pong: test message" assert pong.timestamp is not None await client.stop() - assert client.get_state() == "disconnected" finally: await client.force_stop() @@ -43,14 +41,12 @@ async def test_should_start_and_connect_to_server_using_tcp(self): try: await client.start() - assert client.get_state() == "connected" pong = await client.ping("test message") assert pong.message == "pong: test message" assert pong.timestamp is not None await client.stop() - assert client.get_state() == "disconnected" finally: await client.force_stop() @@ -77,8 +73,6 @@ async def test_should_raise_exception_group_on_failed_cleanup(self): assert len(exc.exceptions) > 0 assert isinstance(exc.exceptions[0], StopError) assert "Failed to disconnect session" in exc.exceptions[0].message - else: - assert client.get_state() == "disconnected" finally: await client.force_stop() @@ -90,7 +84,6 @@ async def test_should_force_stop_without_cleanup(self): await client.create_session(on_permission_request=PermissionHandler.approve_all) await client.force_stop() - assert client.get_state() == "disconnected" @pytest.mark.asyncio async def test_should_get_status_with_version_and_protocol_info(self): @@ -321,8 +314,10 @@ def on_list_models(): return custom_models client = CopilotClient( - CopilotClientOptions(connection=RuntimeConnection.for_stdio(path=CLI_PATH)), - on_list_models=on_list_models, + CopilotClientOptions( + connection=RuntimeConnection.for_stdio(path=CLI_PATH), + on_list_models=on_list_models, + ), ) try: @@ -359,8 +354,10 @@ def on_list_models(): return custom_models client = CopilotClient( - CopilotClientOptions(connection=RuntimeConnection.for_stdio(path=CLI_PATH)), - on_list_models=on_list_models, + CopilotClientOptions( + connection=RuntimeConnection.for_stdio(path=CLI_PATH), + on_list_models=on_list_models, + ), ) try: diff --git a/python/e2e/test_client_lifecycle_e2e.py b/python/e2e/test_client_lifecycle_e2e.py index 388499667..7dcc20ba5 100644 --- a/python/e2e/test_client_lifecycle_e2e.py +++ b/python/e2e/test_client_lifecycle_e2e.py @@ -180,12 +180,7 @@ def disposed_handler(_event): async def test_stop_disconnects_client_and_disposes_rpc_surface(self, ctx: E2ETestContext): client = _make_isolated_client(ctx) await client.start() - try: - assert client.get_state() == "connected" - finally: - await client.stop() - - assert client.get_state() == "disconnected" + await client.stop() with pytest.raises(RuntimeError): _ = client.rpc diff --git a/python/e2e/test_client_options_e2e.py b/python/e2e/test_client_options_e2e.py index 3d7e2c272..f2d898fb8 100644 --- a/python/e2e/test_client_options_e2e.py +++ b/python/e2e/test_client_options_e2e.py @@ -1,6 +1,6 @@ """ E2E coverage for ``CopilotClient`` configuration options exposed via -``CopilotClientOptions`` / ``RuntimeConnection`` and ``CopilotClient(..., auto_start=...)``. +``CopilotClientOptions`` and ``RuntimeConnection``. Mirrors ``dotnet/test/ClientOptionsTests.cs``. The two CliUrl-conflict tests (``Should_Throw_When_GitHubToken_Used_With_CliUrl`` and @@ -164,38 +164,11 @@ def _assert_arg_value(args: list[str], name: str, expected_value: str) -> None: class TestClientOptions: - async def test_autostart_false_requires_explicit_start(self, ctx: E2ETestContext): - client = CopilotClient(_make_options(ctx), auto_start=False) - try: - assert client.get_state() == "disconnected" - - with pytest.raises(RuntimeError) as exc_info: - await client.create_session( - on_permission_request=PermissionHandler.approve_all, - ) - # Python raises "Client not connected" — equivalent intent to C#'s "StartAsync". - assert ( - "not connected" in str(exc_info.value).lower() - or "start" in str(exc_info.value).lower() - ) - - await client.start() - assert client.get_state() == "connected" - - session = await client.create_session( - on_permission_request=PermissionHandler.approve_all, - ) - assert session.session_id - await session.disconnect() - finally: - await client.stop() - async def test_should_listen_on_configured_tcp_port(self, ctx: E2ETestContext): port = _get_available_port() client = CopilotClient(_make_options(ctx, use_tcp=True, port=port)) try: await client.start() - assert client.get_state() == "connected" assert client.runtime_port == port response = await client.rpc.ping(PingRequest(message="fixed-port")) @@ -252,7 +225,6 @@ async def test_should_propagate_process_options_to_spawned_cli(self, ctx: E2ETes }, use_logged_in_user=False, ), - auto_start=False, ) try: await client.start() diff --git a/python/test_client.py b/python/test_client.py index 29e1265f1..55a2092c3 100644 --- a/python/test_client.py +++ b/python/test_client.py @@ -475,8 +475,10 @@ def handler(): return custom_models client = CopilotClient( - CopilotClientOptions(connection=RuntimeConnection.for_stdio(path=CLI_PATH)), - on_list_models=handler, + CopilotClientOptions( + connection=RuntimeConnection.for_stdio(path=CLI_PATH), + on_list_models=handler, + ), ) await client.start() try: @@ -507,8 +509,10 @@ def handler(): return custom_models client = CopilotClient( - CopilotClientOptions(connection=RuntimeConnection.for_stdio(path=CLI_PATH)), - on_list_models=handler, + CopilotClientOptions( + connection=RuntimeConnection.for_stdio(path=CLI_PATH), + on_list_models=handler, + ), ) await client.start() try: @@ -536,8 +540,10 @@ async def handler(): return custom_models client = CopilotClient( - CopilotClientOptions(connection=RuntimeConnection.for_stdio(path=CLI_PATH)), - on_list_models=handler, + CopilotClientOptions( + connection=RuntimeConnection.for_stdio(path=CLI_PATH), + on_list_models=handler, + ), ) await client.start() try: @@ -567,8 +573,10 @@ def handler(): return custom_models client = CopilotClient( - CopilotClientOptions(connection=RuntimeConnection.for_stdio(path=CLI_PATH)), - on_list_models=handler, + CopilotClientOptions( + connection=RuntimeConnection.for_stdio(path=CLI_PATH), + on_list_models=handler, + ), ) models = await client.list_models() assert len(handler_calls) == 1 From 5676e4f88cc8e064f99faccc4fc80f26fe19294d Mon Sep 17 00:00:00 2001 From: Steve Sanderson Date: Fri, 22 May 2026 14:15:16 +0100 Subject: [PATCH 20/21] Flatten CopilotClient options + drop unused SessionConfig TypedDicts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make the Python public API idiomatically Pythonic, matching the conventions used by ``openai``, ``anthropic``, ``httpx``, etc. **CopilotClient: flat kw-only ctor params** The ``CopilotClientOptions`` dataclass was a port of the TS / .NET ``CopilotClientOptions`` interface and is not how a Python API client is typically configured. Python users expect ``CopilotClient(connection=..., log_level=..., github_token=...)`` rather than ``CopilotClient(CopilotClientOptions(connection=..., log_level=..., github_token=...))``. Changes: - All 12 options previously on ``CopilotClientOptions`` are now kw-only parameters on ``CopilotClient.__init__``: ``connection``, ``working_directory``, ``log_level``, ``env``, ``github_token``, ``base_directory``, ``use_logged_in_user``, ``telemetry``, ``session_fs``, ``session_idle_timeout_seconds``, ``enable_remote_sessions``, ``on_list_models``. - ``CopilotClientOptions`` is renamed to ``_CopilotClientOptions`` and is now an implementation detail used only internally to carry the resolved options around. The public ``__all__`` no longer exports it. - IDE autocomplete on ``CopilotClient(`` now lists every option directly with its type, default, and docstring. Pyright / ty catch unknown kwargs at the call site. **Drop vestigial SessionConfig / ResumeSessionConfig / SessionConfigBase** These TypedDicts mirrored the TS / .NET ``SessionConfig`` interfaces but were never used anywhere in Python — ``create_session()`` and ``resume_session()`` already take flat kw-only parameters (the idiomatic form). The TypedDicts were just unreferenced documentation noise. Updates: - README, all docs Python snippets, all 25 ``test/scenarios/**/python/main.py`` fixtures, every E2E test, every unit test now use the new flat ctor. - The test class ``TestSubprocessOptions`` (E2E) and the unit-style ``test_telemetry_config_in_subprocess_config`` test exercised the ``CopilotClientOptions`` dataclass shape itself; they're deleted since Python's type system already enforces ctor-kwarg correctness. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- docs/auth/byok.md | 24 +- docs/features/remote-sessions.md | 4 +- docs/observability/opentelemetry.md | 6 +- docs/setup/backend-services.md | 6 +- docs/troubleshooting/debugging.md | 4 +- python/README.md | 19 +- python/copilot/__init__.py | 8 - python/copilot/client.py | 140 +++++----- python/copilot/session.py | 127 --------- python/e2e/test_agent_and_compact_rpc_e2e.py | 26 +- python/e2e/test_client_e2e.py | 64 ++--- python/e2e/test_client_lifecycle_e2e.py | 12 +- python/e2e/test_client_options_e2e.py | 64 +---- python/e2e/test_commands_e2e.py | 22 +- python/e2e/test_connection_token.py | 22 +- python/e2e/test_multi_client_e2e.py | 28 +- python/e2e/test_pending_work_resume_e2e.py | 84 ++---- python/e2e/test_per_session_auth_e2e.py | 12 +- python/e2e/test_rpc_e2e.py | 26 +- python/e2e/test_rpc_server_e2e.py | 12 +- python/e2e/test_session_e2e.py | 12 +- python/e2e/test_session_fs_e2e.py | 29 +- python/e2e/test_session_fs_sqlite_e2e.py | 14 +- python/e2e/test_streaming_fidelity_e2e.py | 22 +- python/e2e/test_subagent_hooks_e2e.py | 12 +- python/e2e/test_suspend_e2e.py | 24 +- python/e2e/test_telemetry_e2e.py | 36 +-- .../test_ui_elicitation_multi_client_e2e.py | 28 +- python/e2e/testharness/context.py | 18 +- python/test_client.py | 257 ++++++------------ python/test_commands_and_elicitation.py | 74 ++--- python/test_telemetry.py | 13 - .../auth/byok-anthropic/python/main.py | 4 +- test/scenarios/auth/byok-azure/python/main.py | 4 +- .../scenarios/auth/byok-ollama/python/main.py | 4 +- .../scenarios/auth/byok-openai/python/main.py | 4 +- test/scenarios/auth/gh-app/python/main.py | 4 +- .../app-backend-to-server/python/main.py | 4 +- .../bundling/app-direct-server/python/main.py | 10 +- .../bundling/container-proxy/python/main.py | 10 +- .../bundling/fully-bundled/python/main.py | 6 +- test/scenarios/callbacks/hooks/python/main.py | 6 +- .../callbacks/permissions/python/main.py | 6 +- .../callbacks/user-input/python/main.py | 6 +- test/scenarios/modes/default/python/main.py | 6 +- test/scenarios/modes/minimal/python/main.py | 6 +- .../prompts/attachments/python/main.py | 6 +- .../prompts/reasoning-effort/python/main.py | 6 +- .../prompts/system-message/python/main.py | 6 +- .../concurrent-sessions/python/main.py | 6 +- .../sessions/infinite-sessions/python/main.py | 6 +- .../sessions/session-resume/python/main.py | 6 +- .../sessions/streaming/python/main.py | 6 +- .../tools/custom-agents/python/main.py | 6 +- .../tools/mcp-servers/python/main.py | 6 +- test/scenarios/tools/no-tools/python/main.py | 6 +- test/scenarios/tools/skills/python/main.py | 6 +- .../tools/tool-filtering/python/main.py | 6 +- .../tools/tool-overrides/python/main.py | 6 +- .../tools/virtual-filesystem/python/main.py | 6 +- .../transport/reconnect/python/main.py | 10 +- test/scenarios/transport/stdio/python/main.py | 6 +- test/scenarios/transport/tcp/python/main.py | 10 +- 63 files changed, 457 insertions(+), 982 deletions(-) diff --git a/docs/auth/byok.md b/docs/auth/byok.md index 1b153fc2f..4afb149e8 100644 --- a/docs/auth/byok.md +++ b/docs/auth/byok.md @@ -369,22 +369,20 @@ const client = new CopilotClient({ Python ```python -from copilot import CopilotClient, CopilotClientOptions +from copilot import CopilotClient from copilot.client import ModelInfo, ModelCapabilities, ModelSupports, ModelLimits client = CopilotClient( - CopilotClientOptions( - on_list_models=lambda: [ - ModelInfo( - id="my-custom-model", - name="My Custom Model", - capabilities=ModelCapabilities( - supports=ModelSupports(vision=False, reasoning_effort=False), - limits=ModelLimits(max_context_window_tokens=128000), - ), - ) - ], - ), + on_list_models=lambda: [ + ModelInfo( + id="my-custom-model", + name="My Custom Model", + capabilities=ModelCapabilities( + supports=ModelSupports(vision=False, reasoning_effort=False), + limits=ModelLimits(max_context_window_tokens=128000), + ), + ) + ], ) ``` diff --git a/docs/features/remote-sessions.md b/docs/features/remote-sessions.md index ade22da15..33556e43a 100644 --- a/docs/features/remote-sessions.md +++ b/docs/features/remote-sessions.md @@ -38,9 +38,9 @@ session.on("session.info", (event) => { ```python -from copilot import CopilotClient, CopilotClientOptions +from copilot import CopilotClient -client = CopilotClient(CopilotClientOptions(enable_remote_sessions=True)) +client = CopilotClient(enable_remote_sessions=True) session = await client.create_session( working_directory="/path/to/github-repo", on_permission_request=lambda req: {"allowed": True}, diff --git a/docs/observability/opentelemetry.md b/docs/observability/opentelemetry.md index 94888aaf1..1f9581ba5 100644 --- a/docs/observability/opentelemetry.md +++ b/docs/observability/opentelemetry.md @@ -27,13 +27,13 @@ const client = new CopilotClient({ ```python -from copilot import CopilotClient, CopilotClientOptions +from copilot import CopilotClient -client = CopilotClient(CopilotClientOptions( +client = CopilotClient( telemetry={ "otlp_endpoint": "http://localhost:4318", }, -)) +) ``` diff --git a/docs/setup/backend-services.md b/docs/setup/backend-services.md index 6dbcf1a0e..dfe9c19af 100644 --- a/docs/setup/backend-services.md +++ b/docs/setup/backend-services.md @@ -144,12 +144,12 @@ res.json({ content: response?.data.content }); Python ```python -from copilot import CopilotClient, CopilotClientOptions, RuntimeConnection +from copilot import CopilotClient, RuntimeConnection from copilot.session import PermissionHandler -client = CopilotClient(CopilotClientOptions( +client = CopilotClient( connection=RuntimeConnection.for_uri("localhost:4321"), -)) +) await client.start() session = await client.create_session(on_permission_request=PermissionHandler.approve_all, model="gpt-4.1", session_id=f"user-{user_id}-{int(time.time())}") diff --git a/docs/troubleshooting/debugging.md b/docs/troubleshooting/debugging.md index a648c067c..95c9b3a7d 100644 --- a/docs/troubleshooting/debugging.md +++ b/docs/troubleshooting/debugging.md @@ -32,9 +32,9 @@ const client = new CopilotClient({ Python ```python -from copilot import CopilotClient, CopilotClientOptions +from copilot import CopilotClient -client = CopilotClient(CopilotClientOptions(log_level="debug")) +client = CopilotClient(log_level="debug") ``` diff --git a/python/README.md b/python/README.md index a2deee659..4e415e320 100644 --- a/python/README.md +++ b/python/README.md @@ -133,22 +133,20 @@ async with CopilotClient() as client: > **Note:** For manual lifecycle management, see [Manual Resource Management](#manual-resource-management) above. ```python -from copilot import CopilotClient, CopilotClientOptions, RuntimeConnection +from copilot import CopilotClient, RuntimeConnection # Connect to an existing CLI server -client = CopilotClient( - CopilotClientOptions(connection=RuntimeConnection.for_uri("localhost:3000")) -) +client = CopilotClient(connection=RuntimeConnection.for_uri("localhost:3000")) ``` **CopilotClient Constructor:** ```python -CopilotClient() # spawn the bundled runtime with defaults -CopilotClient(CopilotClientOptions(...)) # customise via options +CopilotClient() # spawn the bundled runtime with defaults +CopilotClient(connection=..., log_level="debug", github_token=..., ...) ``` -**CopilotClientOptions** — configure the client: +All options are kw-only parameters: - `connection` (RuntimeConnection | None): How to reach the runtime. Use `RuntimeConnection.for_stdio(...)`, `RuntimeConnection.for_tcp(...)`, or @@ -161,6 +159,7 @@ CopilotClient(CopilotClientOptions(...)) # customise via options - `use_logged_in_user` (bool | None): Whether to use logged-in user for authentication (default: True, but False when `github_token` is provided). - `telemetry` (dict | None): OpenTelemetry configuration for the CLI process. Providing this enables telemetry — no separate flag needed. See [Telemetry](#telemetry) below. - `enable_remote_sessions` (bool): Enable remote/cloud session support (default: False). +- `on_list_models` (callable | None): Custom handler for `list_models()`. When provided, the handler is called instead of querying the runtime. **RuntimeConnection variants:** @@ -531,13 +530,13 @@ async with await client.create_session( The SDK supports OpenTelemetry for distributed tracing. Provide a `telemetry` config to enable trace export and automatic W3C Trace Context propagation. ```python -from copilot import CopilotClient, CopilotClientOptions +from copilot import CopilotClient -client = CopilotClient(CopilotClientOptions( +client = CopilotClient( telemetry={ "otlp_endpoint": "http://localhost:4318", }, -)) +) ``` **TelemetryConfig options:** diff --git a/python/copilot/__init__.py b/python/copilot/__init__.py index 0c351a3ed..ee53264d2 100644 --- a/python/copilot/__init__.py +++ b/python/copilot/__init__.py @@ -9,7 +9,6 @@ CloudSessionOptions, CloudSessionRepository, CopilotClient, - CopilotClientOptions, GetAuthStatusResponse, GetStatusResponse, LogLevel, @@ -87,10 +86,7 @@ PreToolUseHookInput, PreToolUseHookOutput, ProviderConfig, - ResumeSessionConfig, SessionCapabilities, - SessionConfig, - SessionConfigBase, SessionEndHandler, SessionEndHookInput, SessionEndHookOutput, @@ -140,7 +136,6 @@ "CommandContext", "CommandDefinition", "CopilotClient", - "CopilotClientOptions", "CopilotSession", "CreateSessionFsHandler", "ElicitationContext", @@ -188,12 +183,9 @@ "PreToolUseHookOutput", "ProviderConfig", "RemoteSessionMode", - "ResumeSessionConfig", "RuntimeConnection", "SessionBackgroundEvent", "SessionCapabilities", - "SessionConfig", - "SessionConfigBase", "SessionContext", "SessionCreatedEvent", "SessionDeletedEvent", diff --git a/python/copilot/client.py b/python/copilot/client.py index 62f12eb96..b9f67ab9d 100644 --- a/python/copilot/client.py +++ b/python/copilot/client.py @@ -165,9 +165,7 @@ class RuntimeConnection: Example: >>> CopilotClient() # default: stdio with the bundled runtime - >>> CopilotClient( - ... CopilotClientOptions(connection=RuntimeConnection.for_uri("localhost:3000")) - ... ) + >>> CopilotClient(connection=RuntimeConnection.for_uri("localhost:3000")) """ @staticmethod @@ -282,73 +280,25 @@ class UriRuntimeConnection(RuntimeConnection): @dataclass -class CopilotClientOptions: - """Configuration options for a :class:`CopilotClient`. +class _CopilotClientOptions: + """Internal configuration carrier used by :class:`CopilotClient`. - All process-management options (``working_directory``, ``log_level``, - ``env``, ``github_token``, …) apply only when the SDK spawns the runtime - (stdio / tcp connections). They are ignored when connecting to an - existing runtime via :meth:`RuntimeConnection.uri`. + This is not part of the public API: ``CopilotClient`` accepts all of + these options as keyword arguments directly. """ connection: RuntimeConnection | None = None - """How to reach the runtime. - - Defaults to :meth:`RuntimeConnection.stdio` with the bundled binary. - """ - working_directory: str | None = None - """Working directory for the runtime process. ``None`` uses the current directory.""" - log_level: LogLevel = "info" - """Log level for the runtime process.""" - env: dict[str, str] | None = None - """Environment variables for the runtime process. ``None`` inherits the current env.""" - github_token: str | None = None - """GitHub token for authentication. Takes priority over other auth methods.""" - base_directory: str | None = None - """Base directory for Copilot data (session state, config, etc.). - - Sets the ``COPILOT_HOME`` environment variable on the spawned runtime. - When ``None``, the runtime defaults to ``~/.copilot``. - """ - use_logged_in_user: bool | None = None - """Use the logged-in user for authentication. - - ``None`` (default) resolves to ``True`` unless ``github_token`` is set. - """ - telemetry: TelemetryConfig | None = None - """OpenTelemetry configuration. Providing this enables telemetry.""" - session_fs: SessionFsConfig | None = None - """Connection-level session filesystem provider configuration.""" - session_idle_timeout_seconds: int | None = None - """Server-wide session idle timeout in seconds. - - Sessions without activity for this duration are automatically cleaned up. - Set to ``None`` or ``0`` to disable. - """ - enable_remote_sessions: bool = False - """Enable remote session support (Mission Control integration). - - When ``True``, sessions in a GitHub repository working directory are - accessible from GitHub web and mobile. - """ - on_list_models: Callable[[], list[ModelInfo] | Awaitable[list[ModelInfo]]] | None = None - """Custom handler for :meth:`CopilotClient.list_models`. - - When provided, the handler is called instead of querying the runtime - server. Matches the ``onListModels`` / ``OnListModels`` option in the - TypeScript and .NET SDKs. - """ # ============================================================================ @@ -1082,21 +1032,65 @@ class CopilotClient: >>> # Or connect to an existing server >>> client = CopilotClient( - ... CopilotClientOptions(connection=RuntimeConnection.for_uri("localhost:3000")) + ... connection=RuntimeConnection.for_uri("localhost:3000"), ... ) """ def __init__( self, - options: CopilotClientOptions | None = None, + *, + connection: RuntimeConnection | None = None, + working_directory: str | None = None, + log_level: LogLevel = "info", + env: dict[str, str] | None = None, + github_token: str | None = None, + base_directory: str | None = None, + use_logged_in_user: bool | None = None, + telemetry: TelemetryConfig | None = None, + session_fs: SessionFsConfig | None = None, + session_idle_timeout_seconds: int | None = None, + enable_remote_sessions: bool = False, + on_list_models: Callable[[], list[ModelInfo] | Awaitable[list[ModelInfo]]] | None = None, ): """ Initialize a new CopilotClient. + All process-management options (``working_directory``, ``log_level``, + ``env``, ``github_token``, …) apply only when the SDK spawns the runtime + (stdio / tcp connections). They are ignored when connecting to an + existing runtime via :meth:`RuntimeConnection.for_uri`. + Args: - options: Client configuration. Defaults to ``CopilotClientOptions()`` - with a default :meth:`RuntimeConnection.for_stdio` connection using - the bundled runtime binary. + connection: How to reach the runtime. Defaults to + :meth:`RuntimeConnection.for_stdio` with the bundled binary. + working_directory: Working directory for the runtime process. + ``None`` uses the current directory. + log_level: Log level for the runtime process. Defaults to ``"info"``. + env: Environment variables for the runtime process. ``None`` inherits + the current env. + github_token: GitHub token for authentication. Takes priority over + other auth methods. + base_directory: Base directory for Copilot data (session state, + config, etc.). Sets the ``COPILOT_HOME`` environment variable on + the spawned runtime. When ``None``, the runtime defaults to + ``~/.copilot``. + use_logged_in_user: Use the logged-in user for authentication. + ``None`` (default) resolves to ``True`` unless ``github_token`` + is set. + telemetry: OpenTelemetry configuration. Providing this enables + telemetry. + session_fs: Connection-level session filesystem provider + configuration. + session_idle_timeout_seconds: Server-wide session idle timeout in + seconds. Sessions without activity for this duration are + automatically cleaned up. Set to ``None`` or ``0`` to disable. + enable_remote_sessions: Enable remote session support (Mission + Control integration). When ``True``, sessions in a GitHub + repository working directory are accessible from GitHub web + and mobile. + on_list_models: Custom handler for :meth:`list_models`. When + provided, the handler is called instead of querying the runtime + server. Example: >>> # Default — spawns runtime using stdio with the bundled binary @@ -1104,24 +1098,34 @@ def __init__( >>> >>> # Connect to an existing runtime >>> client = CopilotClient( - ... CopilotClientOptions(connection=RuntimeConnection.for_uri("localhost:3000")) + ... connection=RuntimeConnection.for_uri("localhost:3000"), ... ) >>> >>> # Custom runtime path with specific log level >>> client = CopilotClient( - ... CopilotClientOptions( - ... connection=RuntimeConnection.for_stdio(path="/usr/local/bin/copilot"), - ... log_level="debug", - ... ) + ... connection=RuntimeConnection.for_stdio(path="/usr/local/bin/copilot"), + ... log_level="debug", ... ) """ - if options is None: - options = CopilotClientOptions() + options = _CopilotClientOptions( + connection=connection, + working_directory=working_directory, + log_level=log_level, + env=env, + github_token=github_token, + base_directory=base_directory, + use_logged_in_user=use_logged_in_user, + telemetry=telemetry, + session_fs=session_fs, + session_idle_timeout_seconds=session_idle_timeout_seconds, + enable_remote_sessions=enable_remote_sessions, + on_list_models=on_list_models, + ) connection = ( options.connection if options.connection is not None else RuntimeConnection.for_stdio() ) - self._options: CopilotClientOptions = options + self._options: _CopilotClientOptions = options self._connection: RuntimeConnection = connection self._on_list_models = options.on_list_models diff --git a/python/copilot/session.py b/python/copilot/session.py index 1da8e59ab..9798835e9 100644 --- a/python/copilot/session.py +++ b/python/copilot/session.py @@ -944,133 +944,6 @@ class ProviderConfig(TypedDict, total=False): max_output_tokens: int -class SessionConfigBase(TypedDict, total=False): - """Shared configuration fields between :class:`SessionConfig` and - :class:`ResumeSessionConfig`. - - See those classes for the create-only / resume-only fields. - """ - - # 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. When specified, only these tools will be available. - # Applies to the full merged tool catalog (built-in, MCP, and custom tools - # registered via tools=). Takes precedence over excluded_tools. - available_tools: list[str] - # List of tool names to disable. Applies to all tools including custom tools - # registered via tools=. Ignored if available_tools is set. - excluded_tools: list[str] - # Optional handler for permission requests from the server. When omitted, - # requests are surfaced as events and left pending for manual resolution. - on_permission_request: _PermissionHandlerFn | None - # 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 - # Enables or disables internal session telemetry for this session. When False, - # disables session telemetry. When omitted (the default) or True, telemetry is enabled for - # GitHub-authenticated sessions. When a custom provider (BYOK) is configured, - # session telemetry is always disabled regardless of this setting. - # This is independent of the client OpenTelemetry configuration. - enable_session_telemetry: bool - # 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. - # Defaults to False. - streaming: bool - # Include sub-agent streaming events in the event stream. When True, streaming - # delta events from sub-agents (e.g., assistant.message_delta, - # assistant.reasoning_delta, assistant.streaming_delta with agentId set) are - # forwarded to this connection. When False, only non-streaming sub-agent events - # and subagent.* lifecycle events are forwarded; streaming deltas from sub-agents - # are suppressed. Defaults to True. - include_sub_agent_streaming_events: bool - # MCP server configurations for the session - mcp_servers: dict[str, MCPServerConfig] - # Custom agent configurations for the session - custom_agents: list[CustomAgentConfig] - # Configuration for the default agent. - # Use excluded_tools to hide tools from the default agent - # while keeping them available to sub-agents. - default_agent: DefaultAgentConfig - # Name of the custom agent to activate when the session starts. - # Must match the name of one of the agents in custom_agents. - agent: str - # 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] - # Additional directories to search for custom instruction files. - instruction_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 - # Optional event handler that is registered on the session before the - # session.create / session.resume RPC is issued, ensuring early events - # (e.g. session.start) are delivered. Equivalent to calling session.on(handler) - # immediately after creation, but executes earlier in the lifecycle so no - # events are missed. - on_event: Callable[[SessionEvent], None] - # Slash commands to register with the session. - # When the CLI has a TUI, each command appears as /name for the user to invoke. - commands: list[CommandDefinition] - # Handler for elicitation requests from the server. - # When provided, the server calls back to this client for form-based UI dialogs. - on_elicitation_request: ElicitationHandler - # Handler for exit-plan-mode requests from the server. - on_exit_plan_mode_request: ExitPlanModeHandler - # Handler for auto-mode-switch requests from the server. - on_auto_mode_switch_request: AutoModeSwitchHandler - # Handler factory for session-scoped sessionFs operations. - create_session_fs_handler: CreateSessionFsHandler - - -class SessionConfig(SessionConfigBase, total=False): - """Configuration for creating a session. - - Inherits all shared fields from :class:`SessionConfigBase`; only the - create-specific fields appear here. - """ - - session_id: str # Optional custom session ID - - -class ResumeSessionConfig(SessionConfigBase, total=False): - """Configuration for resuming a session. - - Inherits all shared fields from :class:`SessionConfigBase`; only the - resume-specific fields appear here. - """ - - # When True, skips emitting the session.resume event. - # Useful for reconnecting to a session without triggering resume-related side effects. - disable_resume: bool - # When True, instructs the runtime to continue any tool calls or permission prompts - # that were still pending when the session was last suspended. When False (the - # default), the runtime treats pending work as interrupted on resume. - # - # For permission requests, the runtime re-emits ``permission.requested`` so the - # registered ``on_permission_request`` handler can re-prompt; for external tool - # calls, the consumer is expected to supply the result via the corresponding - # low-level RPC method. - continue_pending_work: bool - - SessionEventHandler = Callable[[SessionEvent], None] diff --git a/python/e2e/test_agent_and_compact_rpc_e2e.py b/python/e2e/test_agent_and_compact_rpc_e2e.py index 1209ca1d4..14ea01ff2 100644 --- a/python/e2e/test_agent_and_compact_rpc_e2e.py +++ b/python/e2e/test_agent_and_compact_rpc_e2e.py @@ -4,7 +4,7 @@ import pytest -from copilot import CopilotClient, CopilotClientOptions, RuntimeConnection +from copilot import CopilotClient, RuntimeConnection from copilot.generated.rpc import AgentSelectRequest from copilot.session import PermissionHandler @@ -17,9 +17,7 @@ class TestAgentSelectionRpc: @pytest.mark.asyncio async def test_should_list_available_custom_agents(self): """Test listing available custom agents via RPC.""" - client = CopilotClient( - CopilotClientOptions(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) - ) + client = CopilotClient(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) try: await client.start() @@ -57,9 +55,7 @@ async def test_should_list_available_custom_agents(self): @pytest.mark.asyncio async def test_should_return_null_when_no_agent_is_selected(self): """Test getCurrent returns null when no agent is selected.""" - client = CopilotClient( - CopilotClientOptions(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) - ) + client = CopilotClient(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) try: await client.start() @@ -86,9 +82,7 @@ async def test_should_return_null_when_no_agent_is_selected(self): @pytest.mark.asyncio async def test_should_select_and_get_current_agent(self): """Test selecting an agent and verifying getCurrent returns it.""" - client = CopilotClient( - CopilotClientOptions(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) - ) + client = CopilotClient(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) try: await client.start() @@ -123,9 +117,7 @@ async def test_should_select_and_get_current_agent(self): @pytest.mark.asyncio async def test_should_deselect_current_agent(self): """Test deselecting the current agent.""" - client = CopilotClient( - CopilotClientOptions(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) - ) + client = CopilotClient(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) try: await client.start() @@ -157,9 +149,7 @@ async def test_should_deselect_current_agent(self): @pytest.mark.asyncio async def test_should_return_empty_list_when_no_custom_agents_configured(self): """Test listing agents returns no custom agents when none configured.""" - client = CopilotClient( - CopilotClientOptions(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) - ) + client = CopilotClient(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) try: await client.start() @@ -184,9 +174,7 @@ async def test_should_return_empty_list_when_no_custom_agents_configured(self): @pytest.mark.asyncio async def test_should_call_agent_reload(self): """Test reloading agents via RPC.""" - client = CopilotClient( - CopilotClientOptions(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) - ) + client = CopilotClient(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) reload_agent = { "name": f"reload-test-agent-{uuid.uuid4().hex}", "display_name": "Reload Agent", diff --git a/python/e2e/test_client_e2e.py b/python/e2e/test_client_e2e.py index f3d3c8d44..1e8ea82e5 100644 --- a/python/e2e/test_client_e2e.py +++ b/python/e2e/test_client_e2e.py @@ -2,7 +2,7 @@ import pytest -from copilot import CopilotClient, CopilotClientOptions, RuntimeConnection +from copilot import CopilotClient, RuntimeConnection from copilot.client import ( ModelCapabilities, ModelInfo, @@ -18,9 +18,7 @@ class TestClient: @pytest.mark.asyncio async def test_should_start_and_connect_to_server_using_stdio(self): - client = CopilotClient( - CopilotClientOptions(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) - ) + client = CopilotClient(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) try: await client.start() @@ -35,9 +33,7 @@ async def test_should_start_and_connect_to_server_using_stdio(self): @pytest.mark.asyncio async def test_should_start_and_connect_to_server_using_tcp(self): - client = CopilotClient( - CopilotClientOptions(connection=RuntimeConnection.for_tcp(path=CLI_PATH)) - ) + client = CopilotClient(connection=RuntimeConnection.for_tcp(path=CLI_PATH)) try: await client.start() @@ -54,9 +50,7 @@ async def test_should_start_and_connect_to_server_using_tcp(self): async def test_should_raise_exception_group_on_failed_cleanup(self): import asyncio - client = CopilotClient( - CopilotClientOptions(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) - ) + client = CopilotClient(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) try: await client.create_session(on_permission_request=PermissionHandler.approve_all) @@ -78,18 +72,14 @@ async def test_should_raise_exception_group_on_failed_cleanup(self): @pytest.mark.asyncio async def test_should_force_stop_without_cleanup(self): - client = CopilotClient( - CopilotClientOptions(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) - ) + client = CopilotClient(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) await client.create_session(on_permission_request=PermissionHandler.approve_all) await client.force_stop() @pytest.mark.asyncio async def test_should_get_status_with_version_and_protocol_info(self): - client = CopilotClient( - CopilotClientOptions(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) - ) + client = CopilotClient(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) try: await client.start() @@ -107,9 +97,7 @@ async def test_should_get_status_with_version_and_protocol_info(self): @pytest.mark.asyncio async def test_should_get_auth_status(self): - client = CopilotClient( - CopilotClientOptions(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) - ) + client = CopilotClient(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) try: await client.start() @@ -127,9 +115,7 @@ async def test_should_get_auth_status(self): @pytest.mark.asyncio async def test_should_list_models_when_authenticated(self): - client = CopilotClient( - CopilotClientOptions(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) - ) + client = CopilotClient(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) try: await client.start() @@ -157,9 +143,7 @@ async def test_should_list_models_when_authenticated(self): @pytest.mark.asyncio async def test_should_cache_models_list(self): """Test that list_models caches results to avoid rate limiting""" - client = CopilotClient( - CopilotClientOptions(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) - ) + client = CopilotClient(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) try: await client.start() @@ -204,10 +188,8 @@ async def test_should_cache_models_list(self): async def test_should_report_error_with_stderr_when_cli_fails_to_start(self): """Test that CLI startup errors include stderr output in the error message.""" client = CopilotClient( - CopilotClientOptions( - connection=RuntimeConnection.for_stdio( - path=CLI_PATH, args=["--nonexistent-flag-for-testing"] - ) + connection=RuntimeConnection.for_stdio( + path=CLI_PATH, args=["--nonexistent-flag-for-testing"] ) ) @@ -239,9 +221,7 @@ async def test_should_report_error_with_stderr_when_cli_fails_to_start(self): @pytest.mark.asyncio async def test_should_not_throw_when_disposing_session_after_stopping_client(self): """Disconnecting a session after the client is stopped must not raise.""" - client = CopilotClient( - CopilotClientOptions(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) - ) + client = CopilotClient(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) try: await client.start() @@ -260,9 +240,7 @@ async def test_should_not_throw_when_disposing_session_after_stopping_client(sel @pytest.mark.asyncio async def test_should_create_session_without_permission_handler(self): """`create_session` allows omitting an `on_permission_request` handler.""" - client = CopilotClient( - CopilotClientOptions(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) - ) + client = CopilotClient(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) try: await client.start() @@ -277,9 +255,7 @@ async def test_should_create_session_without_permission_handler(self): @pytest.mark.asyncio async def test_should_resume_session_without_permission_handler(self): """`resume_session` allows omitting an `on_permission_request` handler.""" - client = CopilotClient( - CopilotClientOptions(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) - ) + client = CopilotClient(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) try: await client.start() @@ -314,10 +290,8 @@ def on_list_models(): return custom_models client = CopilotClient( - CopilotClientOptions( - connection=RuntimeConnection.for_stdio(path=CLI_PATH), - on_list_models=on_list_models, - ), + connection=RuntimeConnection.for_stdio(path=CLI_PATH), + on_list_models=on_list_models, ) try: @@ -354,10 +328,8 @@ def on_list_models(): return custom_models client = CopilotClient( - CopilotClientOptions( - connection=RuntimeConnection.for_stdio(path=CLI_PATH), - on_list_models=on_list_models, - ), + connection=RuntimeConnection.for_stdio(path=CLI_PATH), + on_list_models=on_list_models, ) try: diff --git a/python/e2e/test_client_lifecycle_e2e.py b/python/e2e/test_client_lifecycle_e2e.py index 7dcc20ba5..d5a2fb681 100644 --- a/python/e2e/test_client_lifecycle_e2e.py +++ b/python/e2e/test_client_lifecycle_e2e.py @@ -14,7 +14,7 @@ import pytest -from copilot import CopilotClient, CopilotClientOptions, RuntimeConnection +from copilot import CopilotClient, RuntimeConnection from copilot.session import PermissionHandler from .testharness import E2ETestContext @@ -59,12 +59,10 @@ def _make_isolated_client(ctx: E2ETestContext) -> CopilotClient: "fake-token-for-e2e-tests" if os.environ.get("GITHUB_ACTIONS") == "true" else None ) return CopilotClient( - CopilotClientOptions( - connection=RuntimeConnection.for_stdio(path=ctx.cli_path), - working_directory=ctx.work_dir, - env=ctx.get_env(), - github_token=github_token, - ) + connection=RuntimeConnection.for_stdio(path=ctx.cli_path), + working_directory=ctx.work_dir, + env=ctx.get_env(), + github_token=github_token, ) diff --git a/python/e2e/test_client_options_e2e.py b/python/e2e/test_client_options_e2e.py index f2d898fb8..706ca4dcc 100644 --- a/python/e2e/test_client_options_e2e.py +++ b/python/e2e/test_client_options_e2e.py @@ -20,7 +20,7 @@ import pytest -from copilot import CopilotClient, CopilotClientOptions, RuntimeConnection +from copilot import CopilotClient, RuntimeConnection from copilot.generated.rpc import PingRequest from copilot.session import PermissionHandler @@ -38,10 +38,10 @@ def _make_options( cli_path: str | None = None, cli_args: list[str] | None = None, **overrides, -) -> CopilotClientOptions: - """Build a ``CopilotClientOptions`` pre-populated for the test harness.""" +) -> dict[str, object]: + """Build CopilotClient kwargs pre-populated for the test harness.""" if use_tcp: - connection = RuntimeConnection.for_tcp( + connection: RuntimeConnection = RuntimeConnection.for_tcp( port=port, connection_token=connection_token, path=cli_path if cli_path is not None else ctx.cli_path, @@ -52,7 +52,7 @@ def _make_options( path=cli_path if cli_path is not None else ctx.cli_path, args=tuple(cli_args or []), ) - base = { + base: dict[str, object] = { "connection": connection, "working_directory": ctx.work_dir, "env": ctx.get_env(), @@ -61,7 +61,7 @@ def _make_options( ), } base.update(overrides) - return CopilotClientOptions(**base) + return base def _get_available_port() -> int: @@ -166,7 +166,7 @@ def _assert_arg_value(args: list[str], name: str, expected_value: str) -> None: class TestClientOptions: async def test_should_listen_on_configured_tcp_port(self, ctx: E2ETestContext): port = _get_available_port() - client = CopilotClient(_make_options(ctx, use_tcp=True, port=port)) + client = CopilotClient(**_make_options(ctx, use_tcp=True, port=port)) try: await client.start() assert client.runtime_port == port @@ -182,7 +182,7 @@ async def test_should_use_client_cwd_for_default_workingdirectory(self, ctx: E2E with open(os.path.join(client_cwd, "marker.txt"), "w") as f: f.write("I am in the client cwd") - client = CopilotClient(_make_options(ctx, working_directory=client_cwd)) + client = CopilotClient(**_make_options(ctx, working_directory=client_cwd)) try: session = await client.create_session( on_permission_request=PermissionHandler.approve_all, @@ -272,51 +272,3 @@ async def test_should_propagate_process_options_to_spawned_cli(self, ctx: E2ETes await client.stop() except Exception: await client.force_stop() - - -# --------------------------------------------------------------------------- -# Unit-style tests mirroring the property-only tests in -# dotnet/test/ClientOptionsTests.cs. These exercise the CopilotClientOptions -# dataclass shape only — no client / proxy required. -# --------------------------------------------------------------------------- - - -class TestSubprocessOptions: - """Mirrors the unit-style ClientOptions tests in the C# baseline.""" - - async def test_should_accept_github_token_option(self): - # Mirrors: Should_Accept_GitHubToken_Option - config = CopilotClientOptions(github_token="gho_test_token") - assert config.github_token == "gho_test_token" - - async def test_should_default_use_logged_in_user_to_none(self): - # Mirrors: Should_Default_UseLoggedInUser_To_Null - config = CopilotClientOptions() - assert config.use_logged_in_user is None - - async def test_should_allow_explicit_use_logged_in_user_false(self): - # Mirrors: Should_Allow_Explicit_UseLoggedInUser_False - config = CopilotClientOptions(use_logged_in_user=False) - assert config.use_logged_in_user is False - - async def test_should_allow_explicit_use_logged_in_user_true_with_github_token(self): - # Mirrors: Should_Allow_Explicit_UseLoggedInUser_True_With_GitHubToken - config = CopilotClientOptions(github_token="gho_test_token", use_logged_in_user=True) - assert config.use_logged_in_user is True - assert config.github_token == "gho_test_token" - - # NOTE: Should_Throw_When_GitHubToken_Used_With_CliUrl and - # Should_Throw_When_UseLoggedInUser_Used_With_CliUrl from the C# baseline - # do not apply to Python: ExternalServerConfig has no github_token / - # use_logged_in_user fields at all (they live only on CopilotClientOptions), - # so the conflicting configuration is impossible to express. - - async def test_should_default_session_idle_timeout_seconds_to_none(self): - # Mirrors: Should_Default_SessionIdleTimeoutSeconds_To_Null - config = CopilotClientOptions() - assert config.session_idle_timeout_seconds is None - - async def test_should_accept_session_idle_timeout_seconds_option(self): - # Mirrors: Should_Accept_SessionIdleTimeoutSeconds_Option - config = CopilotClientOptions(session_idle_timeout_seconds=600) - assert config.session_idle_timeout_seconds == 600 diff --git a/python/e2e/test_commands_e2e.py b/python/e2e/test_commands_e2e.py index 305807702..e0a0d63f1 100644 --- a/python/e2e/test_commands_e2e.py +++ b/python/e2e/test_commands_e2e.py @@ -15,7 +15,7 @@ import pytest import pytest_asyncio -from copilot import CopilotClient, CopilotClientOptions, RuntimeConnection +from copilot import CopilotClient, RuntimeConnection from copilot.session import CommandDefinition, PermissionHandler from .testharness.context import SNAPSHOTS_DIR, get_cli_path_for_tests @@ -55,14 +55,12 @@ async def setup(self): # Client 1 uses TCP mode so a second client can connect self._client1 = CopilotClient( - CopilotClientOptions( - connection=RuntimeConnection.for_tcp( - path=self.cli_path, connection_token="py-tcp-shared-test-token" - ), - working_directory=self.work_dir, - env=self._get_env(), - github_token=github_token, - ) + connection=RuntimeConnection.for_tcp( + path=self.cli_path, connection_token="py-tcp-shared-test-token" + ), + working_directory=self.work_dir, + env=self._get_env(), + github_token=github_token, ) # Trigger connection to get the port @@ -75,10 +73,8 @@ async def setup(self): assert actual_port is not None self._client2 = CopilotClient( - CopilotClientOptions( - connection=RuntimeConnection.for_uri( - f"localhost:{actual_port}", connection_token="py-tcp-shared-test-token" - ) + connection=RuntimeConnection.for_uri( + f"localhost:{actual_port}", connection_token="py-tcp-shared-test-token" ) ) diff --git a/python/e2e/test_connection_token.py b/python/e2e/test_connection_token.py index 509486446..1c7addbd9 100644 --- a/python/e2e/test_connection_token.py +++ b/python/e2e/test_connection_token.py @@ -11,7 +11,7 @@ import pytest import pytest_asyncio -from copilot import CopilotClient, CopilotClientOptions, RuntimeConnection +from copilot import CopilotClient, RuntimeConnection from copilot.session import PermissionHandler from .testharness.proxy import CapiProxy @@ -46,14 +46,10 @@ async def setup(self): ) self._client = CopilotClient( - CopilotClientOptions( - connection=RuntimeConnection.for_tcp( - path=self.cli_path, connection_token=self.token - ), - working_directory=self.work_dir, - env=self.get_env(), - github_token=github_token, - ) + connection=RuntimeConnection.for_tcp(path=self.cli_path, connection_token=self.token), + working_directory=self.work_dir, + env=self.get_env(), + github_token=github_token, ) # Trigger the spawn + connect handshake so the server is listening. @@ -136,9 +132,7 @@ async def test_wrong_token_is_rejected(self, explicit_token_ctx: ConnectionToken assert port is not None wrong = CopilotClient( - CopilotClientOptions( - connection=RuntimeConnection.for_uri(f"localhost:{port}", connection_token="wrong") - ) + connection=RuntimeConnection.for_uri(f"localhost:{port}", connection_token="wrong") ) try: with pytest.raises(Exception, match="AUTHENTICATION_FAILED"): @@ -156,9 +150,7 @@ async def test_missing_token_is_rejected(self, explicit_token_ctx: ConnectionTok port = explicit_token_ctx.client.runtime_port assert port is not None - no_token = CopilotClient( - CopilotClientOptions(connection=RuntimeConnection.for_uri(f"localhost:{port}")) - ) + no_token = CopilotClient(connection=RuntimeConnection.for_uri(f"localhost:{port}")) try: with pytest.raises(Exception, match="AUTHENTICATION_FAILED"): await no_token.start() diff --git a/python/e2e/test_multi_client_e2e.py b/python/e2e/test_multi_client_e2e.py index 8ebe62c18..deadbfc86 100644 --- a/python/e2e/test_multi_client_e2e.py +++ b/python/e2e/test_multi_client_e2e.py @@ -14,7 +14,7 @@ import pytest_asyncio from pydantic import BaseModel, Field -from copilot import CopilotClient, CopilotClientOptions, RuntimeConnection, define_tool +from copilot import CopilotClient, RuntimeConnection, define_tool from copilot.generated.rpc import PermissionDecisionApproveOnce, PermissionDecisionReject from copilot.session import PermissionHandler, PermissionNoResult from copilot.tools import ToolInvocation @@ -53,14 +53,12 @@ async def setup(self): # Client 1 uses TCP mode so a second client can connect to the same server self._client1 = CopilotClient( - CopilotClientOptions( - connection=RuntimeConnection.for_tcp( - path=self.cli_path, connection_token="py-tcp-shared-test-token" - ), - working_directory=self.work_dir, - env=self.get_env(), - github_token=github_token, - ) + connection=RuntimeConnection.for_tcp( + path=self.cli_path, connection_token="py-tcp-shared-test-token" + ), + working_directory=self.work_dir, + env=self.get_env(), + github_token=github_token, ) # Trigger connection by creating and disconnecting an init session @@ -74,10 +72,8 @@ async def setup(self): assert actual_port is not None, "Client 1 should have an actual port after connecting" self._client2 = CopilotClient( - CopilotClientOptions( - connection=RuntimeConnection.for_uri( - f"localhost:{actual_port}", connection_token="py-tcp-shared-test-token" - ) + connection=RuntimeConnection.for_uri( + f"localhost:{actual_port}", connection_token="py-tcp-shared-test-token" ) ) @@ -429,10 +425,8 @@ def ephemeral_tool(params: InputParams, invocation: ToolInvocation) -> str: # Recreate client2 for future tests (but don't rejoin the session) actual_port = mctx.client1.runtime_port mctx._client2 = CopilotClient( - CopilotClientOptions( - connection=RuntimeConnection.for_uri( - f"localhost:{actual_port}", connection_token="py-tcp-shared-test-token" - ) + connection=RuntimeConnection.for_uri( + f"localhost:{actual_port}", connection_token="py-tcp-shared-test-token" ) ) diff --git a/python/e2e/test_pending_work_resume_e2e.py b/python/e2e/test_pending_work_resume_e2e.py index 38c071d09..4b1dfbff8 100644 --- a/python/e2e/test_pending_work_resume_e2e.py +++ b/python/e2e/test_pending_work_resume_e2e.py @@ -16,7 +16,7 @@ import pytest -from copilot import CopilotClient, CopilotClientOptions, RuntimeConnection +from copilot import CopilotClient, RuntimeConnection from copilot.generated.rpc import ( HandlePendingToolCallRequest, PermissionDecisionRequest, @@ -43,12 +43,10 @@ def _make_subprocess_client(ctx: E2ETestContext, *, use_stdio: bool = True) -> C path=ctx.cli_path, connection_token="py-tcp-shared-test-token" ) return CopilotClient( - CopilotClientOptions( - connection=connection, - working_directory=ctx.work_dir, - env=ctx.get_env(), - github_token=github_token, - ) + connection=connection, + working_directory=ctx.work_dir, + env=ctx.get_env(), + github_token=github_token, ) @@ -156,10 +154,8 @@ def original_tool_handler(args): return f"ORIGINAL_SHOULD_NOT_RUN_{args.get('value', '')}" suspended_client = CopilotClient( - CopilotClientOptions( - connection=RuntimeConnection.for_uri( - cli_url, connection_token="py-tcp-shared-test-token" - ) + connection=RuntimeConnection.for_uri( + cli_url, connection_token="py-tcp-shared-test-token" ) ) session1 = await suspended_client.create_session( @@ -186,10 +182,8 @@ def resumed_tool_handler(args): return f"PERMISSION_RESUMED_{args['value'].upper()}" resumed_client = CopilotClient( - CopilotClientOptions( - connection=RuntimeConnection.for_uri( - cli_url, connection_token="py-tcp-shared-test-token" - ) + connection=RuntimeConnection.for_uri( + cli_url, connection_token="py-tcp-shared-test-token" ) ) try: @@ -245,10 +239,8 @@ async def blocking_external_tool(args): return await release_original suspended_client = CopilotClient( - CopilotClientOptions( - connection=RuntimeConnection.for_uri( - cli_url, connection_token="py-tcp-shared-test-token" - ) + connection=RuntimeConnection.for_uri( + cli_url, connection_token="py-tcp-shared-test-token" ) ) session1 = await suspended_client.create_session( @@ -270,10 +262,8 @@ async def blocking_external_tool(args): await suspended_client.force_stop() resumed_client = CopilotClient( - CopilotClientOptions( - connection=RuntimeConnection.for_uri( - cli_url, connection_token="py-tcp-shared-test-token" - ) + connection=RuntimeConnection.for_uri( + cli_url, connection_token="py-tcp-shared-test-token" ) ) try: @@ -329,10 +319,8 @@ async def tool_b(args): return await release_b suspended_client = CopilotClient( - CopilotClientOptions( - connection=RuntimeConnection.for_uri( - cli_url, connection_token="py-tcp-shared-test-token" - ) + connection=RuntimeConnection.for_uri( + cli_url, connection_token="py-tcp-shared-test-token" ) ) session1 = await suspended_client.create_session( @@ -364,10 +352,8 @@ async def tool_b(args): await suspended_client.force_stop() resumed_client = CopilotClient( - CopilotClientOptions( - connection=RuntimeConnection.for_uri( - cli_url, connection_token="py-tcp-shared-test-token" - ) + connection=RuntimeConnection.for_uri( + cli_url, connection_token="py-tcp-shared-test-token" ) ) try: @@ -412,10 +398,8 @@ async def test_should_resume_successfully_when_no_pending_work_exists( cli_url = f"localhost:{server.runtime_port}" first_client = CopilotClient( - CopilotClientOptions( - connection=RuntimeConnection.for_uri( - cli_url, connection_token="py-tcp-shared-test-token" - ) + connection=RuntimeConnection.for_uri( + cli_url, connection_token="py-tcp-shared-test-token" ) ) try: @@ -432,10 +416,8 @@ async def test_should_resume_successfully_when_no_pending_work_exists( await _safe_force_stop(first_client) resumed_client = CopilotClient( - CopilotClientOptions( - connection=RuntimeConnection.for_uri( - cli_url, connection_token="py-tcp-shared-test-token" - ) + connection=RuntimeConnection.for_uri( + cli_url, connection_token="py-tcp-shared-test-token" ) ) try: @@ -477,10 +459,8 @@ async def blocking_external_tool(args): cli_url = f"localhost:{server.runtime_port}" suspended_client = CopilotClient( - CopilotClientOptions( - connection=RuntimeConnection.for_uri( - cli_url, connection_token="py-tcp-shared-test-token" - ) + connection=RuntimeConnection.for_uri( + cli_url, connection_token="py-tcp-shared-test-token" ) ) session1 = await suspended_client.create_session( @@ -502,10 +482,8 @@ async def blocking_external_tool(args): await suspended_client.force_stop() resumed_client = CopilotClient( - CopilotClientOptions( - connection=RuntimeConnection.for_uri( - cli_url, connection_token="py-tcp-shared-test-token" - ) + connection=RuntimeConnection.for_uri( + cli_url, connection_token="py-tcp-shared-test-token" ) ) try: @@ -558,10 +536,8 @@ async def test_should_report_continuependingwork_true_in_resume_event( cli_url = f"localhost:{server.runtime_port}" first_client = CopilotClient( - CopilotClientOptions( - connection=RuntimeConnection.for_uri( - cli_url, connection_token="py-tcp-shared-test-token" - ) + connection=RuntimeConnection.for_uri( + cli_url, connection_token="py-tcp-shared-test-token" ) ) try: @@ -579,10 +555,8 @@ async def test_should_report_continuependingwork_true_in_resume_event( await _safe_force_stop(first_client) resumed_client = CopilotClient( - CopilotClientOptions( - connection=RuntimeConnection.for_uri( - cli_url, connection_token="py-tcp-shared-test-token" - ) + connection=RuntimeConnection.for_uri( + cli_url, connection_token="py-tcp-shared-test-token" ) ) try: diff --git a/python/e2e/test_per_session_auth_e2e.py b/python/e2e/test_per_session_auth_e2e.py index 092cb8642..0aa42cdaa 100644 --- a/python/e2e/test_per_session_auth_e2e.py +++ b/python/e2e/test_per_session_auth_e2e.py @@ -2,7 +2,7 @@ import pytest -from copilot.client import CopilotClient, CopilotClientOptions, RuntimeConnection +from copilot.client import CopilotClient, RuntimeConnection from copilot.session import PermissionHandler from .testharness import E2ETestContext @@ -97,12 +97,10 @@ async def test_should_return_unauthenticated_when_no_token_provided( env = without_auth_env(auth_ctx.get_env()) env["COPILOT_DEBUG_GITHUB_API_URL"] = auth_ctx.proxy_url no_token_client = CopilotClient( - CopilotClientOptions( - connection=RuntimeConnection.for_stdio(path=auth_ctx.cli_path), - working_directory=auth_ctx.work_dir, - env=env, - use_logged_in_user=False, - ) + connection=RuntimeConnection.for_stdio(path=auth_ctx.cli_path), + working_directory=auth_ctx.work_dir, + env=env, + use_logged_in_user=False, ) try: diff --git a/python/e2e/test_rpc_e2e.py b/python/e2e/test_rpc_e2e.py index 837574526..b825db060 100644 --- a/python/e2e/test_rpc_e2e.py +++ b/python/e2e/test_rpc_e2e.py @@ -2,7 +2,7 @@ import pytest -from copilot import CopilotClient, CopilotClientOptions, RuntimeConnection +from copilot import CopilotClient, RuntimeConnection from copilot.generated.rpc import ModelsListRequest, PingRequest from copilot.session import PermissionHandler @@ -15,9 +15,7 @@ class TestRpc: @pytest.mark.asyncio async def test_should_call_rpc_ping_with_typed_params(self): """Test calling rpc.ping with typed params and result""" - client = CopilotClient( - CopilotClientOptions(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) - ) + client = CopilotClient(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) try: await client.start() @@ -33,9 +31,7 @@ async def test_should_call_rpc_ping_with_typed_params(self): @pytest.mark.asyncio async def test_should_call_rpc_models_list(self): """Test calling rpc.models.list with typed result""" - client = CopilotClient( - CopilotClientOptions(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) - ) + client = CopilotClient(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) try: await client.start() @@ -58,9 +54,7 @@ async def test_should_call_rpc_models_list(self): @pytest.mark.asyncio async def test_should_call_rpc_account_get_quota(self): """Test calling rpc.account.getQuota when authenticated""" - client = CopilotClient( - CopilotClientOptions(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) - ) + client = CopilotClient(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) try: await client.start() @@ -121,9 +115,7 @@ async def test_get_and_set_session_mode(self): """Test getting and setting session mode""" from copilot.generated.rpc import ModeSetRequest, SessionMode - client = CopilotClient( - CopilotClientOptions(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) - ) + client = CopilotClient(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) try: await client.start() @@ -155,9 +147,7 @@ async def test_read_update_and_delete_plan(self): """Test reading, updating, and deleting plan""" from copilot.generated.rpc import PlanUpdateRequest - client = CopilotClient( - CopilotClientOptions(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) - ) + client = CopilotClient(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) try: await client.start() @@ -200,9 +190,7 @@ async def test_create_list_and_read_workspace_files(self): WorkspacesReadFileRequest, ) - client = CopilotClient( - CopilotClientOptions(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) - ) + client = CopilotClient(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) try: await client.start() diff --git a/python/e2e/test_rpc_server_e2e.py b/python/e2e/test_rpc_server_e2e.py index 6180d754a..481e50d7b 100644 --- a/python/e2e/test_rpc_server_e2e.py +++ b/python/e2e/test_rpc_server_e2e.py @@ -12,7 +12,7 @@ import pytest -from copilot import CopilotClient, CopilotClientOptions, RuntimeConnection +from copilot import CopilotClient, RuntimeConnection from copilot.generated.rpc import ( AccountGetQuotaRequest, MCPDiscoverRequest, @@ -55,12 +55,10 @@ def _make_authed_client(ctx: E2ETestContext, token: str) -> CopilotClient: env = ctx.get_env() env["COPILOT_DEBUG_GITHUB_API_URL"] = ctx.proxy_url return CopilotClient( - CopilotClientOptions( - connection=RuntimeConnection.for_stdio(path=ctx.cli_path), - working_directory=ctx.work_dir, - env=env, - github_token=token, - ) + connection=RuntimeConnection.for_stdio(path=ctx.cli_path), + working_directory=ctx.work_dir, + env=env, + github_token=token, ) diff --git a/python/e2e/test_session_e2e.py b/python/e2e/test_session_e2e.py index fffdedf7a..db9b61e24 100644 --- a/python/e2e/test_session_e2e.py +++ b/python/e2e/test_session_e2e.py @@ -6,7 +6,7 @@ import pytest -from copilot import CopilotClient, CopilotClientOptions, RuntimeConnection +from copilot import CopilotClient, RuntimeConnection from copilot.generated.session_events import SessionModelChangeData from copilot.session import PermissionHandler from copilot.tools import Tool, ToolResult @@ -243,12 +243,10 @@ async def test_should_resume_a_session_using_a_new_client(self, ctx: E2ETestCont "fake-token-for-e2e-tests" if os.environ.get("GITHUB_ACTIONS") == "true" else None ) new_client = CopilotClient( - CopilotClientOptions( - connection=RuntimeConnection.for_stdio(path=ctx.cli_path), - working_directory=ctx.work_dir, - env=ctx.get_env(), - github_token=github_token, - ) + connection=RuntimeConnection.for_stdio(path=ctx.cli_path), + working_directory=ctx.work_dir, + env=ctx.get_env(), + github_token=github_token, ) try: diff --git a/python/e2e/test_session_fs_e2e.py b/python/e2e/test_session_fs_e2e.py index b4afb2226..9d00057ec 100644 --- a/python/e2e/test_session_fs_e2e.py +++ b/python/e2e/test_session_fs_e2e.py @@ -14,7 +14,6 @@ from copilot import ( CopilotClient, - CopilotClientOptions, RuntimeConnection, SessionFsConfig, define_tool, @@ -50,13 +49,11 @@ @pytest_asyncio.fixture(scope="module", loop_scope="module") async def session_fs_client(ctx: E2ETestContext): client = CopilotClient( - CopilotClientOptions( - connection=RuntimeConnection.for_stdio(path=ctx.cli_path), - working_directory=ctx.work_dir, - env=ctx.get_env(), - github_token=DEFAULT_GITHUB_TOKEN, - session_fs=SESSION_FS_CONFIG, - ) + connection=RuntimeConnection.for_stdio(path=ctx.cli_path), + working_directory=ctx.work_dir, + env=ctx.get_env(), + github_token=DEFAULT_GITHUB_TOKEN, + session_fs=SESSION_FS_CONFIG, ) yield client try: @@ -122,12 +119,10 @@ async def test_should_load_session_data_from_fs_provider_on_resume( async def test_should_reject_setprovider_when_sessions_already_exist(self, ctx: E2ETestContext): client1 = CopilotClient( - CopilotClientOptions( - connection=RuntimeConnection.for_tcp(path=ctx.cli_path), - working_directory=ctx.work_dir, - env=ctx.get_env(), - github_token=DEFAULT_GITHUB_TOKEN, - ) + connection=RuntimeConnection.for_tcp(path=ctx.cli_path), + working_directory=ctx.work_dir, + env=ctx.get_env(), + github_token=DEFAULT_GITHUB_TOKEN, ) session = None client2 = None @@ -140,10 +135,8 @@ async def test_should_reject_setprovider_when_sessions_already_exist(self, ctx: assert actual_port is not None client2 = CopilotClient( - CopilotClientOptions( - connection=RuntimeConnection.for_uri(f"localhost:{actual_port}"), - session_fs=SESSION_FS_CONFIG, - ) + connection=RuntimeConnection.for_uri(f"localhost:{actual_port}"), + session_fs=SESSION_FS_CONFIG, ) with pytest.raises(Exception): diff --git a/python/e2e/test_session_fs_sqlite_e2e.py b/python/e2e/test_session_fs_sqlite_e2e.py index df89939b6..565c55336 100644 --- a/python/e2e/test_session_fs_sqlite_e2e.py +++ b/python/e2e/test_session_fs_sqlite_e2e.py @@ -12,7 +12,7 @@ import pytest import pytest_asyncio -from copilot import CopilotClient, CopilotClientOptions, RuntimeConnection, SessionFsConfig +from copilot import CopilotClient, RuntimeConnection, SessionFsConfig from copilot.generated.rpc import ( SessionFSReaddirWithTypesEntry, SessionFSReaddirWithTypesEntryType, @@ -199,13 +199,11 @@ def factory(session): @pytest_asyncio.fixture(scope="module", loop_scope="module") async def sqlite_client(ctx: E2ETestContext): client = CopilotClient( - CopilotClientOptions( - connection=RuntimeConnection.for_stdio(path=ctx.cli_path), - working_directory=ctx.work_dir, - env=ctx.get_env(), - github_token=DEFAULT_GITHUB_TOKEN, - session_fs=SESSION_FS_CONFIG, - ) + connection=RuntimeConnection.for_stdio(path=ctx.cli_path), + working_directory=ctx.work_dir, + env=ctx.get_env(), + github_token=DEFAULT_GITHUB_TOKEN, + session_fs=SESSION_FS_CONFIG, ) yield client try: diff --git a/python/e2e/test_streaming_fidelity_e2e.py b/python/e2e/test_streaming_fidelity_e2e.py index b389eeb45..79b34fc91 100644 --- a/python/e2e/test_streaming_fidelity_e2e.py +++ b/python/e2e/test_streaming_fidelity_e2e.py @@ -4,7 +4,7 @@ import pytest -from copilot import CopilotClient, CopilotClientOptions, RuntimeConnection +from copilot import CopilotClient, RuntimeConnection from copilot.session import PermissionHandler from .testharness import E2ETestContext @@ -78,12 +78,10 @@ async def test_should_produce_deltas_after_session_resume(self, ctx: E2ETestCont "fake-token-for-e2e-tests" if os.environ.get("GITHUB_ACTIONS") == "true" else None ) new_client = CopilotClient( - CopilotClientOptions( - connection=RuntimeConnection.for_stdio(path=ctx.cli_path), - working_directory=ctx.work_dir, - env=ctx.get_env(), - github_token=github_token, - ) + connection=RuntimeConnection.for_stdio(path=ctx.cli_path), + working_directory=ctx.work_dir, + env=ctx.get_env(), + github_token=github_token, ) try: @@ -130,12 +128,10 @@ async def test_should_not_produce_deltas_after_session_resume_with_streaming_dis # Resume with streaming disabled new_client = CopilotClient( - CopilotClientOptions( - connection=RuntimeConnection.for_stdio(path=ctx.cli_path), - working_directory=ctx.work_dir, - env=ctx.get_env(), - github_token=github_token, - ) + connection=RuntimeConnection.for_stdio(path=ctx.cli_path), + working_directory=ctx.work_dir, + env=ctx.get_env(), + github_token=github_token, ) try: session2 = await new_client.resume_session( diff --git a/python/e2e/test_subagent_hooks_e2e.py b/python/e2e/test_subagent_hooks_e2e.py index 434ed704b..1ca2a54c1 100644 --- a/python/e2e/test_subagent_hooks_e2e.py +++ b/python/e2e/test_subagent_hooks_e2e.py @@ -7,7 +7,7 @@ import pytest -from copilot.client import CopilotClient, CopilotClientOptions, RuntimeConnection +from copilot.client import CopilotClient, RuntimeConnection from copilot.session import PermissionHandler from .testharness import E2ETestContext @@ -50,12 +50,10 @@ async def on_post_tool_use(input_data, invocation): "fake-token-for-e2e-tests" if os.environ.get("GITHUB_ACTIONS") == "true" else None ) client = CopilotClient( - CopilotClientOptions( - connection=RuntimeConnection.for_stdio(path=ctx.cli_path), - working_directory=ctx.work_dir, - env=env, - github_token=github_token, - ) + connection=RuntimeConnection.for_stdio(path=ctx.cli_path), + working_directory=ctx.work_dir, + env=env, + github_token=github_token, ) session = await client.create_session( diff --git a/python/e2e/test_suspend_e2e.py b/python/e2e/test_suspend_e2e.py index 2aa16bba3..b0f74140c 100644 --- a/python/e2e/test_suspend_e2e.py +++ b/python/e2e/test_suspend_e2e.py @@ -14,7 +14,7 @@ import pytest -from copilot import CopilotClient, CopilotClientOptions, RuntimeConnection +from copilot import CopilotClient, RuntimeConnection from copilot.generated.rpc import PermissionDecisionUserNotAvailable from copilot.session import PermissionHandler from copilot.tools import Tool, ToolInvocation, ToolResult @@ -37,12 +37,10 @@ def _make_subprocess_client(ctx: E2ETestContext, *, use_stdio: bool = True) -> C path=ctx.cli_path, connection_token="py-tcp-shared-test-token" ) return CopilotClient( - CopilotClientOptions( - connection=connection, - working_directory=ctx.work_dir, - env=ctx.get_env(), - github_token=github_token, - ) + connection=connection, + working_directory=ctx.work_dir, + env=ctx.get_env(), + github_token=github_token, ) @@ -107,10 +105,8 @@ async def test_should_allow_resume_and_continue_conversation_after_suspend( session_id: str first_client = CopilotClient( - CopilotClientOptions( - connection=RuntimeConnection.for_uri( - cli_url, connection_token="py-tcp-shared-test-token" - ) + connection=RuntimeConnection.for_uri( + cli_url, connection_token="py-tcp-shared-test-token" ) ) try: @@ -128,10 +124,8 @@ async def test_should_allow_resume_and_continue_conversation_after_suspend( await _safe_force_stop(first_client) resumed_client = CopilotClient( - CopilotClientOptions( - connection=RuntimeConnection.for_uri( - cli_url, connection_token="py-tcp-shared-test-token" - ) + connection=RuntimeConnection.for_uri( + cli_url, connection_token="py-tcp-shared-test-token" ) ) try: diff --git a/python/e2e/test_telemetry_e2e.py b/python/e2e/test_telemetry_e2e.py index 8d37beb90..f18a9fb88 100644 --- a/python/e2e/test_telemetry_e2e.py +++ b/python/e2e/test_telemetry_e2e.py @@ -22,7 +22,7 @@ import pytest -from copilot import CopilotClient, CopilotClientOptions, RuntimeConnection, TelemetryConfig +from copilot import CopilotClient, RuntimeConnection, TelemetryConfig from copilot._telemetry import get_trace_context, trace_context from copilot.session import PermissionHandler from copilot.tools import Tool, ToolInvocation, ToolResult @@ -81,18 +81,16 @@ def echo(invocation: ToolInvocation) -> ToolResult: "fake-token-for-e2e-tests" if os.environ.get("GITHUB_ACTIONS") == "true" else None ) client = CopilotClient( - CopilotClientOptions( - connection=RuntimeConnection.for_stdio(path=ctx.cli_path), - working_directory=ctx.work_dir, - env=ctx.get_env(), - github_token=github_token, - telemetry=TelemetryConfig( - file_path=str(telemetry_path), - exporter_type="file", - source_name=source_name, - capture_content=True, - ), - ) + connection=RuntimeConnection.for_stdio(path=ctx.cli_path), + working_directory=ctx.work_dir, + env=ctx.get_env(), + github_token=github_token, + telemetry=TelemetryConfig( + file_path=str(telemetry_path), + exporter_type="file", + source_name=source_name, + capture_content=True, + ), ) try: @@ -208,18 +206,6 @@ async def test_can_set_all_properties(self): assert cfg["capture_content"] is True -class TestSubprocessConfigTelemetry: - """Mirrors CopilotClientOptions_Telemetry_DefaultsToNull.""" - - async def test_telemetry_defaults_to_none(self): - config = CopilotClientOptions(connection=RuntimeConnection.for_stdio()) - assert config.telemetry is None - - # NOTE: CopilotClientOptions_Clone_CopiesTelemetry from the C# baseline has - # no Python equivalent: SubprocessConfig is a plain dataclass with no - # Clone() method, so there is nothing meaningful to test. - - class TestTelemetryHelpers: """Mirrors TelemetryHelpers_Restores_W3C_Trace_Context.""" diff --git a/python/e2e/test_ui_elicitation_multi_client_e2e.py b/python/e2e/test_ui_elicitation_multi_client_e2e.py index e683a63ac..398b83ee8 100644 --- a/python/e2e/test_ui_elicitation_multi_client_e2e.py +++ b/python/e2e/test_ui_elicitation_multi_client_e2e.py @@ -16,7 +16,7 @@ import pytest import pytest_asyncio -from copilot import CopilotClient, CopilotClientOptions, RuntimeConnection +from copilot import CopilotClient, RuntimeConnection from copilot.generated.session_events import CapabilitiesChangedData from copilot.session import ( ElicitationContext, @@ -62,14 +62,12 @@ async def setup(self): # Client 1 uses TCP mode so additional clients can connect self._client1 = CopilotClient( - CopilotClientOptions( - connection=RuntimeConnection.for_tcp( - path=self.cli_path, connection_token="py-tcp-shared-test-token" - ), - working_directory=self.work_dir, - env=self._get_env(), - github_token=github_token, - ) + connection=RuntimeConnection.for_tcp( + path=self.cli_path, connection_token="py-tcp-shared-test-token" + ), + working_directory=self.work_dir, + env=self._get_env(), + github_token=github_token, ) # Trigger connection to obtain the TCP port @@ -82,10 +80,8 @@ async def setup(self): assert self._actual_port is not None self._client2 = CopilotClient( - CopilotClientOptions( - connection=RuntimeConnection.for_uri( - f"localhost:{self._actual_port}", connection_token="py-tcp-shared-test-token" - ) + connection=RuntimeConnection.for_uri( + f"localhost:{self._actual_port}", connection_token="py-tcp-shared-test-token" ) ) @@ -139,10 +135,8 @@ def make_external_client(self) -> CopilotClient: """Create a new external client connected to the same CLI server.""" assert self._actual_port is not None return CopilotClient( - CopilotClientOptions( - connection=RuntimeConnection.for_uri( - f"localhost:{self._actual_port}", connection_token="py-tcp-shared-test-token" - ) + connection=RuntimeConnection.for_uri( + f"localhost:{self._actual_port}", connection_token="py-tcp-shared-test-token" ) ) diff --git a/python/e2e/testharness/context.py b/python/e2e/testharness/context.py index bf1d8cfe9..2ed439fbe 100644 --- a/python/e2e/testharness/context.py +++ b/python/e2e/testharness/context.py @@ -12,7 +12,7 @@ from pathlib import Path from typing import Any -from copilot import CopilotClient, CopilotClientOptions, RuntimeConnection +from copilot import CopilotClient, RuntimeConnection from .proxy import CapiProxy @@ -79,15 +79,13 @@ async def setup(self, cli_args: list[str] | None = None): # Create the shared client (like Node.js/Go do) self._client = CopilotClient( - CopilotClientOptions( - connection=RuntimeConnection.for_stdio( - path=self.cli_path, - args=tuple(cli_args or []), - ), - working_directory=self.work_dir, - env=self.get_env(), - github_token=DEFAULT_GITHUB_TOKEN, - ) + connection=RuntimeConnection.for_stdio( + path=self.cli_path, + args=tuple(cli_args or []), + ), + working_directory=self.work_dir, + env=self.get_env(), + github_token=DEFAULT_GITHUB_TOKEN, ) async def teardown(self, test_failed: bool = False): diff --git a/python/test_client.py b/python/test_client.py index 55a2092c3..2ed57657e 100644 --- a/python/test_client.py +++ b/python/test_client.py @@ -10,7 +10,6 @@ from copilot import ( CopilotClient, - CopilotClientOptions, RuntimeConnection, StdioRuntimeConnection, define_tool, @@ -30,9 +29,7 @@ class TestPermissionHandlerOptional: @pytest.mark.asyncio async def test_create_session_allows_missing_permission_handler(self): - client = CopilotClient( - CopilotClientOptions(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) - ) + client = CopilotClient(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) await client.start() try: session = await client.create_session() @@ -42,9 +39,7 @@ async def test_create_session_allows_missing_permission_handler(self): @pytest.mark.asyncio async def test_create_session_allows_none_permission_handler(self): - client = CopilotClient( - CopilotClientOptions(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) - ) + client = CopilotClient(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) await client.start() try: session = await client.create_session(on_permission_request=None) @@ -54,9 +49,7 @@ async def test_create_session_allows_none_permission_handler(self): @pytest.mark.asyncio async def test_v2_permission_adapter_rejects_no_result(self): - client = CopilotClient( - CopilotClientOptions(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) - ) + client = CopilotClient(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) await client.start() try: session = await client.create_session( @@ -80,9 +73,7 @@ async def test_v2_permission_adapter_rejects_no_result(self): @pytest.mark.asyncio async def test_resume_session_allows_none_permission_handler(self): - client = CopilotClient( - CopilotClientOptions(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) - ) + client = CopilotClient(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) await client.start() try: session = await client.create_session( @@ -97,9 +88,7 @@ async def test_resume_session_allows_none_permission_handler(self): class TestCreateSessionConfig: @pytest.mark.asyncio async def test_create_session_forwards_cloud_options(self): - client = CopilotClient( - CopilotClientOptions(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) - ) + client = CopilotClient(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) await client.start() try: captured = {} @@ -135,59 +124,47 @@ async def mock_request(method, params): class TestURLParsing: def test_parse_port_only_url(self): - client = CopilotClient(CopilotClientOptions(connection=RuntimeConnection.for_uri("8080"))) + client = CopilotClient(connection=RuntimeConnection.for_uri("8080")) assert client._runtime_port == 8080 assert client._actual_host == "localhost" assert client._is_external_server def test_parse_host_port_url(self): - client = CopilotClient( - CopilotClientOptions(connection=RuntimeConnection.for_uri("127.0.0.1:9000")) - ) + client = CopilotClient(connection=RuntimeConnection.for_uri("127.0.0.1:9000")) assert client._runtime_port == 9000 assert client._actual_host == "127.0.0.1" assert client._is_external_server def test_parse_http_url(self): - client = CopilotClient( - CopilotClientOptions(connection=RuntimeConnection.for_uri("http://localhost:7000")) - ) + client = CopilotClient(connection=RuntimeConnection.for_uri("http://localhost:7000")) assert client._runtime_port == 7000 assert client._actual_host == "localhost" assert client._is_external_server def test_parse_https_url(self): - client = CopilotClient( - CopilotClientOptions(connection=RuntimeConnection.for_uri("https://example.com:443")) - ) + client = CopilotClient(connection=RuntimeConnection.for_uri("https://example.com:443")) assert client._runtime_port == 443 assert client._actual_host == "example.com" assert client._is_external_server def test_invalid_url_format(self): with pytest.raises(ValueError, match="Invalid cli_url format"): - CopilotClient(CopilotClientOptions(connection=RuntimeConnection.for_uri("invalid-url"))) + CopilotClient(connection=RuntimeConnection.for_uri("invalid-url")) def test_invalid_port_too_high(self): with pytest.raises(ValueError, match="Invalid port in cli_url"): - CopilotClient( - CopilotClientOptions(connection=RuntimeConnection.for_uri("localhost:99999")) - ) + CopilotClient(connection=RuntimeConnection.for_uri("localhost:99999")) def test_invalid_port_zero(self): with pytest.raises(ValueError, match="Invalid port in cli_url"): - CopilotClient(CopilotClientOptions(connection=RuntimeConnection.for_uri("localhost:0"))) + CopilotClient(connection=RuntimeConnection.for_uri("localhost:0")) def test_invalid_port_negative(self): with pytest.raises(ValueError, match="Invalid port in cli_url"): - CopilotClient( - CopilotClientOptions(connection=RuntimeConnection.for_uri("localhost:-1")) - ) + CopilotClient(connection=RuntimeConnection.for_uri("localhost:-1")) def test_is_external_server_true(self): - client = CopilotClient( - CopilotClientOptions(connection=RuntimeConnection.for_uri("localhost:8080")) - ) + client = CopilotClient(connection=RuntimeConnection.for_uri("localhost:8080")) assert client._is_external_server @@ -195,83 +172,69 @@ class TestSessionFsConfig: def test_missing_initial_cwd(self): with pytest.raises(ValueError, match="session_fs.initial_working_directory is required"): CopilotClient( - CopilotClientOptions( - connection=RuntimeConnection.for_stdio(path=CLI_PATH), - log_level="error", - session_fs={ - "initial_working_directory": "", - "session_state_path": "/session-state", - "conventions": "posix", - }, - ) + connection=RuntimeConnection.for_stdio(path=CLI_PATH), + log_level="error", + session_fs={ + "initial_working_directory": "", + "session_state_path": "/session-state", + "conventions": "posix", + }, ) def test_missing_session_state_path(self): with pytest.raises(ValueError, match="session_fs.session_state_path is required"): CopilotClient( - CopilotClientOptions( - connection=RuntimeConnection.for_stdio(path=CLI_PATH), - log_level="error", - session_fs={ - "initial_working_directory": "/", - "session_state_path": "", - "conventions": "posix", - }, - ) + connection=RuntimeConnection.for_stdio(path=CLI_PATH), + log_level="error", + session_fs={ + "initial_working_directory": "/", + "session_state_path": "", + "conventions": "posix", + }, ) class TestAuthOptions: def test_accepts_github_token(self): client = CopilotClient( - CopilotClientOptions( - connection=RuntimeConnection.for_stdio(path=CLI_PATH), - github_token="gho_test_token", - log_level="error", - ) + connection=RuntimeConnection.for_stdio(path=CLI_PATH), + github_token="gho_test_token", + log_level="error", ) assert isinstance(client._options.connection, StdioRuntimeConnection) assert client._options.github_token == "gho_test_token" def test_default_use_logged_in_user_true_without_token(self): client = CopilotClient( - CopilotClientOptions( - connection=RuntimeConnection.for_stdio(path=CLI_PATH), log_level="error" - ) + connection=RuntimeConnection.for_stdio(path=CLI_PATH), log_level="error" ) assert isinstance(client._options.connection, StdioRuntimeConnection) assert client._options.use_logged_in_user is True def test_default_use_logged_in_user_false_with_token(self): client = CopilotClient( - CopilotClientOptions( - connection=RuntimeConnection.for_stdio(path=CLI_PATH), - github_token="gho_test_token", - log_level="error", - ) + connection=RuntimeConnection.for_stdio(path=CLI_PATH), + github_token="gho_test_token", + log_level="error", ) assert isinstance(client._options.connection, StdioRuntimeConnection) assert client._options.use_logged_in_user is False def test_explicit_use_logged_in_user_true_with_token(self): client = CopilotClient( - CopilotClientOptions( - connection=RuntimeConnection.for_stdio(path=CLI_PATH), - github_token="gho_test_token", - use_logged_in_user=True, - log_level="error", - ) + connection=RuntimeConnection.for_stdio(path=CLI_PATH), + github_token="gho_test_token", + use_logged_in_user=True, + log_level="error", ) assert isinstance(client._options.connection, StdioRuntimeConnection) assert client._options.use_logged_in_user is True def test_explicit_use_logged_in_user_false_without_token(self): client = CopilotClient( - CopilotClientOptions( - connection=RuntimeConnection.for_stdio(path=CLI_PATH), - use_logged_in_user=False, - log_level="error", - ) + connection=RuntimeConnection.for_stdio(path=CLI_PATH), + use_logged_in_user=False, + log_level="error", ) assert isinstance(client._options.connection, StdioRuntimeConnection) assert client._options.use_logged_in_user is False @@ -280,20 +243,16 @@ def test_explicit_use_logged_in_user_false_without_token(self): class TestSessionIdleTimeoutSeconds: def test_accepts_session_idle_timeout_seconds(self): client = CopilotClient( - CopilotClientOptions( - connection=RuntimeConnection.for_stdio(path=CLI_PATH), - session_idle_timeout_seconds=600, - log_level="error", - ) + connection=RuntimeConnection.for_stdio(path=CLI_PATH), + session_idle_timeout_seconds=600, + log_level="error", ) assert isinstance(client._options.connection, StdioRuntimeConnection) assert client._options.session_idle_timeout_seconds == 600 def test_default_session_idle_timeout_seconds_is_none(self): client = CopilotClient( - CopilotClientOptions( - connection=RuntimeConnection.for_stdio(path=CLI_PATH), log_level="error" - ) + connection=RuntimeConnection.for_stdio(path=CLI_PATH), log_level="error" ) assert isinstance(client._options.connection, StdioRuntimeConnection) assert client._options.session_idle_timeout_seconds is None @@ -302,20 +261,16 @@ def test_default_session_idle_timeout_seconds_is_none(self): class TestCopilotHome: def test_accepts_copilot_home(self): client = CopilotClient( - CopilotClientOptions( - connection=RuntimeConnection.for_stdio(path=CLI_PATH), - base_directory="/custom/copilot/home", - log_level="error", - ) + connection=RuntimeConnection.for_stdio(path=CLI_PATH), + base_directory="/custom/copilot/home", + log_level="error", ) assert isinstance(client._options.connection, StdioRuntimeConnection) assert client._options.base_directory == "/custom/copilot/home" def test_default_copilot_home_is_none(self): client = CopilotClient( - CopilotClientOptions( - connection=RuntimeConnection.for_stdio(path=CLI_PATH), log_level="error" - ) + connection=RuntimeConnection.for_stdio(path=CLI_PATH), log_level="error" ) assert isinstance(client._options.connection, StdioRuntimeConnection) assert client._options.base_directory is None @@ -324,9 +279,7 @@ def test_default_copilot_home_is_none(self): class TestOverridesBuiltInTool: @pytest.mark.asyncio async def test_overrides_built_in_tool_sent_in_tool_definition(self): - client = CopilotClient( - CopilotClientOptions(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) - ) + client = CopilotClient(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) await client.start() try: @@ -355,9 +308,7 @@ def grep(params) -> str: @pytest.mark.asyncio async def test_resume_session_sends_overrides_built_in_tool(self): - client = CopilotClient( - CopilotClientOptions(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) - ) + client = CopilotClient(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) await client.start() try: @@ -394,9 +345,7 @@ def grep(params) -> str: class TestInstructionDirectories: @pytest.mark.asyncio async def test_create_session_sends_instruction_directories(self): - client = CopilotClient( - CopilotClientOptions(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) - ) + client = CopilotClient(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) await client.start() try: @@ -424,9 +373,7 @@ async def mock_request(method, params): @pytest.mark.asyncio async def test_resume_session_sends_instruction_directories(self): - client = CopilotClient( - CopilotClientOptions(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) - ) + client = CopilotClient(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) await client.start() try: @@ -475,10 +422,8 @@ def handler(): return custom_models client = CopilotClient( - CopilotClientOptions( - connection=RuntimeConnection.for_stdio(path=CLI_PATH), - on_list_models=handler, - ), + connection=RuntimeConnection.for_stdio(path=CLI_PATH), + on_list_models=handler, ) await client.start() try: @@ -509,10 +454,8 @@ def handler(): return custom_models client = CopilotClient( - CopilotClientOptions( - connection=RuntimeConnection.for_stdio(path=CLI_PATH), - on_list_models=handler, - ), + connection=RuntimeConnection.for_stdio(path=CLI_PATH), + on_list_models=handler, ) await client.start() try: @@ -540,10 +483,8 @@ async def handler(): return custom_models client = CopilotClient( - CopilotClientOptions( - connection=RuntimeConnection.for_stdio(path=CLI_PATH), - on_list_models=handler, - ), + connection=RuntimeConnection.for_stdio(path=CLI_PATH), + on_list_models=handler, ) await client.start() try: @@ -573,10 +514,8 @@ def handler(): return custom_models client = CopilotClient( - CopilotClientOptions( - connection=RuntimeConnection.for_stdio(path=CLI_PATH), - on_list_models=handler, - ), + connection=RuntimeConnection.for_stdio(path=CLI_PATH), + on_list_models=handler, ) models = await client.list_models() assert len(handler_calls) == 1 @@ -586,9 +525,7 @@ def handler(): class TestSessionConfigForwarding: @pytest.mark.asyncio async def test_create_session_forwards_client_name(self): - client = CopilotClient( - CopilotClientOptions(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) - ) + client = CopilotClient(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) await client.start() try: @@ -609,9 +546,7 @@ async def mock_request(method, params): @pytest.mark.asyncio async def test_resume_session_forwards_client_name(self): - client = CopilotClient( - CopilotClientOptions(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) - ) + client = CopilotClient(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) await client.start() try: @@ -641,9 +576,7 @@ async def mock_request(method, params): @pytest.mark.asyncio async def test_create_session_forwards_enable_session_telemetry(self): - client = CopilotClient( - CopilotClientOptions(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) - ) + client = CopilotClient(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) await client.start() try: @@ -665,9 +598,7 @@ async def mock_request(method, params): @pytest.mark.asyncio async def test_resume_session_forwards_enable_session_telemetry(self): - client = CopilotClient( - CopilotClientOptions(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) - ) + client = CopilotClient(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) await client.start() try: @@ -696,9 +627,7 @@ async def mock_request(method, params): @pytest.mark.asyncio async def test_create_session_forwards_provider_headers(self): - client = CopilotClient( - CopilotClientOptions(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) - ) + client = CopilotClient(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) await client.start() try: @@ -736,9 +665,7 @@ async def mock_request(method, params): @pytest.mark.asyncio async def test_resume_session_forwards_provider_headers(self): - client = CopilotClient( - CopilotClientOptions(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) - ) + client = CopilotClient(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) await client.start() try: @@ -781,9 +708,7 @@ async def mock_request(method, params): @pytest.mark.asyncio async def test_session_send_forwards_request_headers(self): - client = CopilotClient( - CopilotClientOptions(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) - ) + client = CopilotClient(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) await client.start() try: @@ -815,9 +740,7 @@ async def mock_request(method, params): @pytest.mark.asyncio async def test_create_session_forwards_agent(self): - client = CopilotClient( - CopilotClientOptions(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) - ) + client = CopilotClient(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) await client.start() try: @@ -840,9 +763,7 @@ async def mock_request(method, params): @pytest.mark.asyncio async def test_resume_session_forwards_agent(self): - client = CopilotClient( - CopilotClientOptions(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) - ) + client = CopilotClient(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) await client.start() try: @@ -872,9 +793,7 @@ async def mock_request(method, params): @pytest.mark.asyncio async def test_create_session_defaults_include_sub_agent_streaming_events_to_true(self): - client = CopilotClient( - CopilotClientOptions(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) - ) + client = CopilotClient(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) await client.start() try: @@ -897,9 +816,7 @@ async def mock_request(method, params): async def test_create_session_preserves_explicit_false_include_sub_agent_streaming_events( self, ): - client = CopilotClient( - CopilotClientOptions(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) - ) + client = CopilotClient(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) await client.start() try: @@ -921,9 +838,7 @@ async def mock_request(method, params): @pytest.mark.asyncio async def test_resume_session_defaults_include_sub_agent_streaming_events_to_true(self): - client = CopilotClient( - CopilotClientOptions(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) - ) + client = CopilotClient(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) await client.start() try: @@ -953,9 +868,7 @@ async def mock_request(method, params): async def test_resume_session_preserves_explicit_false_include_sub_agent_streaming_events( self, ): - client = CopilotClient( - CopilotClientOptions(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) - ) + client = CopilotClient(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) await client.start() try: @@ -984,9 +897,7 @@ async def mock_request(method, params): @pytest.mark.asyncio async def test_resume_session_forwards_continue_pending_work(self): - client = CopilotClient( - CopilotClientOptions(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) - ) + client = CopilotClient(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) await client.start() try: @@ -1015,9 +926,7 @@ async def mock_request(method, params): @pytest.mark.asyncio async def test_resume_session_omits_continue_pending_work_by_default(self): - client = CopilotClient( - CopilotClientOptions(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) - ) + client = CopilotClient(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) await client.start() try: @@ -1045,9 +954,7 @@ async def mock_request(method, params): @pytest.mark.asyncio async def test_set_model_sends_correct_rpc(self): - client = CopilotClient( - CopilotClientOptions(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) - ) + client = CopilotClient(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) await client.start() try: @@ -1075,9 +982,7 @@ async def mock_request(method, params): class TestCopilotClientContextManager: @pytest.mark.asyncio async def test_aenter_calls_start_and_returns_self(self): - client = CopilotClient( - CopilotClientOptions(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) - ) + client = CopilotClient(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) with patch.object(client, "start", new_callable=AsyncMock) as mock_start: result = await client.__aenter__() mock_start.assert_awaited_once() @@ -1085,9 +990,7 @@ async def test_aenter_calls_start_and_returns_self(self): @pytest.mark.asyncio async def test_aexit_calls_stop(self): - client = CopilotClient( - CopilotClientOptions(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) - ) + client = CopilotClient(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) with patch.object(client, "stop", new_callable=AsyncMock) as mock_stop: await client.__aexit__(None, None, None) mock_stop.assert_awaited_once() diff --git a/python/test_commands_and_elicitation.py b/python/test_commands_and_elicitation.py index 432bbf9b1..7f708c74f 100644 --- a/python/test_commands_and_elicitation.py +++ b/python/test_commands_and_elicitation.py @@ -10,7 +10,7 @@ import pytest -from copilot import CopilotClient, CopilotClientOptions, RuntimeConnection +from copilot import CopilotClient, RuntimeConnection from copilot.session import ( AutoModeSwitchRequest, AutoModeSwitchResponse, @@ -49,9 +49,7 @@ class TestCommands: @pytest.mark.asyncio async def test_forwards_commands_in_session_create_rpc(self): """Verifies that commands (name + description) are serialized in session.create payload.""" - client = CopilotClient( - CopilotClientOptions(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) - ) + client = CopilotClient(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) await client.start() try: @@ -90,9 +88,7 @@ async def mock_request(method, params): @pytest.mark.asyncio async def test_forwards_commands_in_session_resume_rpc(self): """Verifies that commands are serialized in session.resume payload.""" - client = CopilotClient( - CopilotClientOptions(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) - ) + client = CopilotClient(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) await client.start() try: @@ -130,9 +126,7 @@ async def mock_request(method, params): @pytest.mark.asyncio async def test_routes_command_execute_event_to_correct_handler(self): """Verifies the command dispatch works for command.execute events.""" - client = CopilotClient( - CopilotClientOptions(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) - ) + client = CopilotClient(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) await client.start() try: @@ -203,9 +197,7 @@ async def mock_request(method, params): @pytest.mark.asyncio async def test_sends_error_when_command_handler_throws(self): """Verifies error is sent via RPC when a command handler raises.""" - client = CopilotClient( - CopilotClientOptions(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) - ) + client = CopilotClient(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) await client.start() try: @@ -263,9 +255,7 @@ async def mock_request(method, params): @pytest.mark.asyncio async def test_sends_error_for_unknown_command(self): """Verifies error is sent via RPC for an unrecognized command.""" - client = CopilotClient( - CopilotClientOptions(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) - ) + client = CopilotClient(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) await client.start() try: @@ -326,9 +316,7 @@ class TestUiElicitation: @pytest.mark.asyncio async def test_reads_capabilities_from_session_create_response(self): """Verifies capabilities are parsed from session.create response.""" - client = CopilotClient( - CopilotClientOptions(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) - ) + client = CopilotClient(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) await client.start() try: @@ -352,9 +340,7 @@ async def mock_request(method, params): @pytest.mark.asyncio async def test_defaults_capabilities_when_not_injected(self): """Verifies capabilities default to empty when server returns none.""" - client = CopilotClient( - CopilotClientOptions(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) - ) + client = CopilotClient(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) await client.start() try: @@ -371,9 +357,7 @@ async def test_defaults_capabilities_when_not_injected(self): @pytest.mark.asyncio async def test_elicitation_throws_when_capability_is_missing(self): """Verifies that UI methods throw when elicitation is not supported.""" - client = CopilotClient( - CopilotClientOptions(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) - ) + client = CopilotClient(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) await client.start() try: @@ -400,9 +384,7 @@ async def test_elicitation_throws_when_capability_is_missing(self): @pytest.mark.asyncio async def test_confirm_throws_when_capability_is_missing(self): """Verifies confirm throws when elicitation is not supported.""" - client = CopilotClient( - CopilotClientOptions(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) - ) + client = CopilotClient(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) await client.start() try: @@ -426,9 +408,7 @@ class TestOnElicitationContext: @pytest.mark.asyncio async def test_sends_request_elicitation_flag_when_handler_provided(self): """Verifies requestElicitation=true is sent when onElicitationContext is provided.""" - client = CopilotClient( - CopilotClientOptions(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) - ) + client = CopilotClient(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) await client.start() try: @@ -460,9 +440,7 @@ async def elicitation_handler( @pytest.mark.asyncio async def test_does_not_send_request_elicitation_when_no_handler(self): """Verifies requestElicitation=false when no handler is provided.""" - client = CopilotClient( - CopilotClientOptions(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) - ) + client = CopilotClient(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) await client.start() try: @@ -490,9 +468,7 @@ async def mock_request(method, params): @pytest.mark.asyncio async def test_sends_mode_callback_flags_when_handlers_provided(self): """Verifies mode callback flags are sent when handlers are provided.""" - client = CopilotClient( - CopilotClientOptions(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) - ) + client = CopilotClient(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) await client.start() try: @@ -531,9 +507,7 @@ def auto_handler( @pytest.mark.asyncio async def test_sends_mode_callback_flags_on_resume_when_handlers_provided(self): """Verifies mode callback flags are sent on session.resume.""" - client = CopilotClient( - CopilotClientOptions(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) - ) + client = CopilotClient(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) await client.start() try: @@ -566,9 +540,7 @@ async def mock_request(method, params): @pytest.mark.asyncio async def test_dispatches_mode_callback_requests_to_registered_handlers(self): """Verifies direct mode requests are dispatched to registered handlers.""" - client = CopilotClient( - CopilotClientOptions(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) - ) + client = CopilotClient(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) await client.start() try: @@ -630,9 +602,7 @@ async def auto_handler( @pytest.mark.asyncio async def test_sends_cancel_when_elicitation_handler_throws(self): """Verifies auto-cancel when the elicitation handler raises.""" - client = CopilotClient( - CopilotClientOptions(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) - ) + client = CopilotClient(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) await client.start() try: @@ -677,9 +647,7 @@ async def mock_request(method, params): @pytest.mark.asyncio async def test_dispatches_elicitation_requested_event_to_handler(self): """Verifies that an elicitation.requested event dispatches to the handler.""" - client = CopilotClient( - CopilotClientOptions(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) - ) + client = CopilotClient(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) await client.start() try: @@ -740,9 +708,7 @@ async def mock_request(method, params): @pytest.mark.asyncio async def test_elicitation_handler_receives_full_schema(self): """Verifies that requestedSchema passes type, properties, and required to handler.""" - client = CopilotClient( - CopilotClientOptions(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) - ) + client = CopilotClient(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) await client.start() try: @@ -818,9 +784,7 @@ class TestCapabilitiesChanged: @pytest.mark.asyncio async def test_capabilities_changed_event_updates_session(self): """Verifies that a capabilities.changed event updates session capabilities.""" - client = CopilotClient( - CopilotClientOptions(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) - ) + client = CopilotClient(connection=RuntimeConnection.for_stdio(path=CLI_PATH)) await client.start() try: diff --git a/python/test_telemetry.py b/python/test_telemetry.py index f123f5e8b..6481fd525 100644 --- a/python/test_telemetry.py +++ b/python/test_telemetry.py @@ -4,7 +4,6 @@ from unittest.mock import patch -from copilot import CopilotClientOptions, RuntimeConnection from copilot._telemetry import get_trace_context, trace_context from copilot.client import TelemetryConfig @@ -74,18 +73,6 @@ def test_telemetry_config_type(self): assert config["otlp_endpoint"] == "http://localhost:4318" assert config["capture_content"] is True - def test_telemetry_config_in_subprocess_config(self): - """TelemetryConfig can be used in SubprocessConfig.""" - config = CopilotClientOptions( - connection=RuntimeConnection.for_stdio(), - telemetry={ - "otlp_endpoint": "http://localhost:4318", - "exporter_type": "otlp-http", - }, - ) - assert config.telemetry is not None - assert config.telemetry["otlp_endpoint"] == "http://localhost:4318" - def test_telemetry_env_var_mapping(self): """TelemetryConfig fields map to expected environment variable names.""" config: TelemetryConfig = { diff --git a/test/scenarios/auth/byok-anthropic/python/main.py b/test/scenarios/auth/byok-anthropic/python/main.py index 75a3ece08..5b623ff87 100644 --- a/test/scenarios/auth/byok-anthropic/python/main.py +++ b/test/scenarios/auth/byok-anthropic/python/main.py @@ -2,7 +2,7 @@ import os import sys -from copilot import CopilotClient, CopilotClientOptions +from copilot import CopilotClient ANTHROPIC_API_KEY = os.environ.get("ANTHROPIC_API_KEY") ANTHROPIC_MODEL = os.environ.get("ANTHROPIC_MODEL", "claude-sonnet-4-20250514") @@ -14,7 +14,7 @@ async def main(): - client = CopilotClient(CopilotClientOptions()) + client = CopilotClient() try: session = await client.create_session( diff --git a/test/scenarios/auth/byok-azure/python/main.py b/test/scenarios/auth/byok-azure/python/main.py index ed03f8dfc..031ff47ee 100644 --- a/test/scenarios/auth/byok-azure/python/main.py +++ b/test/scenarios/auth/byok-azure/python/main.py @@ -2,7 +2,7 @@ import os import sys -from copilot import CopilotClient, CopilotClientOptions +from copilot import CopilotClient AZURE_OPENAI_ENDPOINT = os.environ.get("AZURE_OPENAI_ENDPOINT") AZURE_OPENAI_API_KEY = os.environ.get("AZURE_OPENAI_API_KEY") @@ -15,7 +15,7 @@ async def main(): - client = CopilotClient(CopilotClientOptions()) + client = CopilotClient() try: session = await client.create_session( diff --git a/test/scenarios/auth/byok-ollama/python/main.py b/test/scenarios/auth/byok-ollama/python/main.py index e355b175e..90c4838f8 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 -from copilot import CopilotClient, CopilotClientOptions +from copilot import CopilotClient OLLAMA_BASE_URL = os.environ.get("OLLAMA_BASE_URL", "http://localhost:11434/v1") OLLAMA_MODEL = os.environ.get("OLLAMA_MODEL", "llama3.2:3b") @@ -12,7 +12,7 @@ async def main(): - client = CopilotClient(CopilotClientOptions()) + client = CopilotClient() try: session = await client.create_session( diff --git a/test/scenarios/auth/byok-openai/python/main.py b/test/scenarios/auth/byok-openai/python/main.py index e5f0f432b..e9c673aa0 100644 --- a/test/scenarios/auth/byok-openai/python/main.py +++ b/test/scenarios/auth/byok-openai/python/main.py @@ -2,7 +2,7 @@ import os import sys -from copilot import CopilotClient, CopilotClientOptions +from copilot import CopilotClient 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") @@ -14,7 +14,7 @@ async def main(): - client = CopilotClient(CopilotClientOptions()) + client = CopilotClient() try: session = await client.create_session( diff --git a/test/scenarios/auth/gh-app/python/main.py b/test/scenarios/auth/gh-app/python/main.py index ee14464cb..0d5a5ee9d 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, CopilotClientOptions +from copilot import CopilotClient DEVICE_CODE_URL = "https://github.com/login/device/code" ACCESS_TOKEN_URL = "https://github.com/login/oauth/access_token" @@ -79,7 +79,7 @@ async def main(): display_name = f" ({user.get('name')})" if user.get("name") else "" print(f"Authenticated as: {user.get('login')}{display_name}") - client = CopilotClient(CopilotClientOptions(github_token=token)) + client = CopilotClient(github_token=token) try: session = await client.create_session(model="claude-haiku-4.5") 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 108ca5345..d53d89854 100644 --- a/test/scenarios/bundling/app-backend-to-server/python/main.py +++ b/test/scenarios/bundling/app-backend-to-server/python/main.py @@ -6,7 +6,7 @@ from flask import Flask, jsonify, request -from copilot import CopilotClient, CopilotClientOptions, RuntimeConnection +from copilot import CopilotClient, RuntimeConnection app = Flask(__name__) @@ -14,7 +14,7 @@ async def ask_copilot(prompt: str) -> str: - client = CopilotClient(CopilotClientOptions(connection=RuntimeConnection.for_uri(CLI_URL))) + client = CopilotClient(connection=RuntimeConnection.for_uri(CLI_URL)) try: session = await client.create_session(model="claude-haiku-4.5") diff --git a/test/scenarios/bundling/app-direct-server/python/main.py b/test/scenarios/bundling/app-direct-server/python/main.py index 67ca82978..1bf32b475 100644 --- a/test/scenarios/bundling/app-direct-server/python/main.py +++ b/test/scenarios/bundling/app-direct-server/python/main.py @@ -1,16 +1,14 @@ import asyncio import os -from copilot import CopilotClient, CopilotClientOptions, RuntimeConnection +from copilot import CopilotClient, RuntimeConnection async def main(): client = CopilotClient( - CopilotClientOptions( - connection=RuntimeConnection.for_uri( - os.environ.get("COPILOT_CLI_URL", "localhost:3000"), - ), - ) + connection=RuntimeConnection.for_uri( + os.environ.get("COPILOT_CLI_URL", "localhost:3000"), + ), ) try: diff --git a/test/scenarios/bundling/container-proxy/python/main.py b/test/scenarios/bundling/container-proxy/python/main.py index 67ca82978..1bf32b475 100644 --- a/test/scenarios/bundling/container-proxy/python/main.py +++ b/test/scenarios/bundling/container-proxy/python/main.py @@ -1,16 +1,14 @@ import asyncio import os -from copilot import CopilotClient, CopilotClientOptions, RuntimeConnection +from copilot import CopilotClient, RuntimeConnection async def main(): client = CopilotClient( - CopilotClientOptions( - connection=RuntimeConnection.for_uri( - os.environ.get("COPILOT_CLI_URL", "localhost:3000"), - ), - ) + connection=RuntimeConnection.for_uri( + os.environ.get("COPILOT_CLI_URL", "localhost:3000"), + ), ) try: diff --git a/test/scenarios/bundling/fully-bundled/python/main.py b/test/scenarios/bundling/fully-bundled/python/main.py index 7f7d9aebb..63c309995 100644 --- a/test/scenarios/bundling/fully-bundled/python/main.py +++ b/test/scenarios/bundling/fully-bundled/python/main.py @@ -1,14 +1,12 @@ import asyncio import os -from copilot import CopilotClient, CopilotClientOptions +from copilot import CopilotClient async def main(): client = CopilotClient( - CopilotClientOptions( - github_token=os.environ.get("GITHUB_TOKEN"), - ) + github_token=os.environ.get("GITHUB_TOKEN"), ) try: diff --git a/test/scenarios/callbacks/hooks/python/main.py b/test/scenarios/callbacks/hooks/python/main.py index c2f719d1a..3a7b5906c 100644 --- a/test/scenarios/callbacks/hooks/python/main.py +++ b/test/scenarios/callbacks/hooks/python/main.py @@ -1,7 +1,7 @@ import asyncio import os -from copilot import CopilotClient, CopilotClientOptions +from copilot import CopilotClient from copilot.generated.rpc import PermissionDecisionApproveOnce hook_log: list[str] = [] @@ -42,9 +42,7 @@ async def on_error_occurred(input_data, invocation): async def main(): client = CopilotClient( - CopilotClientOptions( - github_token=os.environ.get("GITHUB_TOKEN"), - ) + github_token=os.environ.get("GITHUB_TOKEN"), ) try: diff --git a/test/scenarios/callbacks/permissions/python/main.py b/test/scenarios/callbacks/permissions/python/main.py index 3944f4781..138d6310a 100644 --- a/test/scenarios/callbacks/permissions/python/main.py +++ b/test/scenarios/callbacks/permissions/python/main.py @@ -1,7 +1,7 @@ import asyncio import os -from copilot import CopilotClient, CopilotClientOptions +from copilot import CopilotClient from copilot.generated.rpc import PermissionDecisionApproveOnce # Track which tools requested permission @@ -19,9 +19,7 @@ async def auto_approve_tool(input_data, invocation): async def main(): client = CopilotClient( - CopilotClientOptions( - github_token=os.environ.get("GITHUB_TOKEN"), - ) + github_token=os.environ.get("GITHUB_TOKEN"), ) try: diff --git a/test/scenarios/callbacks/user-input/python/main.py b/test/scenarios/callbacks/user-input/python/main.py index 418d13417..9eff3c7cc 100644 --- a/test/scenarios/callbacks/user-input/python/main.py +++ b/test/scenarios/callbacks/user-input/python/main.py @@ -1,7 +1,7 @@ import asyncio import os -from copilot import CopilotClient, CopilotClientOptions +from copilot import CopilotClient from copilot.generated.rpc import PermissionDecisionApproveOnce input_log: list[str] = [] @@ -22,9 +22,7 @@ async def handle_user_input(request, invocation): async def main(): client = CopilotClient( - CopilotClientOptions( - github_token=os.environ.get("GITHUB_TOKEN"), - ) + github_token=os.environ.get("GITHUB_TOKEN"), ) try: diff --git a/test/scenarios/modes/default/python/main.py b/test/scenarios/modes/default/python/main.py index 6d2be1cd1..3bb6e10a3 100644 --- a/test/scenarios/modes/default/python/main.py +++ b/test/scenarios/modes/default/python/main.py @@ -1,14 +1,12 @@ import asyncio import os -from copilot import CopilotClient, CopilotClientOptions +from copilot import CopilotClient async def main(): client = CopilotClient( - CopilotClientOptions( - github_token=os.environ.get("GITHUB_TOKEN"), - ) + github_token=os.environ.get("GITHUB_TOKEN"), ) try: diff --git a/test/scenarios/modes/minimal/python/main.py b/test/scenarios/modes/minimal/python/main.py index 4151eb21a..71811d377 100644 --- a/test/scenarios/modes/minimal/python/main.py +++ b/test/scenarios/modes/minimal/python/main.py @@ -1,14 +1,12 @@ import asyncio import os -from copilot import CopilotClient, CopilotClientOptions +from copilot import CopilotClient async def main(): client = CopilotClient( - CopilotClientOptions( - github_token=os.environ.get("GITHUB_TOKEN"), - ) + github_token=os.environ.get("GITHUB_TOKEN"), ) try: diff --git a/test/scenarios/prompts/attachments/python/main.py b/test/scenarios/prompts/attachments/python/main.py index 463034402..998770298 100644 --- a/test/scenarios/prompts/attachments/python/main.py +++ b/test/scenarios/prompts/attachments/python/main.py @@ -1,16 +1,14 @@ import asyncio import os -from copilot import CopilotClient, CopilotClientOptions +from copilot import CopilotClient SYSTEM_PROMPT = """You are a helpful assistant. Answer questions about attached files concisely.""" async def main(): client = CopilotClient( - CopilotClientOptions( - github_token=os.environ.get("GITHUB_TOKEN"), - ) + github_token=os.environ.get("GITHUB_TOKEN"), ) try: diff --git a/test/scenarios/prompts/reasoning-effort/python/main.py b/test/scenarios/prompts/reasoning-effort/python/main.py index 7ab5526e6..ae4b0264f 100644 --- a/test/scenarios/prompts/reasoning-effort/python/main.py +++ b/test/scenarios/prompts/reasoning-effort/python/main.py @@ -1,14 +1,12 @@ import asyncio import os -from copilot import CopilotClient, CopilotClientOptions +from copilot import CopilotClient async def main(): client = CopilotClient( - CopilotClientOptions( - github_token=os.environ.get("GITHUB_TOKEN"), - ) + github_token=os.environ.get("GITHUB_TOKEN"), ) try: diff --git a/test/scenarios/prompts/system-message/python/main.py b/test/scenarios/prompts/system-message/python/main.py index ca37baee2..490347234 100644 --- a/test/scenarios/prompts/system-message/python/main.py +++ b/test/scenarios/prompts/system-message/python/main.py @@ -1,16 +1,14 @@ import asyncio import os -from copilot import CopilotClient, CopilotClientOptions +from copilot import CopilotClient PIRATE_PROMPT = """You are a pirate. Always respond in pirate speak. Say 'Arrr!' in every response. Use nautical terms and pirate slang throughout.""" async def main(): client = CopilotClient( - CopilotClientOptions( - github_token=os.environ.get("GITHUB_TOKEN"), - ) + github_token=os.environ.get("GITHUB_TOKEN"), ) try: diff --git a/test/scenarios/sessions/concurrent-sessions/python/main.py b/test/scenarios/sessions/concurrent-sessions/python/main.py index 490b7d691..beee2ba06 100644 --- a/test/scenarios/sessions/concurrent-sessions/python/main.py +++ b/test/scenarios/sessions/concurrent-sessions/python/main.py @@ -1,7 +1,7 @@ import asyncio import os -from copilot import CopilotClient, CopilotClientOptions +from copilot import CopilotClient PIRATE_PROMPT = "You are a pirate. Always say Arrr!" ROBOT_PROMPT = "You are a robot. Always say BEEP BOOP!" @@ -9,9 +9,7 @@ async def main(): client = CopilotClient( - CopilotClientOptions( - github_token=os.environ.get("GITHUB_TOKEN"), - ) + github_token=os.environ.get("GITHUB_TOKEN"), ) try: diff --git a/test/scenarios/sessions/infinite-sessions/python/main.py b/test/scenarios/sessions/infinite-sessions/python/main.py index 666b3c793..a41b2b4fa 100644 --- a/test/scenarios/sessions/infinite-sessions/python/main.py +++ b/test/scenarios/sessions/infinite-sessions/python/main.py @@ -1,14 +1,12 @@ import asyncio import os -from copilot import CopilotClient, CopilotClientOptions +from copilot import CopilotClient async def main(): client = CopilotClient( - CopilotClientOptions( - github_token=os.environ.get("GITHUB_TOKEN"), - ) + github_token=os.environ.get("GITHUB_TOKEN"), ) try: diff --git a/test/scenarios/sessions/session-resume/python/main.py b/test/scenarios/sessions/session-resume/python/main.py index 2f4bd7434..6d7ae02a2 100644 --- a/test/scenarios/sessions/session-resume/python/main.py +++ b/test/scenarios/sessions/session-resume/python/main.py @@ -1,14 +1,12 @@ import asyncio import os -from copilot import CopilotClient, CopilotClientOptions +from copilot import CopilotClient async def main(): client = CopilotClient( - CopilotClientOptions( - github_token=os.environ.get("GITHUB_TOKEN"), - ) + github_token=os.environ.get("GITHUB_TOKEN"), ) try: diff --git a/test/scenarios/sessions/streaming/python/main.py b/test/scenarios/sessions/streaming/python/main.py index 3916a5713..6c1c19f7b 100644 --- a/test/scenarios/sessions/streaming/python/main.py +++ b/test/scenarios/sessions/streaming/python/main.py @@ -1,14 +1,12 @@ import asyncio import os -from copilot import CopilotClient, CopilotClientOptions +from copilot import CopilotClient async def main(): client = CopilotClient( - CopilotClientOptions( - github_token=os.environ.get("GITHUB_TOKEN"), - ) + github_token=os.environ.get("GITHUB_TOKEN"), ) try: diff --git a/test/scenarios/tools/custom-agents/python/main.py b/test/scenarios/tools/custom-agents/python/main.py index a6572bcb8..aa5e254ae 100644 --- a/test/scenarios/tools/custom-agents/python/main.py +++ b/test/scenarios/tools/custom-agents/python/main.py @@ -1,7 +1,7 @@ import asyncio import os -from copilot import CopilotClient, CopilotClientOptions +from copilot import CopilotClient from copilot.tools import Tool @@ -11,9 +11,7 @@ async def analyze_handler(args): async def main(): client = CopilotClient( - CopilotClientOptions( - github_token=os.environ.get("GITHUB_TOKEN"), - ) + github_token=os.environ.get("GITHUB_TOKEN"), ) try: diff --git a/test/scenarios/tools/mcp-servers/python/main.py b/test/scenarios/tools/mcp-servers/python/main.py index e7d790289..80319e79a 100644 --- a/test/scenarios/tools/mcp-servers/python/main.py +++ b/test/scenarios/tools/mcp-servers/python/main.py @@ -1,14 +1,12 @@ import asyncio import os -from copilot import CopilotClient, CopilotClientOptions +from copilot import CopilotClient async def main(): client = CopilotClient( - CopilotClientOptions( - github_token=os.environ.get("GITHUB_TOKEN"), - ) + github_token=os.environ.get("GITHUB_TOKEN"), ) try: diff --git a/test/scenarios/tools/no-tools/python/main.py b/test/scenarios/tools/no-tools/python/main.py index 139565fd5..61fa98ee1 100644 --- a/test/scenarios/tools/no-tools/python/main.py +++ b/test/scenarios/tools/no-tools/python/main.py @@ -1,7 +1,7 @@ import asyncio import os -from copilot import CopilotClient, CopilotClientOptions +from copilot import CopilotClient SYSTEM_PROMPT = """You are a minimal assistant with no tools available. You cannot execute code, read files, edit files, search, or perform any actions. @@ -11,9 +11,7 @@ async def main(): client = CopilotClient( - CopilotClientOptions( - github_token=os.environ.get("GITHUB_TOKEN"), - ) + github_token=os.environ.get("GITHUB_TOKEN"), ) try: diff --git a/test/scenarios/tools/skills/python/main.py b/test/scenarios/tools/skills/python/main.py index 0c76c36af..30b82fc1f 100644 --- a/test/scenarios/tools/skills/python/main.py +++ b/test/scenarios/tools/skills/python/main.py @@ -2,15 +2,13 @@ import os from pathlib import Path -from copilot import CopilotClient, CopilotClientOptions +from copilot import CopilotClient from copilot.generated.rpc import PermissionDecisionApproveOnce async def main(): client = CopilotClient( - CopilotClientOptions( - github_token=os.environ.get("GITHUB_TOKEN"), - ) + github_token=os.environ.get("GITHUB_TOKEN"), ) try: diff --git a/test/scenarios/tools/tool-filtering/python/main.py b/test/scenarios/tools/tool-filtering/python/main.py index d126bf67c..711e8301e 100644 --- a/test/scenarios/tools/tool-filtering/python/main.py +++ b/test/scenarios/tools/tool-filtering/python/main.py @@ -1,16 +1,14 @@ import asyncio import os -from copilot import CopilotClient, CopilotClientOptions +from copilot import CopilotClient 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.""" async def main(): client = CopilotClient( - CopilotClientOptions( - github_token=os.environ.get("GITHUB_TOKEN"), - ) + github_token=os.environ.get("GITHUB_TOKEN"), ) try: diff --git a/test/scenarios/tools/tool-overrides/python/main.py b/test/scenarios/tools/tool-overrides/python/main.py index b96cbd0db..9aaaa9022 100644 --- a/test/scenarios/tools/tool-overrides/python/main.py +++ b/test/scenarios/tools/tool-overrides/python/main.py @@ -3,7 +3,7 @@ from pydantic import BaseModel, Field -from copilot import CopilotClient, CopilotClientOptions, define_tool +from copilot import CopilotClient, define_tool from copilot.session import PermissionHandler @@ -22,9 +22,7 @@ def custom_grep(params: GrepParams) -> str: async def main(): client = CopilotClient( - CopilotClientOptions( - github_token=os.environ.get("GITHUB_TOKEN"), - ) + github_token=os.environ.get("GITHUB_TOKEN"), ) try: diff --git a/test/scenarios/tools/virtual-filesystem/python/main.py b/test/scenarios/tools/virtual-filesystem/python/main.py index 101d072f3..048ba1fd1 100644 --- a/test/scenarios/tools/virtual-filesystem/python/main.py +++ b/test/scenarios/tools/virtual-filesystem/python/main.py @@ -3,7 +3,7 @@ from pydantic import BaseModel, Field -from copilot import CopilotClient, CopilotClientOptions, define_tool +from copilot import CopilotClient, define_tool from copilot.generated.rpc import PermissionDecisionApproveOnce # In-memory virtual filesystem @@ -50,9 +50,7 @@ async def auto_approve_tool(input_data, invocation): async def main(): client = CopilotClient( - CopilotClientOptions( - github_token=os.environ.get("GITHUB_TOKEN"), - ) + github_token=os.environ.get("GITHUB_TOKEN"), ) try: diff --git a/test/scenarios/transport/reconnect/python/main.py b/test/scenarios/transport/reconnect/python/main.py index dac6abeab..cc79f9721 100644 --- a/test/scenarios/transport/reconnect/python/main.py +++ b/test/scenarios/transport/reconnect/python/main.py @@ -2,16 +2,14 @@ import os import sys -from copilot import CopilotClient, CopilotClientOptions, RuntimeConnection +from copilot import CopilotClient, RuntimeConnection async def main(): client = CopilotClient( - CopilotClientOptions( - connection=RuntimeConnection.for_uri( - os.environ.get("COPILOT_CLI_URL", "localhost:3000"), - ), - ) + connection=RuntimeConnection.for_uri( + os.environ.get("COPILOT_CLI_URL", "localhost:3000"), + ), ) try: diff --git a/test/scenarios/transport/stdio/python/main.py b/test/scenarios/transport/stdio/python/main.py index 7f7d9aebb..63c309995 100644 --- a/test/scenarios/transport/stdio/python/main.py +++ b/test/scenarios/transport/stdio/python/main.py @@ -1,14 +1,12 @@ import asyncio import os -from copilot import CopilotClient, CopilotClientOptions +from copilot import CopilotClient async def main(): client = CopilotClient( - CopilotClientOptions( - github_token=os.environ.get("GITHUB_TOKEN"), - ) + github_token=os.environ.get("GITHUB_TOKEN"), ) try: diff --git a/test/scenarios/transport/tcp/python/main.py b/test/scenarios/transport/tcp/python/main.py index 67ca82978..1bf32b475 100644 --- a/test/scenarios/transport/tcp/python/main.py +++ b/test/scenarios/transport/tcp/python/main.py @@ -1,16 +1,14 @@ import asyncio import os -from copilot import CopilotClient, CopilotClientOptions, RuntimeConnection +from copilot import CopilotClient, RuntimeConnection async def main(): client = CopilotClient( - CopilotClientOptions( - connection=RuntimeConnection.for_uri( - os.environ.get("COPILOT_CLI_URL", "localhost:3000"), - ), - ) + connection=RuntimeConnection.for_uri( + os.environ.get("COPILOT_CLI_URL", "localhost:3000"), + ), ) try: From 886c12da7e1f76e1c26c8b9aa698502f183d050c Mon Sep 17 00:00:00 2001 From: Steve Sanderson Date: Fri, 22 May 2026 14:25:35 +0100 Subject: [PATCH 21/21] Fix missing ** spread on _make_options in propagate-options test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The multi-line _make_options(...) call in test_should_propagate_process_options_to_spawned_cli was missed by my earlier sed pass that converted CopilotClient(_make_options(...)) to CopilotClient(**_make_options(...)) — the regex only matched single-line forms. Add the missing ** here. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- python/e2e/test_client_options_e2e.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/e2e/test_client_options_e2e.py b/python/e2e/test_client_options_e2e.py index 706ca4dcc..614aec5df 100644 --- a/python/e2e/test_client_options_e2e.py +++ b/python/e2e/test_client_options_e2e.py @@ -207,7 +207,7 @@ async def test_should_propagate_process_options_to_spawned_cli(self, ctx: E2ETes f.write(FAKE_STDIO_CLI_SCRIPT) client = CopilotClient( - _make_options( + **_make_options( ctx, cli_path=cli_path, base_directory=copilot_home_from_option,