diff --git a/.github/actions/setup-copilot/action.yml b/.github/actions/setup-copilot/action.yml
index 94cc00e88..840a1bcb9 100644
--- a/.github/actions/setup-copilot/action.yml
+++ b/.github/actions/setup-copilot/action.yml
@@ -17,8 +17,8 @@ runs:
shell: bash
- name: Set CLI path
id: cli-path
- run: echo "path=$(pwd)/nodejs/node_modules/@github/copilot/index.js" >> $GITHUB_OUTPUT
+ run: echo "path=${COPILOT_CLI_PATH:-$(which copilot-core)}" >> $GITHUB_OUTPUT
shell: bash
- name: Verify CLI works
- run: node ${{ steps.cli-path.outputs.path }} --version
+ run: ${{ steps.cli-path.outputs.path }} --version
shell: bash
diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md
index 7c362ab53..a51ef5ec4 100644
--- a/.github/copilot-instructions.md
+++ b/.github/copilot-instructions.md
@@ -37,7 +37,7 @@
- Tools: each SDK has helper APIs to expose functions as tools; prefer the language's `DefineTool`/`@define_tool`/`AIFunctionFactory.Create` patterns (see language READMEs).
- Infinite sessions are enabled by default and persist workspace state to `~/.copilot/session-state/{sessionId}`; compaction events are emitted (`session.compaction_start`, `session.compaction_complete`). See language READMEs for usage.
- Streaming: when `streaming`/`Streaming=true` you receive delta events (`assistant.message_delta`, `assistant.reasoning_delta`) and final events (`assistant.message`, `assistant.reasoning`) — tests expect this behavior.
-- Type generation is centralized in `nodejs/scripts/generate-session-types.ts` and requires the `@github/copilot` schema to be present (often via `npm link` or installed package).
+- Type generation is centralized in `nodejs/scripts/generate-session-types.ts` and requires the schema from `copilot-core` / `experimental-copilot-server` (the Rust runtime) to be present.
## Integration & environment notes ⚠️
@@ -50,4 +50,4 @@
- SDK code: `nodejs/src`, `python/copilot`, `go`, `dotnet/src`
- Unit tests: `nodejs/test`, `python/*`, `go/*`, `dotnet/test`
- E2E tests: `*/e2e/` folders that use the shared replay proxy and `test/snapshots/`
-- Generated types: update schema in `@github/copilot` then run `cd nodejs && npm run generate:session-types` and commit generated files in `src/generated` or language generated location.
+- Generated types: update schema in `copilot-core` / `experimental-copilot-server` (the Rust runtime) then run `cd nodejs && npm run generate:session-types` and commit generated files in `src/generated` or language generated location.
diff --git a/docs/auth/byok.md b/docs/auth/byok.md
index b244c4532..2f3bc5855 100644
--- a/docs/auth/byok.md
+++ b/docs/auth/byok.md
@@ -321,7 +321,7 @@ const session = await client.createSession({
// ✅ Correct: Model specified
const session = await client.createSession({
- model: "gpt-4", // Required!
+ model: "gpt-4.1", // Required!
provider: { type: "openai", baseUrl: "..." },
});
```
diff --git a/docs/go/reference/api.md b/docs/go/reference/api.md
new file mode 100644
index 000000000..f365594de
--- /dev/null
+++ b/docs/go/reference/api.md
@@ -0,0 +1,2907 @@
+
+
+# copilot
+
+```go
+import "github.com/github/copilot-sdk/go"
+```
+
+Package copilot provides a Go SDK for interacting with the GitHub Copilot CLI.
+
+The copilot package enables Go applications to communicate with the Copilot CLI server, create and manage conversation sessions, and integrate custom tools.
+
+Basic usage:
+
+```
+client := copilot.NewClient(nil)
+if err := client.Start(); err != nil {
+ log.Fatal(err)
+}
+defer client.Stop()
+
+session, err := client.CreateSession(&copilot.SessionConfig{
+ Model: "gpt-4.1",
+})
+if err != nil {
+ log.Fatal(err)
+}
+
+session.On(func(event copilot.SessionEvent) {
+ if event.Type == "assistant.message" {
+ fmt.Println(event.Data.Content)
+ }
+})
+
+session.Send(copilot.MessageOptions{Prompt: "Hello!"})
+```
+
+Package copilot provides a Go SDK for interacting with the GitHub Copilot CLI.
+
+## Index
+
+- [Constants](<#constants>)
+- [func Bool\(v bool\) \*bool](<#Bool>)
+- [func Float64\(v float64\) \*float64](<#Float64>)
+- [func GetSdkProtocolVersion\(\) int](<#GetSdkProtocolVersion>)
+- [type Attachment](<#Attachment>)
+- [type AttachmentType](<#AttachmentType>)
+- [type AzureProviderOptions](<#AzureProviderOptions>)
+- [type Client](<#Client>)
+ - [func NewClient\(options \*ClientOptions\) \*Client](<#NewClient>)
+ - [func \(c \*Client\) CreateSession\(ctx context.Context, config \*SessionConfig\) \(\*Session, error\)](<#Client.CreateSession>)
+ - [func \(c \*Client\) DeleteSession\(ctx context.Context, sessionID string\) error](<#Client.DeleteSession>)
+ - [func \(c \*Client\) ForceStop\(\)](<#Client.ForceStop>)
+ - [func \(c \*Client\) GetAuthStatus\(ctx context.Context\) \(\*GetAuthStatusResponse, error\)](<#Client.GetAuthStatus>)
+ - [func \(c \*Client\) GetForegroundSessionID\(ctx context.Context\) \(\*string, error\)](<#Client.GetForegroundSessionID>)
+ - [func \(c \*Client\) GetStatus\(ctx context.Context\) \(\*GetStatusResponse, error\)](<#Client.GetStatus>)
+ - [func \(c \*Client\) ListModels\(ctx context.Context\) \(\[\]ModelInfo, error\)](<#Client.ListModels>)
+ - [func \(c \*Client\) ListSessions\(ctx context.Context\) \(\[\]SessionMetadata, error\)](<#Client.ListSessions>)
+ - [func \(c \*Client\) On\(handler SessionLifecycleHandler\) func\(\)](<#Client.On>)
+ - [func \(c \*Client\) OnEventType\(eventType SessionLifecycleEventType, handler SessionLifecycleHandler\) func\(\)](<#Client.OnEventType>)
+ - [func \(c \*Client\) Ping\(ctx context.Context, message string\) \(\*PingResponse, error\)](<#Client.Ping>)
+ - [func \(c \*Client\) ResumeSession\(ctx context.Context, sessionID string\) \(\*Session, error\)](<#Client.ResumeSession>)
+ - [func \(c \*Client\) ResumeSessionWithOptions\(ctx context.Context, sessionID string, config \*ResumeSessionConfig\) \(\*Session, error\)](<#Client.ResumeSessionWithOptions>)
+ - [func \(c \*Client\) SetForegroundSessionID\(ctx context.Context, sessionID string\) error](<#Client.SetForegroundSessionID>)
+ - [func \(c \*Client\) Start\(ctx context.Context\) error](<#Client.Start>)
+ - [func \(c \*Client\) State\(\) ConnectionState](<#Client.State>)
+ - [func \(c \*Client\) Stop\(\) error](<#Client.Stop>)
+- [type ClientOptions](<#ClientOptions>)
+- [type CodeChanges](<#CodeChanges>)
+- [type CompactionTokensUsed](<#CompactionTokensUsed>)
+- [type ConnectionState](<#ConnectionState>)
+- [type ContextClass](<#ContextClass>)
+- [type ContextUnion](<#ContextUnion>)
+ - [func \(x \*ContextUnion\) MarshalJSON\(\) \(\[\]byte, error\)](<#ContextUnion.MarshalJSON>)
+ - [func \(x \*ContextUnion\) UnmarshalJSON\(data \[\]byte\) error](<#ContextUnion.UnmarshalJSON>)
+- [type CustomAgentConfig](<#CustomAgentConfig>)
+- [type Data](<#Data>)
+- [type End](<#End>)
+- [type ErrorClass](<#ErrorClass>)
+- [type ErrorOccurredHandler](<#ErrorOccurredHandler>)
+- [type ErrorOccurredHookInput](<#ErrorOccurredHookInput>)
+- [type ErrorOccurredHookOutput](<#ErrorOccurredHookOutput>)
+- [type ErrorUnion](<#ErrorUnion>)
+ - [func \(x \*ErrorUnion\) MarshalJSON\(\) \(\[\]byte, error\)](<#ErrorUnion.MarshalJSON>)
+ - [func \(x \*ErrorUnion\) UnmarshalJSON\(data \[\]byte\) error](<#ErrorUnion.UnmarshalJSON>)
+- [type GetAuthStatusResponse](<#GetAuthStatusResponse>)
+- [type GetStatusResponse](<#GetStatusResponse>)
+- [type HookInvocation](<#HookInvocation>)
+- [type InfiniteSessionConfig](<#InfiniteSessionConfig>)
+- [type MCPLocalServerConfig](<#MCPLocalServerConfig>)
+- [type MCPRemoteServerConfig](<#MCPRemoteServerConfig>)
+- [type MCPServerConfig](<#MCPServerConfig>)
+- [type MessageOptions](<#MessageOptions>)
+- [type Metadata](<#Metadata>)
+- [type ModelBilling](<#ModelBilling>)
+- [type ModelCapabilities](<#ModelCapabilities>)
+- [type ModelInfo](<#ModelInfo>)
+- [type ModelLimits](<#ModelLimits>)
+- [type ModelMetric](<#ModelMetric>)
+- [type ModelPolicy](<#ModelPolicy>)
+- [type ModelSupports](<#ModelSupports>)
+- [type ModelVisionLimits](<#ModelVisionLimits>)
+- [type PermissionHandler](<#PermissionHandler>)
+- [type PermissionInvocation](<#PermissionInvocation>)
+- [type PermissionRequest](<#PermissionRequest>)
+- [type PermissionRequestResult](<#PermissionRequestResult>)
+- [type PingResponse](<#PingResponse>)
+- [type PostToolUseHandler](<#PostToolUseHandler>)
+- [type PostToolUseHookInput](<#PostToolUseHookInput>)
+- [type PostToolUseHookOutput](<#PostToolUseHookOutput>)
+- [type PreToolUseHandler](<#PreToolUseHandler>)
+- [type PreToolUseHookInput](<#PreToolUseHookInput>)
+- [type PreToolUseHookOutput](<#PreToolUseHookOutput>)
+- [type ProviderConfig](<#ProviderConfig>)
+- [type QuotaSnapshot](<#QuotaSnapshot>)
+- [type Repository](<#Repository>)
+- [type Requests](<#Requests>)
+- [type Result](<#Result>)
+- [type ResumeSessionConfig](<#ResumeSessionConfig>)
+- [type Role](<#Role>)
+- [type SelectionClass](<#SelectionClass>)
+- [type Session](<#Session>)
+ - [func \(s \*Session\) Abort\(ctx context.Context\) error](<#Session.Abort>)
+ - [func \(s \*Session\) Destroy\(\) error](<#Session.Destroy>)
+ - [func \(s \*Session\) GetMessages\(ctx context.Context\) \(\[\]SessionEvent, error\)](<#Session.GetMessages>)
+ - [func \(s \*Session\) On\(handler SessionEventHandler\) func\(\)](<#Session.On>)
+ - [func \(s \*Session\) Send\(ctx context.Context, options MessageOptions\) \(string, error\)](<#Session.Send>)
+ - [func \(s \*Session\) SendAndWait\(ctx context.Context, options MessageOptions\) \(\*SessionEvent, error\)](<#Session.SendAndWait>)
+ - [func \(s \*Session\) WorkspacePath\(\) string](<#Session.WorkspacePath>)
+- [type SessionConfig](<#SessionConfig>)
+- [type SessionEndHandler](<#SessionEndHandler>)
+- [type SessionEndHookInput](<#SessionEndHookInput>)
+- [type SessionEndHookOutput](<#SessionEndHookOutput>)
+- [type SessionEvent](<#SessionEvent>)
+ - [func UnmarshalSessionEvent\(data \[\]byte\) \(SessionEvent, error\)](<#UnmarshalSessionEvent>)
+ - [func \(r \*SessionEvent\) Marshal\(\) \(\[\]byte, error\)](<#SessionEvent.Marshal>)
+- [type SessionEventHandler](<#SessionEventHandler>)
+- [type SessionEventType](<#SessionEventType>)
+- [type SessionHooks](<#SessionHooks>)
+- [type SessionLifecycleEvent](<#SessionLifecycleEvent>)
+- [type SessionLifecycleEventMetadata](<#SessionLifecycleEventMetadata>)
+- [type SessionLifecycleEventType](<#SessionLifecycleEventType>)
+- [type SessionLifecycleHandler](<#SessionLifecycleHandler>)
+- [type SessionMetadata](<#SessionMetadata>)
+- [type SessionStartHandler](<#SessionStartHandler>)
+- [type SessionStartHookInput](<#SessionStartHookInput>)
+- [type SessionStartHookOutput](<#SessionStartHookOutput>)
+- [type ShutdownType](<#ShutdownType>)
+- [type SourceType](<#SourceType>)
+- [type Start](<#Start>)
+- [type SystemMessageAppendConfig](<#SystemMessageAppendConfig>)
+- [type SystemMessageConfig](<#SystemMessageConfig>)
+- [type SystemMessageReplaceConfig](<#SystemMessageReplaceConfig>)
+- [type Tool](<#Tool>)
+ - [func DefineTool\[T any, U any\]\(name, description string, handler func\(T, ToolInvocation\) \(U, error\)\) Tool](<#DefineTool>)
+- [type ToolBinaryResult](<#ToolBinaryResult>)
+- [type ToolHandler](<#ToolHandler>)
+- [type ToolInvocation](<#ToolInvocation>)
+- [type ToolRequest](<#ToolRequest>)
+- [type ToolRequestType](<#ToolRequestType>)
+- [type ToolResult](<#ToolResult>)
+- [type Usage](<#Usage>)
+- [type UserInputHandler](<#UserInputHandler>)
+- [type UserInputInvocation](<#UserInputInvocation>)
+- [type UserInputRequest](<#UserInputRequest>)
+- [type UserInputResponse](<#UserInputResponse>)
+- [type UserPromptSubmittedHandler](<#UserPromptSubmittedHandler>)
+- [type UserPromptSubmittedHookInput](<#UserPromptSubmittedHookInput>)
+- [type UserPromptSubmittedHookOutput](<#UserPromptSubmittedHookOutput>)
+
+
+## Constants
+
+SdkProtocolVersion is the SDK protocol version. This must match the version expected by the copilot\-agent\-runtime server.
+
+```go
+const SdkProtocolVersion = 2
+```
+
+
+## func [Bool]()
+
+```go
+func Bool(v bool) *bool
+```
+
+Bool returns a pointer to the given bool value. Use for setting AutoStart or AutoRestart: AutoStart: Bool\(false\)
+
+
+## func [Float64]()
+
+```go
+func Float64(v float64) *float64
+```
+
+Float64 returns a pointer to the given float64 value. Use for setting thresholds: BackgroundCompactionThreshold: Float64\(0.80\)
+
+
+## func [GetSdkProtocolVersion]()
+
+```go
+func GetSdkProtocolVersion() int
+```
+
+GetSdkProtocolVersion returns the SDK protocol version.
+
+
+## type [Attachment]()
+
+
+
+```go
+type Attachment struct {
+ DisplayName string `json:"displayName"`
+ Path *string `json:"path,omitempty"`
+ Type AttachmentType `json:"type"`
+ FilePath *string `json:"filePath,omitempty"`
+ Selection *SelectionClass `json:"selection,omitempty"`
+ Text *string `json:"text,omitempty"`
+}
+```
+
+
+## type [AttachmentType]()
+
+
+
+```go
+type AttachmentType string
+```
+
+
+
+```go
+const (
+ Directory AttachmentType = "directory"
+ File AttachmentType = "file"
+ Selection AttachmentType = "selection"
+)
+```
+
+
+## type [AzureProviderOptions]()
+
+AzureProviderOptions contains Azure\-specific provider configuration
+
+```go
+type AzureProviderOptions struct {
+ // APIVersion is the Azure API version. Defaults to "2024-10-21".
+ APIVersion string `json:"apiVersion,omitempty"`
+}
+```
+
+
+## type [Client]()
+
+Client manages the connection to the Copilot CLI server and provides session management.
+
+The Client can either spawn a CLI server process or connect to an existing server. It handles JSON\-RPC communication, session lifecycle, tool execution, and permission requests.
+
+Example:
+
+```
+// Create a client with default options (spawns CLI server using stdio)
+client := copilot.NewClient(nil)
+
+// Or connect to an existing server
+client := copilot.NewClient(&copilot.ClientOptions{
+ CLIUrl: "localhost:3000",
+})
+
+if err := client.Start(); err != nil {
+ log.Fatal(err)
+}
+defer client.Stop()
+```
+
+```go
+type Client struct {
+ // contains filtered or unexported fields
+}
+```
+
+
+### func [NewClient]()
+
+```go
+func NewClient(options *ClientOptions) *Client
+```
+
+NewClient creates a new Copilot CLI client with the given options.
+
+If options is nil, default options are used \(spawns CLI server using stdio\). The client is not connected after creation; call [Client.Start](<#Client.Start>) to connect.
+
+Example:
+
+```
+// Default options
+client := copilot.NewClient(nil)
+
+// Custom options
+client := copilot.NewClient(&copilot.ClientOptions{
+ CLIPath: "/usr/local/bin/copilot",
+ LogLevel: "debug",
+})
+```
+
+
+### func \(\*Client\) [CreateSession]()
+
+```go
+func (c *Client) CreateSession(ctx context.Context, config *SessionConfig) (*Session, error)
+```
+
+CreateSession creates 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 AutoStart is enabled, this will automatically start the connection.
+
+The config parameter is optional; pass nil for default settings.
+
+Returns the created session or an error if session creation fails.
+
+Example:
+
+```
+// Basic session
+session, err := client.CreateSession(context.Background(), nil)
+
+// Session with model and tools
+session, err := client.CreateSession(context.Background(), &copilot.SessionConfig{
+ Model: "gpt-4.1",
+ Tools: []copilot.Tool{
+ {
+ Name: "get_weather",
+ Description: "Get weather for a location",
+ Handler: weatherHandler,
+ },
+ },
+})
+```
+
+
+### func \(\*Client\) [DeleteSession]()
+
+```go
+func (c *Client) DeleteSession(ctx context.Context, sessionID string) error
+```
+
+DeleteSession permanently deletes a session and all its conversation history.
+
+The session cannot be resumed after deletion. If the session is in the local sessions map, it will be removed.
+
+Example:
+
+```
+if err := client.DeleteSession(context.Background(), "session-123"); err != nil {
+ log.Fatal(err)
+}
+```
+
+
+### func \(\*Client\) [ForceStop]()
+
+```go
+func (c *Client) ForceStop()
+```
+
+ForceStop forcefully stops the CLI server without graceful cleanup.
+
+Use this when [Client.Stop](<#Client.Stop>) fails or takes too long. This method:
+
+- Clears all sessions immediately without destroying them
+- Force closes the connection
+- Kills the CLI process \(if spawned by this client\)
+
+Example:
+
+```
+// If normal stop hangs, force stop
+done := make(chan struct{})
+go func() {
+ client.Stop()
+ close(done)
+}()
+
+select {
+case <-done:
+ // Stopped successfully
+case <-time.After(5 * time.Second):
+ client.ForceStop()
+}
+```
+
+
+### func \(\*Client\) [GetAuthStatus]()
+
+```go
+func (c *Client) GetAuthStatus(ctx context.Context) (*GetAuthStatusResponse, error)
+```
+
+GetAuthStatus returns current authentication status
+
+
+### func \(\*Client\) [GetForegroundSessionID]()
+
+```go
+func (c *Client) GetForegroundSessionID(ctx context.Context) (*string, error)
+```
+
+GetForegroundSessionID returns the ID of the session currently displayed in the TUI.
+
+This is only available when connecting to a server running in TUI\+server mode \(\-\-ui\-server\). Returns nil if no foreground session is set.
+
+Example:
+
+```
+sessionID, err := client.GetForegroundSessionID()
+if err != nil {
+ log.Fatal(err)
+}
+if sessionID != nil {
+ fmt.Printf("TUI is displaying session: %s\n", *sessionID)
+}
+```
+
+
+### func \(\*Client\) [GetStatus]()
+
+```go
+func (c *Client) GetStatus(ctx context.Context) (*GetStatusResponse, error)
+```
+
+GetStatus returns CLI status including version and protocol information
+
+
+### func \(\*Client\) [ListModels]()
+
+```go
+func (c *Client) ListModels(ctx context.Context) ([]ModelInfo, error)
+```
+
+ListModels returns available models with their metadata.
+
+Results are cached after the first successful call to avoid rate limiting. The cache is cleared when the client disconnects.
+
+
+### func \(\*Client\) [ListSessions]()
+
+```go
+func (c *Client) ListSessions(ctx context.Context) ([]SessionMetadata, error)
+```
+
+ListSessions returns metadata about all sessions known to the server.
+
+Returns a list of SessionMetadata for all available sessions, including their IDs, timestamps, and optional summaries.
+
+Example:
+
+```
+sessions, err := client.ListSessions(context.Background())
+if err != nil {
+ log.Fatal(err)
+}
+for _, session := range sessions {
+ fmt.Printf("Session: %s\n", session.SessionID)
+}
+```
+
+
+### func \(\*Client\) [On]()
+
+```go
+func (c *Client) On(handler SessionLifecycleHandler) func()
+```
+
+On subscribes to all session lifecycle events.
+
+Lifecycle events are emitted when sessions are created, deleted, updated, or change foreground/background state \(in TUI\+server mode\).
+
+Returns a function that, when called, unsubscribes the handler.
+
+Example:
+
+```
+unsubscribe := client.On(func(event copilot.SessionLifecycleEvent) {
+ fmt.Printf("Session %s: %s\n", event.SessionID, event.Type)
+})
+defer unsubscribe()
+```
+
+
+### func \(\*Client\) [OnEventType]()
+
+```go
+func (c *Client) OnEventType(eventType SessionLifecycleEventType, handler SessionLifecycleHandler) func()
+```
+
+OnEventType subscribes to a specific session lifecycle event type.
+
+Returns a function that, when called, unsubscribes the handler.
+
+Example:
+
+```
+unsubscribe := client.OnEventType(copilot.SessionLifecycleForeground, func(event copilot.SessionLifecycleEvent) {
+ fmt.Printf("Session %s is now in foreground\n", event.SessionID)
+})
+defer unsubscribe()
+```
+
+
+### func \(\*Client\) [Ping]()
+
+```go
+func (c *Client) Ping(ctx context.Context, message string) (*PingResponse, error)
+```
+
+Ping sends a ping request to the server to verify connectivity.
+
+The message parameter is optional and will be echoed back in the response. Returns a PingResponse containing the message and server timestamp, or an error.
+
+Example:
+
+```
+resp, err := client.Ping(context.Background(), "health check")
+if err != nil {
+ log.Printf("Server unreachable: %v", err)
+} else {
+ log.Printf("Server responded at %d", resp.Timestamp)
+}
+```
+
+
+### func \(\*Client\) [ResumeSession]()
+
+```go
+func (c *Client) ResumeSession(ctx context.Context, sessionID string) (*Session, error)
+```
+
+ResumeSession resumes an existing conversation session by its ID using default options.
+
+This is a convenience method that calls [Client.ResumeSessionWithOptions](<#Client.ResumeSessionWithOptions>) with nil config.
+
+Example:
+
+```
+session, err := client.ResumeSession(context.Background(), "session-123")
+```
+
+
+### func \(\*Client\) [ResumeSessionWithOptions]()
+
+```go
+func (c *Client) ResumeSessionWithOptions(ctx context.Context, sessionID string, config *ResumeSessionConfig) (*Session, error)
+```
+
+ResumeSessionWithOptions resumes an existing conversation session with additional configuration.
+
+This allows you to continue a previous conversation, maintaining all conversation history. The session must have been previously created and not deleted.
+
+Example:
+
+```
+session, err := client.ResumeSessionWithOptions(context.Background(), "session-123", &copilot.ResumeSessionConfig{
+ Tools: []copilot.Tool{myNewTool},
+})
+```
+
+
+### func \(\*Client\) [SetForegroundSessionID]()
+
+```go
+func (c *Client) SetForegroundSessionID(ctx context.Context, sessionID string) error
+```
+
+SetForegroundSessionID requests the TUI to switch to displaying the specified session.
+
+This is only available when connecting to a server running in TUI\+server mode \(\-\-ui\-server\).
+
+Example:
+
+```
+if err := client.SetForegroundSessionID("session-123"); err != nil {
+ log.Fatal(err)
+}
+```
+
+
+### func \(\*Client\) [Start]()
+
+```go
+func (c *Client) Start(ctx context.Context) error
+```
+
+Start starts the CLI server \(if not using an external server\) and establishes a connection.
+
+If connecting to an external server \(via CLIUrl\), only establishes the connection. Otherwise, spawns the CLI server process and then connects.
+
+This method is called automatically when creating a session if AutoStart is true \(default\).
+
+Returns an error if the server fails to start or the connection fails.
+
+Example:
+
+```
+client := copilot.NewClient(&copilot.ClientOptions{AutoStart: boolPtr(false)})
+if err := client.Start(context.Background()); err != nil {
+ log.Fatal("Failed to start:", err)
+}
+// Now ready to create sessions
+```
+
+
+### func \(\*Client\) [State]()
+
+```go
+func (c *Client) State() ConnectionState
+```
+
+State returns the current connection state of the client.
+
+Possible states: StateDisconnected, StateConnecting, StateConnected, StateError.
+
+Example:
+
+```
+if client.State() == copilot.StateConnected {
+ session, err := client.CreateSession(context.Background(), nil)
+}
+```
+
+
+### func \(\*Client\) [Stop]()
+
+```go
+func (c *Client) Stop() error
+```
+
+Stop stops the CLI server and closes all active sessions.
+
+This method performs graceful cleanup:
+
+1. Destroys all active sessions
+2. Closes the JSON\-RPC connection
+3. Terminates the CLI server process \(if spawned by this client\)
+
+Returns an error that aggregates all errors encountered during cleanup.
+
+Example:
+
+```
+if err := client.Stop(); err != nil {
+ log.Printf("Cleanup error: %v", err)
+}
+```
+
+
+## type [ClientOptions]()
+
+ClientOptions configures the CopilotClient
+
+```go
+type ClientOptions struct {
+ // CLIPath is the path to the Copilot CLI executable (default: "copilot")
+ CLIPath string
+ // Cwd is the working directory for the CLI process (default: "" = inherit from current process)
+ Cwd string
+ // Port for TCP transport (default: 0 = random port)
+ Port int
+ // UseStdio controls whether to use stdio transport instead of TCP.
+ // Default: nil (use default = true, i.e. stdio). Use Bool(false) to explicitly select TCP.
+ UseStdio *bool
+ // CLIUrl is the URL of an existing Copilot CLI server to connect to over TCP
+ // Format: "host:port", "http://host:port", or just "port" (defaults to localhost)
+ // Examples: "localhost:8080", "http://127.0.0.1:9000", "8080"
+ // Mutually exclusive with CLIPath, UseStdio
+ CLIUrl string
+ // LogLevel for the CLI server
+ LogLevel string
+ // AutoStart automatically starts the CLI server on first use (default: true).
+ // Use Bool(false) to disable.
+ AutoStart *bool
+ // AutoRestart automatically restarts the CLI server if it crashes (default: true).
+ // Use Bool(false) to disable.
+ AutoRestart *bool
+ // Env is the environment variables for the CLI process (default: inherits from current process).
+ // Each entry is of the form "key=value".
+ // If Env is nil, the new process uses the current process's environment.
+ // If Env contains duplicate environment keys, only the last value in the
+ // slice for each duplicate key is used.
+ Env []string
+ // GithubToken is the GitHub token to use for authentication.
+ // When provided, the token is passed to the CLI server via environment variable.
+ // This takes priority over other authentication methods.
+ GithubToken string
+ // UseLoggedInUser controls whether to use the logged-in user for authentication.
+ // When true, the CLI server will attempt to use stored OAuth tokens or gh CLI auth.
+ // When false, only explicit tokens (GithubToken or environment variables) are used.
+ // Default: true (but defaults to false when GithubToken is provided).
+ // Use Bool(false) to explicitly disable.
+ UseLoggedInUser *bool
+}
+```
+
+
+## type [CodeChanges]()
+
+
+
+```go
+type CodeChanges struct {
+ FilesModified []string `json:"filesModified"`
+ LinesAdded float64 `json:"linesAdded"`
+ LinesRemoved float64 `json:"linesRemoved"`
+}
+```
+
+
+## type [CompactionTokensUsed]()
+
+
+
+```go
+type CompactionTokensUsed struct {
+ CachedInput float64 `json:"cachedInput"`
+ Input float64 `json:"input"`
+ Output float64 `json:"output"`
+}
+```
+
+
+## type [ConnectionState]()
+
+ConnectionState represents the client connection state
+
+```go
+type ConnectionState string
+```
+
+
+
+```go
+const (
+ StateDisconnected ConnectionState = "disconnected"
+ StateConnecting ConnectionState = "connecting"
+ StateConnected ConnectionState = "connected"
+ StateError ConnectionState = "error"
+)
+```
+
+
+## type [ContextClass]()
+
+
+
+```go
+type ContextClass struct {
+ Branch *string `json:"branch,omitempty"`
+ Cwd string `json:"cwd"`
+ GitRoot *string `json:"gitRoot,omitempty"`
+ Repository *string `json:"repository,omitempty"`
+}
+```
+
+
+## type [ContextUnion]()
+
+
+
+```go
+type ContextUnion struct {
+ ContextClass *ContextClass
+ String *string
+}
+```
+
+
+### func \(\*ContextUnion\) [MarshalJSON]()
+
+```go
+func (x *ContextUnion) MarshalJSON() ([]byte, error)
+```
+
+
+
+
+### func \(\*ContextUnion\) [UnmarshalJSON]()
+
+```go
+func (x *ContextUnion) UnmarshalJSON(data []byte) error
+```
+
+
+
+
+## type [CustomAgentConfig]()
+
+CustomAgentConfig configures a custom agent
+
+```go
+type CustomAgentConfig struct {
+ // Name is the unique name of the custom agent
+ Name string `json:"name"`
+ // DisplayName is the display name for UI purposes
+ DisplayName string `json:"displayName,omitempty"`
+ // Description of what the agent does
+ Description string `json:"description,omitempty"`
+ // Tools is the list of tool names the agent can use (nil for all tools)
+ Tools []string `json:"tools,omitempty"`
+ // Prompt is the prompt content for the agent
+ Prompt string `json:"prompt"`
+ // MCPServers are MCP servers specific to this agent
+ MCPServers map[string]MCPServerConfig `json:"mcpServers,omitempty"`
+ // Infer indicates whether the agent should be available for model inference
+ Infer *bool `json:"infer,omitempty"`
+}
+```
+
+
+## type [Data]()
+
+
+
+```go
+type Data struct {
+ Context *ContextUnion `json:"context"`
+ CopilotVersion *string `json:"copilotVersion,omitempty"`
+ Producer *string `json:"producer,omitempty"`
+ SelectedModel *string `json:"selectedModel,omitempty"`
+ SessionID *string `json:"sessionId,omitempty"`
+ StartTime *time.Time `json:"startTime,omitempty"`
+ Version *float64 `json:"version,omitempty"`
+ EventCount *float64 `json:"eventCount,omitempty"`
+ ResumeTime *time.Time `json:"resumeTime,omitempty"`
+ ErrorType *string `json:"errorType,omitempty"`
+ Message *string `json:"message,omitempty"`
+ ProviderCallID *string `json:"providerCallId,omitempty"`
+ Stack *string `json:"stack,omitempty"`
+ StatusCode *int64 `json:"statusCode,omitempty"`
+ InfoType *string `json:"infoType,omitempty"`
+ NewModel *string `json:"newModel,omitempty"`
+ PreviousModel *string `json:"previousModel,omitempty"`
+ HandoffTime *time.Time `json:"handoffTime,omitempty"`
+ RemoteSessionID *string `json:"remoteSessionId,omitempty"`
+ Repository *Repository `json:"repository,omitempty"`
+ SourceType *SourceType `json:"sourceType,omitempty"`
+ Summary *string `json:"summary,omitempty"`
+ MessagesRemovedDuringTruncation *float64 `json:"messagesRemovedDuringTruncation,omitempty"`
+ PerformedBy *string `json:"performedBy,omitempty"`
+ PostTruncationMessagesLength *float64 `json:"postTruncationMessagesLength,omitempty"`
+ PostTruncationTokensInMessages *float64 `json:"postTruncationTokensInMessages,omitempty"`
+ PreTruncationMessagesLength *float64 `json:"preTruncationMessagesLength,omitempty"`
+ PreTruncationTokensInMessages *float64 `json:"preTruncationTokensInMessages,omitempty"`
+ TokenLimit *float64 `json:"tokenLimit,omitempty"`
+ TokensRemovedDuringTruncation *float64 `json:"tokensRemovedDuringTruncation,omitempty"`
+ EventsRemoved *float64 `json:"eventsRemoved,omitempty"`
+ UpToEventID *string `json:"upToEventId,omitempty"`
+ CodeChanges *CodeChanges `json:"codeChanges,omitempty"`
+ CurrentModel *string `json:"currentModel,omitempty"`
+ ErrorReason *string `json:"errorReason,omitempty"`
+ ModelMetrics map[string]ModelMetric `json:"modelMetrics,omitempty"`
+ SessionStartTime *float64 `json:"sessionStartTime,omitempty"`
+ ShutdownType *ShutdownType `json:"shutdownType,omitempty"`
+ TotalAPIDurationMS *float64 `json:"totalApiDurationMs,omitempty"`
+ TotalPremiumRequests *float64 `json:"totalPremiumRequests,omitempty"`
+ CurrentTokens *float64 `json:"currentTokens,omitempty"`
+ MessagesLength *float64 `json:"messagesLength,omitempty"`
+ CheckpointNumber *float64 `json:"checkpointNumber,omitempty"`
+ CheckpointPath *string `json:"checkpointPath,omitempty"`
+ CompactionTokensUsed *CompactionTokensUsed `json:"compactionTokensUsed,omitempty"`
+ Error *ErrorUnion `json:"error"`
+ MessagesRemoved *float64 `json:"messagesRemoved,omitempty"`
+ PostCompactionTokens *float64 `json:"postCompactionTokens,omitempty"`
+ PreCompactionMessagesLength *float64 `json:"preCompactionMessagesLength,omitempty"`
+ PreCompactionTokens *float64 `json:"preCompactionTokens,omitempty"`
+ RequestID *string `json:"requestId,omitempty"`
+ Success *bool `json:"success,omitempty"`
+ SummaryContent *string `json:"summaryContent,omitempty"`
+ TokensRemoved *float64 `json:"tokensRemoved,omitempty"`
+ Attachments []Attachment `json:"attachments,omitempty"`
+ Content *string `json:"content,omitempty"`
+ Source *string `json:"source,omitempty"`
+ TransformedContent *string `json:"transformedContent,omitempty"`
+ TurnID *string `json:"turnId,omitempty"`
+ Intent *string `json:"intent,omitempty"`
+ ReasoningID *string `json:"reasoningId,omitempty"`
+ DeltaContent *string `json:"deltaContent,omitempty"`
+ EncryptedContent *string `json:"encryptedContent,omitempty"`
+ MessageID *string `json:"messageId,omitempty"`
+ ParentToolCallID *string `json:"parentToolCallId,omitempty"`
+ ReasoningOpaque *string `json:"reasoningOpaque,omitempty"`
+ ReasoningText *string `json:"reasoningText,omitempty"`
+ ToolRequests []ToolRequest `json:"toolRequests,omitempty"`
+ TotalResponseSizeBytes *float64 `json:"totalResponseSizeBytes,omitempty"`
+ APICallID *string `json:"apiCallId,omitempty"`
+ CacheReadTokens *float64 `json:"cacheReadTokens,omitempty"`
+ CacheWriteTokens *float64 `json:"cacheWriteTokens,omitempty"`
+ Cost *float64 `json:"cost,omitempty"`
+ Duration *float64 `json:"duration,omitempty"`
+ Initiator *string `json:"initiator,omitempty"`
+ InputTokens *float64 `json:"inputTokens,omitempty"`
+ Model *string `json:"model,omitempty"`
+ OutputTokens *float64 `json:"outputTokens,omitempty"`
+ QuotaSnapshots map[string]QuotaSnapshot `json:"quotaSnapshots,omitempty"`
+ Reason *string `json:"reason,omitempty"`
+ Arguments interface{} `json:"arguments"`
+ ToolCallID *string `json:"toolCallId,omitempty"`
+ ToolName *string `json:"toolName,omitempty"`
+ MCPServerName *string `json:"mcpServerName,omitempty"`
+ MCPToolName *string `json:"mcpToolName,omitempty"`
+ PartialOutput *string `json:"partialOutput,omitempty"`
+ ProgressMessage *string `json:"progressMessage,omitempty"`
+ IsUserRequested *bool `json:"isUserRequested,omitempty"`
+ Result *Result `json:"result,omitempty"`
+ ToolTelemetry map[string]interface{} `json:"toolTelemetry,omitempty"`
+ AllowedTools []string `json:"allowedTools,omitempty"`
+ Name *string `json:"name,omitempty"`
+ Path *string `json:"path,omitempty"`
+ AgentDescription *string `json:"agentDescription,omitempty"`
+ AgentDisplayName *string `json:"agentDisplayName,omitempty"`
+ AgentName *string `json:"agentName,omitempty"`
+ Tools []string `json:"tools"`
+ HookInvocationID *string `json:"hookInvocationId,omitempty"`
+ HookType *string `json:"hookType,omitempty"`
+ Input interface{} `json:"input"`
+ Output interface{} `json:"output"`
+ Metadata *Metadata `json:"metadata,omitempty"`
+ Role *Role `json:"role,omitempty"`
+}
+```
+
+
+## type [End]()
+
+
+
+```go
+type End struct {
+ Character float64 `json:"character"`
+ Line float64 `json:"line"`
+}
+```
+
+
+## type [ErrorClass]()
+
+
+
+```go
+type ErrorClass struct {
+ Code *string `json:"code,omitempty"`
+ Message string `json:"message"`
+ Stack *string `json:"stack,omitempty"`
+}
+```
+
+
+## type [ErrorOccurredHandler]()
+
+ErrorOccurredHandler handles error\-occurred hook invocations
+
+```go
+type ErrorOccurredHandler func(input ErrorOccurredHookInput, invocation HookInvocation) (*ErrorOccurredHookOutput, error)
+```
+
+
+## type [ErrorOccurredHookInput]()
+
+ErrorOccurredHookInput is the input for an error\-occurred hook
+
+```go
+type ErrorOccurredHookInput struct {
+ Timestamp int64 `json:"timestamp"`
+ Cwd string `json:"cwd"`
+ Error string `json:"error"`
+ ErrorContext string `json:"errorContext"` // "model_call", "tool_execution", "system", "user_input"
+ Recoverable bool `json:"recoverable"`
+}
+```
+
+
+## type [ErrorOccurredHookOutput]()
+
+ErrorOccurredHookOutput is the output for an error\-occurred hook
+
+```go
+type ErrorOccurredHookOutput struct {
+ SuppressOutput bool `json:"suppressOutput,omitempty"`
+ ErrorHandling string `json:"errorHandling,omitempty"` // "retry", "skip", "abort"
+ RetryCount int `json:"retryCount,omitempty"`
+ UserNotification string `json:"userNotification,omitempty"`
+}
+```
+
+
+## type [ErrorUnion]()
+
+
+
+```go
+type ErrorUnion struct {
+ ErrorClass *ErrorClass
+ String *string
+}
+```
+
+
+### func \(\*ErrorUnion\) [MarshalJSON]()
+
+```go
+func (x *ErrorUnion) MarshalJSON() ([]byte, error)
+```
+
+
+
+
+### func \(\*ErrorUnion\) [UnmarshalJSON]()
+
+```go
+func (x *ErrorUnion) UnmarshalJSON(data []byte) error
+```
+
+
+
+
+## type [GetAuthStatusResponse]()
+
+GetAuthStatusResponse is the response from auth.getStatus
+
+```go
+type GetAuthStatusResponse struct {
+ IsAuthenticated bool `json:"isAuthenticated"`
+ AuthType *string `json:"authType,omitempty"`
+ Host *string `json:"host,omitempty"`
+ Login *string `json:"login,omitempty"`
+ StatusMessage *string `json:"statusMessage,omitempty"`
+}
+```
+
+
+## type [GetStatusResponse]()
+
+GetStatusResponse is the response from status.get
+
+```go
+type GetStatusResponse struct {
+ Version string `json:"version"`
+ ProtocolVersion int `json:"protocolVersion"`
+}
+```
+
+
+## type [HookInvocation]()
+
+HookInvocation provides context about a hook invocation
+
+```go
+type HookInvocation struct {
+ SessionID string
+}
+```
+
+
+## type [InfiniteSessionConfig]()
+
+InfiniteSessionConfig configures infinite sessions with automatic context compaction and workspace persistence. When enabled, sessions automatically manage context window limits through background compaction and persist state to a workspace directory.
+
+```go
+type InfiniteSessionConfig struct {
+ // Enabled controls whether infinite sessions are enabled (default: true)
+ Enabled *bool `json:"enabled,omitempty"`
+ // BackgroundCompactionThreshold is the context utilization (0.0-1.0) at which
+ // background compaction starts. Default: 0.80
+ BackgroundCompactionThreshold *float64 `json:"backgroundCompactionThreshold,omitempty"`
+ // BufferExhaustionThreshold is the context utilization (0.0-1.0) at which
+ // the session blocks until compaction completes. Default: 0.95
+ BufferExhaustionThreshold *float64 `json:"bufferExhaustionThreshold,omitempty"`
+}
+```
+
+
+## type [MCPLocalServerConfig]()
+
+MCPLocalServerConfig configures a local/stdio MCP server
+
+```go
+type MCPLocalServerConfig struct {
+ Tools []string `json:"tools"`
+ Type string `json:"type,omitempty"` // "local" or "stdio"
+ Timeout int `json:"timeout,omitempty"`
+ Command string `json:"command"`
+ Args []string `json:"args"`
+ Env map[string]string `json:"env,omitempty"`
+ Cwd string `json:"cwd,omitempty"`
+}
+```
+
+
+## type [MCPRemoteServerConfig]()
+
+MCPRemoteServerConfig configures a remote MCP server \(HTTP or SSE\)
+
+```go
+type MCPRemoteServerConfig struct {
+ Tools []string `json:"tools"`
+ Type string `json:"type"` // "http" or "sse"
+ Timeout int `json:"timeout,omitempty"`
+ URL string `json:"url"`
+ Headers map[string]string `json:"headers,omitempty"`
+}
+```
+
+
+## type [MCPServerConfig]()
+
+MCPServerConfig can be either MCPLocalServerConfig or MCPRemoteServerConfig Use a map\[string\]any for flexibility, or create separate configs
+
+```go
+type MCPServerConfig map[string]any
+```
+
+
+## type [MessageOptions]()
+
+MessageOptions configures a message to send
+
+```go
+type MessageOptions struct {
+ // Prompt is the message to send
+ Prompt string
+ // Attachments are file or directory attachments
+ Attachments []Attachment
+ // Mode is the message delivery mode (default: "enqueue")
+ Mode string
+}
+```
+
+
+## type [Metadata]()
+
+
+
+```go
+type Metadata struct {
+ PromptVersion *string `json:"promptVersion,omitempty"`
+ Variables map[string]interface{} `json:"variables,omitempty"`
+}
+```
+
+
+## type [ModelBilling]()
+
+ModelBilling contains model billing information
+
+```go
+type ModelBilling struct {
+ Multiplier float64 `json:"multiplier"`
+}
+```
+
+
+## type [ModelCapabilities]()
+
+ModelCapabilities contains model capabilities and limits
+
+```go
+type ModelCapabilities struct {
+ Supports ModelSupports `json:"supports"`
+ Limits ModelLimits `json:"limits"`
+}
+```
+
+
+## type [ModelInfo]()
+
+ModelInfo contains information about an available model
+
+```go
+type ModelInfo struct {
+ ID string `json:"id"`
+ Name string `json:"name"`
+ Capabilities ModelCapabilities `json:"capabilities"`
+ Policy *ModelPolicy `json:"policy,omitempty"`
+ Billing *ModelBilling `json:"billing,omitempty"`
+ SupportedReasoningEfforts []string `json:"supportedReasoningEfforts,omitempty"`
+ DefaultReasoningEffort string `json:"defaultReasoningEffort,omitempty"`
+}
+```
+
+
+## type [ModelLimits]()
+
+ModelLimits contains model limits
+
+```go
+type ModelLimits struct {
+ MaxPromptTokens *int `json:"max_prompt_tokens,omitempty"`
+ MaxContextWindowTokens int `json:"max_context_window_tokens"`
+ Vision *ModelVisionLimits `json:"vision,omitempty"`
+}
+```
+
+
+## type [ModelMetric]()
+
+
+
+```go
+type ModelMetric struct {
+ Requests Requests `json:"requests"`
+ Usage Usage `json:"usage"`
+}
+```
+
+
+## type [ModelPolicy]()
+
+ModelPolicy contains model policy state
+
+```go
+type ModelPolicy struct {
+ State string `json:"state"`
+ Terms string `json:"terms"`
+}
+```
+
+
+## type [ModelSupports]()
+
+ModelSupports contains model support flags
+
+```go
+type ModelSupports struct {
+ Vision bool `json:"vision"`
+ ReasoningEffort bool `json:"reasoningEffort"`
+}
+```
+
+
+## type [ModelVisionLimits]()
+
+ModelVisionLimits contains vision\-specific limits
+
+```go
+type ModelVisionLimits struct {
+ SupportedMediaTypes []string `json:"supported_media_types"`
+ MaxPromptImages int `json:"max_prompt_images"`
+ MaxPromptImageSize int `json:"max_prompt_image_size"`
+}
+```
+
+
+## type [PermissionHandler]()
+
+PermissionHandler executes a permission request The handler should return a PermissionRequestResult. Returning an error denies the permission.
+
+```go
+type PermissionHandler func(request PermissionRequest, invocation PermissionInvocation) (PermissionRequestResult, error)
+```
+
+
+## type [PermissionInvocation]()
+
+PermissionInvocation provides context about a permission request
+
+```go
+type PermissionInvocation struct {
+ SessionID string
+}
+```
+
+
+## type [PermissionRequest]()
+
+PermissionRequest represents a permission request from the server
+
+```go
+type PermissionRequest struct {
+ Kind string `json:"kind"`
+ ToolCallID string `json:"toolCallId,omitempty"`
+ Extra map[string]any `json:"-"` // Additional fields vary by kind
+}
+```
+
+
+## type [PermissionRequestResult]()
+
+PermissionRequestResult represents the result of a permission request
+
+```go
+type PermissionRequestResult struct {
+ Kind string `json:"kind"`
+ Rules []any `json:"rules,omitempty"`
+}
+```
+
+
+## type [PingResponse]()
+
+PingResponse is the response from a ping request
+
+```go
+type PingResponse struct {
+ Message string `json:"message"`
+ Timestamp int64 `json:"timestamp"`
+ ProtocolVersion *int `json:"protocolVersion,omitempty"`
+}
+```
+
+
+## type [PostToolUseHandler]()
+
+PostToolUseHandler handles post\-tool\-use hook invocations
+
+```go
+type PostToolUseHandler func(input PostToolUseHookInput, invocation HookInvocation) (*PostToolUseHookOutput, error)
+```
+
+
+## type [PostToolUseHookInput]()
+
+PostToolUseHookInput is the input for a post\-tool\-use hook
+
+```go
+type PostToolUseHookInput struct {
+ Timestamp int64 `json:"timestamp"`
+ Cwd string `json:"cwd"`
+ ToolName string `json:"toolName"`
+ ToolArgs any `json:"toolArgs"`
+ ToolResult any `json:"toolResult"`
+}
+```
+
+
+## type [PostToolUseHookOutput]()
+
+PostToolUseHookOutput is the output for a post\-tool\-use hook
+
+```go
+type PostToolUseHookOutput struct {
+ ModifiedResult any `json:"modifiedResult,omitempty"`
+ AdditionalContext string `json:"additionalContext,omitempty"`
+ SuppressOutput bool `json:"suppressOutput,omitempty"`
+}
+```
+
+
+## type [PreToolUseHandler]()
+
+PreToolUseHandler handles pre\-tool\-use hook invocations
+
+```go
+type PreToolUseHandler func(input PreToolUseHookInput, invocation HookInvocation) (*PreToolUseHookOutput, error)
+```
+
+
+## type [PreToolUseHookInput]()
+
+PreToolUseHookInput is the input for a pre\-tool\-use hook
+
+```go
+type PreToolUseHookInput struct {
+ Timestamp int64 `json:"timestamp"`
+ Cwd string `json:"cwd"`
+ ToolName string `json:"toolName"`
+ ToolArgs any `json:"toolArgs"`
+}
+```
+
+
+## type [PreToolUseHookOutput]()
+
+PreToolUseHookOutput is the output for a pre\-tool\-use hook
+
+```go
+type PreToolUseHookOutput struct {
+ PermissionDecision string `json:"permissionDecision,omitempty"` // "allow", "deny", "ask"
+ PermissionDecisionReason string `json:"permissionDecisionReason,omitempty"`
+ ModifiedArgs any `json:"modifiedArgs,omitempty"`
+ AdditionalContext string `json:"additionalContext,omitempty"`
+ SuppressOutput bool `json:"suppressOutput,omitempty"`
+}
+```
+
+
+## type [ProviderConfig]()
+
+ProviderConfig configures a custom model provider
+
+```go
+type ProviderConfig struct {
+ // Type is the provider type: "openai", "azure", or "anthropic". Defaults to "openai".
+ Type string `json:"type,omitempty"`
+ // WireApi is the API format (openai/azure only): "completions" or "responses". Defaults to "completions".
+ WireApi string `json:"wireApi,omitempty"`
+ // BaseURL is the API endpoint URL
+ BaseURL string `json:"baseUrl"`
+ // APIKey is the API key. Optional for local providers like Ollama.
+ APIKey string `json:"apiKey,omitempty"`
+ // BearerToken for authentication. Sets the Authorization header directly.
+ // Use this for services requiring bearer token auth instead of API key.
+ // Takes precedence over APIKey when both are set.
+ BearerToken string `json:"bearerToken,omitempty"`
+ // Azure contains Azure-specific options
+ Azure *AzureProviderOptions `json:"azure,omitempty"`
+}
+```
+
+
+## type [QuotaSnapshot]()
+
+
+
+```go
+type QuotaSnapshot struct {
+ EntitlementRequests float64 `json:"entitlementRequests"`
+ IsUnlimitedEntitlement bool `json:"isUnlimitedEntitlement"`
+ Overage float64 `json:"overage"`
+ OverageAllowedWithExhaustedQuota bool `json:"overageAllowedWithExhaustedQuota"`
+ RemainingPercentage float64 `json:"remainingPercentage"`
+ ResetDate *time.Time `json:"resetDate,omitempty"`
+ UsageAllowedWithExhaustedQuota bool `json:"usageAllowedWithExhaustedQuota"`
+ UsedRequests float64 `json:"usedRequests"`
+}
+```
+
+
+## type [Repository]()
+
+
+
+```go
+type Repository struct {
+ Branch *string `json:"branch,omitempty"`
+ Name string `json:"name"`
+ Owner string `json:"owner"`
+}
+```
+
+
+## type [Requests]()
+
+
+
+```go
+type Requests struct {
+ Cost float64 `json:"cost"`
+ Count float64 `json:"count"`
+}
+```
+
+
+## type [Result]()
+
+
+
+```go
+type Result struct {
+ Content string `json:"content"`
+ DetailedContent *string `json:"detailedContent,omitempty"`
+}
+```
+
+
+## type [ResumeSessionConfig]()
+
+ResumeSessionConfig configures options when resuming a session
+
+```go
+type ResumeSessionConfig struct {
+ // Model to use for this session. Can change the model when resuming.
+ Model string
+ // Tools exposes caller-implemented tools to the CLI
+ Tools []Tool
+ // SystemMessage configures system message customization
+ SystemMessage *SystemMessageConfig
+ // AvailableTools is a list of tool names to allow. When specified, only these tools will be available.
+ // Takes precedence over ExcludedTools.
+ AvailableTools []string
+ // ExcludedTools is a list of tool names to disable. All other tools remain available.
+ // Ignored if AvailableTools is specified.
+ ExcludedTools []string
+ // Provider configures a custom model provider
+ Provider *ProviderConfig
+ // ReasoningEffort level for models that support it.
+ // Valid values: "low", "medium", "high", "xhigh"
+ ReasoningEffort string
+ // OnPermissionRequest is a handler for permission requests from the server
+ OnPermissionRequest PermissionHandler
+ // OnUserInputRequest is a handler for user input requests from the agent (enables ask_user tool)
+ OnUserInputRequest UserInputHandler
+ // Hooks configures hook handlers for session lifecycle events
+ Hooks *SessionHooks
+ // WorkingDirectory is the working directory for the session.
+ // Tool operations will be relative to this directory.
+ WorkingDirectory string
+ // ConfigDir overrides the default configuration directory location.
+ ConfigDir string
+ // Streaming enables streaming of assistant message and reasoning chunks.
+ // When true, assistant.message_delta and assistant.reasoning_delta events
+ // with deltaContent are sent as the response is generated.
+ Streaming bool
+ // MCPServers configures MCP servers for the session
+ MCPServers map[string]MCPServerConfig
+ // CustomAgents configures custom agents for the session
+ CustomAgents []CustomAgentConfig
+ // SkillDirectories is a list of directories to load skills from
+ SkillDirectories []string
+ // DisabledSkills is a list of skill names to disable
+ DisabledSkills []string
+ // InfiniteSessions configures infinite sessions for persistent workspaces and automatic compaction.
+ InfiniteSessions *InfiniteSessionConfig
+ // DisableResume, when true, skips emitting the session.resume event.
+ // Useful for reconnecting to a session without triggering resume-related side effects.
+ DisableResume bool
+}
+```
+
+
+## type [Role]()
+
+
+
+```go
+type Role string
+```
+
+
+
+```go
+const (
+ Developer Role = "developer"
+ System Role = "system"
+)
+```
+
+
+## type [SelectionClass]()
+
+
+
+```go
+type SelectionClass struct {
+ End End `json:"end"`
+ Start Start `json:"start"`
+}
+```
+
+
+## type [Session]()
+
+Session represents a single conversation session with the Copilot CLI.
+
+A session maintains conversation state, handles events, and manages tool execution. Sessions are created via [Client.CreateSession](<#Client.CreateSession>) or resumed via [Client.ResumeSession](<#Client.ResumeSession>).
+
+The session provides methods to send messages, subscribe to events, retrieve conversation history, and manage the session lifecycle. All methods are safe for concurrent use.
+
+Example usage:
+
+```
+session, err := client.CreateSession(copilot.SessionConfig{
+ Model: "gpt-4.1",
+})
+if err != nil {
+ log.Fatal(err)
+}
+defer session.Destroy()
+
+// Subscribe to events
+unsubscribe := session.On(func(event copilot.SessionEvent) {
+ if event.Type == "assistant.message" {
+ fmt.Println("Assistant:", event.Data.Content)
+ }
+})
+defer unsubscribe()
+
+// Send a message
+messageID, err := session.Send(copilot.MessageOptions{
+ Prompt: "Hello, world!",
+})
+```
+
+```go
+type Session struct {
+ // SessionID is the unique identifier for this session.
+ SessionID string
+ // contains filtered or unexported fields
+}
+```
+
+
+### func \(\*Session\) [Abort]()
+
+```go
+func (s *Session) Abort(ctx context.Context) error
+```
+
+Abort aborts the currently processing message in this session.
+
+Use this to cancel a long\-running request. The session remains valid and can continue to be used for new messages.
+
+Returns an error if the session has been destroyed or the connection fails.
+
+Example:
+
+```
+// Start a long-running request in a goroutine
+go func() {
+ session.Send(context.Background(), copilot.MessageOptions{
+ Prompt: "Write a very long story...",
+ })
+}()
+
+// Abort after 5 seconds
+time.Sleep(5 * time.Second)
+if err := session.Abort(context.Background()); err != nil {
+ log.Printf("Failed to abort: %v", err)
+}
+```
+
+
+### func \(\*Session\) [Destroy]()
+
+```go
+func (s *Session) Destroy() error
+```
+
+Destroy destroys this session and releases all associated resources.
+
+After calling this method, the session can no longer be used. All event handlers and tool handlers are cleared. To continue the conversation, use [Client.ResumeSession](<#Client.ResumeSession>) with the session ID.
+
+Returns an error if the connection fails.
+
+Example:
+
+```
+// Clean up when done
+if err := session.Destroy(); err != nil {
+ log.Printf("Failed to destroy session: %v", err)
+}
+```
+
+
+### func \(\*Session\) [GetMessages]()
+
+```go
+func (s *Session) GetMessages(ctx context.Context) ([]SessionEvent, error)
+```
+
+GetMessages retrieves all events and messages from this session's history.
+
+This returns the complete conversation history including user messages, assistant responses, tool executions, and other session events in chronological order.
+
+Returns an error if the session has been destroyed or the connection fails.
+
+Example:
+
+```
+events, err := session.GetMessages(context.Background())
+if err != nil {
+ log.Printf("Failed to get messages: %v", err)
+ return
+}
+for _, event := range events {
+ if event.Type == "assistant.message" {
+ fmt.Println("Assistant:", event.Data.Content)
+ }
+}
+```
+
+
+### func \(\*Session\) [On]()
+
+```go
+func (s *Session) On(handler SessionEventHandler) func()
+```
+
+On subscribes to events from this session.
+
+Events include assistant messages, tool executions, errors, and session state changes. Multiple handlers can be registered and will all receive events. Handlers are called synchronously in the order they were registered.
+
+The returned function can be called to unsubscribe the handler. It is safe to call the unsubscribe function multiple times.
+
+Example:
+
+```
+unsubscribe := session.On(func(event copilot.SessionEvent) {
+ switch event.Type {
+ case "assistant.message":
+ fmt.Println("Assistant:", event.Data.Content)
+ case "session.error":
+ fmt.Println("Error:", event.Data.Message)
+ }
+})
+
+// Later, to stop receiving events:
+unsubscribe()
+```
+
+
+### func \(\*Session\) [Send]()
+
+```go
+func (s *Session) Send(ctx context.Context, options MessageOptions) (string, error)
+```
+
+Send sends a message to this session and waits for the response.
+
+The message is processed asynchronously. Subscribe to events via [Session.On](<#Session.On>) to receive streaming responses and other session events.
+
+Parameters:
+
+- options: The message options including the prompt and optional attachments.
+
+Returns the message ID of the response, which can be used to correlate events, or an error if the session has been destroyed or the connection fails.
+
+Example:
+
+```
+messageID, err := session.Send(context.Background(), copilot.MessageOptions{
+ Prompt: "Explain this code",
+ Attachments: []copilot.Attachment{
+ {Type: "file", Path: "./main.go"},
+ },
+})
+if err != nil {
+ log.Printf("Failed to send message: %v", err)
+}
+```
+
+
+### func \(\*Session\) [SendAndWait]()
+
+```go
+func (s *Session) SendAndWait(ctx context.Context, options MessageOptions) (*SessionEvent, error)
+```
+
+SendAndWait sends a message to this session and waits until the session becomes idle.
+
+This is a convenience method that combines [Session.Send](<#Session.Send>) with waiting for the session.idle event. Use this when you want to block until the assistant has finished processing the message.
+
+Events are still delivered to handlers registered via [Session.On](<#Session.On>) while waiting.
+
+Parameters:
+
+- options: The message options including the prompt and optional attachments.
+- timeout: How long to wait for completion. Defaults to 60 seconds if zero. Controls how long to wait; does not abort in\-flight agent work.
+
+Returns the final assistant message event, or nil if none was received. Returns an error if the timeout is reached or the connection fails.
+
+Example:
+
+```
+response, err := session.SendAndWait(context.Background(), copilot.MessageOptions{
+ Prompt: "What is 2+2?",
+}) // Use default 60s timeout
+if err != nil {
+ log.Printf("Failed: %v", err)
+}
+if response != nil {
+ fmt.Println(*response.Data.Content)
+}
+```
+
+
+### func \(\*Session\) [WorkspacePath]()
+
+```go
+func (s *Session) WorkspacePath() string
+```
+
+WorkspacePath returns the path to the session workspace directory when infinite sessions are enabled. Contains checkpoints/, plan.md, and files/ subdirectories. Returns empty string if infinite sessions are disabled.
+
+
+## type [SessionConfig]()
+
+SessionConfig configures a new session
+
+```go
+type SessionConfig struct {
+ // SessionID is an optional custom session ID
+ SessionID string
+ // Model to use for this session
+ Model string
+ // ReasoningEffort level for models that support it.
+ // Valid values: "low", "medium", "high", "xhigh"
+ // Only applies to models where capabilities.supports.reasoningEffort is true.
+ ReasoningEffort string
+ // ConfigDir overrides the default configuration directory location.
+ // When specified, the session will use this directory for storing config and state.
+ ConfigDir string
+ // Tools exposes caller-implemented tools to the CLI
+ Tools []Tool
+ // SystemMessage configures system message customization
+ SystemMessage *SystemMessageConfig
+ // AvailableTools is a list of tool names to allow. When specified, only these tools will be available.
+ // Takes precedence over ExcludedTools.
+ AvailableTools []string
+ // ExcludedTools is a list of tool names to disable. All other tools remain available.
+ // Ignored if AvailableTools is specified.
+ ExcludedTools []string
+ // OnPermissionRequest is a handler for permission requests from the server
+ OnPermissionRequest PermissionHandler
+ // OnUserInputRequest is a handler for user input requests from the agent (enables ask_user tool)
+ OnUserInputRequest UserInputHandler
+ // Hooks configures hook handlers for session lifecycle events
+ Hooks *SessionHooks
+ // WorkingDirectory is the working directory for the session.
+ // Tool operations will be relative to this directory.
+ WorkingDirectory string
+ // Streaming enables streaming of assistant message and reasoning chunks.
+ // When true, assistant.message_delta and assistant.reasoning_delta events
+ // with deltaContent are sent as the response is generated.
+ Streaming bool
+ // Provider configures a custom model provider (BYOK)
+ Provider *ProviderConfig
+ // MCPServers configures MCP servers for the session
+ MCPServers map[string]MCPServerConfig
+ // CustomAgents configures custom agents for the session
+ CustomAgents []CustomAgentConfig
+ // SkillDirectories is a list of directories to load skills from
+ SkillDirectories []string
+ // DisabledSkills is a list of skill names to disable
+ DisabledSkills []string
+ // InfiniteSessions configures infinite sessions for persistent workspaces and automatic compaction.
+ // When enabled (default), sessions automatically manage context limits and persist state.
+ InfiniteSessions *InfiniteSessionConfig
+}
+```
+
+
+## type [SessionEndHandler]()
+
+SessionEndHandler handles session\-end hook invocations
+
+```go
+type SessionEndHandler func(input SessionEndHookInput, invocation HookInvocation) (*SessionEndHookOutput, error)
+```
+
+
+## type [SessionEndHookInput]()
+
+SessionEndHookInput is the input for a session\-end hook
+
+```go
+type SessionEndHookInput struct {
+ Timestamp int64 `json:"timestamp"`
+ Cwd string `json:"cwd"`
+ Reason string `json:"reason"` // "complete", "error", "abort", "timeout", "user_exit"
+ FinalMessage string `json:"finalMessage,omitempty"`
+ Error string `json:"error,omitempty"`
+}
+```
+
+
+## type [SessionEndHookOutput]()
+
+SessionEndHookOutput is the output for a session\-end hook
+
+```go
+type SessionEndHookOutput struct {
+ SuppressOutput bool `json:"suppressOutput,omitempty"`
+ CleanupActions []string `json:"cleanupActions,omitempty"`
+ SessionSummary string `json:"sessionSummary,omitempty"`
+}
+```
+
+
+## type [SessionEvent]()
+
+
+
+```go
+type SessionEvent struct {
+ Data Data `json:"data"`
+ Ephemeral *bool `json:"ephemeral,omitempty"`
+ ID string `json:"id"`
+ ParentID *string `json:"parentId"`
+ Timestamp time.Time `json:"timestamp"`
+ Type SessionEventType `json:"type"`
+}
+```
+
+
+### func [UnmarshalSessionEvent]()
+
+```go
+func UnmarshalSessionEvent(data []byte) (SessionEvent, error)
+```
+
+
+
+
+### func \(\*SessionEvent\) [Marshal]()
+
+```go
+func (r *SessionEvent) Marshal() ([]byte, error)
+```
+
+
+
+
+## type [SessionEventHandler]()
+
+SessionEventHandler is a callback for session events
+
+```go
+type SessionEventHandler func(event SessionEvent)
+```
+
+
+## type [SessionEventType]()
+
+
+
+```go
+type SessionEventType string
+```
+
+
+
+```go
+const (
+ Abort SessionEventType = "abort"
+ AssistantIntent SessionEventType = "assistant.intent"
+ AssistantMessage SessionEventType = "assistant.message"
+ AssistantMessageDelta SessionEventType = "assistant.message_delta"
+ AssistantReasoning SessionEventType = "assistant.reasoning"
+ AssistantReasoningDelta SessionEventType = "assistant.reasoning_delta"
+ AssistantTurnEnd SessionEventType = "assistant.turn_end"
+ AssistantTurnStart SessionEventType = "assistant.turn_start"
+ AssistantUsage SessionEventType = "assistant.usage"
+ HookEnd SessionEventType = "hook.end"
+ HookStart SessionEventType = "hook.start"
+ PendingMessagesModified SessionEventType = "pending_messages.modified"
+ SessionCompactionComplete SessionEventType = "session.compaction_complete"
+ SessionCompactionStart SessionEventType = "session.compaction_start"
+ SessionError SessionEventType = "session.error"
+ SessionHandoff SessionEventType = "session.handoff"
+ SessionIdle SessionEventType = "session.idle"
+ SessionInfo SessionEventType = "session.info"
+ SessionModelChange SessionEventType = "session.model_change"
+ SessionResume SessionEventType = "session.resume"
+ SessionShutdown SessionEventType = "session.shutdown"
+ SessionSnapshotRewind SessionEventType = "session.snapshot_rewind"
+ SessionStart SessionEventType = "session.start"
+ SessionTruncation SessionEventType = "session.truncation"
+ SessionUsageInfo SessionEventType = "session.usage_info"
+ SkillInvoked SessionEventType = "skill.invoked"
+ SubagentCompleted SessionEventType = "subagent.completed"
+ SubagentFailed SessionEventType = "subagent.failed"
+ SubagentSelected SessionEventType = "subagent.selected"
+ SubagentStarted SessionEventType = "subagent.started"
+ SystemMessage SessionEventType = "system.message"
+ ToolExecutionComplete SessionEventType = "tool.execution_complete"
+ ToolExecutionPartialResult SessionEventType = "tool.execution_partial_result"
+ ToolExecutionProgress SessionEventType = "tool.execution_progress"
+ ToolExecutionStart SessionEventType = "tool.execution_start"
+ ToolUserRequested SessionEventType = "tool.user_requested"
+ UserMessage SessionEventType = "user.message"
+)
+```
+
+
+## type [SessionHooks]()
+
+SessionHooks configures hook handlers for a session
+
+```go
+type SessionHooks struct {
+ OnPreToolUse PreToolUseHandler
+ OnPostToolUse PostToolUseHandler
+ OnUserPromptSubmitted UserPromptSubmittedHandler
+ OnSessionStart SessionStartHandler
+ OnSessionEnd SessionEndHandler
+ OnErrorOccurred ErrorOccurredHandler
+}
+```
+
+
+## type [SessionLifecycleEvent]()
+
+SessionLifecycleEvent represents a session lifecycle notification
+
+```go
+type SessionLifecycleEvent struct {
+ Type SessionLifecycleEventType `json:"type"`
+ SessionID string `json:"sessionId"`
+ Metadata *SessionLifecycleEventMetadata `json:"metadata,omitempty"`
+}
+```
+
+
+## type [SessionLifecycleEventMetadata]()
+
+SessionLifecycleEventMetadata contains optional metadata for lifecycle events
+
+```go
+type SessionLifecycleEventMetadata struct {
+ StartTime string `json:"startTime"`
+ ModifiedTime string `json:"modifiedTime"`
+ Summary *string `json:"summary,omitempty"`
+}
+```
+
+
+## type [SessionLifecycleEventType]()
+
+SessionLifecycleEventType represents the type of session lifecycle event
+
+```go
+type SessionLifecycleEventType string
+```
+
+
+
+```go
+const (
+ SessionLifecycleCreated SessionLifecycleEventType = "session.created"
+ SessionLifecycleDeleted SessionLifecycleEventType = "session.deleted"
+ SessionLifecycleUpdated SessionLifecycleEventType = "session.updated"
+ SessionLifecycleForeground SessionLifecycleEventType = "session.foreground"
+ SessionLifecycleBackground SessionLifecycleEventType = "session.background"
+)
+```
+
+
+## type [SessionLifecycleHandler]()
+
+SessionLifecycleHandler is a callback for session lifecycle events
+
+```go
+type SessionLifecycleHandler func(event SessionLifecycleEvent)
+```
+
+
+## type [SessionMetadata]()
+
+SessionMetadata contains metadata about a session
+
+```go
+type SessionMetadata struct {
+ SessionID string `json:"sessionId"`
+ StartTime string `json:"startTime"`
+ ModifiedTime string `json:"modifiedTime"`
+ Summary *string `json:"summary,omitempty"`
+ IsRemote bool `json:"isRemote"`
+}
+```
+
+
+## type [SessionStartHandler]()
+
+SessionStartHandler handles session\-start hook invocations
+
+```go
+type SessionStartHandler func(input SessionStartHookInput, invocation HookInvocation) (*SessionStartHookOutput, error)
+```
+
+
+## type [SessionStartHookInput]()
+
+SessionStartHookInput is the input for a session\-start hook
+
+```go
+type SessionStartHookInput struct {
+ Timestamp int64 `json:"timestamp"`
+ Cwd string `json:"cwd"`
+ Source string `json:"source"` // "startup", "resume", "new"
+ InitialPrompt string `json:"initialPrompt,omitempty"`
+}
+```
+
+
+## type [SessionStartHookOutput]()
+
+SessionStartHookOutput is the output for a session\-start hook
+
+```go
+type SessionStartHookOutput struct {
+ AdditionalContext string `json:"additionalContext,omitempty"`
+ ModifiedConfig map[string]any `json:"modifiedConfig,omitempty"`
+}
+```
+
+
+## type [ShutdownType]()
+
+
+
+```go
+type ShutdownType string
+```
+
+
+
+```go
+const (
+ Error ShutdownType = "error"
+ Routine ShutdownType = "routine"
+)
+```
+
+
+## type [SourceType]()
+
+
+
+```go
+type SourceType string
+```
+
+
+
+```go
+const (
+ Local SourceType = "local"
+ Remote SourceType = "remote"
+)
+```
+
+
+## type [Start]()
+
+
+
+```go
+type Start struct {
+ Character float64 `json:"character"`
+ Line float64 `json:"line"`
+}
+```
+
+
+## type [SystemMessageAppendConfig]()
+
+SystemMessageAppendConfig is append mode: use CLI foundation with optional appended content.
+
+```go
+type SystemMessageAppendConfig struct {
+ // Mode is optional, defaults to "append"
+ Mode string `json:"mode,omitempty"`
+ // Content provides additional instructions appended after SDK-managed sections
+ Content string `json:"content,omitempty"`
+}
+```
+
+
+## type [SystemMessageConfig]()
+
+SystemMessageConfig represents system message configuration for session creation. Use SystemMessageAppendConfig for default behavior, SystemMessageReplaceConfig for full control. In Go, use one struct or the other based on your needs.
+
+```go
+type SystemMessageConfig struct {
+ Mode string `json:"mode,omitempty"`
+ Content string `json:"content,omitempty"`
+}
+```
+
+
+## type [SystemMessageReplaceConfig]()
+
+SystemMessageReplaceConfig is replace mode: use caller\-provided system message entirely. Removes all SDK guardrails including security restrictions.
+
+```go
+type SystemMessageReplaceConfig struct {
+ // Mode must be "replace"
+ Mode string `json:"mode"`
+ // Content is the complete system message (required)
+ Content string `json:"content"`
+}
+```
+
+
+## type [Tool]()
+
+Tool describes a caller\-implemented tool that can be invoked by Copilot
+
+```go
+type Tool struct {
+ Name string `json:"name"`
+ Description string `json:"description,omitempty"`
+ Parameters map[string]any `json:"parameters,omitempty"`
+ Handler ToolHandler `json:"-"`
+}
+```
+
+
+### func [DefineTool]()
+
+```go
+func DefineTool[T any, U any](name, description string, handler func(T, ToolInvocation) (U, error)) Tool
+```
+
+DefineTool creates a Tool with automatic JSON schema generation from a typed handler function. The handler receives typed arguments \(automatically unmarshaled from JSON\) and the raw ToolInvocation. The handler can return any value \- strings pass through directly, other types are JSON\-serialized.
+
+Example:
+
+```
+type GetWeatherParams struct {
+ City string `json:"city" jsonschema:"city name"`
+ Unit string `json:"unit" jsonschema:"temperature unit (celsius or fahrenheit)"`
+}
+
+tool := copilot.DefineTool("get_weather", "Get weather for a city",
+ func(params GetWeatherParams, inv copilot.ToolInvocation) (any, error) {
+ return fmt.Sprintf("Weather in %s: 22°%s", params.City, params.Unit), nil
+ })
+```
+
+
+## type [ToolBinaryResult]()
+
+ToolBinaryResult represents binary payloads returned by tools.
+
+```go
+type ToolBinaryResult struct {
+ Data string `json:"data"`
+ MimeType string `json:"mimeType"`
+ Type string `json:"type"`
+ Description string `json:"description,omitempty"`
+}
+```
+
+
+## type [ToolHandler]()
+
+ToolHandler executes a tool invocation. The handler should return a ToolResult. Returning an error marks the tool execution as a failure.
+
+```go
+type ToolHandler func(invocation ToolInvocation) (ToolResult, error)
+```
+
+
+## type [ToolInvocation]()
+
+ToolInvocation describes a tool call initiated by Copilot
+
+```go
+type ToolInvocation struct {
+ SessionID string
+ ToolCallID string
+ ToolName string
+ Arguments any
+}
+```
+
+
+## type [ToolRequest]()
+
+
+
+```go
+type ToolRequest struct {
+ Arguments interface{} `json:"arguments"`
+ Name string `json:"name"`
+ ToolCallID string `json:"toolCallId"`
+ Type *ToolRequestType `json:"type,omitempty"`
+}
+```
+
+
+## type [ToolRequestType]()
+
+
+
+```go
+type ToolRequestType string
+```
+
+
+
+```go
+const (
+ Custom ToolRequestType = "custom"
+ Function ToolRequestType = "function"
+)
+```
+
+
+## type [ToolResult]()
+
+ToolResult represents the result of a tool invocation.
+
+```go
+type ToolResult struct {
+ TextResultForLLM string `json:"textResultForLlm"`
+ BinaryResultsForLLM []ToolBinaryResult `json:"binaryResultsForLlm,omitempty"`
+ ResultType string `json:"resultType"`
+ Error string `json:"error,omitempty"`
+ SessionLog string `json:"sessionLog,omitempty"`
+ ToolTelemetry map[string]any `json:"toolTelemetry,omitempty"`
+}
+```
+
+
+## type [Usage]()
+
+
+
+```go
+type Usage struct {
+ CacheReadTokens float64 `json:"cacheReadTokens"`
+ CacheWriteTokens float64 `json:"cacheWriteTokens"`
+ InputTokens float64 `json:"inputTokens"`
+ OutputTokens float64 `json:"outputTokens"`
+}
+```
+
+
+## type [UserInputHandler]()
+
+UserInputHandler handles user input requests from the agent The handler should return a UserInputResponse. Returning an error fails the request.
+
+```go
+type UserInputHandler func(request UserInputRequest, invocation UserInputInvocation) (UserInputResponse, error)
+```
+
+
+## type [UserInputInvocation]()
+
+UserInputInvocation provides context about a user input request
+
+```go
+type UserInputInvocation struct {
+ SessionID string
+}
+```
+
+
+## type [UserInputRequest]()
+
+UserInputRequest represents a request for user input from the agent
+
+```go
+type UserInputRequest struct {
+ Question string
+ Choices []string
+ AllowFreeform *bool
+}
+```
+
+
+## type [UserInputResponse]()
+
+UserInputResponse represents the user's response to an input request
+
+```go
+type UserInputResponse struct {
+ Answer string
+ WasFreeform bool
+}
+```
+
+
+## type [UserPromptSubmittedHandler]()
+
+UserPromptSubmittedHandler handles user\-prompt\-submitted hook invocations
+
+```go
+type UserPromptSubmittedHandler func(input UserPromptSubmittedHookInput, invocation HookInvocation) (*UserPromptSubmittedHookOutput, error)
+```
+
+
+## type [UserPromptSubmittedHookInput]()
+
+UserPromptSubmittedHookInput is the input for a user\-prompt\-submitted hook
+
+```go
+type UserPromptSubmittedHookInput struct {
+ Timestamp int64 `json:"timestamp"`
+ Cwd string `json:"cwd"`
+ Prompt string `json:"prompt"`
+}
+```
+
+
+## type [UserPromptSubmittedHookOutput]()
+
+UserPromptSubmittedHookOutput is the output for a user\-prompt\-submitted hook
+
+```go
+type UserPromptSubmittedHookOutput struct {
+ ModifiedPrompt string `json:"modifiedPrompt,omitempty"`
+ AdditionalContext string `json:"additionalContext,omitempty"`
+ SuppressOutput bool `json:"suppressOutput,omitempty"`
+}
+```
+
+# jsonrpc2
+
+```go
+import "github.com/github/copilot-sdk/go/internal/jsonrpc2"
+```
+
+## Index
+
+- [type Client](<#Client>)
+ - [func NewClient\(stdin io.WriteCloser, stdout io.ReadCloser\) \*Client](<#NewClient>)
+ - [func \(c \*Client\) Notify\(method string, params any\) error](<#Client.Notify>)
+ - [func \(c \*Client\) Request\(method string, params any\) \(json.RawMessage, error\)](<#Client.Request>)
+ - [func \(c \*Client\) SetRequestHandler\(method string, handler RequestHandler\)](<#Client.SetRequestHandler>)
+ - [func \(c \*Client\) Start\(\)](<#Client.Start>)
+ - [func \(c \*Client\) Stop\(\)](<#Client.Stop>)
+- [type Error](<#Error>)
+ - [func \(e \*Error\) Error\(\) string](<#Error.Error>)
+- [type NotificationHandler](<#NotificationHandler>)
+- [type Request](<#Request>)
+ - [func \(r \*Request\) IsCall\(\) bool](<#Request.IsCall>)
+- [type RequestHandler](<#RequestHandler>)
+ - [func NotificationHandlerFor\[In any\]\(handler func\(params In\)\) RequestHandler](<#NotificationHandlerFor>)
+ - [func RequestHandlerFor\[In, Out any\]\(handler func\(params In\) \(Out, \*Error\)\) RequestHandler](<#RequestHandlerFor>)
+- [type Response](<#Response>)
+
+
+
+## type [Client]()
+
+Client is a minimal JSON\-RPC 2.0 client for stdio transport
+
+```go
+type Client struct {
+ // contains filtered or unexported fields
+}
+```
+
+
+### func [NewClient]()
+
+```go
+func NewClient(stdin io.WriteCloser, stdout io.ReadCloser) *Client
+```
+
+NewClient creates a new JSON\-RPC client
+
+
+### func \(\*Client\) [Notify]()
+
+```go
+func (c *Client) Notify(method string, params any) error
+```
+
+Notify sends a JSON\-RPC notification \(no response expected\)
+
+
+### func \(\*Client\) [Request]()
+
+```go
+func (c *Client) Request(method string, params any) (json.RawMessage, error)
+```
+
+Request sends a JSON\-RPC request and waits for the response
+
+
+### func \(\*Client\) [SetRequestHandler]()
+
+```go
+func (c *Client) SetRequestHandler(method string, handler RequestHandler)
+```
+
+SetRequestHandler registers a handler for incoming requests from the server
+
+
+### func \(\*Client\) [Start]()
+
+```go
+func (c *Client) Start()
+```
+
+Start begins listening for messages in a background goroutine
+
+
+### func \(\*Client\) [Stop]()
+
+```go
+func (c *Client) Stop()
+```
+
+Stop stops the client and cleans up
+
+
+## type [Error]()
+
+Error represents a JSON\-RPC error response
+
+```go
+type Error struct {
+ Code int `json:"code"`
+ Message string `json:"message"`
+ Data map[string]any `json:"data,omitempty"`
+}
+```
+
+
+### func \(\*Error\) [Error]()
+
+```go
+func (e *Error) Error() string
+```
+
+
+
+
+## type [NotificationHandler]()
+
+NotificationHandler handles incoming notifications
+
+```go
+type NotificationHandler func(method string, params json.RawMessage)
+```
+
+
+## type [Request]()
+
+Request represents a JSON\-RPC 2.0 request
+
+```go
+type Request struct {
+ JSONRPC string `json:"jsonrpc"`
+ ID json.RawMessage `json:"id"` // nil for notifications
+ Method string `json:"method"`
+ Params json.RawMessage `json:"params"`
+}
+```
+
+
+### func \(\*Request\) [IsCall]()
+
+```go
+func (r *Request) IsCall() bool
+```
+
+
+
+
+## type [RequestHandler]()
+
+RequestHandler handles incoming server requests and returns a result or error
+
+```go
+type RequestHandler func(params json.RawMessage) (json.RawMessage, *Error)
+```
+
+
+### func [NotificationHandlerFor]()
+
+```go
+func NotificationHandlerFor[In any](handler func(params In)) RequestHandler
+```
+
+
+
+
+### func [RequestHandlerFor]()
+
+```go
+func RequestHandlerFor[In, Out any](handler func(params In) (Out, *Error)) RequestHandler
+```
+
+RequestHandlerFor creates a RequestHandler from a typed function
+
+
+## type [Response]()
+
+Response represents a JSON\-RPC 2.0 response
+
+```go
+type Response struct {
+ JSONRPC string `json:"jsonrpc"`
+ ID json.RawMessage `json:"id,omitempty"`
+ Result json.RawMessage `json:"result,omitempty"`
+ Error *Error `json:"error,omitempty"`
+}
+```
+
+# testharness
+
+```go
+import "github.com/github/copilot-sdk/go/internal/e2e/testharness"
+```
+
+## Index
+
+- [func CLIPath\(\) string](<#CLIPath>)
+- [func GetFinalAssistantMessage\(ctx context.Context, session \*copilot.Session\) \(\*copilot.SessionEvent, error\)](<#GetFinalAssistantMessage>)
+- [func GetNextEventOfType\(session \*copilot.Session, eventType copilot.SessionEventType, timeout time.Duration\) \(\*copilot.SessionEvent, error\)](<#GetNextEventOfType>)
+- [type CapiProxy](<#CapiProxy>)
+ - [func NewCapiProxy\(\) \*CapiProxy](<#NewCapiProxy>)
+ - [func \(p \*CapiProxy\) Configure\(filePath, workDir string\) error](<#CapiProxy.Configure>)
+ - [func \(p \*CapiProxy\) GetExchanges\(\) \(\[\]ParsedHttpExchange, error\)](<#CapiProxy.GetExchanges>)
+ - [func \(p \*CapiProxy\) Start\(\) \(string, error\)](<#CapiProxy.Start>)
+ - [func \(p \*CapiProxy\) Stop\(\) error](<#CapiProxy.Stop>)
+ - [func \(p \*CapiProxy\) StopWithOptions\(skipWritingCache bool\) error](<#CapiProxy.StopWithOptions>)
+ - [func \(p \*CapiProxy\) URL\(\) string](<#CapiProxy.URL>)
+- [type ChatCompletionChoice](<#ChatCompletionChoice>)
+- [type ChatCompletionMessage](<#ChatCompletionMessage>)
+- [type ChatCompletionRequest](<#ChatCompletionRequest>)
+- [type ChatCompletionResponse](<#ChatCompletionResponse>)
+- [type ChatCompletionTool](<#ChatCompletionTool>)
+- [type ChatCompletionToolFunction](<#ChatCompletionToolFunction>)
+- [type FunctionCall](<#FunctionCall>)
+- [type Message](<#Message>)
+- [type ParsedHttpExchange](<#ParsedHttpExchange>)
+- [type TestContext](<#TestContext>)
+ - [func NewTestContext\(t \*testing.T\) \*TestContext](<#NewTestContext>)
+ - [func \(c \*TestContext\) Close\(testFailed bool\)](<#TestContext.Close>)
+ - [func \(c \*TestContext\) ConfigureForTest\(t \*testing.T\)](<#TestContext.ConfigureForTest>)
+ - [func \(c \*TestContext\) Env\(\) \[\]string](<#TestContext.Env>)
+ - [func \(c \*TestContext\) GetExchanges\(\) \(\[\]ParsedHttpExchange, error\)](<#TestContext.GetExchanges>)
+ - [func \(c \*TestContext\) NewClient\(\) \*copilot.Client](<#TestContext.NewClient>)
+- [type ToolCall](<#ToolCall>)
+
+
+
+## func [CLIPath]()
+
+```go
+func CLIPath() string
+```
+
+CLIPath returns the path to the Copilot CLI, discovering it once and caching.
+
+
+## func [GetFinalAssistantMessage]()
+
+```go
+func GetFinalAssistantMessage(ctx context.Context, session *copilot.Session) (*copilot.SessionEvent, error)
+```
+
+GetFinalAssistantMessage waits for and returns the final assistant message from a session turn.
+
+
+## func [GetNextEventOfType]()
+
+```go
+func GetNextEventOfType(session *copilot.Session, eventType copilot.SessionEventType, timeout time.Duration) (*copilot.SessionEvent, error)
+```
+
+GetNextEventOfType waits for and returns the next event of the specified type from a session.
+
+
+## type [CapiProxy]()
+
+CapiProxy manages a child process that acts as a replaying proxy to AI endpoints. It spawns the shared test harness server from test/harness/server.ts.
+
+```go
+type CapiProxy struct {
+ // contains filtered or unexported fields
+}
+```
+
+
+### func [NewCapiProxy]()
+
+```go
+func NewCapiProxy() *CapiProxy
+```
+
+NewCapiProxy creates a new proxy instance.
+
+
+### func \(\*CapiProxy\) [Configure]()
+
+```go
+func (p *CapiProxy) Configure(filePath, workDir string) error
+```
+
+Configure sends configuration to the proxy.
+
+
+### func \(\*CapiProxy\) [GetExchanges]()
+
+```go
+func (p *CapiProxy) GetExchanges() ([]ParsedHttpExchange, error)
+```
+
+GetExchanges retrieves the captured HTTP exchanges from the proxy.
+
+
+### func \(\*CapiProxy\) [Start]()
+
+```go
+func (p *CapiProxy) Start() (string, error)
+```
+
+Start launches the proxy server and returns its URL.
+
+
+### func \(\*CapiProxy\) [Stop]()
+
+```go
+func (p *CapiProxy) Stop() error
+```
+
+Stop gracefully shuts down the proxy server.
+
+
+### func \(\*CapiProxy\) [StopWithOptions]()
+
+```go
+func (p *CapiProxy) StopWithOptions(skipWritingCache bool) error
+```
+
+StopWithOptions gracefully shuts down the proxy server. If skipWritingCache is true, the proxy won't write captured exchanges to disk.
+
+
+### func \(\*CapiProxy\) [URL]()
+
+```go
+func (p *CapiProxy) URL() string
+```
+
+URL returns the proxy URL, or empty if not started.
+
+
+## type [ChatCompletionChoice]()
+
+ChatCompletionChoice represents a choice in the response.
+
+```go
+type ChatCompletionChoice struct {
+ Index int `json:"index"`
+ Message ChatCompletionMessage `json:"message"`
+ FinishReason string `json:"finish_reason"`
+}
+```
+
+
+## type [ChatCompletionMessage]()
+
+ChatCompletionMessage represents a message in the chat completion request.
+
+```go
+type ChatCompletionMessage struct {
+ Role string `json:"role"`
+ Content string `json:"content,omitempty"`
+ ToolCallID string `json:"tool_call_id,omitempty"`
+ ToolCalls []ToolCall `json:"tool_calls,omitempty"`
+}
+```
+
+
+## type [ChatCompletionRequest]()
+
+ChatCompletionRequest represents an OpenAI chat completion request.
+
+```go
+type ChatCompletionRequest struct {
+ Model string `json:"model"`
+ Messages []ChatCompletionMessage `json:"messages"`
+ Tools []ChatCompletionTool `json:"tools,omitempty"`
+}
+```
+
+
+## type [ChatCompletionResponse]()
+
+ChatCompletionResponse represents an OpenAI chat completion response.
+
+```go
+type ChatCompletionResponse struct {
+ ID string `json:"id"`
+ Model string `json:"model"`
+ Choices []ChatCompletionChoice `json:"choices"`
+}
+```
+
+
+## type [ChatCompletionTool]()
+
+ChatCompletionTool represents a tool in the chat completion request.
+
+```go
+type ChatCompletionTool struct {
+ Type string `json:"type"`
+ Function ChatCompletionToolFunction `json:"function"`
+}
+```
+
+
+## type [ChatCompletionToolFunction]()
+
+ChatCompletionToolFunction represents a function tool.
+
+```go
+type ChatCompletionToolFunction struct {
+ Name string `json:"name"`
+ Description string `json:"description,omitempty"`
+}
+```
+
+
+## type [FunctionCall]()
+
+FunctionCall represents the function details in a tool call.
+
+```go
+type FunctionCall struct {
+ Name string `json:"name"`
+ Arguments string `json:"arguments"`
+}
+```
+
+
+## type [Message]()
+
+Message is an alias for ChatCompletionMessage for test convenience.
+
+```go
+type Message = ChatCompletionMessage
+```
+
+
+## type [ParsedHttpExchange]()
+
+ParsedHttpExchange represents a captured HTTP exchange.
+
+```go
+type ParsedHttpExchange struct {
+ Request ChatCompletionRequest `json:"request"`
+ Response *ChatCompletionResponse `json:"response,omitempty"`
+}
+```
+
+
+## type [TestContext]()
+
+TestContext holds shared resources for E2E tests.
+
+```go
+type TestContext struct {
+ CLIPath string
+ HomeDir string
+ WorkDir string
+ ProxyURL string
+ // contains filtered or unexported fields
+}
+```
+
+
+### func [NewTestContext]()
+
+```go
+func NewTestContext(t *testing.T) *TestContext
+```
+
+NewTestContext creates a new test context with isolated directories and a replaying proxy.
+
+
+### func \(\*TestContext\) [Close]()
+
+```go
+func (c *TestContext) Close(testFailed bool)
+```
+
+Close cleans up the test context resources.
+
+
+### func \(\*TestContext\) [ConfigureForTest]()
+
+```go
+func (c *TestContext) ConfigureForTest(t *testing.T)
+```
+
+ConfigureForTest configures the proxy for a specific subtest. Call this at the start of each t.Run subtest.
+
+
+### func \(\*TestContext\) [Env]()
+
+```go
+func (c *TestContext) Env() []string
+```
+
+Env returns environment variables configured for isolated testing.
+
+
+### func \(\*TestContext\) [GetExchanges]()
+
+```go
+func (c *TestContext) GetExchanges() ([]ParsedHttpExchange, error)
+```
+
+GetExchanges retrieves the captured HTTP exchanges from the proxy.
+
+
+### func \(\*TestContext\) [NewClient]()
+
+```go
+func (c *TestContext) NewClient() *copilot.Client
+```
+
+NewClient creates a CopilotClient configured for this test context.
+
+
+## type [ToolCall]()
+
+ToolCall represents a tool call in an assistant message.
+
+```go
+type ToolCall struct {
+ ID string `json:"id"`
+ Type string `json:"type"`
+ Function FunctionCall `json:"function"`
+}
+```
+
+Generated by [gomarkdoc]()
diff --git a/docs/guides/setup/bundled-cli.md b/docs/guides/setup/bundled-cli.md
index 9fc88f098..075aa360f 100644
--- a/docs/guides/setup/bundled-cli.md
+++ b/docs/guides/setup/bundled-cli.md
@@ -51,11 +51,11 @@ flowchart LR
### 1. Include the CLI in Your Project
-The CLI is distributed as part of the `@github/copilot` npm package. You can also obtain platform-specific binaries for your distribution pipeline.
+The CLI is distributed as the `copilot-core` binary. You can obtain platform-specific binaries for your distribution pipeline.
```bash
-# The CLI is available from the @github/copilot package
-npm install @github/copilot
+# The CLI is available as the copilot-core binary
+npm install copilot-core
```
### 2. Point the SDK to Your Bundled CLI
diff --git a/docs/guides/setup/local-cli.md b/docs/guides/setup/local-cli.md
index 8d9573ebe..5175648a7 100644
--- a/docs/guides/setup/local-cli.md
+++ b/docs/guides/setup/local-cli.md
@@ -133,7 +133,7 @@ While defaults work great, you can customize the local setup:
```typescript
const client = new CopilotClient({
- // Override CLI location (default: bundled with @github/copilot)
+ // Override CLI location (default: bundled with copilot-core)
cliPath: "/usr/local/bin/copilot",
// Set log level for debugging
diff --git a/dotnet/src/Client.cs b/dotnet/src/Client.cs
index 74f1c66f2..a36627cd5 100644
--- a/dotnet/src/Client.cs
+++ b/dotnet/src/Client.cs
@@ -36,7 +36,7 @@ namespace GitHub.Copilot.SDK;
/// await using var client = new CopilotClient();
///
/// // Create a session
-/// await using var session = await client.CreateSessionAsync(new SessionConfig { Model = "gpt-4" });
+/// await using var session = await client.CreateSessionAsync(new SessionConfig { Model = "gpt-4.1" });
///
/// // Handle events
/// using var subscription = session.On(evt =>
@@ -335,7 +335,7 @@ private async Task CleanupConnectionAsync(List? errors)
/// // Session with model and tools
/// var session = await client.CreateSessionAsync(new SessionConfig
/// {
- /// Model = "gpt-4",
+ /// Model = "gpt-4.1",
/// Tools = [AIFunctionFactory.Create(MyToolMethod)]
/// });
///
diff --git a/dotnet/src/Generated/SessionEvents.cs b/dotnet/src/Generated/SessionEvents.cs
index 022588396..5660e8623 100644
--- a/dotnet/src/Generated/SessionEvents.cs
+++ b/dotnet/src/Generated/SessionEvents.cs
@@ -4,7 +4,7 @@
// AUTO-GENERATED FILE - DO NOT EDIT
//
-// Generated from: @github/copilot/session-events.schema.json
+// Generated from: copilot-core/session-events.schema.json
// Generated by: scripts/generate-session-types.ts
// Generated at: 2026-02-06T20:38:23.832Z
//
diff --git a/dotnet/src/GitHub.Copilot.SDK.csproj b/dotnet/src/GitHub.Copilot.SDK.csproj
index 019788cfa..bc78a19b0 100644
--- a/dotnet/src/GitHub.Copilot.SDK.csproj
+++ b/dotnet/src/GitHub.Copilot.SDK.csproj
@@ -6,6 +6,7 @@
enable
true
0.1.0
+ 0.0.410
SDK for programmatic control of GitHub Copilot CLI
GitHub
GitHub
@@ -30,10 +31,7 @@
-
-
-
-
+
<_VersionPropsContent>
diff --git a/dotnet/src/Session.cs b/dotnet/src/Session.cs
index aa2d5b045..0973081ff 100644
--- a/dotnet/src/Session.cs
+++ b/dotnet/src/Session.cs
@@ -26,7 +26,7 @@ namespace GitHub.Copilot.SDK;
///
///
///
-/// await using var session = await client.CreateSessionAsync(new SessionConfig { Model = "gpt-4" });
+/// await using var session = await client.CreateSessionAsync(new SessionConfig { Model = "gpt-4.1" });
///
/// // Subscribe to events
/// using var subscription = session.On(evt =>
diff --git a/dotnet/test/Harness/E2ETestContext.cs b/dotnet/test/Harness/E2ETestContext.cs
index 2518ca69e..8a5dac8f0 100644
--- a/dotnet/test/Harness/E2ETestContext.cs
+++ b/dotnet/test/Harness/E2ETestContext.cs
@@ -58,11 +58,20 @@ private static string GetCliPath(string repoRoot)
var envPath = Environment.GetEnvironmentVariable("COPILOT_CLI_PATH");
if (!string.IsNullOrEmpty(envPath)) return envPath;
- var path = Path.Combine(repoRoot, "nodejs/node_modules/@github/copilot/index.js");
- if (!File.Exists(path))
- throw new InvalidOperationException($"CLI not found at {path}. Run 'npm install' in the nodejs directory first.");
+ // Resolve copilot-core from PATH
+ var pathVar = Environment.GetEnvironmentVariable("PATH") ?? "";
+ var separator = OperatingSystem.IsWindows() ? ';' : ':';
+ var binaryName = OperatingSystem.IsWindows() ? "copilot-core.exe" : "copilot-core";
- return path;
+ foreach (var dir in pathVar.Split(separator, StringSplitOptions.RemoveEmptyEntries))
+ {
+ var candidate = Path.Combine(dir, binaryName);
+ if (File.Exists(candidate))
+ return candidate;
+ }
+
+ throw new InvalidOperationException(
+ $"copilot-core not found on PATH. Install it or set COPILOT_CLI_PATH.");
}
public async Task ConfigureForTestAsync(string testFile, [CallerMemberName] string? testName = null)
diff --git a/go/README.md b/go/README.md
index 582071019..54e1230f7 100644
--- a/go/README.md
+++ b/go/README.md
@@ -395,7 +395,7 @@ session, err := client.CreateSession(context.Background(), &copilot.SessionConfi
```go
session, err := client.CreateSession(context.Background(), &copilot.SessionConfig{
- Model: "gpt-4",
+ Model: "gpt-4.1",
Provider: &copilot.ProviderConfig{
Type: "openai",
BaseURL: "https://my-api.example.com/v1",
@@ -408,7 +408,7 @@ session, err := client.CreateSession(context.Background(), &copilot.SessionConfi
```go
session, err := client.CreateSession(context.Background(), &copilot.SessionConfig{
- Model: "gpt-4",
+ Model: "gpt-4.1",
Provider: &copilot.ProviderConfig{
Type: "azure", // Must be "azure" for Azure endpoints, NOT "openai"
BaseURL: "https://my-resource.openai.azure.com", // Just the host, no path
diff --git a/go/client.go b/go/client.go
index 319c6588c..d5859ae05 100644
--- a/go/client.go
+++ b/go/client.go
@@ -12,7 +12,7 @@
// defer client.Stop()
//
// session, err := client.CreateSession(&copilot.SessionConfig{
-// Model: "gpt-4",
+// Model: "gpt-4.1",
// })
// if err != nil {
// log.Fatal(err)
@@ -97,12 +97,12 @@ type Client struct {
//
// // Custom options
// client := copilot.NewClient(&copilot.ClientOptions{
-// CLIPath: "/usr/local/bin/copilot",
+// CLIPath: "/usr/local/bin/copilot-core",
// LogLevel: "debug",
// })
func NewClient(options *ClientOptions) *Client {
opts := ClientOptions{
- CLIPath: "copilot",
+ CLIPath: "copilot-core",
Cwd: "",
Port: 0,
LogLevel: "info",
@@ -423,7 +423,7 @@ func (c *Client) ensureConnected() error {
//
// // Session with model and tools
// session, err := client.CreateSession(context.Background(), &copilot.SessionConfig{
-// Model: "gpt-4",
+// Model: "gpt-4.1",
// Tools: []copilot.Tool{
// {
// Name: "get_weather",
diff --git a/go/client_test.go b/go/client_test.go
index 176dad8c5..8c086226c 100644
--- a/go/client_test.go
+++ b/go/client_test.go
@@ -2,7 +2,7 @@ package copilot
import (
"os"
- "path/filepath"
+ "os/exec"
"reflect"
"regexp"
"testing"
@@ -378,9 +378,13 @@ func TestClient_EnvOptions(t *testing.T) {
}
func findCLIPathForTest() string {
- abs, _ := filepath.Abs("../nodejs/node_modules/@github/copilot/index.js")
- if fileExistsForTest(abs) {
- return abs
+ // Check COPILOT_CLI_PATH env var first
+ if envPath := os.Getenv("COPILOT_CLI_PATH"); envPath != "" && fileExistsForTest(envPath) {
+ return envPath
+ }
+ // Fall back to copilot-core on PATH
+ if path, err := exec.LookPath("copilot-core"); err == nil {
+ return path
}
return ""
}
diff --git a/go/generated_session_events.go b/go/generated_session_events.go
index ec4de9bea..6a7c26229 100644
--- a/go/generated_session_events.go
+++ b/go/generated_session_events.go
@@ -1,6 +1,6 @@
// AUTO-GENERATED FILE - DO NOT EDIT
//
-// Generated from: @github/copilot/session-events.schema.json
+// Generated from: copilot-core/session-events.schema.json
// Generated by: scripts/generate-session-types.ts
// Generated at: 2026-02-06T20:38:23.463Z
//
diff --git a/go/internal/e2e/testharness/context.go b/go/internal/e2e/testharness/context.go
index 570594edc..2aeeea9a4 100644
--- a/go/internal/e2e/testharness/context.go
+++ b/go/internal/e2e/testharness/context.go
@@ -26,12 +26,8 @@ func CLIPath() string {
return
}
- // Look for CLI in sibling nodejs directory's node_modules
- abs, err := filepath.Abs("../../../nodejs/node_modules/@github/copilot/index.js")
- if err == nil && fileExists(abs) {
- cliPath = abs
- return
- }
+ // Fall back to copilot-core on PATH
+ cliPath = "copilot-core"
})
return cliPath
}
@@ -52,7 +48,7 @@ func NewTestContext(t *testing.T) *TestContext {
cliPath := CLIPath()
if cliPath == "" || !fileExists(cliPath) {
- t.Fatalf("CLI not found at %s. Run 'npm install' in the nodejs directory first.", cliPath)
+ t.Fatalf("CLI not found at %s. Ensure 'copilot-core' is installed and on PATH, or set COPILOT_CLI_PATH.", cliPath)
}
homeDir, err := os.MkdirTemp("", "copilot-test-config-")
diff --git a/go/session.go b/go/session.go
index 37cfe52f8..4914c6f27 100644
--- a/go/session.go
+++ b/go/session.go
@@ -28,7 +28,7 @@ type sessionHandler struct {
// Example usage:
//
// session, err := client.CreateSession(copilot.SessionConfig{
-// Model: "gpt-4",
+// Model: "gpt-4.1",
// })
// if err != nil {
// log.Fatal(err)
diff --git a/go/test.sh b/go/test.sh
index c3f33fb0b..e7189120f 100644
--- a/go/test.sh
+++ b/go/test.sh
@@ -15,15 +15,13 @@ fi
# Determine COPILOT_CLI_PATH
if [ -z "$COPILOT_CLI_PATH" ]; then
- # Try to find it relative to the SDK
- SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
- POTENTIAL_PATH="$SCRIPT_DIR/../nodejs/node_modules/@github/copilot/index.js"
- if [ -f "$POTENTIAL_PATH" ]; then
- export COPILOT_CLI_PATH="$POTENTIAL_PATH"
+ # Try to find copilot-core on PATH
+ if command -v copilot-core &> /dev/null; then
+ export COPILOT_CLI_PATH="$(command -v copilot-core)"
echo "📍 Auto-detected CLI path: $COPILOT_CLI_PATH"
else
- echo "❌ COPILOT_CLI_PATH environment variable not set"
- echo " Run: export COPILOT_CLI_PATH=/path/to/dist-cli/index.js"
+ echo "❌ COPILOT_CLI_PATH environment variable not set and copilot-core not found on PATH"
+ echo " Run: export COPILOT_CLI_PATH=/path/to/copilot-core"
exit 1
fi
fi
diff --git a/go/types.go b/go/types.go
index a3b38ee31..1002cdc55 100644
--- a/go/types.go
+++ b/go/types.go
@@ -14,7 +14,7 @@ const (
// ClientOptions configures the CopilotClient
type ClientOptions struct {
- // CLIPath is the path to the Copilot CLI executable (default: "copilot")
+ // CLIPath is the path to the Copilot CLI executable (default: "copilot-core")
CLIPath string
// Cwd is the working directory for the CLI process (default: "" = inherit from current process)
Cwd string
diff --git a/justfile b/justfile
index 8cf72c732..4a8922e1c 100644
--- a/justfile
+++ b/justfile
@@ -85,6 +85,17 @@ playground:
@echo "=== Starting SDK Playground ==="
@cd demos/playground && npm install && npm start
+# Generate Go reference docs (Markdown + HTML)
+docs-go: docs-go-md docs-go-serve
+
+# Generate Go Markdown reference docs via gomarkdoc
+docs-go-md:
+ @./scripts/generate-go-docs.sh markdown
+
+# Start local Go HTML doc server (pkgsite) at http://localhost:6060
+docs-go-serve:
+ @./scripts/generate-go-docs.sh serve
+
# Validate documentation code examples
validate-docs: validate-docs-extract validate-docs-check
diff --git a/nodejs/README.md b/nodejs/README.md
index 3a78f4199..b20428b4e 100644
--- a/nodejs/README.md
+++ b/nodejs/README.md
@@ -506,7 +506,7 @@ await session.sendAndWait({ prompt: "Hello!" });
```typescript
const session = await client.createSession({
- model: "gpt-4",
+ model: "gpt-4.1",
provider: {
type: "openai",
baseUrl: "https://my-api.example.com/v1",
@@ -519,7 +519,7 @@ const session = await client.createSession({
```typescript
const session = await client.createSession({
- model: "gpt-4",
+ model: "gpt-4.1",
provider: {
type: "azure", // Must be "azure" for Azure endpoints, NOT "openai"
baseUrl: "https://my-resource.openai.azure.com", // Just the host, no path
diff --git a/nodejs/examples/basic-example.ts b/nodejs/examples/basic-example.ts
index b0b993138..2c38dc240 100644
--- a/nodejs/examples/basic-example.ts
+++ b/nodejs/examples/basic-example.ts
@@ -20,7 +20,7 @@ const lookupFactTool = defineTool("lookup_fact", {
handler: ({ topic }) => facts[topic.toLowerCase()] ?? `No fact stored for ${topic}.`,
});
-// Create client - will auto-start CLI server (searches PATH for "copilot")
+// Create client - will auto-start CLI server (searches PATH for "copilot-core")
const client = new CopilotClient({ logLevel: "info" });
const session = await client.createSession({ tools: [lookupFactTool] });
console.log(`✅ Session created: ${session.sessionId}\n`);
diff --git a/nodejs/package-lock.json b/nodejs/package-lock.json
index 266d994e3..1c92e7b76 100644
--- a/nodejs/package-lock.json
+++ b/nodejs/package-lock.json
@@ -9,7 +9,6 @@
"version": "0.1.8",
"license": "MIT",
"dependencies": {
- "@github/copilot": "^0.0.405",
"vscode-jsonrpc": "^8.2.1",
"zod": "^4.3.6"
},
@@ -661,119 +660,6 @@
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
}
},
- "node_modules/@github/copilot": {
- "version": "0.0.405",
- "resolved": "https://registry.npmjs.org/@github/copilot/-/copilot-0.0.405.tgz",
- "integrity": "sha512-zp0kGSkoKrO4MTWefAxU5w2VEc02QnhPY3FmVxOeduh6ayDIz2V368mXxs46ThremdMnMyZPL1k989BW4NpOVw==",
- "license": "SEE LICENSE IN LICENSE.md",
- "bin": {
- "copilot": "npm-loader.js"
- },
- "optionalDependencies": {
- "@github/copilot-darwin-arm64": "0.0.405",
- "@github/copilot-darwin-x64": "0.0.405",
- "@github/copilot-linux-arm64": "0.0.405",
- "@github/copilot-linux-x64": "0.0.405",
- "@github/copilot-win32-arm64": "0.0.405",
- "@github/copilot-win32-x64": "0.0.405"
- }
- },
- "node_modules/@github/copilot-darwin-arm64": {
- "version": "0.0.405",
- "resolved": "https://registry.npmjs.org/@github/copilot-darwin-arm64/-/copilot-darwin-arm64-0.0.405.tgz",
- "integrity": "sha512-RVFpU1cEMqjR0rLpwLwbIfT7XzqqVoQX99G6nsj+WrHu3TIeCgfffyd2YShd4QwZYsMRoTfKB+rirQ+0G5Uiig==",
- "cpu": [
- "arm64"
- ],
- "license": "SEE LICENSE IN LICENSE.md",
- "optional": true,
- "os": [
- "darwin"
- ],
- "bin": {
- "copilot-darwin-arm64": "copilot"
- }
- },
- "node_modules/@github/copilot-darwin-x64": {
- "version": "0.0.405",
- "resolved": "https://registry.npmjs.org/@github/copilot-darwin-x64/-/copilot-darwin-x64-0.0.405.tgz",
- "integrity": "sha512-Xj2FyPzpZlfqPTuMrXtPNEijSmm2ivHvyMWgy5Ijv7Slabxe+2s3WXDaokE3SQHodK6M0Yle2yrx9kxiwWA+qw==",
- "cpu": [
- "x64"
- ],
- "license": "SEE LICENSE IN LICENSE.md",
- "optional": true,
- "os": [
- "darwin"
- ],
- "bin": {
- "copilot-darwin-x64": "copilot"
- }
- },
- "node_modules/@github/copilot-linux-arm64": {
- "version": "0.0.405",
- "resolved": "https://registry.npmjs.org/@github/copilot-linux-arm64/-/copilot-linux-arm64-0.0.405.tgz",
- "integrity": "sha512-16Wiq8EYB6ghwqZdYytnNkcCN4sT3jyt9XkjfMxI5DDdjLuPc8wbj5VV5pw8S6lZvBL4eAwXGE3+fPqXKxH6GQ==",
- "cpu": [
- "arm64"
- ],
- "license": "SEE LICENSE IN LICENSE.md",
- "optional": true,
- "os": [
- "linux"
- ],
- "bin": {
- "copilot-linux-arm64": "copilot"
- }
- },
- "node_modules/@github/copilot-linux-x64": {
- "version": "0.0.405",
- "resolved": "https://registry.npmjs.org/@github/copilot-linux-x64/-/copilot-linux-x64-0.0.405.tgz",
- "integrity": "sha512-HXpg7p235//pAuCvcL9m2EeIrL/K6OUEkFeHF3BFHzqUJR4a69gKLsxtUg0ZctypHqo2SehGCRAyVippTVlTyg==",
- "cpu": [
- "x64"
- ],
- "license": "SEE LICENSE IN LICENSE.md",
- "optional": true,
- "os": [
- "linux"
- ],
- "bin": {
- "copilot-linux-x64": "copilot"
- }
- },
- "node_modules/@github/copilot-win32-arm64": {
- "version": "0.0.405",
- "resolved": "https://registry.npmjs.org/@github/copilot-win32-arm64/-/copilot-win32-arm64-0.0.405.tgz",
- "integrity": "sha512-4JCUMiRjP7zB3j1XpEtJq7b7cxTzuwDJ9o76jayAL8HL9NhqKZ6Ys6uxhDA6f/l0N2GVD1TEICxsnPgadz6srg==",
- "cpu": [
- "arm64"
- ],
- "license": "SEE LICENSE IN LICENSE.md",
- "optional": true,
- "os": [
- "win32"
- ],
- "bin": {
- "copilot-win32-arm64": "copilot.exe"
- }
- },
- "node_modules/@github/copilot-win32-x64": {
- "version": "0.0.405",
- "resolved": "https://registry.npmjs.org/@github/copilot-win32-x64/-/copilot-win32-x64-0.0.405.tgz",
- "integrity": "sha512-uHoJ9N8kZbTLbzgqBE1szHwLElv2f+P2OWlqmRSawQhwPl0s7u55dka7mZYvj2ZoNvIyb0OyShCO56OpmCcy/w==",
- "cpu": [
- "x64"
- ],
- "license": "SEE LICENSE IN LICENSE.md",
- "optional": true,
- "os": [
- "win32"
- ],
- "bin": {
- "copilot-win32-x64": "copilot.exe"
- }
- },
"node_modules/@glideapps/ts-necessities": {
"version": "2.2.3",
"resolved": "https://registry.npmjs.org/@glideapps/ts-necessities/-/ts-necessities-2.2.3.tgz",
diff --git a/nodejs/package.json b/nodejs/package.json
index b6e23f401..fbe6ef96b 100644
--- a/nodejs/package.json
+++ b/nodejs/package.json
@@ -40,7 +40,7 @@
"author": "GitHub",
"license": "MIT",
"dependencies": {
- "@github/copilot": "^0.0.405",
+
"vscode-jsonrpc": "^8.2.1",
"zod": "^4.3.6"
},
diff --git a/nodejs/scripts/generate-csharp-session-types.ts b/nodejs/scripts/generate-csharp-session-types.ts
index cf2951173..d27a9a332 100644
--- a/nodejs/scripts/generate-csharp-session-types.ts
+++ b/nodejs/scripts/generate-csharp-session-types.ts
@@ -660,7 +660,7 @@ export function generateCSharpSessionTypes(schema: JSONSchema7, generatedAt: str
// AUTO-GENERATED FILE - DO NOT EDIT
//
-// Generated from: @github/copilot/session-events.schema.json
+// Generated from: copilot-core/session-events.schema.json
// Generated by: scripts/generate-session-types.ts
// Generated at: ${generatedAt}
//
diff --git a/nodejs/scripts/generate-session-types.ts b/nodejs/scripts/generate-session-types.ts
index 8a0063a3e..268168547 100644
--- a/nodejs/scripts/generate-session-types.ts
+++ b/nodejs/scripts/generate-session-types.ts
@@ -5,15 +5,14 @@
/**
* Generate session event types for all SDKs from the JSON schema
*
- * This script reads the session-events.schema.json from the @github/copilot package
- * (which should be npm linked from copilot-agent-runtime/dist-cli) and generates
- * TypeScript, Python, Go, and C# type definitions for all SDKs.
+ * This script reads the session-events.schema.json from the copilot-core package
+ * and generates TypeScript, Python, Go, and C# type definitions for all SDKs.
*
* Workflow:
* 1. The schema is defined in copilot-agent-runtime using Zod schemas
* 2. copilot-agent-runtime/script/generate-session-types.ts generates the JSON schema
* 3. copilot-agent-runtime/esbuild.ts copies the schema to dist-cli/
- * 4. This script reads the schema from the linked @github/copilot package
+ * 4. This script reads the schema from copilot-core
* 5. Generates types for nodejs/src/generated/, python/copilot/generated/, go/generated/, and dotnet/src/Generated/
*
* Usage:
@@ -36,10 +35,10 @@ const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
async function getSchemaPath(): Promise {
- // Read from the @github/copilot package
+ // Read from the copilot-core schema location
const schemaPath = path.join(
__dirname,
- "../node_modules/@github/copilot/schemas/session-events.schema.json"
+ "../../experimental-copilot-server/schemas/session-events.schema.json"
);
try {
@@ -49,7 +48,7 @@ async function getSchemaPath(): Promise {
} catch (_error) {
throw new Error(
`Schema file not found at ${schemaPath}. ` +
- `Make sure @github/copilot package is installed or linked.`
+ `Make sure copilot-core schema is available.`
);
}
}
@@ -64,12 +63,12 @@ async function generateTypeScriptTypes(schemaPath: string) {
bannerComment: `/**
* AUTO-GENERATED FILE - DO NOT EDIT
*
- * Generated from: @github/copilot/session-events.schema.json
+ * Generated from: copilot-core/session-events.schema.json
* Generated by: scripts/generate-session-types.ts
* Generated at: ${new Date().toISOString()}
*
* To update these types:
- * 1. Update the schema in copilot-agent-runtime
+ * 1. Update the schema in copilot-core
* 2. Run: npm run generate:session-types
*/`,
style: {
@@ -230,12 +229,12 @@ $2`
const banner = `"""
AUTO-GENERATED FILE - DO NOT EDIT
-Generated from: @github/copilot/session-events.schema.json
+Generated from: copilot-core/session-events.schema.json
Generated by: scripts/generate-session-types.ts
Generated at: ${new Date().toISOString()}
To update these types:
-1. Update the schema in copilot-agent-runtime
+1. Update the schema in copilot-core
2. Run: npm run generate:session-types
"""
@@ -295,12 +294,12 @@ async function generateGoTypes(schemaPath: string) {
const generatedCode = result.lines.join("\n");
const banner = `// AUTO-GENERATED FILE - DO NOT EDIT
//
-// Generated from: @github/copilot/session-events.schema.json
+// Generated from: copilot-core/session-events.schema.json
// Generated by: scripts/generate-session-types.ts
// Generated at: ${new Date().toISOString()}
//
// To update these types:
-// 1. Update the schema in copilot-agent-runtime
+// 1. Update the schema in copilot-core
// 2. Run: npm run generate:session-types
`;
diff --git a/nodejs/src/client.ts b/nodejs/src/client.ts
index af6260c9a..1eff47e6c 100644
--- a/nodejs/src/client.ts
+++ b/nodejs/src/client.ts
@@ -11,19 +11,13 @@
* @module client
*/
-import { spawn, type ChildProcess } from "node:child_process";
-import { existsSync } from "node:fs";
-import { Socket } from "node:net";
-import { dirname, join } from "node:path";
-import { fileURLToPath } from "node:url";
-import {
- createMessageConnection,
- MessageConnection,
- StreamMessageReader,
- StreamMessageWriter,
-} from "vscode-jsonrpc/node.js";
+import type { MessageConnection } from "vscode-jsonrpc/node.js";
import { getSdkProtocolVersion } from "./sdkProtocolVersion.js";
import { CopilotSession } from "./session.js";
+import type { Transport } from "./transport.js";
+import { StdioTransport, getBundledCliPath } from "./stdio-transport.js";
+import { TcpTransport, parseCliUrl } from "./tcp-transport.js";
+import { WasmTransport } from "./wasm-transport.js";
import type {
ConnectionState,
CopilotClientOptions,
@@ -88,7 +82,7 @@ function toJsonSchema(parameters: Tool["parameters"]): Record |
* const client = new CopilotClient({ cliUrl: "localhost:3000" });
*
* // Create a session
- * const session = await client.createSession({ model: "gpt-4" });
+ * const session = await client.createSession({ model: "gpt-4.1" });
*
* // Send messages and handle responses
* session.on((event) => {
@@ -104,36 +98,22 @@ function toJsonSchema(parameters: Tool["parameters"]): Record |
* ```
*/
-/**
- * Gets the path to the bundled CLI from the @github/copilot package.
- * Uses index.js directly rather than npm-loader.js (which spawns the native binary).
- */
-function getBundledCliPath(): string {
- // Find the actual location of the @github/copilot package by resolving its sdk export
- const sdkUrl = import.meta.resolve("@github/copilot/sdk");
- const sdkPath = fileURLToPath(sdkUrl);
- // sdkPath is like .../node_modules/@github/copilot/sdk/index.js
- // Go up two levels to get the package root, then append index.js
- return join(dirname(dirname(sdkPath)), "index.js");
-}
-
export class CopilotClient {
- private cliProcess: ChildProcess | null = null;
+ private transport: Transport | null = null;
private connection: MessageConnection | null = null;
- private socket: Socket | null = null;
- private actualPort: number | null = null;
- private actualHost: string = "localhost";
private state: ConnectionState = "disconnected";
private sessions: Map = new Map();
private options: Required<
- Omit
+ Omit
> & {
cliUrl?: string;
githubToken?: string;
useLoggedInUser?: boolean;
+ runtime?: "native" | "wasm";
+ wasmModule?: CopilotClientOptions["wasmModule"];
+ apiUrl?: string;
+ httpPost?: CopilotClientOptions["httpPost"];
};
- private isExternalServer: boolean = false;
- private forceStopping: boolean = false;
private modelsCache: ModelInfo[] | null = null;
private modelsCacheLock: Promise = Promise.resolve();
private sessionLifecycleHandlers: Set = new Set();
@@ -164,6 +144,20 @@ export class CopilotClient {
* ```
*/
constructor(options: CopilotClientOptions = {}) {
+ // Validate runtime option
+ if (
+ options.runtime === "wasm" &&
+ (options.cliPath || options.cliUrl || options.useStdio === true)
+ ) {
+ throw new Error(
+ "runtime 'wasm' is mutually exclusive with cliPath, cliUrl, and useStdio"
+ );
+ }
+
+ if (options.runtime === "wasm" && !options.wasmModule) {
+ throw new Error("wasmModule is required when runtime is 'wasm'");
+ }
+
// Validate mutually exclusive options
if (options.cliUrl && (options.useStdio === true || options.cliPath)) {
throw new Error("cliUrl is mutually exclusive with useStdio and cliPath");
@@ -176,60 +170,73 @@ export class CopilotClient {
);
}
- // Parse cliUrl if provided
- if (options.cliUrl) {
- const { host, port } = this.parseCliUrl(options.cliUrl);
- this.actualHost = host;
- this.actualPort = port;
- this.isExternalServer = true;
- }
-
this.options = {
- cliPath: options.cliPath || getBundledCliPath(),
+ cliPath: options.runtime === "wasm" ? undefined : (options.cliPath || getBundledCliPath()),
cliArgs: options.cliArgs ?? [],
cwd: options.cwd ?? process.cwd(),
port: options.port || 0,
- useStdio: options.cliUrl ? false : (options.useStdio ?? true), // Default to stdio unless cliUrl is provided
+ useStdio: options.cliUrl ? false : (options.useStdio ?? true),
cliUrl: options.cliUrl,
logLevel: options.logLevel || "debug",
autoStart: options.autoStart ?? true,
autoRestart: options.autoRestart ?? true,
env: options.env ?? process.env,
githubToken: options.githubToken,
- // Default useLoggedInUser to false when githubToken is provided, otherwise true
- useLoggedInUser: options.useLoggedInUser ?? (options.githubToken ? false : true),
+ // Default useLoggedInUser to false (secure by default)
+ useLoggedInUser: options.useLoggedInUser ?? false,
+ runtime: options.runtime,
+ wasmModule: options.wasmModule,
+ apiUrl: options.apiUrl,
+ httpPost: options.httpPost,
};
- }
-
- /**
- * Parse CLI URL into host and port
- * Supports formats: "host:port", "http://host:port", "https://host:port", or just "port"
- */
- private parseCliUrl(url: string): { host: string; port: number } {
- // Remove protocol if present
- let cleanUrl = url.replace(/^https?:\/\//, "");
- // Check if it's just a port number
- if (/^\d+$/.test(cleanUrl)) {
- return { host: "localhost", port: parseInt(cleanUrl, 10) };
- }
-
- // Parse host:port format
- const parts = cleanUrl.split(":");
- if (parts.length !== 2) {
- throw new Error(
- `Invalid cliUrl format: ${url}. Expected "host:port", "http://host:port", or "port"`
- );
- }
-
- const host = parts[0] || "localhost";
- const port = parseInt(parts[1], 10);
-
- if (isNaN(port) || port <= 0 || port > 65535) {
- throw new Error(`Invalid port in cliUrl: ${url}`);
+ // Create the appropriate transport
+ if (options.runtime === "wasm") {
+ this.transport = new WasmTransport({
+ wasmModule: options.wasmModule,
+ authToken: options.githubToken,
+ apiUrl: options.apiUrl,
+ httpPost: options.httpPost,
+ });
+ } else if (options.cliUrl) {
+ const { host, port } = parseCliUrl(options.cliUrl);
+ this.transport = new TcpTransport({
+ host,
+ port,
+ isExternalServer: true,
+ });
+ } else if (this.options.useStdio) {
+ this.transport = new StdioTransport({
+ cliPath: this.options.cliPath,
+ cliArgs: this.options.cliArgs,
+ cwd: this.options.cwd,
+ logLevel: this.options.logLevel,
+ env: this.options.env,
+ githubToken: this.options.githubToken,
+ useLoggedInUser: this.options.useLoggedInUser,
+ onExit: () => {
+ if (this.options.autoRestart && this.state === "connected") {
+ void this.reconnect();
+ }
+ },
+ });
+ } else {
+ this.transport = new TcpTransport({
+ cliPath: this.options.cliPath,
+ cliArgs: this.options.cliArgs,
+ cwd: this.options.cwd,
+ logLevel: this.options.logLevel,
+ env: this.options.env,
+ githubToken: this.options.githubToken,
+ useLoggedInUser: this.options.useLoggedInUser,
+ port: this.options.port,
+ onExit: () => {
+ if (this.options.autoRestart && this.state === "connected") {
+ void this.reconnect();
+ }
+ },
+ });
}
-
- return { host, port };
}
/**
@@ -258,13 +265,11 @@ export class CopilotClient {
this.state = "connecting";
try {
- // Only start CLI server process if not connecting to external server
- if (!this.isExternalServer) {
- await this.startCLIServer();
- }
+ await this.transport!.start();
+ this.connection = this.transport!.connection;
- // Connect to the server
- await this.connectToServer();
+ // Attach session routing handlers
+ this.attachConnectionHandlers();
// Verify protocol version compatibility
await this.verifyProtocolVersion();
@@ -330,52 +335,17 @@ export class CopilotClient {
}
this.sessions.clear();
- // Close connection
- if (this.connection) {
- try {
- this.connection.dispose();
- } catch (error) {
- errors.push(
- new Error(
- `Failed to dispose connection: ${error instanceof Error ? error.message : String(error)}`
- )
- );
- }
- this.connection = null;
- }
-
// Clear models cache
this.modelsCache = null;
- if (this.socket) {
- try {
- this.socket.end();
- } catch (error) {
- errors.push(
- new Error(
- `Failed to close socket: ${error instanceof Error ? error.message : String(error)}`
- )
- );
- }
- this.socket = null;
- }
-
- // Kill CLI process (only if we spawned it)
- if (this.cliProcess && !this.isExternalServer) {
- try {
- this.cliProcess.kill();
- } catch (error) {
- errors.push(
- new Error(
- `Failed to kill CLI process: ${error instanceof Error ? error.message : String(error)}`
- )
- );
- }
- this.cliProcess = null;
+ // Stop transport (handles connection, socket, process cleanup)
+ if (this.transport) {
+ const transportErrors = await this.transport.stop();
+ errors.push(...transportErrors);
}
+ this.connection = null;
this.state = "disconnected";
- this.actualPort = null;
return errors;
}
@@ -406,45 +376,19 @@ export class CopilotClient {
* ```
*/
async forceStop(): Promise {
- this.forceStopping = true;
-
// Clear sessions immediately without trying to destroy them
this.sessions.clear();
- // Force close connection
- if (this.connection) {
- try {
- this.connection.dispose();
- } catch {
- // Ignore errors during force stop
- }
- this.connection = null;
- }
-
// Clear models cache
this.modelsCache = null;
- if (this.socket) {
- try {
- this.socket.destroy(); // destroy() is more forceful than end()
- } catch {
- // Ignore errors
- }
- this.socket = null;
- }
-
- // Force kill CLI process (only if we spawned it)
- if (this.cliProcess && !this.isExternalServer) {
- try {
- this.cliProcess.kill("SIGKILL");
- } catch {
- // Ignore errors
- }
- this.cliProcess = null;
+ // Force stop transport (handles connection, socket, process cleanup)
+ if (this.transport) {
+ await this.transport.forceStop();
}
+ this.connection = null;
this.state = "disconnected";
- this.actualPort = null;
}
/**
@@ -465,7 +409,7 @@ export class CopilotClient {
*
* // Session with model and tools
* const session = await client.createSession({
- * model: "gpt-4",
+ * model: "gpt-4.1",
* tools: [{
* name: "get_weather",
* description: "Get weather for a location",
@@ -508,6 +452,9 @@ export class CopilotClient {
skillDirectories: config.skillDirectories,
disabledSkills: config.disabledSkills,
infiniteSessions: config.infiniteSessions,
+ configDiscovery: config.configDiscovery,
+ agentDiscovery: config.agentDiscovery,
+ sessionStorage: config.sessionStorage,
});
const { sessionId, workspacePath } = response as {
@@ -590,6 +537,9 @@ export class CopilotClient {
disabledSkills: config.disabledSkills,
infiniteSessions: config.infiniteSessions,
disableResume: config.disableResume,
+ configDiscovery: config.configDiscovery,
+ agentDiscovery: config.agentDiscovery,
+ sessionStorage: config.sessionStorage,
});
const { sessionId: resumedSessionId, workspacePath } = response as {
@@ -978,192 +928,6 @@ export class CopilotClient {
};
}
- /**
- * Start the CLI server process
- */
- private async startCLIServer(): Promise {
- return new Promise((resolve, reject) => {
- const args = [
- ...this.options.cliArgs,
- "--headless",
- "--no-auto-update",
- "--log-level",
- this.options.logLevel,
- ];
-
- // Choose transport mode
- if (this.options.useStdio) {
- args.push("--stdio");
- } else if (this.options.port > 0) {
- args.push("--port", this.options.port.toString());
- }
-
- // Add auth-related flags
- if (this.options.githubToken) {
- args.push("--auth-token-env", "COPILOT_SDK_AUTH_TOKEN");
- }
- if (!this.options.useLoggedInUser) {
- args.push("--no-auto-login");
- }
-
- // Suppress debug/trace output that might pollute stdout
- const envWithoutNodeDebug = { ...this.options.env };
- delete envWithoutNodeDebug.NODE_DEBUG;
-
- // Set auth token in environment if provided
- if (this.options.githubToken) {
- envWithoutNodeDebug.COPILOT_SDK_AUTH_TOKEN = this.options.githubToken;
- }
-
- // Verify CLI exists before attempting to spawn
- if (!existsSync(this.options.cliPath)) {
- throw new Error(
- `Copilot CLI not found at ${this.options.cliPath}. Ensure @github/copilot is installed.`
- );
- }
-
- const stdioConfig: ["pipe", "pipe", "pipe"] | ["ignore", "pipe", "pipe"] = this.options
- .useStdio
- ? ["pipe", "pipe", "pipe"]
- : ["ignore", "pipe", "pipe"];
-
- // For .js files, spawn node explicitly; for executables, spawn directly
- const isJsFile = this.options.cliPath.endsWith(".js");
- if (isJsFile) {
- this.cliProcess = spawn(process.execPath, [this.options.cliPath, ...args], {
- stdio: stdioConfig,
- cwd: this.options.cwd,
- env: envWithoutNodeDebug,
- });
- } else {
- this.cliProcess = spawn(this.options.cliPath, args, {
- stdio: stdioConfig,
- cwd: this.options.cwd,
- env: envWithoutNodeDebug,
- });
- }
-
- let stdout = "";
- let resolved = false;
-
- // For stdio mode, we're ready immediately after spawn
- if (this.options.useStdio) {
- resolved = true;
- resolve();
- } else {
- // For TCP mode, wait for port announcement
- this.cliProcess.stdout?.on("data", (data: Buffer) => {
- stdout += data.toString();
- const match = stdout.match(/listening on port (\d+)/i);
- if (match && !resolved) {
- this.actualPort = parseInt(match[1], 10);
- resolved = true;
- resolve();
- }
- });
- }
-
- this.cliProcess.stderr?.on("data", (data: Buffer) => {
- // Forward CLI stderr to parent's stderr so debug logs are visible
- const lines = data.toString().split("\n");
- for (const line of lines) {
- if (line.trim()) {
- process.stderr.write(`[CLI subprocess] ${line}\n`);
- }
- }
- });
-
- this.cliProcess.on("error", (error) => {
- if (!resolved) {
- resolved = true;
- reject(new Error(`Failed to start CLI server: ${error.message}`));
- }
- });
-
- this.cliProcess.on("exit", (code) => {
- if (!resolved) {
- resolved = true;
- reject(new Error(`CLI server exited with code ${code}`));
- } else if (this.options.autoRestart && this.state === "connected") {
- void this.reconnect();
- }
- });
-
- // Timeout after 10 seconds
- setTimeout(() => {
- if (!resolved) {
- resolved = true;
- reject(new Error("Timeout waiting for CLI server to start"));
- }
- }, 10000);
- });
- }
-
- /**
- * Connect to the CLI server (via socket or stdio)
- */
- private async connectToServer(): Promise {
- if (this.options.useStdio) {
- return this.connectViaStdio();
- } else {
- return this.connectViaTcp();
- }
- }
-
- /**
- * Connect via stdio pipes
- */
- private async connectViaStdio(): Promise {
- if (!this.cliProcess) {
- throw new Error("CLI process not started");
- }
-
- // Add error handler to stdin to prevent unhandled rejections during forceStop
- this.cliProcess.stdin?.on("error", (err) => {
- if (!this.forceStopping) {
- throw err;
- }
- });
-
- // Create JSON-RPC connection over stdin/stdout
- this.connection = createMessageConnection(
- new StreamMessageReader(this.cliProcess.stdout!),
- new StreamMessageWriter(this.cliProcess.stdin!)
- );
-
- this.attachConnectionHandlers();
- this.connection.listen();
- }
-
- /**
- * Connect to the CLI server via TCP socket
- */
- private async connectViaTcp(): Promise {
- if (!this.actualPort) {
- throw new Error("Server port not available");
- }
-
- return new Promise((resolve, reject) => {
- this.socket = new Socket();
-
- this.socket.connect(this.actualPort!, this.actualHost, () => {
- // Create JSON-RPC connection
- this.connection = createMessageConnection(
- new StreamMessageReader(this.socket!),
- new StreamMessageWriter(this.socket!)
- );
-
- this.attachConnectionHandlers();
- this.connection.listen();
- resolve();
- });
-
- this.socket.on("error", (error) => {
- reject(new Error(`Failed to connect to CLI server: ${error.message}`));
- });
- });
- }
-
private attachConnectionHandlers(): void {
if (!this.connection) {
return;
diff --git a/nodejs/src/filesystem.ts b/nodejs/src/filesystem.ts
new file mode 100644
index 000000000..53521ae91
--- /dev/null
+++ b/nodejs/src/filesystem.ts
@@ -0,0 +1,113 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *--------------------------------------------------------------------------------------------*/
+
+/**
+ * FileSystem provider interface for the Copilot SDK.
+ * Allows integrators to provide custom filesystem implementations
+ * (e.g., in-memory for WASM/testing, or proxied to host).
+ */
+export interface FileSystemProvider {
+ readFile(path: string): Promise;
+ writeFile(path: string, content: string): Promise;
+ exists(path: string): Promise;
+ readDir(path: string): Promise;
+ mkdir(path: string, options?: { recursive?: boolean }): Promise;
+ remove(path: string): Promise;
+}
+
+/**
+ * In-memory filesystem implementation for WASM, testing, and sandboxed scenarios.
+ * Files are stored in a Map, no disk I/O.
+ *
+ * @example
+ * ```typescript
+ * const fs = new InMemoryFileSystem();
+ * await fs.writeFile("/doc.txt", "Hello world");
+ * const content = await fs.readFile("/doc.txt");
+ * ```
+ */
+export class InMemoryFileSystem implements FileSystemProvider {
+ private files: Map = new Map();
+
+ constructor(initialFiles?: Record) {
+ if (initialFiles) {
+ for (const [path, content] of Object.entries(initialFiles)) {
+ this.files.set(this.normalize(path), content);
+ }
+ }
+ }
+
+ private normalize(path: string): string {
+ // Normalize path separators and remove trailing slashes
+ return path.replace(/\\/g, "/").replace(/\/+$/, "") || "/";
+ }
+
+ async readFile(path: string): Promise {
+ const normalized = this.normalize(path);
+ const content = this.files.get(normalized);
+ if (content === undefined) {
+ throw new Error(`ENOENT: no such file: ${normalized}`);
+ }
+ return content;
+ }
+
+ async writeFile(path: string, content: string): Promise {
+ this.files.set(this.normalize(path), content);
+ }
+
+ async exists(path: string): Promise {
+ const normalized = this.normalize(path);
+ // Check exact match or any file under this directory
+ if (this.files.has(normalized)) return true;
+ const prefix = normalized.endsWith("/") ? normalized : normalized + "/";
+ for (const key of this.files.keys()) {
+ if (key.startsWith(prefix)) return true;
+ }
+ return false;
+ }
+
+ async readDir(path: string): Promise {
+ const normalized = this.normalize(path);
+ const prefix = normalized === "/" ? "/" : normalized + "/";
+ const entries = new Set();
+ for (const key of this.files.keys()) {
+ if (key.startsWith(prefix)) {
+ const relative = key.slice(prefix.length);
+ const firstSegment = relative.split("/")[0];
+ if (firstSegment) entries.add(firstSegment);
+ }
+ }
+ return [...entries].sort();
+ }
+
+ async mkdir(_path: string, _options?: { recursive?: boolean }): Promise {
+ // No-op for in-memory FS — directories are implicit
+ }
+
+ async remove(path: string): Promise {
+ const normalized = this.normalize(path);
+ this.files.delete(normalized);
+ // Also remove any files under this directory
+ const prefix = normalized + "/";
+ for (const key of this.files.keys()) {
+ if (key.startsWith(prefix)) {
+ this.files.delete(key);
+ }
+ }
+ }
+
+ /** Get all files (for testing/debugging) */
+ getAllFiles(): Record {
+ const result: Record = {};
+ for (const [path, content] of this.files) {
+ result[path] = content;
+ }
+ return result;
+ }
+
+ /** Synchronous write for initialization */
+ writeSync(path: string, content: string): void {
+ this.files.set(this.normalize(path), content);
+ }
+}
diff --git a/nodejs/src/generated/session-events.ts b/nodejs/src/generated/session-events.ts
index 86783a043..816fbed63 100644
--- a/nodejs/src/generated/session-events.ts
+++ b/nodejs/src/generated/session-events.ts
@@ -1,7 +1,7 @@
/**
* AUTO-GENERATED FILE - DO NOT EDIT
*
- * Generated from: @github/copilot/session-events.schema.json
+ * Generated from: copilot-core/session-events.schema.json
* Generated by: scripts/generate-session-types.ts
* Generated at: 2026-02-06T20:38:23.139Z
*
diff --git a/nodejs/src/index.ts b/nodejs/src/index.ts
index 4f9fcbf6a..3976b0c74 100644
--- a/nodejs/src/index.ts
+++ b/nodejs/src/index.ts
@@ -11,6 +11,23 @@
export { CopilotClient } from "./client.js";
export { CopilotSession, type AssistantMessageEvent } from "./session.js";
export { defineTool } from "./types.js";
+export { type Transport } from "./transport.js";
+export { StdioTransport, type StdioTransportOptions } from "./stdio-transport.js";
+export { TcpTransport, parseCliUrl, type TcpTransportOptions } from "./tcp-transport.js";
+export {
+ WasmTransport,
+ WasmConnection,
+ type WasmModule,
+ type WasmTransportOptions,
+} from "./wasm-transport.js";
+export {
+ preset,
+ type PresetName,
+ type PresetConfig,
+ type PresetSessionConfig,
+ type PresetClientConfig,
+} from "./presets.js";
+export { InMemoryFileSystem, type FileSystemProvider } from "./filesystem.js";
export type {
ConnectionState,
CopilotClientOptions,
diff --git a/nodejs/src/presets.ts b/nodejs/src/presets.ts
new file mode 100644
index 000000000..775800433
--- /dev/null
+++ b/nodejs/src/presets.ts
@@ -0,0 +1,87 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *--------------------------------------------------------------------------------------------*/
+
+import type { SessionConfig, CopilotClientOptions } from "./types.js";
+
+/**
+ * Preset configurations for common use cases.
+ * Presets provide sensible defaults for different deployment scenarios.
+ */
+
+export type PresetName = "cli" | "filesystem" | "minimal";
+
+export interface PresetSessionConfig extends Partial {}
+export interface PresetClientConfig extends Partial {}
+
+export interface PresetConfig {
+ client: PresetClientConfig;
+ session: PresetSessionConfig;
+}
+
+/**
+ * Returns a preset configuration for the given scenario.
+ *
+ * @param name - The preset name
+ * @param overrides - Optional overrides to merge on top of the preset
+ * @returns Preset configuration for client and session
+ *
+ * @example
+ * ```typescript
+ * // Full CLI experience
+ * const config = preset("cli");
+ * const client = new CopilotClient(config.client);
+ * const session = await client.createSession(config.session);
+ *
+ * // Minimal — just LLM chat, no tools
+ * const config = preset("minimal");
+ * ```
+ */
+export function preset(name: PresetName, overrides?: Partial): PresetConfig {
+ const base = PRESETS[name];
+ if (!base) {
+ throw new Error(
+ `Unknown preset: ${name}. Available presets: ${Object.keys(PRESETS).join(", ")}`
+ );
+ }
+
+ return {
+ client: { ...base.client, ...overrides?.client },
+ session: { ...base.session, ...overrides?.session },
+ };
+}
+
+const PRESETS: Record = {
+ cli: {
+ client: {
+ useLoggedInUser: true,
+ autoStart: true,
+ autoRestart: true,
+ },
+ session: {
+ configDiscovery: true,
+ agentDiscovery: true,
+ sessionStorage: "disk",
+ },
+ },
+ filesystem: {
+ client: {
+ autoStart: true,
+ },
+ session: {
+ configDiscovery: false,
+ agentDiscovery: false,
+ sessionStorage: "memory",
+ },
+ },
+ minimal: {
+ client: {
+ autoStart: true,
+ },
+ session: {
+ configDiscovery: false,
+ agentDiscovery: false,
+ sessionStorage: "memory",
+ },
+ },
+};
diff --git a/nodejs/src/session.ts b/nodejs/src/session.ts
index 19da9bd10..f56ea3984 100644
--- a/nodejs/src/session.ts
+++ b/nodejs/src/session.ts
@@ -38,7 +38,7 @@ export type AssistantMessageEvent = Extract {
@@ -112,7 +112,7 @@ export class CopilotSession {
mode: options.mode,
});
- return (response as { messageId: string }).messageId;
+ return (response as { messageId?: string }).messageId ?? "";
}
/**
diff --git a/nodejs/src/stdio-transport.ts b/nodejs/src/stdio-transport.ts
new file mode 100644
index 000000000..ecbbeabdb
--- /dev/null
+++ b/nodejs/src/stdio-transport.ts
@@ -0,0 +1,211 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *--------------------------------------------------------------------------------------------*/
+
+import { spawn, type ChildProcess } from "node:child_process";
+import {
+ createMessageConnection,
+ type MessageConnection,
+ StreamMessageReader,
+ StreamMessageWriter,
+} from "vscode-jsonrpc/node.js";
+import type { Transport } from "./transport.js";
+
+/**
+ * Gets the path to the copilot-core CLI binary.
+ * Assumes copilot-core is available on PATH.
+ */
+export function getBundledCliPath(): string {
+ return "copilot-core";
+}
+
+export interface StdioTransportOptions {
+ cliPath?: string;
+ cliArgs?: string[];
+ cwd?: string;
+ logLevel?: string;
+ env?: Record;
+ githubToken?: string;
+ useLoggedInUser?: boolean;
+ onExit?: (code: number | null) => void;
+}
+
+/**
+ * Transport that communicates with the Copilot CLI via stdin/stdout pipes.
+ */
+export class StdioTransport implements Transport {
+ private cliProcess: ChildProcess | null = null;
+ private _connection: MessageConnection | null = null;
+ private forceStopping = false;
+ private readonly opts: Required<
+ Omit
+ > &
+ Pick;
+
+ get connection(): MessageConnection | null {
+ return this._connection;
+ }
+
+ constructor(options: StdioTransportOptions = {}) {
+ this.opts = {
+ cliPath: options.cliPath || getBundledCliPath(),
+ cliArgs: options.cliArgs ?? [],
+ cwd: options.cwd ?? process.cwd(),
+ logLevel: options.logLevel || "debug",
+ env: options.env ?? process.env,
+ githubToken: options.githubToken,
+ useLoggedInUser: options.useLoggedInUser,
+ onExit: options.onExit,
+ };
+ }
+
+ async start(): Promise {
+ await this.spawnCLI();
+ this.connectStdio();
+ }
+
+ async stop(): Promise {
+ const errors: Error[] = [];
+
+ if (this._connection) {
+ try {
+ this._connection.dispose();
+ } catch (error) {
+ errors.push(
+ new Error(
+ `Failed to dispose connection: ${error instanceof Error ? error.message : String(error)}`
+ )
+ );
+ }
+ this._connection = null;
+ }
+
+ if (this.cliProcess) {
+ try {
+ this.cliProcess.kill();
+ } catch (error) {
+ errors.push(
+ new Error(
+ `Failed to kill CLI process: ${error instanceof Error ? error.message : String(error)}`
+ )
+ );
+ }
+ this.cliProcess = null;
+ }
+
+ return errors;
+ }
+
+ async forceStop(): Promise {
+ this.forceStopping = true;
+
+ if (this._connection) {
+ try {
+ this._connection.dispose();
+ } catch {
+ // Ignore errors during force stop
+ }
+ this._connection = null;
+ }
+
+ if (this.cliProcess) {
+ try {
+ this.cliProcess.kill("SIGKILL");
+ } catch {
+ // Ignore errors
+ }
+ this.cliProcess = null;
+ }
+ }
+
+ private spawnCLI(): Promise {
+ return new Promise((resolve, reject) => {
+ const args = [
+ ...this.opts.cliArgs,
+ "--headless",
+ "--no-auto-update",
+ "--log-level",
+ this.opts.logLevel,
+ "--stdio",
+ ];
+
+ if (this.opts.githubToken) {
+ args.push("--auth-token-env", "COPILOT_SDK_AUTH_TOKEN");
+ }
+ if (!this.opts.useLoggedInUser) {
+ args.push("--no-auto-login");
+ }
+
+ const envWithoutNodeDebug = { ...this.opts.env };
+ delete envWithoutNodeDebug.NODE_DEBUG;
+ if (this.opts.githubToken) {
+ envWithoutNodeDebug.COPILOT_SDK_AUTH_TOKEN = this.opts.githubToken;
+ }
+
+ const stdioConfig: ["pipe", "pipe", "pipe"] = ["pipe", "pipe", "pipe"];
+
+ this.cliProcess = spawn(this.opts.cliPath, args, {
+ stdio: stdioConfig,
+ cwd: this.opts.cwd,
+ env: envWithoutNodeDebug,
+ });
+
+ let resolved = false;
+
+ // For stdio mode, we're ready immediately after spawn
+ resolved = true;
+ resolve();
+
+ this.cliProcess.stderr?.on("data", (data: Buffer) => {
+ const lines = data.toString().split("\n");
+ for (const line of lines) {
+ if (line.trim()) {
+ process.stderr.write(`[CLI subprocess] ${line}\n`);
+ }
+ }
+ });
+
+ this.cliProcess.on("error", (error) => {
+ if (!resolved) {
+ resolved = true;
+ reject(new Error(`Failed to start CLI server: ${error.message}`));
+ }
+ });
+
+ this.cliProcess.on("exit", (code) => {
+ if (!resolved) {
+ resolved = true;
+ reject(new Error(`CLI server exited with code ${code}`));
+ } else {
+ this.opts.onExit?.(code);
+ }
+ });
+
+ setTimeout(() => {
+ if (!resolved) {
+ resolved = true;
+ reject(new Error("Timeout waiting for CLI server to start"));
+ }
+ }, 10000);
+ });
+ }
+
+ private connectStdio(): void {
+ if (!this.cliProcess) {
+ throw new Error("CLI process not started");
+ }
+
+ this.cliProcess.stdin?.on("error", (err) => {
+ if (!this.forceStopping) {
+ throw err;
+ }
+ });
+
+ this._connection = createMessageConnection(
+ new StreamMessageReader(this.cliProcess.stdout!),
+ new StreamMessageWriter(this.cliProcess.stdin!)
+ );
+
+ this._connection.listen();
+ }
+}
diff --git a/nodejs/src/tcp-transport.ts b/nodejs/src/tcp-transport.ts
new file mode 100644
index 000000000..a33d2747f
--- /dev/null
+++ b/nodejs/src/tcp-transport.ts
@@ -0,0 +1,289 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *--------------------------------------------------------------------------------------------*/
+
+import { spawn, type ChildProcess } from "node:child_process";
+import { Socket } from "node:net";
+import {
+ createMessageConnection,
+ type MessageConnection,
+ StreamMessageReader,
+ StreamMessageWriter,
+} from "vscode-jsonrpc/node.js";
+import type { Transport } from "./transport.js";
+import { getBundledCliPath } from "./stdio-transport.js";
+
+export interface TcpTransportOptions {
+ cliPath?: string;
+ cliArgs?: string[];
+ cwd?: string;
+ logLevel?: string;
+ env?: Record;
+ githubToken?: string;
+ useLoggedInUser?: boolean;
+ port?: number;
+ host?: string;
+ isExternalServer?: boolean;
+ onExit?: (code: number | null) => void;
+}
+
+/**
+ * Parse CLI URL into host and port.
+ * Supports formats: "host:port", "http://host:port", "https://host:port", or just "port"
+ */
+export function parseCliUrl(url: string): { host: string; port: number } {
+ let cleanUrl = url.replace(/^https?:\/\//, "");
+
+ if (/^\d+$/.test(cleanUrl)) {
+ return { host: "localhost", port: parseInt(cleanUrl, 10) };
+ }
+
+ const parts = cleanUrl.split(":");
+ if (parts.length !== 2) {
+ throw new Error(
+ `Invalid cliUrl format: ${url}. Expected "host:port", "http://host:port", or "port"`
+ );
+ }
+
+ const host = parts[0] || "localhost";
+ const port = parseInt(parts[1], 10);
+
+ if (isNaN(port) || port <= 0 || port > 65535) {
+ throw new Error(`Invalid port in cliUrl: ${url}`);
+ }
+
+ return { host, port };
+}
+
+/**
+ * Transport that communicates with the Copilot CLI via TCP socket.
+ * Supports two modes: spawning a CLI process with --port, or connecting to an existing server.
+ */
+export class TcpTransport implements Transport {
+ private cliProcess: ChildProcess | null = null;
+ private socket: Socket | null = null;
+ private _connection: MessageConnection | null = null;
+ private actualPort: number | null;
+ private readonly host: string;
+ private readonly isExternalServer: boolean;
+ private readonly opts: Required<
+ Omit<
+ TcpTransportOptions,
+ "githubToken" | "useLoggedInUser" | "onExit" | "port" | "host" | "isExternalServer"
+ >
+ > &
+ Pick;
+ private readonly requestedPort: number;
+
+ get connection(): MessageConnection | null {
+ return this._connection;
+ }
+
+ constructor(options: TcpTransportOptions = {}) {
+ this.host = options.host ?? "localhost";
+ this.isExternalServer = options.isExternalServer ?? false;
+ this.requestedPort = options.port ?? 0;
+ this.actualPort = this.isExternalServer ? this.requestedPort : null;
+ this.opts = {
+ cliPath: options.cliPath || getBundledCliPath(),
+ cliArgs: options.cliArgs ?? [],
+ cwd: options.cwd ?? process.cwd(),
+ logLevel: options.logLevel || "debug",
+ env: options.env ?? process.env,
+ githubToken: options.githubToken,
+ useLoggedInUser: options.useLoggedInUser,
+ onExit: options.onExit,
+ };
+ }
+
+ async start(): Promise {
+ if (!this.isExternalServer) {
+ await this.spawnCLI();
+ }
+ await this.connectTcp();
+ }
+
+ async stop(): Promise {
+ const errors: Error[] = [];
+
+ if (this._connection) {
+ try {
+ this._connection.dispose();
+ } catch (error) {
+ errors.push(
+ new Error(
+ `Failed to dispose connection: ${error instanceof Error ? error.message : String(error)}`
+ )
+ );
+ }
+ this._connection = null;
+ }
+
+ if (this.socket) {
+ try {
+ this.socket.end();
+ } catch (error) {
+ errors.push(
+ new Error(
+ `Failed to close socket: ${error instanceof Error ? error.message : String(error)}`
+ )
+ );
+ }
+ this.socket = null;
+ }
+
+ if (this.cliProcess && !this.isExternalServer) {
+ try {
+ this.cliProcess.kill();
+ } catch (error) {
+ errors.push(
+ new Error(
+ `Failed to kill CLI process: ${error instanceof Error ? error.message : String(error)}`
+ )
+ );
+ }
+ this.cliProcess = null;
+ }
+
+ this.actualPort = null;
+ return errors;
+ }
+
+ async forceStop(): Promise {
+ if (this._connection) {
+ try {
+ this._connection.dispose();
+ } catch {
+ // Ignore errors during force stop
+ }
+ this._connection = null;
+ }
+
+ if (this.socket) {
+ try {
+ this.socket.destroy();
+ } catch {
+ // Ignore errors
+ }
+ this.socket = null;
+ }
+
+ if (this.cliProcess && !this.isExternalServer) {
+ try {
+ this.cliProcess.kill("SIGKILL");
+ } catch {
+ // Ignore errors
+ }
+ this.cliProcess = null;
+ }
+
+ this.actualPort = null;
+ }
+
+ private spawnCLI(): Promise {
+ return new Promise((resolve, reject) => {
+ const args = [
+ ...this.opts.cliArgs,
+ "--headless",
+ "--no-auto-update",
+ "--log-level",
+ this.opts.logLevel,
+ ];
+
+ if (this.requestedPort > 0) {
+ args.push("--port", this.requestedPort.toString());
+ }
+
+ if (this.opts.githubToken) {
+ args.push("--auth-token-env", "COPILOT_SDK_AUTH_TOKEN");
+ }
+ if (!this.opts.useLoggedInUser) {
+ args.push("--no-auto-login");
+ }
+
+ const envWithoutNodeDebug = { ...this.opts.env };
+ delete envWithoutNodeDebug.NODE_DEBUG;
+ if (this.opts.githubToken) {
+ envWithoutNodeDebug.COPILOT_SDK_AUTH_TOKEN = this.opts.githubToken;
+ }
+
+ const stdioConfig: ["ignore", "pipe", "pipe"] = ["ignore", "pipe", "pipe"];
+
+ this.cliProcess = spawn(this.opts.cliPath, args, {
+ stdio: stdioConfig,
+ cwd: this.opts.cwd,
+ env: envWithoutNodeDebug,
+ });
+
+ let stdout = "";
+ let resolved = false;
+
+ // For TCP mode, wait for port announcement
+ this.cliProcess.stdout?.on("data", (data: Buffer) => {
+ stdout += data.toString();
+ const match = stdout.match(/listening on port (\d+)/i);
+ if (match && !resolved) {
+ this.actualPort = parseInt(match[1], 10);
+ resolved = true;
+ resolve();
+ }
+ });
+
+ this.cliProcess.stderr?.on("data", (data: Buffer) => {
+ const lines = data.toString().split("\n");
+ for (const line of lines) {
+ if (line.trim()) {
+ process.stderr.write(`[CLI subprocess] ${line}\n`);
+ }
+ }
+ });
+
+ this.cliProcess.on("error", (error) => {
+ if (!resolved) {
+ resolved = true;
+ reject(new Error(`Failed to start CLI server: ${error.message}`));
+ }
+ });
+
+ this.cliProcess.on("exit", (code) => {
+ if (!resolved) {
+ resolved = true;
+ reject(new Error(`CLI server exited with code ${code}`));
+ } else {
+ this.opts.onExit?.(code);
+ }
+ });
+
+ setTimeout(() => {
+ if (!resolved) {
+ resolved = true;
+ reject(new Error("Timeout waiting for CLI server to start"));
+ }
+ }, 10000);
+ });
+ }
+
+ private connectTcp(): Promise {
+ if (!this.actualPort) {
+ throw new Error("Server port not available");
+ }
+
+ return new Promise((resolve, reject) => {
+ this.socket = new Socket();
+
+ this.socket.connect(this.actualPort!, this.host, () => {
+ this._connection = createMessageConnection(
+ new StreamMessageReader(this.socket!),
+ new StreamMessageWriter(this.socket!)
+ );
+
+ this._connection.listen();
+ resolve();
+ });
+
+ this.socket.on("error", (error) => {
+ reject(new Error(`Failed to connect to CLI server: ${error.message}`));
+ });
+ });
+ }
+}
diff --git a/nodejs/src/transport.ts b/nodejs/src/transport.ts
new file mode 100644
index 000000000..7fa31cd43
--- /dev/null
+++ b/nodejs/src/transport.ts
@@ -0,0 +1,23 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *--------------------------------------------------------------------------------------------*/
+
+import type { MessageConnection } from "vscode-jsonrpc/node.js";
+
+/**
+ * Transport interface for communicating with the Copilot runtime.
+ * Abstracts the details of how messages are sent/received.
+ */
+export interface Transport {
+ /** The underlying JSON-RPC connection, available after start() resolves */
+ readonly connection: MessageConnection | null;
+
+ /** Start the transport and establish connection */
+ start(): Promise;
+
+ /** Graceful shutdown */
+ stop(): Promise;
+
+ /** Forceful shutdown without cleanup */
+ forceStop(): Promise;
+}
diff --git a/nodejs/src/types.ts b/nodejs/src/types.ts
index ffb968017..1c0f477b1 100644
--- a/nodejs/src/types.ts
+++ b/nodejs/src/types.ts
@@ -10,13 +10,16 @@
import type { SessionEvent as GeneratedSessionEvent } from "./generated/session-events.js";
export type SessionEvent = GeneratedSessionEvent;
+import type { FileSystemProvider } from "./filesystem.js";
+export type { FileSystemProvider } from "./filesystem.js";
+
/**
* Options for creating a CopilotClient
*/
export interface CopilotClientOptions {
/**
* Path to the CLI executable or JavaScript entry point.
- * If not specified, uses the bundled CLI from the @github/copilot package.
+ * If not specified, uses the copilot-core binary from PATH.
*/
cliPath?: string;
@@ -86,9 +89,32 @@ export interface CopilotClientOptions {
* Whether to use the logged-in user for authentication.
* When true, the CLI server will attempt to use stored OAuth tokens or gh CLI auth.
* When false, only explicit tokens (githubToken or environment variables) are used.
- * @default true (but defaults to false when githubToken is provided)
+ * @default false
*/
useLoggedInUser?: boolean;
+
+ /**
+ * Runtime mode. When set to "wasm", uses in-process WASM transport.
+ * Mutually exclusive with cliPath, cliUrl, useStdio.
+ */
+ runtime?: "native" | "wasm";
+
+ /**
+ * WASM module instance or loader function.
+ * Required when runtime is "wasm".
+ */
+ wasmModule?: import("./wasm-transport.js").WasmModule | (() => Promise);
+
+ /**
+ * Copilot API URL override. Used with WASM runtime.
+ */
+ apiUrl?: string;
+
+ /**
+ * Custom HTTP POST implementation for the WASM runtime.
+ * Defaults to using fetch().
+ */
+ httpPost?: (url: string, headersJson: string, bodyJson: string) => Promise<{ status: number; body: string }>;
}
/**
@@ -723,6 +749,32 @@ export interface SessionConfig {
* Set to `{ enabled: false }` to disable.
*/
infiniteSessions?: InfiniteSessionConfig;
+
+ /**
+ * Whether to enable config file discovery (scanning .github/, ~/.copilot/, etc.)
+ * @default false
+ */
+ configDiscovery?: boolean;
+
+ /**
+ * Whether to enable agent auto-discovery from .github/agents/
+ * @default false
+ */
+ agentDiscovery?: boolean;
+
+ /**
+ * Session storage mode.
+ * - "memory": In-memory only (default, secure)
+ * - "disk": Persist to ~/.copilot/session-state/
+ * @default "memory"
+ */
+ sessionStorage?: "memory" | "disk";
+
+ /**
+ * FileSystem provider for tool operations.
+ * Required for filesystem-based tools (view, edit, etc.) in WASM mode.
+ */
+ filesystem?: FileSystemProvider;
}
/**
@@ -748,6 +800,10 @@ export type ResumeSessionConfig = Pick<
| "skillDirectories"
| "disabledSkills"
| "infiniteSessions"
+ | "configDiscovery"
+ | "agentDiscovery"
+ | "sessionStorage"
+ | "filesystem"
> & {
/**
* When true, skips emitting the session.resume event.
diff --git a/nodejs/src/wasm-transport.ts b/nodejs/src/wasm-transport.ts
new file mode 100644
index 000000000..0b84abfc6
--- /dev/null
+++ b/nodejs/src/wasm-transport.ts
@@ -0,0 +1,342 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ *--------------------------------------------------------------------------------------------*/
+
+import type { MessageConnection } from "vscode-jsonrpc/node.js";
+import type { Transport } from "./transport.js";
+
+/**
+ * Interface for the WASM module exports.
+ * The WASM module (built from the Rust runtime) exports these functions.
+ */
+export interface WasmModule {
+ init(
+ httpPostFn: (
+ url: string,
+ headersJson: string,
+ bodyJson: string,
+ ) => Promise<{ status: number; body: string }>,
+ onEvent: (method: string, paramsJson: string) => void,
+ onRequest: (method: string, paramsJson: string) => Promise,
+ authToken: string,
+ apiUrl?: string,
+ ): Promise;
+
+ send_jsonrpc(requestJson: string): Promise;
+}
+
+export interface WasmTransportOptions {
+ /** The WASM module instance or a loader function that returns one */
+ wasmModule?: WasmModule | (() => Promise);
+
+ /** Auth token to pass to the WASM runtime */
+ authToken?: string;
+
+ /** Copilot API URL override */
+ apiUrl?: string;
+
+ /** Custom HTTP implementation. Defaults to fetch(). */
+ httpPost?: (
+ url: string,
+ headersJson: string,
+ bodyJson: string,
+ ) => Promise<{ status: number; body: string }>;
+
+ /** Handler for events (notifications) from the WASM runtime */
+ onEvent?: (method: string, params: unknown) => void;
+
+ /** Handler for requests from the WASM runtime (tool calls, permission requests, etc.) */
+ onRequest?: (method: string, params: unknown) => Promise;
+}
+
+/**
+ * Lightweight adapter that mimics MessageConnection for WASM-based communication.
+ * Instead of a stream-based JSON-RPC connection, this serializes requests and
+ * calls the WASM module's send_jsonrpc directly.
+ */
+export class WasmConnection {
+ private notificationHandlers: Map void)[]> = new Map();
+ private requestHandlers: Map Promise> = new Map();
+ private closeHandlers: (() => void)[] = [];
+ private errorHandlers: ((error: Error) => void)[] = [];
+ private disposed = false;
+ private nextId = 1;
+
+ constructor(private wasmModule: WasmModule) {}
+
+ async sendRequest(method: string, params?: unknown): Promise {
+ if (this.disposed) throw new Error("Connection disposed");
+
+ const request = JSON.stringify({
+ jsonrpc: "2.0",
+ id: this.nextId++,
+ method,
+ params: params ?? {},
+ });
+
+ const responseJson = await this.wasmModule.send_jsonrpc(request);
+ const response = JSON.parse(
+ typeof responseJson === "string" ? responseJson : JSON.stringify(responseJson),
+ );
+
+ if (response.error) {
+ throw new Error(response.error.message || JSON.stringify(response.error));
+ }
+
+ return response.result;
+ }
+
+ sendNotification(method: string, params?: unknown): void {
+ const request = JSON.stringify({
+ jsonrpc: "2.0",
+ method,
+ params: params ?? {},
+ });
+ void this.wasmModule.send_jsonrpc(request);
+ }
+
+ onNotification(method: string, handler: (params: unknown) => void): void {
+ if (!this.notificationHandlers.has(method)) {
+ this.notificationHandlers.set(method, []);
+ }
+ this.notificationHandlers.get(method)!.push(handler);
+ }
+
+ onRequest(method: string, handler: (params: unknown) => Promise): void {
+ this.requestHandlers.set(method, handler);
+ }
+
+ onClose(handler: () => void): void {
+ this.closeHandlers.push(handler);
+ }
+
+ onError(handler: (error: Error) => void): void {
+ this.errorHandlers.push(handler);
+ }
+
+ listen(): void {
+ // No-op for WASM — already connected
+ }
+
+ dispose(): void {
+ this.disposed = true;
+ for (const handler of this.closeHandlers) {
+ try {
+ handler();
+ } catch {
+ // Ignore errors during dispose
+ }
+ }
+ }
+
+ /** Called by the WASM bridge when the runtime fires an event */
+ _dispatchEvent(method: string, paramsJson: string): void {
+ const params = JSON.parse(paramsJson);
+ const handlers = this.notificationHandlers.get(method);
+ if (handlers) {
+ for (const h of handlers) {
+ try {
+ h(params);
+ } catch {
+ // Ignore handler errors
+ }
+ }
+ }
+ }
+
+ /** Called by the WASM bridge when the runtime makes a request */
+ async _dispatchRequest(method: string, paramsJson: string): Promise {
+ const params = JSON.parse(paramsJson);
+ const handler = this.requestHandlers.get(method);
+ if (!handler) {
+ return JSON.stringify({ error: { code: -32601, message: `Method not found: ${method}` } });
+ }
+ try {
+ const result = await handler(params);
+ return JSON.stringify(result);
+ } catch (err) {
+ return JSON.stringify({ error: { code: -32603, message: String(err) } });
+ }
+ }
+}
+
+/**
+ * Transport that communicates with the Copilot runtime compiled to WebAssembly.
+ * Loads a WASM module in-process and communicates via direct function calls
+ * instead of spawning a process or connecting via TCP.
+ */
+export class WasmTransport implements Transport {
+ private wasmModule: WasmModule | null = null;
+ private wasmConnection: WasmConnection | null = null;
+ private readonly opts: WasmTransportOptions;
+
+ get connection(): MessageConnection | null {
+ // WasmConnection implements enough of MessageConnection for CopilotClient
+ return this.wasmConnection as unknown as MessageConnection | null;
+ }
+
+ constructor(options: WasmTransportOptions = {}) {
+ this.opts = options;
+ }
+
+ async start(): Promise {
+ if (!this.opts.wasmModule) {
+ throw new Error("wasmModule is required for WASM transport");
+ }
+
+ this.wasmModule =
+ typeof this.opts.wasmModule === "function"
+ ? await this.opts.wasmModule()
+ : this.opts.wasmModule;
+
+ this.wasmConnection = new WasmConnection(this.wasmModule);
+
+ const httpPost = this.opts.httpPost ?? defaultHttpPost;
+
+ await this.wasmModule.init(
+ httpPost,
+ (method: string, paramsJson: string) => {
+ this.translateAndDispatchEvent(method, paramsJson);
+ },
+ async (method: string, paramsJson: string): Promise => {
+ const translated = this.translateRequest(method, paramsJson);
+ return await this.wasmConnection!._dispatchRequest(translated.method, JSON.stringify(translated.params));
+ },
+ this.opts.authToken ?? "",
+ this.opts.apiUrl,
+ );
+ }
+
+ /**
+ * Translates WASM runtime callback names (emit_*, invoke_*) into the
+ * SDK's session.event notification format used by stdio/TCP transports.
+ */
+ private translateAndDispatchEvent(method: string, paramsJson: string): void {
+ const params = JSON.parse(paramsJson);
+ const sessionId = params.sessionId ?? "";
+ const ts = new Date().toISOString();
+ const id = globalThis.crypto?.randomUUID?.() ?? `${Date.now()}-${Math.random()}`;
+
+ let eventType: string | undefined;
+ let data: Record = {};
+ let ephemeral = false;
+
+ switch (method) {
+ case "emit_text_delta":
+ eventType = "assistant.message_delta";
+ data = { messageId: id, deltaContent: params.delta };
+ ephemeral = true;
+ break;
+ case "emit_assistant_message":
+ eventType = "assistant.message";
+ data = { messageId: id, content: params.content };
+ break;
+ case "emit_tool_start":
+ eventType = "tool.execution_start";
+ data = { toolCallId: params.toolCallId, toolName: params.toolName, arguments: params.args };
+ break;
+ case "emit_tool_complete":
+ eventType = "tool.execution_complete";
+ data = { toolCallId: params.toolCallId, success: true, result: { content: params.result } };
+ break;
+ case "emit_idle":
+ eventType = "session.idle";
+ ephemeral = true;
+ break;
+ case "emit_error":
+ eventType = "session.error";
+ data = { message: params.message };
+ break;
+ default:
+ // Unrecognized callbacks (e.g. hook invocations handled via onRequest)
+ return;
+ }
+
+ const notification = {
+ sessionId,
+ event: {
+ type: eventType,
+ id,
+ parentId: null,
+ timestamp: ts,
+ ...(ephemeral && { ephemeral: true }),
+ data: { ...data, sessionId },
+ },
+ };
+
+ this.wasmConnection!._dispatchEvent("session.event", JSON.stringify(notification));
+ }
+
+ /**
+ * Translates WASM runtime request callbacks (invoke_*_hook) into the
+ * SDK's hooks.invoke request format.
+ */
+ private translateRequest(method: string, paramsJson: string): { method: string; params: unknown } {
+ const params = JSON.parse(paramsJson);
+
+ const hookMap: Record = {
+ invoke_pre_tool_use_hook: "preToolUse",
+ invoke_post_tool_use_hook: "postToolUse",
+ invoke_user_prompt_submitted_hook: "userPromptSubmitted",
+ invoke_session_start_hook: "sessionStart",
+ invoke_session_end_hook: "sessionEnd",
+ invoke_error_occurred_hook: "errorOccurred",
+ };
+
+ const hookType = hookMap[method];
+ if (hookType) {
+ return {
+ method: "hooks.invoke",
+ params: {
+ sessionId: params.sessionId,
+ hookType,
+ input: params,
+ },
+ };
+ }
+
+ // Pass through unrecognized requests as-is
+ return { method, params };
+ }
+
+ async stop(): Promise {
+ const errors: Error[] = [];
+ if (this.wasmConnection) {
+ try {
+ this.wasmConnection.dispose();
+ } catch (e) {
+ errors.push(e instanceof Error ? e : new Error(String(e)));
+ }
+ this.wasmConnection = null;
+ }
+ this.wasmModule = null;
+ return errors;
+ }
+
+ async forceStop(): Promise {
+ if (this.wasmConnection) {
+ try {
+ this.wasmConnection.dispose();
+ } catch {
+ // Ignore errors during force stop
+ }
+ this.wasmConnection = null;
+ }
+ this.wasmModule = null;
+ }
+}
+
+async function defaultHttpPost(
+ url: string,
+ headersJson: string,
+ bodyJson: string,
+): Promise<{ status: number; body: string }> {
+ const headers = JSON.parse(headersJson);
+ const response = await fetch(url, {
+ method: "POST",
+ headers,
+ body: bodyJson,
+ });
+ const body = await response.text();
+ return { status: response.status, body };
+}
diff --git a/nodejs/test/client.test.ts b/nodejs/test/client.test.ts
index 4b9b512cb..a678ce6c7 100644
--- a/nodejs/test/client.test.ts
+++ b/nodejs/test/client.test.ts
@@ -1,6 +1,7 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import { describe, expect, it, onTestFinished } from "vitest";
-import { CopilotClient } from "../src/index.js";
+import { CopilotClient, parseCliUrl } from "../src/index.js";
+import { TcpTransport } from "../src/tcp-transport.js";
// This file is for unit tests. Where relevant, prefer to add e2e tests in e2e/*.test.ts instead
@@ -29,84 +30,43 @@ describe("CopilotClient", () => {
describe("URL parsing", () => {
it("should parse port-only URL format", () => {
- const client = new CopilotClient({
- cliUrl: "8080",
- logLevel: "error",
- });
-
- // Verify internal state
- expect((client as any).actualPort).toBe(8080);
- expect((client as any).actualHost).toBe("localhost");
- expect((client as any).isExternalServer).toBe(true);
+ const result = parseCliUrl("8080");
+ expect(result.port).toBe(8080);
+ expect(result.host).toBe("localhost");
});
it("should parse host:port URL format", () => {
- const client = new CopilotClient({
- cliUrl: "127.0.0.1:9000",
- logLevel: "error",
- });
-
- expect((client as any).actualPort).toBe(9000);
- expect((client as any).actualHost).toBe("127.0.0.1");
- expect((client as any).isExternalServer).toBe(true);
+ const result = parseCliUrl("127.0.0.1:9000");
+ expect(result.port).toBe(9000);
+ expect(result.host).toBe("127.0.0.1");
});
it("should parse http://host:port URL format", () => {
- const client = new CopilotClient({
- cliUrl: "http://localhost:7000",
- logLevel: "error",
- });
-
- expect((client as any).actualPort).toBe(7000);
- expect((client as any).actualHost).toBe("localhost");
- expect((client as any).isExternalServer).toBe(true);
+ const result = parseCliUrl("http://localhost:7000");
+ expect(result.port).toBe(7000);
+ expect(result.host).toBe("localhost");
});
it("should parse https://host:port URL format", () => {
- const client = new CopilotClient({
- cliUrl: "https://example.com:443",
- logLevel: "error",
- });
-
- expect((client as any).actualPort).toBe(443);
- expect((client as any).actualHost).toBe("example.com");
- expect((client as any).isExternalServer).toBe(true);
+ const result = parseCliUrl("https://example.com:443");
+ expect(result.port).toBe(443);
+ expect(result.host).toBe("example.com");
});
it("should throw error for invalid URL format", () => {
- expect(() => {
- new CopilotClient({
- cliUrl: "invalid-url",
- logLevel: "error",
- });
- }).toThrow(/Invalid cliUrl format/);
+ expect(() => parseCliUrl("invalid-url")).toThrow(/Invalid cliUrl format/);
});
it("should throw error for invalid port - too high", () => {
- expect(() => {
- new CopilotClient({
- cliUrl: "localhost:99999",
- logLevel: "error",
- });
- }).toThrow(/Invalid port in cliUrl/);
+ expect(() => parseCliUrl("localhost:99999")).toThrow(/Invalid port in cliUrl/);
});
it("should throw error for invalid port - zero", () => {
- expect(() => {
- new CopilotClient({
- cliUrl: "localhost:0",
- logLevel: "error",
- });
- }).toThrow(/Invalid port in cliUrl/);
+ expect(() => parseCliUrl("localhost:0")).toThrow(/Invalid port in cliUrl/);
});
it("should throw error for invalid port - negative", () => {
- expect(() => {
- new CopilotClient({
- cliUrl: "localhost:-1",
- logLevel: "error",
- });
- }).toThrow(/Invalid port in cliUrl/);
+ expect(() => parseCliUrl("localhost:-1")).toThrow(/Invalid port in cliUrl/);
});
it("should throw error when cliUrl is used with useStdio", () => {
@@ -138,13 +98,13 @@ describe("CopilotClient", () => {
expect(client["options"].useStdio).toBe(false);
});
- it("should mark client as using external server", () => {
+ it("should create TcpTransport when cliUrl is provided", () => {
const client = new CopilotClient({
cliUrl: "localhost:8080",
logLevel: "error",
});
- expect((client as any).isExternalServer).toBe(true);
+ expect((client as any).transport).toBeInstanceOf(TcpTransport);
});
});
@@ -158,12 +118,12 @@ describe("CopilotClient", () => {
expect((client as any).options.githubToken).toBe("gho_test_token");
});
- it("should default useLoggedInUser to true when no githubToken", () => {
+ it("should default useLoggedInUser to false (secure by default)", () => {
const client = new CopilotClient({
logLevel: "error",
});
- expect((client as any).options.useLoggedInUser).toBe(true);
+ expect((client as any).options.useLoggedInUser).toBe(false);
});
it("should default useLoggedInUser to false when githubToken is provided", () => {
diff --git a/nodejs/test/phase2.test.ts b/nodejs/test/phase2.test.ts
new file mode 100644
index 000000000..62d5d5cd1
--- /dev/null
+++ b/nodejs/test/phase2.test.ts
@@ -0,0 +1,263 @@
+/* eslint-disable @typescript-eslint/no-explicit-any */
+import { describe, expect, it } from "vitest";
+import { CopilotClient, preset, InMemoryFileSystem, parseCliUrl } from "../src/index.js";
+import { WasmConnection, WasmTransport, type WasmModule } from "../src/wasm-transport.js";
+
+describe("Presets", () => {
+ it("cli preset should enable full CLI experience", () => {
+ const config = preset("cli");
+ expect(config.client.useLoggedInUser).toBe(true);
+ expect(config.client.autoStart).toBe(true);
+ expect(config.client.autoRestart).toBe(true);
+ expect(config.session.configDiscovery).toBe(true);
+ expect(config.session.agentDiscovery).toBe(true);
+ expect(config.session.sessionStorage).toBe("disk");
+ });
+
+ it("filesystem preset should be restricted", () => {
+ const config = preset("filesystem");
+ expect(config.session.configDiscovery).toBe(false);
+ expect(config.session.agentDiscovery).toBe(false);
+ expect(config.session.sessionStorage).toBe("memory");
+ });
+
+ it("minimal preset should be most restrictive", () => {
+ const config = preset("minimal");
+ expect(config.session.configDiscovery).toBe(false);
+ expect(config.session.agentDiscovery).toBe(false);
+ expect(config.session.sessionStorage).toBe("memory");
+ });
+
+ it("should allow overrides", () => {
+ const config = preset("minimal", {
+ session: { configDiscovery: true },
+ });
+ expect(config.session.configDiscovery).toBe(true);
+ });
+
+ it("should throw for unknown preset", () => {
+ expect(() => preset("unknown" as any)).toThrow(/Unknown preset/);
+ });
+});
+
+describe("InMemoryFileSystem", () => {
+ it("should read and write files", async () => {
+ const fs = new InMemoryFileSystem();
+ await fs.writeFile("/test.txt", "hello");
+ expect(await fs.readFile("/test.txt")).toBe("hello");
+ });
+
+ it("should initialize with files", async () => {
+ const fs = new InMemoryFileSystem({ "/a.txt": "aaa", "/b.txt": "bbb" });
+ expect(await fs.readFile("/a.txt")).toBe("aaa");
+ expect(await fs.readFile("/b.txt")).toBe("bbb");
+ });
+
+ it("should throw ENOENT for missing files", async () => {
+ const fs = new InMemoryFileSystem();
+ await expect(fs.readFile("/missing")).rejects.toThrow("ENOENT");
+ });
+
+ it("should check file existence", async () => {
+ const fs = new InMemoryFileSystem({ "/exists.txt": "yes" });
+ expect(await fs.exists("/exists.txt")).toBe(true);
+ expect(await fs.exists("/nope.txt")).toBe(false);
+ });
+
+ it("should check directory existence", async () => {
+ const fs = new InMemoryFileSystem({ "/dir/file.txt": "content" });
+ expect(await fs.exists("/dir")).toBe(true);
+ expect(await fs.exists("/other")).toBe(false);
+ });
+
+ it("should read directory entries", async () => {
+ const fs = new InMemoryFileSystem({
+ "/dir/a.txt": "a",
+ "/dir/b.txt": "b",
+ "/dir/sub/c.txt": "c",
+ });
+ const entries = await fs.readDir("/dir");
+ expect(entries).toEqual(["a.txt", "b.txt", "sub"]);
+ });
+
+ it("should remove files", async () => {
+ const fs = new InMemoryFileSystem({ "/file.txt": "data" });
+ await fs.remove("/file.txt");
+ expect(await fs.exists("/file.txt")).toBe(false);
+ });
+
+ it("should remove directories recursively", async () => {
+ const fs = new InMemoryFileSystem({
+ "/dir/a.txt": "a",
+ "/dir/sub/b.txt": "b",
+ });
+ await fs.remove("/dir");
+ expect(await fs.exists("/dir")).toBe(false);
+ });
+
+ it("should normalize paths", async () => {
+ const fs = new InMemoryFileSystem();
+ await fs.writeFile("/path/to/file.txt", "content");
+ expect(await fs.readFile("/path/to/file.txt")).toBe("content");
+ });
+
+ it("should support writeSync for initialization", () => {
+ const fs = new InMemoryFileSystem();
+ fs.writeSync("/init.txt", "initialized");
+ expect(fs.getAllFiles()).toEqual({ "/init.txt": "initialized" });
+ });
+
+ it("mkdir should be a no-op", async () => {
+ const fs = new InMemoryFileSystem();
+ await fs.mkdir("/dir", { recursive: true }); // should not throw
+ });
+});
+
+describe("WasmConnection", () => {
+ function createMockWasmModule(): WasmModule {
+ return {
+ init: (async () => {}) as any,
+ send_jsonrpc: async (requestJson: string) => {
+ const req = JSON.parse(requestJson);
+ if (req.method === "ping") {
+ return JSON.stringify({
+ jsonrpc: "2.0",
+ id: req.id,
+ result: { message: "pong", timestamp: Date.now() },
+ });
+ }
+ return JSON.stringify({
+ jsonrpc: "2.0",
+ id: req.id,
+ error: { code: -32601, message: "Method not found" },
+ });
+ },
+ };
+ }
+
+ it("should send requests and parse responses", async () => {
+ const conn = new WasmConnection(createMockWasmModule());
+ const result = await conn.sendRequest("ping", {});
+ expect(result).toHaveProperty("message", "pong");
+ });
+
+ it("should throw on error responses", async () => {
+ const conn = new WasmConnection(createMockWasmModule());
+ await expect(conn.sendRequest("unknown.method", {})).rejects.toThrow("Method not found");
+ });
+
+ it("should dispatch events to notification handlers", () => {
+ const conn = new WasmConnection(createMockWasmModule());
+ const received: unknown[] = [];
+ conn.onNotification("test.event", (params) => received.push(params));
+
+ conn._dispatchEvent("test.event", JSON.stringify({ data: "hello" }));
+ expect(received).toEqual([{ data: "hello" }]);
+ });
+
+ it("should dispatch requests to request handlers", async () => {
+ const conn = new WasmConnection(createMockWasmModule());
+ conn.onRequest("tool.call", async (params: any) => {
+ return { result: `handled ${params.toolName}` };
+ });
+
+ const response = await conn._dispatchRequest(
+ "tool.call",
+ JSON.stringify({ toolName: "test" }),
+ );
+ expect(JSON.parse(response)).toEqual({ result: "handled test" });
+ });
+
+ it("should throw after dispose", async () => {
+ const conn = new WasmConnection(createMockWasmModule());
+ conn.dispose();
+ await expect(conn.sendRequest("ping", {})).rejects.toThrow("disposed");
+ });
+});
+
+describe("WasmTransport", () => {
+ it("should require wasmModule option", async () => {
+ const transport = new WasmTransport({});
+ await expect(transport.start()).rejects.toThrow("wasmModule is required");
+ });
+
+ it("should start with a WasmModule instance", async () => {
+ const mockModule: WasmModule = {
+ init: (async () => {}) as any,
+ send_jsonrpc: async () => JSON.stringify({ jsonrpc: "2.0", id: 1, result: {} }),
+ };
+
+ const transport = new WasmTransport({ wasmModule: mockModule });
+ await transport.start();
+ expect(transport.connection).not.toBeNull();
+ });
+
+ it("should start with a WasmModule loader function", async () => {
+ const mockModule: WasmModule = {
+ init: (async () => {}) as any,
+ send_jsonrpc: async () => JSON.stringify({ jsonrpc: "2.0", id: 1, result: {} }),
+ };
+
+ const transport = new WasmTransport({ wasmModule: () => Promise.resolve(mockModule) });
+ await transport.start();
+ expect(transport.connection).not.toBeNull();
+ });
+
+ it("should stop cleanly", async () => {
+ const mockModule: WasmModule = {
+ init: (async () => {}) as any,
+ send_jsonrpc: async () => JSON.stringify({ jsonrpc: "2.0", id: 1, result: {} }),
+ };
+
+ const transport = new WasmTransport({ wasmModule: mockModule });
+ await transport.start();
+ const errors = await transport.stop();
+ expect(errors).toEqual([]);
+ expect(transport.connection).toBeNull();
+ });
+});
+
+describe("Secure defaults", () => {
+ it("should default useLoggedInUser to false", () => {
+ const client = new CopilotClient({ logLevel: "error" });
+ expect((client as any).options.useLoggedInUser).toBe(false);
+ });
+
+ it("should throw when runtime wasm is used with cliPath", () => {
+ expect(
+ () =>
+ new CopilotClient({
+ runtime: "wasm",
+ cliPath: "/bin/cli",
+ }),
+ ).toThrow(/mutually exclusive/);
+ });
+
+ it("should throw when runtime wasm is used with cliUrl", () => {
+ expect(
+ () =>
+ new CopilotClient({
+ runtime: "wasm",
+ cliUrl: "localhost:8080",
+ }),
+ ).toThrow(/mutually exclusive/);
+ });
+});
+
+describe("parseCliUrl", () => {
+ it("should parse port only", () => {
+ expect(parseCliUrl("8080")).toEqual({ host: "localhost", port: 8080 });
+ });
+
+ it("should parse host:port", () => {
+ expect(parseCliUrl("myhost:3000")).toEqual({ host: "myhost", port: 3000 });
+ });
+
+ it("should strip http protocol", () => {
+ expect(parseCliUrl("http://myhost:3000")).toEqual({ host: "myhost", port: 3000 });
+ });
+
+ it("should strip https protocol", () => {
+ expect(parseCliUrl("https://myhost:3000")).toEqual({ host: "myhost", port: 3000 });
+ });
+});
diff --git a/python/README.md b/python/README.md
index 7aa11e1ab..8204fb989 100644
--- a/python/README.md
+++ b/python/README.md
@@ -351,7 +351,7 @@ await session.send({"prompt": "Hello!"})
import os
session = await client.create_session({
- "model": "gpt-4",
+ "model": "gpt-4.1",
"provider": {
"type": "openai",
"base_url": "https://my-api.example.com/v1",
@@ -366,7 +366,7 @@ session = await client.create_session({
import os
session = await client.create_session({
- "model": "gpt-4",
+ "model": "gpt-4.1",
"provider": {
"type": "azure", # Must be "azure" for Azure endpoints, NOT "openai"
"base_url": "https://my-resource.openai.azure.com", # Just the host, no path
diff --git a/python/copilot/client.py b/python/copilot/client.py
index 85b728971..5974b54d4 100644
--- a/python/copilot/client.py
+++ b/python/copilot/client.py
@@ -58,9 +58,9 @@ def _get_bundled_cli_path() -> Optional[str]:
# Determine binary name based on platform
if sys.platform == "win32":
- binary_name = "copilot.exe"
+ binary_name = "copilot-core.exe"
else:
- binary_name = "copilot"
+ binary_name = "copilot-core"
binary_path = bin_dir / binary_name
if binary_path.exists():
@@ -89,7 +89,7 @@ class CopilotClient:
>>> await client.start()
>>>
>>> # Create a session and send a message
- >>> session = await client.create_session({"model": "gpt-4"})
+ >>> session = await client.create_session({"model": "gpt-4.1"})
>>> session.on(lambda event: print(event.type))
>>> await session.send({"prompt": "Hello!"})
>>>
@@ -411,7 +411,7 @@ async def create_session(self, config: Optional[SessionConfig] = None) -> Copilo
>>>
>>> # Session with model and streaming
>>> session = await client.create_session({
- ... "model": "gpt-4",
+ ... "model": "gpt-4.1",
... "streaming": True
... })
"""
diff --git a/python/copilot/generated/session_events.py b/python/copilot/generated/session_events.py
index 84dff82e1..b9ee61b42 100644
--- a/python/copilot/generated/session_events.py
+++ b/python/copilot/generated/session_events.py
@@ -1,7 +1,7 @@
"""
AUTO-GENERATED FILE - DO NOT EDIT
-Generated from: @github/copilot/session-events.schema.json
+Generated from: copilot-core/session-events.schema.json
Generated by: scripts/generate-session-types.ts
Generated at: 2026-02-06T20:38:23.376Z
diff --git a/python/copilot/session.py b/python/copilot/session.py
index fb39e9fc3..db182cad7 100644
--- a/python/copilot/session.py
+++ b/python/copilot/session.py
@@ -123,7 +123,7 @@ async def send(self, options: MessageOptions) -> str:
"mode": options.get("mode"),
},
)
- return response["messageId"]
+ return response.get("messageId", "")
async def send_and_wait(
self, options: MessageOptions, timeout: Optional[float] = None
diff --git a/python/copilot/types.py b/python/copilot/types.py
index 3cecbe64e..0eced35e9 100644
--- a/python/copilot/types.py
+++ b/python/copilot/types.py
@@ -72,7 +72,7 @@ class SelectionAttachment(TypedDict):
class CopilotClientOptions(TypedDict, total=False):
"""Options for creating a CopilotClient"""
- cli_path: str # Path to the Copilot CLI executable (default: "copilot")
+ cli_path: str # Path to the Copilot CLI executable (default: "copilot-core")
# Working directory for the CLI process (default: current process's cwd)
cwd: str
port: int # Port for the CLI server (TCP mode only, default: 0)
diff --git a/python/e2e/testharness/context.py b/python/e2e/testharness/context.py
index 533ee87e7..4fa9b64fb 100644
--- a/python/e2e/testharness/context.py
+++ b/python/e2e/testharness/context.py
@@ -17,14 +17,20 @@
def get_cli_path_for_tests() -> str:
- """Get CLI path for E2E tests. Uses node_modules CLI during development."""
- # Look for CLI in sibling nodejs directory's node_modules
- base_path = Path(__file__).parents[3]
- full_path = base_path / "nodejs" / "node_modules" / "@github" / "copilot" / "index.js"
- if full_path.exists():
- return str(full_path.resolve())
-
- raise RuntimeError("CLI not found for tests. Run 'npm install' in the nodejs directory.")
+ """Get CLI path for E2E tests. Uses copilot-core binary."""
+ # Allow override via environment variable
+ env_path = os.environ.get("COPILOT_CORE_PATH")
+ if env_path and Path(env_path).exists():
+ return str(Path(env_path).resolve())
+
+ # Look for copilot-core on PATH
+ copilot_core = shutil.which("copilot-core")
+ if copilot_core:
+ return copilot_core
+
+ raise RuntimeError(
+ "copilot-core not found. Install it or set COPILOT_CORE_PATH env var."
+ )
CLI_PATH = get_cli_path_for_tests()
diff --git a/python/scripts/build-wheels.mjs b/python/scripts/build-wheels.mjs
index 7d8104083..839b1e005 100644
--- a/python/scripts/build-wheels.mjs
+++ b/python/scripts/build-wheels.mjs
@@ -54,10 +54,10 @@ function getCliVersion() {
}
const packageLock = JSON.parse(readFileSync(packageLockPath, "utf-8"));
- const version = packageLock.packages?.["node_modules/@github/copilot"]?.version;
+ const version = packageLock.packages?.["node_modules/copilot-core"]?.version;
if (!version) {
- throw new Error("Could not find @github/copilot version in package-lock.json");
+ throw new Error("Could not find copilot-core version in package-lock.json");
}
return version;
@@ -83,7 +83,7 @@ async function downloadCliBinary(platform, cliVersion, cacheDir) {
return cachedBinary;
}
- const tarballUrl = `https://registry.npmjs.org/@github/copilot-${platform}/-/copilot-${platform}-${cliVersion}.tgz`;
+ const tarballUrl = `https://registry.npmjs.org/copilot-core-${platform}/-/copilot-core-${platform}-${cliVersion}.tgz`;
console.log(` Downloading from ${tarballUrl}...`);
// Download tarball
@@ -139,7 +139,7 @@ async function downloadCliBinary(platform, cliVersion, cacheDir) {
function getCliLicensePath() {
// Use license from node_modules (requires npm ci in nodejs/ first)
- const licensePath = join(repoRoot, "nodejs", "node_modules", "@github", "copilot", "LICENSE.md");
+ const licensePath = join(repoRoot, "nodejs", "node_modules", "copilot-core", "LICENSE.md");
if (!existsSync(licensePath)) {
throw new Error(
`CLI LICENSE.md not found at ${licensePath}. Run 'npm ci' in nodejs/ first.`
diff --git a/scripts/generate-go-docs.sh b/scripts/generate-go-docs.sh
new file mode 100755
index 000000000..b22bb646d
--- /dev/null
+++ b/scripts/generate-go-docs.sh
@@ -0,0 +1,45 @@
+#!/usr/bin/env bash
+set -euo pipefail
+
+REPO_ROOT="$(cd "$(dirname "$0")/.." && pwd)"
+GO_DIR="$REPO_ROOT/go"
+OUT_DIR="$REPO_ROOT/docs/go/reference"
+
+usage() {
+ echo "Usage: $0 [markdown|serve|all]"
+ echo " markdown Generate Markdown reference docs via gomarkdoc"
+ echo " serve Start local pkgsite HTML doc server"
+ echo " all Generate Markdown then start HTML server"
+ exit 1
+}
+
+generate_markdown() {
+ echo "=== Generating Go Markdown docs ==="
+ mkdir -p "$OUT_DIR"
+
+ go run github.com/princjef/gomarkdoc/cmd/gomarkdoc@latest \
+ --exclude-dirs "$GO_DIR/internal/e2e" \
+ -o "$OUT_DIR/api.md" \
+ "$GO_DIR/..."
+
+ echo "✅ Markdown docs written to $OUT_DIR/api.md"
+}
+
+serve_html() {
+ echo "=== Starting pkgsite HTML doc server ==="
+ echo "Browse to: http://localhost:6060/github.com/github/copilot-sdk/go"
+ echo "Press Ctrl+C to stop."
+
+ go run golang.org/x/pkgsite/cmd/pkgsite@latest \
+ -http=localhost:6060 \
+ "$REPO_ROOT"
+}
+
+MODE="${1:-all}"
+
+case "$MODE" in
+ markdown) generate_markdown ;;
+ serve) serve_html ;;
+ all) generate_markdown; serve_html ;;
+ *) usage ;;
+esac
diff --git a/test/harness/package-lock.json b/test/harness/package-lock.json
deleted file mode 100644
index d1725f037..000000000
--- a/test/harness/package-lock.json
+++ /dev/null
@@ -1,1704 +0,0 @@
-{
- "name": "harness",
- "version": "1.0.0",
- "lockfileVersion": 3,
- "requires": true,
- "packages": {
- "": {
- "name": "harness",
- "version": "1.0.0",
- "license": "ISC",
- "devDependencies": {
- "@github/copilot": "^0.0.403",
- "@types/node": "^25.2.0",
- "openai": "^6.17.0",
- "tsx": "^4.21.0",
- "typescript": "^5.9.3",
- "vitest": "^4.0.18",
- "yaml": "^2.8.2"
- }
- },
- "node_modules/@esbuild/aix-ppc64": {
- "version": "0.27.2",
- "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.2.tgz",
- "integrity": "sha512-GZMB+a0mOMZs4MpDbj8RJp4cw+w1WV5NYD6xzgvzUJ5Ek2jerwfO2eADyI6ExDSUED+1X8aMbegahsJi+8mgpw==",
- "cpu": [
- "ppc64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "aix"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@esbuild/android-arm": {
- "version": "0.27.2",
- "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.2.tgz",
- "integrity": "sha512-DVNI8jlPa7Ujbr1yjU2PfUSRtAUZPG9I1RwW4F4xFB1Imiu2on0ADiI/c3td+KmDtVKNbi+nffGDQMfcIMkwIA==",
- "cpu": [
- "arm"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "android"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@esbuild/android-arm64": {
- "version": "0.27.2",
- "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.2.tgz",
- "integrity": "sha512-pvz8ZZ7ot/RBphf8fv60ljmaoydPU12VuXHImtAs0XhLLw+EXBi2BLe3OYSBslR4rryHvweW5gmkKFwTiFy6KA==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "android"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@esbuild/android-x64": {
- "version": "0.27.2",
- "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.2.tgz",
- "integrity": "sha512-z8Ank4Byh4TJJOh4wpz8g2vDy75zFL0TlZlkUkEwYXuPSgX8yzep596n6mT7905kA9uHZsf/o2OJZubl2l3M7A==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "android"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@esbuild/darwin-arm64": {
- "version": "0.27.2",
- "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.2.tgz",
- "integrity": "sha512-davCD2Zc80nzDVRwXTcQP/28fiJbcOwvdolL0sOiOsbwBa72kegmVU0Wrh1MYrbuCL98Omp5dVhQFWRKR2ZAlg==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "darwin"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@esbuild/darwin-x64": {
- "version": "0.27.2",
- "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.2.tgz",
- "integrity": "sha512-ZxtijOmlQCBWGwbVmwOF/UCzuGIbUkqB1faQRf5akQmxRJ1ujusWsb3CVfk/9iZKr2L5SMU5wPBi1UWbvL+VQA==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "darwin"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@esbuild/freebsd-arm64": {
- "version": "0.27.2",
- "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.2.tgz",
- "integrity": "sha512-lS/9CN+rgqQ9czogxlMcBMGd+l8Q3Nj1MFQwBZJyoEKI50XGxwuzznYdwcav6lpOGv5BqaZXqvBSiB/kJ5op+g==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "freebsd"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@esbuild/freebsd-x64": {
- "version": "0.27.2",
- "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.2.tgz",
- "integrity": "sha512-tAfqtNYb4YgPnJlEFu4c212HYjQWSO/w/h/lQaBK7RbwGIkBOuNKQI9tqWzx7Wtp7bTPaGC6MJvWI608P3wXYA==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "freebsd"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@esbuild/linux-arm": {
- "version": "0.27.2",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.2.tgz",
- "integrity": "sha512-vWfq4GaIMP9AIe4yj1ZUW18RDhx6EPQKjwe7n8BbIecFtCQG4CfHGaHuh7fdfq+y3LIA2vGS/o9ZBGVxIDi9hw==",
- "cpu": [
- "arm"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@esbuild/linux-arm64": {
- "version": "0.27.2",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.2.tgz",
- "integrity": "sha512-hYxN8pr66NsCCiRFkHUAsxylNOcAQaxSSkHMMjcpx0si13t1LHFphxJZUiGwojB1a/Hd5OiPIqDdXONia6bhTw==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@esbuild/linux-ia32": {
- "version": "0.27.2",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.2.tgz",
- "integrity": "sha512-MJt5BRRSScPDwG2hLelYhAAKh9imjHK5+NE/tvnRLbIqUWa+0E9N4WNMjmp/kXXPHZGqPLxggwVhz7QP8CTR8w==",
- "cpu": [
- "ia32"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@esbuild/linux-loong64": {
- "version": "0.27.2",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.2.tgz",
- "integrity": "sha512-lugyF1atnAT463aO6KPshVCJK5NgRnU4yb3FUumyVz+cGvZbontBgzeGFO1nF+dPueHD367a2ZXe1NtUkAjOtg==",
- "cpu": [
- "loong64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@esbuild/linux-mips64el": {
- "version": "0.27.2",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.2.tgz",
- "integrity": "sha512-nlP2I6ArEBewvJ2gjrrkESEZkB5mIoaTswuqNFRv/WYd+ATtUpe9Y09RnJvgvdag7he0OWgEZWhviS1OTOKixw==",
- "cpu": [
- "mips64el"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@esbuild/linux-ppc64": {
- "version": "0.27.2",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.2.tgz",
- "integrity": "sha512-C92gnpey7tUQONqg1n6dKVbx3vphKtTHJaNG2Ok9lGwbZil6DrfyecMsp9CrmXGQJmZ7iiVXvvZH6Ml5hL6XdQ==",
- "cpu": [
- "ppc64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@esbuild/linux-riscv64": {
- "version": "0.27.2",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.2.tgz",
- "integrity": "sha512-B5BOmojNtUyN8AXlK0QJyvjEZkWwy/FKvakkTDCziX95AowLZKR6aCDhG7LeF7uMCXEJqwa8Bejz5LTPYm8AvA==",
- "cpu": [
- "riscv64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@esbuild/linux-s390x": {
- "version": "0.27.2",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.2.tgz",
- "integrity": "sha512-p4bm9+wsPwup5Z8f4EpfN63qNagQ47Ua2znaqGH6bqLlmJ4bx97Y9JdqxgGZ6Y8xVTixUnEkoKSHcpRlDnNr5w==",
- "cpu": [
- "s390x"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@esbuild/linux-x64": {
- "version": "0.27.2",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.2.tgz",
- "integrity": "sha512-uwp2Tip5aPmH+NRUwTcfLb+W32WXjpFejTIOWZFw/v7/KnpCDKG66u4DLcurQpiYTiYwQ9B7KOeMJvLCu/OvbA==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@esbuild/netbsd-arm64": {
- "version": "0.27.2",
- "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.2.tgz",
- "integrity": "sha512-Kj6DiBlwXrPsCRDeRvGAUb/LNrBASrfqAIok+xB0LxK8CHqxZ037viF13ugfsIpePH93mX7xfJp97cyDuTZ3cw==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "netbsd"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@esbuild/netbsd-x64": {
- "version": "0.27.2",
- "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.2.tgz",
- "integrity": "sha512-HwGDZ0VLVBY3Y+Nw0JexZy9o/nUAWq9MlV7cahpaXKW6TOzfVno3y3/M8Ga8u8Yr7GldLOov27xiCnqRZf0tCA==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "netbsd"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@esbuild/openbsd-arm64": {
- "version": "0.27.2",
- "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.2.tgz",
- "integrity": "sha512-DNIHH2BPQ5551A7oSHD0CKbwIA/Ox7+78/AWkbS5QoRzaqlev2uFayfSxq68EkonB+IKjiuxBFoV8ESJy8bOHA==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "openbsd"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@esbuild/openbsd-x64": {
- "version": "0.27.2",
- "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.2.tgz",
- "integrity": "sha512-/it7w9Nb7+0KFIzjalNJVR5bOzA9Vay+yIPLVHfIQYG/j+j9VTH84aNB8ExGKPU4AzfaEvN9/V4HV+F+vo8OEg==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "openbsd"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@esbuild/openharmony-arm64": {
- "version": "0.27.2",
- "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.2.tgz",
- "integrity": "sha512-LRBbCmiU51IXfeXk59csuX/aSaToeG7w48nMwA6049Y4J4+VbWALAuXcs+qcD04rHDuSCSRKdmY63sruDS5qag==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "openharmony"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@esbuild/sunos-x64": {
- "version": "0.27.2",
- "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.2.tgz",
- "integrity": "sha512-kMtx1yqJHTmqaqHPAzKCAkDaKsffmXkPHThSfRwZGyuqyIeBvf08KSsYXl+abf5HDAPMJIPnbBfXvP2ZC2TfHg==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "sunos"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@esbuild/win32-arm64": {
- "version": "0.27.2",
- "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.2.tgz",
- "integrity": "sha512-Yaf78O/B3Kkh+nKABUF++bvJv5Ijoy9AN1ww904rOXZFLWVc5OLOfL56W+C8F9xn5JQZa3UX6m+IktJnIb1Jjg==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "win32"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@esbuild/win32-ia32": {
- "version": "0.27.2",
- "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.2.tgz",
- "integrity": "sha512-Iuws0kxo4yusk7sw70Xa2E2imZU5HoixzxfGCdxwBdhiDgt9vX9VUCBhqcwY7/uh//78A1hMkkROMJq9l27oLQ==",
- "cpu": [
- "ia32"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "win32"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@esbuild/win32-x64": {
- "version": "0.27.2",
- "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.2.tgz",
- "integrity": "sha512-sRdU18mcKf7F+YgheI/zGf5alZatMUTKj/jNS6l744f9u3WFu4v7twcUI9vu4mknF4Y9aDlblIie0IM+5xxaqQ==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "win32"
- ],
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@github/copilot": {
- "version": "0.0.403",
- "resolved": "https://registry.npmjs.org/@github/copilot/-/copilot-0.0.403.tgz",
- "integrity": "sha512-v5jUdtGJReLmE1rmff/LZf+50nzmYQYAaSRNtVNr9g0j0GkCd/noQExe31i1+PudvWU0ZJjltR0B8pUfDRdA9Q==",
- "dev": true,
- "license": "SEE LICENSE IN LICENSE.md",
- "bin": {
- "copilot": "npm-loader.js"
- },
- "optionalDependencies": {
- "@github/copilot-darwin-arm64": "0.0.403",
- "@github/copilot-darwin-x64": "0.0.403",
- "@github/copilot-linux-arm64": "0.0.403",
- "@github/copilot-linux-x64": "0.0.403",
- "@github/copilot-win32-arm64": "0.0.403",
- "@github/copilot-win32-x64": "0.0.403"
- }
- },
- "node_modules/@github/copilot-darwin-arm64": {
- "version": "0.0.403",
- "resolved": "https://registry.npmjs.org/@github/copilot-darwin-arm64/-/copilot-darwin-arm64-0.0.403.tgz",
- "integrity": "sha512-dOw8IleA0d1soHnbr/6wc6vZiYWNTKMgfTe/NET1nCfMzyKDt/0F0I7PT5y+DLujJknTla/ZeEmmBUmliTW4Cg==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "license": "SEE LICENSE IN LICENSE.md",
- "optional": true,
- "os": [
- "darwin"
- ],
- "bin": {
- "copilot-darwin-arm64": "copilot"
- }
- },
- "node_modules/@github/copilot-darwin-x64": {
- "version": "0.0.403",
- "resolved": "https://registry.npmjs.org/@github/copilot-darwin-x64/-/copilot-darwin-x64-0.0.403.tgz",
- "integrity": "sha512-aK2jSNWgY8eiZ+TmrvGhssMCPDTKArc0ip6Ul5OaslpytKks8hyXoRbxGD0N9sKioSUSbvKUf+1AqavbDpJO+w==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "license": "SEE LICENSE IN LICENSE.md",
- "optional": true,
- "os": [
- "darwin"
- ],
- "bin": {
- "copilot-darwin-x64": "copilot"
- }
- },
- "node_modules/@github/copilot-linux-arm64": {
- "version": "0.0.403",
- "resolved": "https://registry.npmjs.org/@github/copilot-linux-arm64/-/copilot-linux-arm64-0.0.403.tgz",
- "integrity": "sha512-KhoR2iR70O6vCkzf0h8/K+p82qAgOvMTgAPm9bVEHvbdGFR7Py9qL5v03bMbPxsA45oNaZAkzDhfTAqWhIAZsQ==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "license": "SEE LICENSE IN LICENSE.md",
- "optional": true,
- "os": [
- "linux"
- ],
- "bin": {
- "copilot-linux-arm64": "copilot"
- }
- },
- "node_modules/@github/copilot-linux-x64": {
- "version": "0.0.403",
- "resolved": "https://registry.npmjs.org/@github/copilot-linux-x64/-/copilot-linux-x64-0.0.403.tgz",
- "integrity": "sha512-eoswUc9vo4TB+/9PgFJLVtzI4dPjkpJXdCsAioVuoqPdNxHxlIHFe9HaVcqMRZxUNY1YHEBZozy+IpUEGjgdfQ==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "license": "SEE LICENSE IN LICENSE.md",
- "optional": true,
- "os": [
- "linux"
- ],
- "bin": {
- "copilot-linux-x64": "copilot"
- }
- },
- "node_modules/@github/copilot-win32-arm64": {
- "version": "0.0.403",
- "resolved": "https://registry.npmjs.org/@github/copilot-win32-arm64/-/copilot-win32-arm64-0.0.403.tgz",
- "integrity": "sha512-djWjzCsp2xPNafMyOZ/ivU328/WvWhdroGie/DugiJBTgQL2SP0quWW1fhTlDwE81a3g9CxfJonaRgOpFTJTcg==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "license": "SEE LICENSE IN LICENSE.md",
- "optional": true,
- "os": [
- "win32"
- ],
- "bin": {
- "copilot-win32-arm64": "copilot.exe"
- }
- },
- "node_modules/@github/copilot-win32-x64": {
- "version": "0.0.403",
- "resolved": "https://registry.npmjs.org/@github/copilot-win32-x64/-/copilot-win32-x64-0.0.403.tgz",
- "integrity": "sha512-lju8cHy2E6Ux7R7tWyLZeksYC2MVZu9i9ocjiBX/qfG2/pNJs7S5OlkwKJ0BSXSbZEHQYq7iHfEWp201bVfk9A==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "license": "SEE LICENSE IN LICENSE.md",
- "optional": true,
- "os": [
- "win32"
- ],
- "bin": {
- "copilot-win32-x64": "copilot.exe"
- }
- },
- "node_modules/@jridgewell/sourcemap-codec": {
- "version": "1.5.5",
- "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
- "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/@rollup/rollup-android-arm-eabi": {
- "version": "4.57.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.57.1.tgz",
- "integrity": "sha512-A6ehUVSiSaaliTxai040ZpZ2zTevHYbvu/lDoeAteHI8QnaosIzm4qwtezfRg1jOYaUmnzLX1AOD6Z+UJjtifg==",
- "cpu": [
- "arm"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "android"
- ]
- },
- "node_modules/@rollup/rollup-android-arm64": {
- "version": "4.57.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.57.1.tgz",
- "integrity": "sha512-dQaAddCY9YgkFHZcFNS/606Exo8vcLHwArFZ7vxXq4rigo2bb494/xKMMwRRQW6ug7Js6yXmBZhSBRuBvCCQ3w==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "android"
- ]
- },
- "node_modules/@rollup/rollup-darwin-arm64": {
- "version": "4.57.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.57.1.tgz",
- "integrity": "sha512-crNPrwJOrRxagUYeMn/DZwqN88SDmwaJ8Cvi/TN1HnWBU7GwknckyosC2gd0IqYRsHDEnXf328o9/HC6OkPgOg==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "darwin"
- ]
- },
- "node_modules/@rollup/rollup-darwin-x64": {
- "version": "4.57.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.57.1.tgz",
- "integrity": "sha512-Ji8g8ChVbKrhFtig5QBV7iMaJrGtpHelkB3lsaKzadFBe58gmjfGXAOfI5FV0lYMH8wiqsxKQ1C9B0YTRXVy4w==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "darwin"
- ]
- },
- "node_modules/@rollup/rollup-freebsd-arm64": {
- "version": "4.57.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.57.1.tgz",
- "integrity": "sha512-R+/WwhsjmwodAcz65guCGFRkMb4gKWTcIeLy60JJQbXrJ97BOXHxnkPFrP+YwFlaS0m+uWJTstrUA9o+UchFug==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "freebsd"
- ]
- },
- "node_modules/@rollup/rollup-freebsd-x64": {
- "version": "4.57.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.57.1.tgz",
- "integrity": "sha512-IEQTCHeiTOnAUC3IDQdzRAGj3jOAYNr9kBguI7MQAAZK3caezRrg0GxAb6Hchg4lxdZEI5Oq3iov/w/hnFWY9Q==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "freebsd"
- ]
- },
- "node_modules/@rollup/rollup-linux-arm-gnueabihf": {
- "version": "4.57.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.57.1.tgz",
- "integrity": "sha512-F8sWbhZ7tyuEfsmOxwc2giKDQzN3+kuBLPwwZGyVkLlKGdV1nvnNwYD0fKQ8+XS6hp9nY7B+ZeK01EBUE7aHaw==",
- "cpu": [
- "arm"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ]
- },
- "node_modules/@rollup/rollup-linux-arm-musleabihf": {
- "version": "4.57.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.57.1.tgz",
- "integrity": "sha512-rGfNUfn0GIeXtBP1wL5MnzSj98+PZe/AXaGBCRmT0ts80lU5CATYGxXukeTX39XBKsxzFpEeK+Mrp9faXOlmrw==",
- "cpu": [
- "arm"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ]
- },
- "node_modules/@rollup/rollup-linux-arm64-gnu": {
- "version": "4.57.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.57.1.tgz",
- "integrity": "sha512-MMtej3YHWeg/0klK2Qodf3yrNzz6CGjo2UntLvk2RSPlhzgLvYEB3frRvbEF2wRKh1Z2fDIg9KRPe1fawv7C+g==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ]
- },
- "node_modules/@rollup/rollup-linux-arm64-musl": {
- "version": "4.57.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.57.1.tgz",
- "integrity": "sha512-1a/qhaaOXhqXGpMFMET9VqwZakkljWHLmZOX48R0I/YLbhdxr1m4gtG1Hq7++VhVUmf+L3sTAf9op4JlhQ5u1Q==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ]
- },
- "node_modules/@rollup/rollup-linux-loong64-gnu": {
- "version": "4.57.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.57.1.tgz",
- "integrity": "sha512-QWO6RQTZ/cqYtJMtxhkRkidoNGXc7ERPbZN7dVW5SdURuLeVU7lwKMpo18XdcmpWYd0qsP1bwKPf7DNSUinhvA==",
- "cpu": [
- "loong64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ]
- },
- "node_modules/@rollup/rollup-linux-loong64-musl": {
- "version": "4.57.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.57.1.tgz",
- "integrity": "sha512-xpObYIf+8gprgWaPP32xiN5RVTi/s5FCR+XMXSKmhfoJjrpRAjCuuqQXyxUa/eJTdAE6eJ+KDKaoEqjZQxh3Gw==",
- "cpu": [
- "loong64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ]
- },
- "node_modules/@rollup/rollup-linux-ppc64-gnu": {
- "version": "4.57.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.57.1.tgz",
- "integrity": "sha512-4BrCgrpZo4hvzMDKRqEaW1zeecScDCR+2nZ86ATLhAoJ5FQ+lbHVD3ttKe74/c7tNT9c6F2viwB3ufwp01Oh2w==",
- "cpu": [
- "ppc64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ]
- },
- "node_modules/@rollup/rollup-linux-ppc64-musl": {
- "version": "4.57.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.57.1.tgz",
- "integrity": "sha512-NOlUuzesGauESAyEYFSe3QTUguL+lvrN1HtwEEsU2rOwdUDeTMJdO5dUYl/2hKf9jWydJrO9OL/XSSf65R5+Xw==",
- "cpu": [
- "ppc64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ]
- },
- "node_modules/@rollup/rollup-linux-riscv64-gnu": {
- "version": "4.57.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.57.1.tgz",
- "integrity": "sha512-ptA88htVp0AwUUqhVghwDIKlvJMD/fmL/wrQj99PRHFRAG6Z5nbWoWG4o81Nt9FT+IuqUQi+L31ZKAFeJ5Is+A==",
- "cpu": [
- "riscv64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ]
- },
- "node_modules/@rollup/rollup-linux-riscv64-musl": {
- "version": "4.57.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.57.1.tgz",
- "integrity": "sha512-S51t7aMMTNdmAMPpBg7OOsTdn4tySRQvklmL3RpDRyknk87+Sp3xaumlatU+ppQ+5raY7sSTcC2beGgvhENfuw==",
- "cpu": [
- "riscv64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ]
- },
- "node_modules/@rollup/rollup-linux-s390x-gnu": {
- "version": "4.57.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.57.1.tgz",
- "integrity": "sha512-Bl00OFnVFkL82FHbEqy3k5CUCKH6OEJL54KCyx2oqsmZnFTR8IoNqBF+mjQVcRCT5sB6yOvK8A37LNm/kPJiZg==",
- "cpu": [
- "s390x"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ]
- },
- "node_modules/@rollup/rollup-linux-x64-gnu": {
- "version": "4.57.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.57.1.tgz",
- "integrity": "sha512-ABca4ceT4N+Tv/GtotnWAeXZUZuM/9AQyCyKYyKnpk4yoA7QIAuBt6Hkgpw8kActYlew2mvckXkvx0FfoInnLg==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ]
- },
- "node_modules/@rollup/rollup-linux-x64-musl": {
- "version": "4.57.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.57.1.tgz",
- "integrity": "sha512-HFps0JeGtuOR2convgRRkHCekD7j+gdAuXM+/i6kGzQtFhlCtQkpwtNzkNj6QhCDp7DRJ7+qC/1Vg2jt5iSOFw==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ]
- },
- "node_modules/@rollup/rollup-openbsd-x64": {
- "version": "4.57.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.57.1.tgz",
- "integrity": "sha512-H+hXEv9gdVQuDTgnqD+SQffoWoc0Of59AStSzTEj/feWTBAnSfSD3+Dql1ZruJQxmykT/JVY0dE8Ka7z0DH1hw==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "openbsd"
- ]
- },
- "node_modules/@rollup/rollup-openharmony-arm64": {
- "version": "4.57.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.57.1.tgz",
- "integrity": "sha512-4wYoDpNg6o/oPximyc/NG+mYUejZrCU2q+2w6YZqrAs2UcNUChIZXjtafAiiZSUc7On8v5NyNj34Kzj/Ltk6dQ==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "openharmony"
- ]
- },
- "node_modules/@rollup/rollup-win32-arm64-msvc": {
- "version": "4.57.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.57.1.tgz",
- "integrity": "sha512-O54mtsV/6LW3P8qdTcamQmuC990HDfR71lo44oZMZlXU4tzLrbvTii87Ni9opq60ds0YzuAlEr/GNwuNluZyMQ==",
- "cpu": [
- "arm64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "win32"
- ]
- },
- "node_modules/@rollup/rollup-win32-ia32-msvc": {
- "version": "4.57.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.57.1.tgz",
- "integrity": "sha512-P3dLS+IerxCT/7D2q2FYcRdWRl22dNbrbBEtxdWhXrfIMPP9lQhb5h4Du04mdl5Woq05jVCDPCMF7Ub0NAjIew==",
- "cpu": [
- "ia32"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "win32"
- ]
- },
- "node_modules/@rollup/rollup-win32-x64-gnu": {
- "version": "4.57.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.57.1.tgz",
- "integrity": "sha512-VMBH2eOOaKGtIJYleXsi2B8CPVADrh+TyNxJ4mWPnKfLB/DBUmzW+5m1xUrcwWoMfSLagIRpjUFeW5CO5hyciQ==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "win32"
- ]
- },
- "node_modules/@rollup/rollup-win32-x64-msvc": {
- "version": "4.57.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.57.1.tgz",
- "integrity": "sha512-mxRFDdHIWRxg3UfIIAwCm6NzvxG0jDX/wBN6KsQFTvKFqqg9vTrWUE68qEjHt19A5wwx5X5aUi2zuZT7YR0jrA==",
- "cpu": [
- "x64"
- ],
- "dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "win32"
- ]
- },
- "node_modules/@standard-schema/spec": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz",
- "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/@types/chai": {
- "version": "5.2.3",
- "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz",
- "integrity": "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@types/deep-eql": "*",
- "assertion-error": "^2.0.1"
- }
- },
- "node_modules/@types/deep-eql": {
- "version": "4.0.2",
- "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz",
- "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/@types/estree": {
- "version": "1.0.8",
- "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz",
- "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/@types/node": {
- "version": "25.2.0",
- "resolved": "https://registry.npmjs.org/@types/node/-/node-25.2.0.tgz",
- "integrity": "sha512-DZ8VwRFUNzuqJ5khrvwMXHmvPe+zGayJhr2CDNiKB1WBE1ST8Djl00D0IC4vvNmHMdj6DlbYRIaFE7WHjlDl5w==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "undici-types": "~7.16.0"
- }
- },
- "node_modules/@vitest/expect": {
- "version": "4.0.18",
- "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.0.18.tgz",
- "integrity": "sha512-8sCWUyckXXYvx4opfzVY03EOiYVxyNrHS5QxX3DAIi5dpJAAkyJezHCP77VMX4HKA2LDT/Jpfo8i2r5BE3GnQQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@standard-schema/spec": "^1.0.0",
- "@types/chai": "^5.2.2",
- "@vitest/spy": "4.0.18",
- "@vitest/utils": "4.0.18",
- "chai": "^6.2.1",
- "tinyrainbow": "^3.0.3"
- },
- "funding": {
- "url": "https://opencollective.com/vitest"
- }
- },
- "node_modules/@vitest/mocker": {
- "version": "4.0.18",
- "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.0.18.tgz",
- "integrity": "sha512-HhVd0MDnzzsgevnOWCBj5Otnzobjy5wLBe4EdeeFGv8luMsGcYqDuFRMcttKWZA5vVO8RFjexVovXvAM4JoJDQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@vitest/spy": "4.0.18",
- "estree-walker": "^3.0.3",
- "magic-string": "^0.30.21"
- },
- "funding": {
- "url": "https://opencollective.com/vitest"
- },
- "peerDependencies": {
- "msw": "^2.4.9",
- "vite": "^6.0.0 || ^7.0.0-0"
- },
- "peerDependenciesMeta": {
- "msw": {
- "optional": true
- },
- "vite": {
- "optional": true
- }
- }
- },
- "node_modules/@vitest/pretty-format": {
- "version": "4.0.18",
- "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.0.18.tgz",
- "integrity": "sha512-P24GK3GulZWC5tz87ux0m8OADrQIUVDPIjjj65vBXYG17ZeU3qD7r+MNZ1RNv4l8CGU2vtTRqixrOi9fYk/yKw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "tinyrainbow": "^3.0.3"
- },
- "funding": {
- "url": "https://opencollective.com/vitest"
- }
- },
- "node_modules/@vitest/runner": {
- "version": "4.0.18",
- "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.0.18.tgz",
- "integrity": "sha512-rpk9y12PGa22Jg6g5M3UVVnTS7+zycIGk9ZNGN+m6tZHKQb7jrP7/77WfZy13Y/EUDd52NDsLRQhYKtv7XfPQw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@vitest/utils": "4.0.18",
- "pathe": "^2.0.3"
- },
- "funding": {
- "url": "https://opencollective.com/vitest"
- }
- },
- "node_modules/@vitest/snapshot": {
- "version": "4.0.18",
- "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.0.18.tgz",
- "integrity": "sha512-PCiV0rcl7jKQjbgYqjtakly6T1uwv/5BQ9SwBLekVg/EaYeQFPiXcgrC2Y7vDMA8dM1SUEAEV82kgSQIlXNMvA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@vitest/pretty-format": "4.0.18",
- "magic-string": "^0.30.21",
- "pathe": "^2.0.3"
- },
- "funding": {
- "url": "https://opencollective.com/vitest"
- }
- },
- "node_modules/@vitest/spy": {
- "version": "4.0.18",
- "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.0.18.tgz",
- "integrity": "sha512-cbQt3PTSD7P2OARdVW3qWER5EGq7PHlvE+QfzSC0lbwO+xnt7+XH06ZzFjFRgzUX//JmpxrCu92VdwvEPlWSNw==",
- "dev": true,
- "license": "MIT",
- "funding": {
- "url": "https://opencollective.com/vitest"
- }
- },
- "node_modules/@vitest/utils": {
- "version": "4.0.18",
- "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.0.18.tgz",
- "integrity": "sha512-msMRKLMVLWygpK3u2Hybgi4MNjcYJvwTb0Ru09+fOyCXIgT5raYP041DRRdiJiI3k/2U6SEbAETB3YtBrUkCFA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@vitest/pretty-format": "4.0.18",
- "tinyrainbow": "^3.0.3"
- },
- "funding": {
- "url": "https://opencollective.com/vitest"
- }
- },
- "node_modules/assertion-error": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz",
- "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/chai": {
- "version": "6.2.2",
- "resolved": "https://registry.npmjs.org/chai/-/chai-6.2.2.tgz",
- "integrity": "sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/es-module-lexer": {
- "version": "1.7.0",
- "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz",
- "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/esbuild": {
- "version": "0.27.2",
- "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.2.tgz",
- "integrity": "sha512-HyNQImnsOC7X9PMNaCIeAm4ISCQXs5a5YasTXVliKv4uuBo1dKrG0A+uQS8M5eXjVMnLg3WgXaKvprHlFJQffw==",
- "dev": true,
- "hasInstallScript": true,
- "license": "MIT",
- "bin": {
- "esbuild": "bin/esbuild"
- },
- "engines": {
- "node": ">=18"
- },
- "optionalDependencies": {
- "@esbuild/aix-ppc64": "0.27.2",
- "@esbuild/android-arm": "0.27.2",
- "@esbuild/android-arm64": "0.27.2",
- "@esbuild/android-x64": "0.27.2",
- "@esbuild/darwin-arm64": "0.27.2",
- "@esbuild/darwin-x64": "0.27.2",
- "@esbuild/freebsd-arm64": "0.27.2",
- "@esbuild/freebsd-x64": "0.27.2",
- "@esbuild/linux-arm": "0.27.2",
- "@esbuild/linux-arm64": "0.27.2",
- "@esbuild/linux-ia32": "0.27.2",
- "@esbuild/linux-loong64": "0.27.2",
- "@esbuild/linux-mips64el": "0.27.2",
- "@esbuild/linux-ppc64": "0.27.2",
- "@esbuild/linux-riscv64": "0.27.2",
- "@esbuild/linux-s390x": "0.27.2",
- "@esbuild/linux-x64": "0.27.2",
- "@esbuild/netbsd-arm64": "0.27.2",
- "@esbuild/netbsd-x64": "0.27.2",
- "@esbuild/openbsd-arm64": "0.27.2",
- "@esbuild/openbsd-x64": "0.27.2",
- "@esbuild/openharmony-arm64": "0.27.2",
- "@esbuild/sunos-x64": "0.27.2",
- "@esbuild/win32-arm64": "0.27.2",
- "@esbuild/win32-ia32": "0.27.2",
- "@esbuild/win32-x64": "0.27.2"
- }
- },
- "node_modules/estree-walker": {
- "version": "3.0.3",
- "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz",
- "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@types/estree": "^1.0.0"
- }
- },
- "node_modules/expect-type": {
- "version": "1.3.0",
- "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.3.0.tgz",
- "integrity": "sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==",
- "dev": true,
- "license": "Apache-2.0",
- "engines": {
- "node": ">=12.0.0"
- }
- },
- "node_modules/fdir": {
- "version": "6.5.0",
- "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz",
- "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=12.0.0"
- },
- "peerDependencies": {
- "picomatch": "^3 || ^4"
- },
- "peerDependenciesMeta": {
- "picomatch": {
- "optional": true
- }
- }
- },
- "node_modules/fsevents": {
- "version": "2.3.3",
- "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
- "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
- "dev": true,
- "hasInstallScript": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "darwin"
- ],
- "engines": {
- "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
- }
- },
- "node_modules/get-tsconfig": {
- "version": "4.13.0",
- "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.13.0.tgz",
- "integrity": "sha512-1VKTZJCwBrvbd+Wn3AOgQP/2Av+TfTCOlE4AcRJE72W1ksZXbAx8PPBR9RzgTeSPzlPMHrbANMH3LbltH73wxQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "resolve-pkg-maps": "^1.0.0"
- },
- "funding": {
- "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1"
- }
- },
- "node_modules/magic-string": {
- "version": "0.30.21",
- "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz",
- "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@jridgewell/sourcemap-codec": "^1.5.5"
- }
- },
- "node_modules/nanoid": {
- "version": "3.3.11",
- "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
- "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==",
- "dev": true,
- "funding": [
- {
- "type": "github",
- "url": "https://github.com/sponsors/ai"
- }
- ],
- "license": "MIT",
- "bin": {
- "nanoid": "bin/nanoid.cjs"
- },
- "engines": {
- "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
- }
- },
- "node_modules/obug": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/obug/-/obug-2.1.1.tgz",
- "integrity": "sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==",
- "dev": true,
- "funding": [
- "https://github.com/sponsors/sxzz",
- "https://opencollective.com/debug"
- ],
- "license": "MIT"
- },
- "node_modules/openai": {
- "version": "6.17.0",
- "resolved": "https://registry.npmjs.org/openai/-/openai-6.17.0.tgz",
- "integrity": "sha512-NHRpPEUPzAvFOAFs9+9pC6+HCw/iWsYsKCMPXH5Kw7BpMxqd8g/A07/1o7Gx2TWtCnzevVRyKMRFqyiHyAlqcA==",
- "dev": true,
- "license": "Apache-2.0",
- "bin": {
- "openai": "bin/cli"
- },
- "peerDependencies": {
- "ws": "^8.18.0",
- "zod": "^3.25 || ^4.0"
- },
- "peerDependenciesMeta": {
- "ws": {
- "optional": true
- },
- "zod": {
- "optional": true
- }
- }
- },
- "node_modules/pathe": {
- "version": "2.0.3",
- "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz",
- "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/picocolors": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
- "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
- "dev": true,
- "license": "ISC"
- },
- "node_modules/picomatch": {
- "version": "4.0.3",
- "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
- "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=12"
- },
- "funding": {
- "url": "https://github.com/sponsors/jonschlinkert"
- }
- },
- "node_modules/postcss": {
- "version": "8.5.6",
- "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz",
- "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==",
- "dev": true,
- "funding": [
- {
- "type": "opencollective",
- "url": "https://opencollective.com/postcss/"
- },
- {
- "type": "tidelift",
- "url": "https://tidelift.com/funding/github/npm/postcss"
- },
- {
- "type": "github",
- "url": "https://github.com/sponsors/ai"
- }
- ],
- "license": "MIT",
- "dependencies": {
- "nanoid": "^3.3.11",
- "picocolors": "^1.1.1",
- "source-map-js": "^1.2.1"
- },
- "engines": {
- "node": "^10 || ^12 || >=14"
- }
- },
- "node_modules/resolve-pkg-maps": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz",
- "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==",
- "dev": true,
- "license": "MIT",
- "funding": {
- "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1"
- }
- },
- "node_modules/rollup": {
- "version": "4.57.1",
- "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.57.1.tgz",
- "integrity": "sha512-oQL6lgK3e2QZeQ7gcgIkS2YZPg5slw37hYufJ3edKlfQSGGm8ICoxswK15ntSzF/a8+h7ekRy7k7oWc3BQ7y8A==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@types/estree": "1.0.8"
- },
- "bin": {
- "rollup": "dist/bin/rollup"
- },
- "engines": {
- "node": ">=18.0.0",
- "npm": ">=8.0.0"
- },
- "optionalDependencies": {
- "@rollup/rollup-android-arm-eabi": "4.57.1",
- "@rollup/rollup-android-arm64": "4.57.1",
- "@rollup/rollup-darwin-arm64": "4.57.1",
- "@rollup/rollup-darwin-x64": "4.57.1",
- "@rollup/rollup-freebsd-arm64": "4.57.1",
- "@rollup/rollup-freebsd-x64": "4.57.1",
- "@rollup/rollup-linux-arm-gnueabihf": "4.57.1",
- "@rollup/rollup-linux-arm-musleabihf": "4.57.1",
- "@rollup/rollup-linux-arm64-gnu": "4.57.1",
- "@rollup/rollup-linux-arm64-musl": "4.57.1",
- "@rollup/rollup-linux-loong64-gnu": "4.57.1",
- "@rollup/rollup-linux-loong64-musl": "4.57.1",
- "@rollup/rollup-linux-ppc64-gnu": "4.57.1",
- "@rollup/rollup-linux-ppc64-musl": "4.57.1",
- "@rollup/rollup-linux-riscv64-gnu": "4.57.1",
- "@rollup/rollup-linux-riscv64-musl": "4.57.1",
- "@rollup/rollup-linux-s390x-gnu": "4.57.1",
- "@rollup/rollup-linux-x64-gnu": "4.57.1",
- "@rollup/rollup-linux-x64-musl": "4.57.1",
- "@rollup/rollup-openbsd-x64": "4.57.1",
- "@rollup/rollup-openharmony-arm64": "4.57.1",
- "@rollup/rollup-win32-arm64-msvc": "4.57.1",
- "@rollup/rollup-win32-ia32-msvc": "4.57.1",
- "@rollup/rollup-win32-x64-gnu": "4.57.1",
- "@rollup/rollup-win32-x64-msvc": "4.57.1",
- "fsevents": "~2.3.2"
- }
- },
- "node_modules/siginfo": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz",
- "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==",
- "dev": true,
- "license": "ISC"
- },
- "node_modules/source-map-js": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
- "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
- "dev": true,
- "license": "BSD-3-Clause",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/stackback": {
- "version": "0.0.2",
- "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz",
- "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/std-env": {
- "version": "3.10.0",
- "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.10.0.tgz",
- "integrity": "sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/tinybench": {
- "version": "2.9.0",
- "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz",
- "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/tinyexec": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.0.2.tgz",
- "integrity": "sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/tinyglobby": {
- "version": "0.2.15",
- "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz",
- "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "fdir": "^6.5.0",
- "picomatch": "^4.0.3"
- },
- "engines": {
- "node": ">=12.0.0"
- },
- "funding": {
- "url": "https://github.com/sponsors/SuperchupuDev"
- }
- },
- "node_modules/tinyrainbow": {
- "version": "3.0.3",
- "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-3.0.3.tgz",
- "integrity": "sha512-PSkbLUoxOFRzJYjjxHJt9xro7D+iilgMX/C9lawzVuYiIdcihh9DXmVibBe8lmcFrRi/VzlPjBxbN7rH24q8/Q==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=14.0.0"
- }
- },
- "node_modules/tsx": {
- "version": "4.21.0",
- "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.21.0.tgz",
- "integrity": "sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "esbuild": "~0.27.0",
- "get-tsconfig": "^4.7.5"
- },
- "bin": {
- "tsx": "dist/cli.mjs"
- },
- "engines": {
- "node": ">=18.0.0"
- },
- "optionalDependencies": {
- "fsevents": "~2.3.3"
- }
- },
- "node_modules/typescript": {
- "version": "5.9.3",
- "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
- "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
- "dev": true,
- "license": "Apache-2.0",
- "bin": {
- "tsc": "bin/tsc",
- "tsserver": "bin/tsserver"
- },
- "engines": {
- "node": ">=14.17"
- }
- },
- "node_modules/undici-types": {
- "version": "7.16.0",
- "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz",
- "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/vite": {
- "version": "7.3.1",
- "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.1.tgz",
- "integrity": "sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "esbuild": "^0.27.0",
- "fdir": "^6.5.0",
- "picomatch": "^4.0.3",
- "postcss": "^8.5.6",
- "rollup": "^4.43.0",
- "tinyglobby": "^0.2.15"
- },
- "bin": {
- "vite": "bin/vite.js"
- },
- "engines": {
- "node": "^20.19.0 || >=22.12.0"
- },
- "funding": {
- "url": "https://github.com/vitejs/vite?sponsor=1"
- },
- "optionalDependencies": {
- "fsevents": "~2.3.3"
- },
- "peerDependencies": {
- "@types/node": "^20.19.0 || >=22.12.0",
- "jiti": ">=1.21.0",
- "less": "^4.0.0",
- "lightningcss": "^1.21.0",
- "sass": "^1.70.0",
- "sass-embedded": "^1.70.0",
- "stylus": ">=0.54.8",
- "sugarss": "^5.0.0",
- "terser": "^5.16.0",
- "tsx": "^4.8.1",
- "yaml": "^2.4.2"
- },
- "peerDependenciesMeta": {
- "@types/node": {
- "optional": true
- },
- "jiti": {
- "optional": true
- },
- "less": {
- "optional": true
- },
- "lightningcss": {
- "optional": true
- },
- "sass": {
- "optional": true
- },
- "sass-embedded": {
- "optional": true
- },
- "stylus": {
- "optional": true
- },
- "sugarss": {
- "optional": true
- },
- "terser": {
- "optional": true
- },
- "tsx": {
- "optional": true
- },
- "yaml": {
- "optional": true
- }
- }
- },
- "node_modules/vitest": {
- "version": "4.0.18",
- "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.0.18.tgz",
- "integrity": "sha512-hOQuK7h0FGKgBAas7v0mSAsnvrIgAvWmRFjmzpJ7SwFHH3g1k2u37JtYwOwmEKhK6ZO3v9ggDBBm0La1LCK4uQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@vitest/expect": "4.0.18",
- "@vitest/mocker": "4.0.18",
- "@vitest/pretty-format": "4.0.18",
- "@vitest/runner": "4.0.18",
- "@vitest/snapshot": "4.0.18",
- "@vitest/spy": "4.0.18",
- "@vitest/utils": "4.0.18",
- "es-module-lexer": "^1.7.0",
- "expect-type": "^1.2.2",
- "magic-string": "^0.30.21",
- "obug": "^2.1.1",
- "pathe": "^2.0.3",
- "picomatch": "^4.0.3",
- "std-env": "^3.10.0",
- "tinybench": "^2.9.0",
- "tinyexec": "^1.0.2",
- "tinyglobby": "^0.2.15",
- "tinyrainbow": "^3.0.3",
- "vite": "^6.0.0 || ^7.0.0",
- "why-is-node-running": "^2.3.0"
- },
- "bin": {
- "vitest": "vitest.mjs"
- },
- "engines": {
- "node": "^20.0.0 || ^22.0.0 || >=24.0.0"
- },
- "funding": {
- "url": "https://opencollective.com/vitest"
- },
- "peerDependencies": {
- "@edge-runtime/vm": "*",
- "@opentelemetry/api": "^1.9.0",
- "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0",
- "@vitest/browser-playwright": "4.0.18",
- "@vitest/browser-preview": "4.0.18",
- "@vitest/browser-webdriverio": "4.0.18",
- "@vitest/ui": "4.0.18",
- "happy-dom": "*",
- "jsdom": "*"
- },
- "peerDependenciesMeta": {
- "@edge-runtime/vm": {
- "optional": true
- },
- "@opentelemetry/api": {
- "optional": true
- },
- "@types/node": {
- "optional": true
- },
- "@vitest/browser-playwright": {
- "optional": true
- },
- "@vitest/browser-preview": {
- "optional": true
- },
- "@vitest/browser-webdriverio": {
- "optional": true
- },
- "@vitest/ui": {
- "optional": true
- },
- "happy-dom": {
- "optional": true
- },
- "jsdom": {
- "optional": true
- }
- }
- },
- "node_modules/why-is-node-running": {
- "version": "2.3.0",
- "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz",
- "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "siginfo": "^2.0.0",
- "stackback": "0.0.2"
- },
- "bin": {
- "why-is-node-running": "cli.js"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/yaml": {
- "version": "2.8.2",
- "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.2.tgz",
- "integrity": "sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A==",
- "dev": true,
- "license": "ISC",
- "bin": {
- "yaml": "bin.mjs"
- },
- "engines": {
- "node": ">= 14.6"
- },
- "funding": {
- "url": "https://github.com/sponsors/eemeli"
- }
- }
- }
-}
diff --git a/test/harness/package.json b/test/harness/package.json
index 7a1a37ad5..e85522b7e 100644
--- a/test/harness/package.json
+++ b/test/harness/package.json
@@ -11,7 +11,6 @@
"test": "vitest run"
},
"devDependencies": {
- "@github/copilot": "^0.0.403",
"@types/node": "^25.2.0",
"openai": "^6.17.0",
"tsx": "^4.21.0",
diff --git a/test/harness/replayingCapiProxy.ts b/test/harness/replayingCapiProxy.ts
index 1602ef2ad..fb79c75e7 100644
--- a/test/harness/replayingCapiProxy.ts
+++ b/test/harness/replayingCapiProxy.ts
@@ -2,7 +2,6 @@
* Copyright (c) Microsoft Corporation. All rights reserved.
*--------------------------------------------------------------------------------------------*/
-import type { retrieveAvailableModels } from "@github/copilot/sdk";
import { existsSync } from "fs";
import { mkdir, readFile, writeFile } from "fs/promises";
import type {
@@ -890,7 +889,14 @@ function convertToStreamingResponseChunks(
}
function createGetModelsResponse(modelIds: string[]): {
- data: Awaited>;
+ data: Array<{
+ id: string;
+ name: string;
+ capabilities: {
+ supports: { vision: boolean };
+ limits: { max_context_window_tokens: number };
+ };
+ }>;
} {
// Obviously the following might not match any given model. We could track the original responses from /models,
// but that risks invalidating the caches too frequently and making this unmaintainable. If this approximation
diff --git a/test/harness/util.ts b/test/harness/util.ts
index b696e06c5..020e07658 100644
--- a/test/harness/util.ts
+++ b/test/harness/util.ts
@@ -2,8 +2,6 @@
* Copyright (c) Microsoft Corporation. All rights reserved.
*--------------------------------------------------------------------------------------------*/
-import type { SessionOptions } from "@github/copilot/sdk";
-
export function iife(fn: () => Promise): Promise {
return fn();
}
@@ -12,7 +10,11 @@ export function sleep(ms: number): Promise {
return new Promise((resolve) => setTimeout(resolve, ms));
}
-type ShellConfigType = NonNullable;
+type ShellConfigType = {
+ shellToolName: string;
+ readShellToolName: string;
+ writeShellToolName: string;
+};
/**
* Shell configuration for platform-specific tool names.