Coding Conventions & Style Patterns
Every coding convention and style pattern enforced or observed in the copilot-sdk polyglot repository (TypeScript Python Go C#). Each rule is cited with file:line references drawn from the actual codebase.
1. Naming Conventions
1.1 TypeScript (Node.js)
| Element | Convention | Example | Citation |
|---|---|---|---|
| Classes | PascalCase | CopilotClient, CopilotSession | client.ts:134, session.ts:59 |
| Interfaces / Types | PascalCase | CopilotClientOptions, SessionConfig | types.ts:16, types.ts:121 |
| Type aliases (unions) | PascalCase | SessionEvent, ToolResult | types.ts:11, types.ts:130 |
| Methods | camelCase | createSession, sendAndWait | session.ts:120, session.ts:154 |
| Private fields | camelCase (no prefix) | this.connection, this.sessions | client.ts:136 |
| Internal methods | _ prefix | _dispatchEvent, _handleBroadcastEvent | session.ts:293, session.ts:325 |
| Constants | UPPER_SNAKE_CASE | MIN_PROTOCOL_VERSION | client.ts:56 |
| Files | kebab-case | client.ts, session-events.ts | directory listing |
| Unused variables | _ prefix | _error, _rpc | eslint.config.js:23-26 |
| Generic type params | Single uppercase / PascalCase | <T>, <K extends SessionEventType> | session.ts:230 |
1.2 Python
| Element | Convention | Example | Citation |
|---|---|---|---|
| Classes | PascalCase | CopilotClient, CopilotSession | client.py:78, session.py:46 |
| Methods | snake_case | create_session, send_and_wait | session.py:118, session.py:153 |
| Private methods | Leading _ | _dispatch_event, _register_tools | session.py:248, session.py:274 |
| Private instance vars | Leading _ | self._client, self._event_handlers | session.py:87-88 |
| Constants | UPPER_SNAKE_CASE | MIN_PROTOCOL_VERSION | client.py:55 |
| Type aliases | PascalCase | ConnectionState, LogLevel | types.py:24 |
| TypedDicts | PascalCase | CopilotClientOptions, MessageOptions | types.py:73 |
| Dataclasses | PascalCase | ToolResult, ToolInvocation | types.py:122 |
| Files | snake_case | client.py, session_events.py | directory listing |
| Module docstrings | Present on all modules | Triple-quoted at file top | client.py:1-12 |
1.3 Go
| Element | Convention | Example | Citation |
|---|---|---|---|
| Exported types | PascalCase | Client, Session | client.go:73, session.go:51 |
| Exported methods | PascalCase | Send, SendAndWait | session.go:113, session.go:160 |
| Unexported helpers | camelCase | newSession, registerTools | session.go:80, session.go:265 |
| Struct fields (exported) | PascalCase | SessionID, RPC | session.go:53 |
| Struct fields (unexported) | camelCase | workspacePath, client | session.go:55-59 |
| Constants | PascalCase prefixed | StateDisconnected, AssistantMessage | types.go:12-15 |
| Enum-style types | PascalCase suffix | ConnectionState, SessionEventType | types.go:9 |
| Files | snake_case | client.go, session.go | directory listing |
| Mutex fields | Short suffix | sessionsMux, toolHandlersM | client.go:82, session.go:60-64 |
| Pointer helper fns | Single-word PascalCase | Bool, String, Float64 | types.go:69-83 |
1.4 C# (.NET)
| Element | Convention | Example | Citation |
|---|---|---|---|
| Classes | PascalCase | CopilotClient, CopilotSession | Client.cs:55, Session.cs:53 |
| Public methods | PascalCase + Async | SendAsync, SendAndWaitAsync | Session.cs:140 |
| Public properties | PascalCase | SessionId, WorkspacePath | Session.cs:75-89 |
| Private fields | _camelCase | _toolHandlers, _rpc | Session.cs:62-69 |
| Constants | PascalCase | MinProtocolVersion | Client.cs:60 |
| Enums | PascalCase members | ConnectionState.Disconnected | Types.cs:19-33 |
| Internal methods | PascalCase | DispatchEvent, RegisterTools | Session.cs:275 |
| Records | PascalCase | SendMessageRequest | Session.cs:754-765 |
| Namespace | Dotted PascalCase | GitHub.Copilot.SDK | Session.cs:12 |
| Files | PascalCase | Client.cs, Session.cs | directory listing |
flowchart LR
subgraph TS["TypeScript"]
direction TB
TS1["Classes: PascalCase"]
TS2["Methods: camelCase"]
TS3["Private: _prefix"]
TS4["Constants: UPPER_SNAKE"]
TS5["Files: kebab-case"]
TS1 --- TS2 --- TS3 --- TS4 --- TS5
end
subgraph PY["Python"]
direction TB
PY1["Classes: PascalCase"]
PY2["Methods: snake_case"]
PY3["Private: _prefix"]
PY4["Constants: UPPER_SNAKE"]
PY5["Files: snake_case"]
PY1 --- PY2 --- PY3 --- PY4 --- PY5
end
subgraph GO["Go"]
direction TB
GO1["Types: PascalCase"]
GO2["Methods: PascalCase"]
GO3["Private: camelCase"]
GO4["Constants: PascalCase"]
GO5["Files: snake_case"]
GO1 --- GO2 --- GO3 --- GO4 --- GO5
end
subgraph CS["C#"]
direction TB
CS1["Classes: PascalCase"]
CS2["Methods: PascalCase+Async"]
CS3["Private: _camelCase"]
CS4["Constants: PascalCase"]
CS5["Files: PascalCase"]
CS1 --- CS2 --- CS3 --- CS4 --- CS5
end
2. File Organization
2.1 TypeScript Internal Structure
flowchart TD
A["1. Copyright header #40;3 lines#41;"] --> B["2. Module JSDoc #40;@module tag#41;"]
B --> C["3. Imports #40;node: → third-party → local#41;"]
C --> D["4. Module-level constants"]
D --> E["5. Helper functions #40;private#41;"]
E --> F["6. Main export class #40;single per file#41;"]
F --> G["7. Type exports / re-exports"]
Citation: nodejs/src/client.ts:1-56, nodejs/src/session.ts:1-32
2.2 Python Internal Structure
flowchart TD
A["1. Module docstring"] --> B["2. Standard library imports"]
B --> C["3. Third-party imports"]
C --> D["4. Local imports"]
D --> E["5. Module-level constants"]
E --> F["6. Private helper functions"]
F --> G["7. Main class #40;single per file#41;"]
Citation: python/copilot/client.py:1-55, python/copilot/session.py:1-44
2.3 Go Internal Structure
flowchart TD
A["1. Package doc comment"] --> B["2. package declaration"]
B --> C["3. import #40;...#41; block"]
C --> D["4. Unexported types #40;helpers#41;"]
D --> E["5. Exported type + doc comment"]
E --> F["6. Constructor function"]
F --> G["7. Exported methods"]
G --> H["8. Unexported methods"]
Citation: go/session.go:1-89, go/client.go:1-52
2.4 C# Internal Structure
flowchart TD
A["1. Copyright header"] --> B["2. Using directives"]
B --> C["3. Namespace declaration #40;file-scoped#41;"]
C --> D["4. XML doc summary"]
D --> E["5. Class declaration #40;sealed partial#41;"]
E --> F["6. Private fields → Properties → Constructor"]
F --> G["7. Public methods → Internal → Private"]
G --> H["8. Nested records/types #40;DTOs#41;"]
Citation: dotnet/src/Session.cs:1-100, dotnet/src/Session.cs:754-811
3. Error Handling Conventions
3.1 TypeScript
- Throwing:
throw new Error("descriptive message")for domain errors - Catch-and-rethrow: Catch specific types (
ConnectionError,ResponseError), rethrow unknown, swallow connection-lost - Unused caught errors: Named
_errorper ESLint rule - Timeout errors: Created inline with template literals
- Handler errors: Silently caught in event dispatchers
Citations: session.ts:177-179, session.ts:303-306, session.ts:378-388
3.2 Python
- Raising:
raise ValueError(...)for validation,raise RuntimeError(...)for system failures - Catch pattern: Broad
except Exceptionwith pylint disable, narrow catches for RPC - Handler isolation: Exceptions caught and printed to stderr
- Deprecation:
warnings.warn("...", DeprecationWarning, stacklevel=2)
Citations: client.py:142, session.py:269-272, session.py:680-686
3.3 Go
- Error wrapping:
fmt.Errorf("failed to send message: %w", err)— always uses%w - Panic for programmer errors:
panic("CLIUrl is mutually exclusive...") - Recover in dispatchers:
defer func() { if r := recover(); r != nil { ... } }() - Multi-return: All fallible public methods return
(value, error) - Nil checks: Pointer fields checked before dereference
Citations: session.go:123, client.go:141, session.go:454-462
3.4 C#
- Exception types:
ArgumentException,InvalidOperationException,TimeoutException,ObjectDisposedException - Null-coalescing throw:
?? throw new InvalidOperationException(...) - Catch-and-swallow: Catches
IOException/ObjectDisposedExceptionfor connection loss - Pattern matching:
switch (evt) { case AssistantMessageEvent: ... }
Citations: Client.cs:124, Session.cs:508, Session.cs:447-454
flowchart LR
subgraph TS["TypeScript"]
direction TB
TS1["throw new Error#40;#41;"]
TS2["catch + rethrow"]
TS3["Swallow in dispatchers"]
TS4["_error for unused"]
TS1 --- TS2 --- TS3 --- TS4
end
subgraph PY["Python"]
direction TB
PY1["raise ValueError/RuntimeError"]
PY2["except Exception #40;broad#41;"]
PY3["Print to stderr"]
PY4["warnings.warn#40;#41;"]
PY1 --- PY2 --- PY3 --- PY4
end
subgraph GO["Go"]
direction TB
GO1["fmt.Errorf#40;%w#41;"]
GO2["#40;value, error#41; return"]
GO3["defer recover#40;#41;"]
GO4["panic#40;#41; for config bugs"]
GO1 --- GO2 --- GO3 --- GO4
end
subgraph CS["C#"]
direction TB
CS1["Typed exceptions"]
CS2["?? throw pattern"]
CS3["Catch IOException"]
CS4["Pattern matching"]
CS1 --- CS2 --- CS3 --- CS4
end
4. Documentation Style
4.1 TypeScript — JSDoc
- Module-level:
/** @module client */after copyright header - Class-level: Multi-line
/** ... */with@exampleblocks - Method-level:
@param,@returns,@throws,@example - Internal marker:
@internal - Deprecation:
@deprecated Use {@link disconnect} instead. - Cross-references:
{@link CopilotClient.createSession}
4.2 Python — Docstrings (Google style)
- Module-level: Triple-quoted at file top
- Class-level: Multi-line with
Attributes:andExample:sections - Method-level: Sections: summary,
Args:,Returns:,Raises:,Example:,Note: - Deprecation:
.. deprecated::reST directive - Cross-references: Sphinx-style
:meth:/:class:
4.3 Go — Go Doc Comments
- Package-level:
// Package copilot provides a Go SDK for ... - Type-level:
// Session represents a single conversation ... - Method-level:
// Send sends a message ...with indented code examples - Cross-references: Go doc link syntax
[Client.CreateSession] - Deprecated:
// Deprecated: Use [Session.Disconnect] instead.
4.4 C# — XML Documentation
<summary>— Required on all public/internal members<remarks>— Multi-<para>blocks for extended descriptions<example><code>— Inline code examples<param>,<returns>,<exception cref="..."><see cref="...">— Cross-references<value>— Property documentation
5. Type System Usage
5.1 TypeScript
- Strict mode:
"strict": truein tsconfig - Discriminated unions:
SessionEventdiscriminated ontypefield - Generics:
Tool<TArgs>,ToolHandler<TArgs> typevsinterface:interfacefor object shapes,typefor unions- Null handling:
| undefined+ strict null checks Record<string, unknown>preferred overanyunknownpreferred overanyfor function args
5.2 Python
- Type annotations: Full hints with Python 3.11+ syntax (
str | None) from __future__ import annotationsfor forward referencesTypedDictwithtotal=Falsefor optional fields@dataclassfor value objectsLiteralfor string union typesCallable+Awaitablefor sync/async flexibility
5.3 Go
- Pointer for optional:
*bool,*string - Pointer helpers:
Bool(v bool) *bool - JSON struct tags:
json:"fieldName,omitempty" - Type aliases for enums:
type ConnectionState string anyfor untyped JSON,json.RawMessagefor deferred parsing
5.4 C#
- Nullable reference types:
string?(project-wide enabled) sealed partial classfor source generation- Records for internal DTOs
- Enums with JSON mapping:
[JsonStringEnumMemberName] IAsyncDisposablefor cleanup- Source-generated JSON:
[JsonSerializable]for AOT volatilefor thread-safe handler fields
6. Async Patterns
6.1 TypeScript — async/await + Promises
- All I/O methods are
asyncreturningPromise<T> sendAndWait:Promise.race([idlePromise, timeoutPromise])- Timeout:
new Promise<never>((_, reject) => { setTimeout(...) }) - Cleanup:
finallyblock clears timeout and unsubscribes - Subscribe-before-send: Avoids race conditions
6.2 Python — asyncio
- All I/O methods are
async def send_and_wait:asyncio.Event()+asyncio.wait_for()- Fire-and-forget:
asyncio.ensure_future(...) - Dual handlers:
inspect.isawaitable(result)for sync/async - Thread safety:
threading.Lock()for handler sets
6.3 Go — Goroutines + Channels
- Public methods accept
context.Contextfirst SendAndWait:context.WithTimeout, buffered channels,select- Fire-and-forget:
go s.executeToolAndRespond(...) - Synchronization:
sync.Mutex/sync.RWMutex/sync.atomic - Default timeout: 60 seconds
6.4 C# — Task-based Async
- All async methods:
MethodNameAsync, returnTask<T> - CancellationToken as last parameter with
default SendAndWaitAsync:TaskCompletionSource<T>- Disposal:
IAsyncDisposable+Interlocked.Exchange - Fire-and-forget:
async voidfor broadcast events - Concurrent collections:
ConcurrentDictionary
flowchart LR
subgraph TS["TypeScript"]
direction TB
TS1["async/await"]
TS2["Promise.race#40;#41;"]
TS3["setTimeout"]
TS4["finally cleanup"]
TS1 --- TS2 --- TS3 --- TS4
end
subgraph PY["Python"]
direction TB
PY1["async def / await"]
PY2["asyncio.Event#40;#41;"]
PY3["asyncio.wait_for#40;#41;"]
PY4["threading.Lock#40;#41;"]
PY1 --- PY2 --- PY3 --- PY4
end
subgraph GO["Go"]
direction TB
GO1["goroutines"]
GO2["channels + select"]
GO3["context.WithTimeout"]
GO4["sync.Mutex"]
GO1 --- GO2 --- GO3 --- GO4
end
subgraph CS["C#"]
direction TB
CS1["async Task#60;T#62;"]
CS2["TaskCompletionSource"]
CS3["CancellationToken"]
CS4["IAsyncDisposable"]
CS1 --- CS2 --- CS3 --- CS4
end
7. Import/Export Patterns
7.1 TypeScript
- Barrel file:
nodejs/src/index.tsre-exports all public API - Named exports only — no default exports
export typefor type-only re-exports- Import groups: (1)
node:, (2)vscode-jsonrpc, (3) local./generated/, (4) local./types.js .jsextensions on all local imports (ESM)import typewhen only types needed
7.2 Python
__init__.pybarrel with explicit__all__- Import groups: (1)
from __future__, (2) stdlib, (3) third-party, (4) relative - Relative imports:
from .module import Name TYPE_CHECKINGguard for circular import avoidance
7.3 Go
- Single import block grouped: stdlib, blank, third-party, blank, internal
- Package = directory:
package copilot/package rpc - Internal packages:
go/internal/jsonrpc2,go/internal/embeddedcli - Exported via capitalization — no explicit export list
7.4 C#
- File-scoped namespaces:
namespace GitHub.Copilot.SDK; - Using directives: System, Microsoft, third-party, project
sealed partial class— sealed + partialinternalvisibility for SDK internals
8. Code Generation Conventions
8.1 Generated File Markers
| Language | Marker | Source | Citation |
|---|---|---|---|
| TS | /** AUTO-GENERATED FILE - DO NOT EDIT */ | api.schema.json | generated/rpc.ts:1-4 |
| Python | """ AUTO-GENERATED FILE - DO NOT EDIT """ | Same | generated/rpc.py:1-4 |
| Go | // AUTO-GENERATED FILE - DO NOT EDIT | Same | rpc/generated_rpc.go:1-2 |
| C# | // AUTO-GENERATED FILE - DO NOT EDIT | Same | Generated/Rpc.cs:5-6 |
8.2 Generated File Locations
| Language | RPC Types | Session Events |
|---|---|---|
| TS | nodejs/src/generated/rpc.ts | nodejs/src/generated/session-events.ts |
| Python | python/copilot/generated/rpc.py | python/copilot/generated/session_events.py |
| Go | go/rpc/generated_rpc.go | go/generated_session_events.go |
| C# | dotnet/src/Generated/Rpc.cs | dotnet/src/Generated/SessionEvents.cs |
8.3 Generated Code Exclusions
- ESLint:
**/generated/**in ignores - Ruff:
"generated"excluded - golangci-lint:
generatedpath excluded - Go formatting:
find . -name "*.go" -not -path "*/generated/*"
8.4 Generation Tooling
flowchart TD
A["JSON Schemas#60;br/#62;api.schema.json + session-events.schema.json"] --> B["generate-session-types.ts #40;quicktype#41;"]
B --> C["rpc.ts"]
B --> D["rpc.py"]
B --> E["generated_rpc.go"]
B --> F["Rpc.cs"]
9. Linting & Formatting Rules
9.1 TypeScript (ESLint + Prettier)
ESLint
- Parser:
@typescript-eslint/parser, ECMAScript 2022, ESM no-unused-vars: error (ignores_-prefixed)no-explicit-any: warnno-console: off- Ignores:
dist/,node_modules/,*.config.ts,**/generated/**
Prettier Settings
| Setting | Value |
|---|---|
| Semicolons | yes |
| Trailing commas | ES5 style |
| Quotes | double |
| Print width | 100 |
| Tab width | 4 spaces |
| Arrow parens | always |
| Line endings | LF only |
9.2 Python (Ruff)
| Setting | Value |
|---|---|
| Line length | 100 |
| Target version | py311 |
| Selected rules | E, W, F, I, UP |
| Format | double quotes, spaces |
| Excludes | generated/ |
9.3 Go (golangci-lint + gofmt)
| Setting | Value |
|---|---|
| Timeout | 5 minutes |
| Enabled | govet, ineffassign, staticcheck |
| Disabled | errcheck (explicit) |
| Excluded | generated/ |
9.4 C# (dotnet format)
- Formatting:
dotnet format - CI:
dotnet format --verify-no-changes - AOT:
<IsAotCompatible>true</IsAotCompatible> - Docs:
<GenerateDocumentationFile>true</GenerateDocumentationFile>
10. Cross-Language Consistency Rules
10.1 API Shape Parity
| Concept | TS | Python | Go | C# |
|---|---|---|---|---|
| Client | CopilotClient | CopilotClient | Client | CopilotClient |
| Session | CopilotSession | CopilotSession | Session | CopilotSession |
| Create | createSession() | create_session() | CreateSession() | CreateSessionAsync() |
| Send | send() | send() | Send() | SendAsync() |
| Send+Wait | sendAndWait() | send_and_wait() | SendAndWait() | SendAndWaitAsync() |
| Subscribe | on() | on() | On() | On() |
| Messages | getMessages() | get_messages() | GetMessages() | GetMessagesAsync() |
| Disconnect | disconnect() | disconnect() | Disconnect() | DisposeAsync() |
| CLI URL | cliUrl | cli_url | CLIUrl | CliUrl |
10.2 Shared Protocol Constants
All four SDKs define the same minimum protocol version (value: 2), ensuring wire-level compatibility.
10.3 Generated Types from Shared Schemas
All SDKs generate RPC types from the same JSON schemas:
api.schema.json→ rpc types in all 4 languagessession-events.schema.json→ event types in all 4 languages
10.4 Consistent Behavioral Patterns
- Event dispatch: All SDKs intercept
external_tool.requestedandpermission.requestedbroadcast events - Handler isolation: Errors don't propagate from handlers to dispatcher
- Unsubscribe pattern:
on()returns cleanup (TS:() => void, Python:Callable, Go:func(), C#:IDisposable) - Resource cleanup: All SDKs clear handlers on disconnect
- Deprecation:
destroy()deprecated fordisconnect()in TS, Python, Go
10.5 CLI URL Naming
flowchart LR
A["Node: cliUrl"] --- B["Python: cli_url"] --- C["Go: CLIUrl"] --- D[".NET: CliUrl"]
10.6 Monorepo Workflow
| Command | Action |
|---|---|
just format | Runs formatters for all four languages |
just lint | Runs linters for all four languages |
just test | Runs test suites for all four languages |
just install | Installs dependencies for all four languages |
Summary of Key Rules
- Never edit generated files — look for
AUTO-GENERATED FILE - DO NOT EDIT - Use language-idiomatic naming — camelCase (TS), snake_case (Py), PascalCase (Go/C#)
- Prefix unused variables with
_in TypeScript (ESLint enforced) - 100-character line limit in TS (Prettier) and Python (Ruff)
- Double quotes everywhere (TS Prettier, Python Ruff)
- 4-space indentation in TypeScript; spaces everywhere
- All public methods must be documented with language-standard format
- Error handling must not crash dispatchers — catch/recover in all languages
- API parity across all four languages — same names, same behavior, shared schemas
errcheckis disabled in Go — explicit opt-out