Skip to content

Update to match runtime changes#737

Merged
SteveSandersonMS merged 10 commits intomainfrom
stevesa/update-for-extensions
Mar 9, 2026
Merged

Update to match runtime changes#737
SteveSandersonMS merged 10 commits intomainfrom
stevesa/update-for-extensions

Conversation

@SteveSandersonMS
Copy link
Contributor

Includes:

  • New RPC APIs (session log, reasoning effort after model switch)
  • New events (system notification, new 'alreadyInUse' flag on sessions)
  • Infrastructure for extensions

Note: E2E tests will fail until we get a corresponding runtime update. They pass locally.

@SteveSandersonMS SteveSandersonMS requested a review from a team as a code owner March 9, 2026 12:45
Copilot AI review requested due to automatic review settings March 9, 2026 12:45
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Updates the multi-language Copilot SDKs to align with recent runtime/protocol changes, including new session-scoped RPCs, new event shapes, and an initial Node.js extension-joining API with accompanying documentation.

Changes:

  • Add session.log RPC support across Node.js/Python/Go/.NET SDKs, plus convenience session.log(...)/Log(...) APIs and E2E coverage.
  • Extend protocol models/events for reasoningEffort on model switch, system.notification event, and alreadyInUse on session start/resume.
  • Introduce Node.js extension infrastructure via @github/copilot-sdk/extension joinSession() and publish extension docs in the npm package.

Reviewed changes

Copilot reviewed 17 out of 23 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
scripts/codegen/csharp.ts Refactors C# RPC emission to support top-level session RPC methods on SessionRpc.
python/copilot/session.py Adds Session.log() wrapper calling the new session.log RPC.
python/copilot/generated/rpc.py Adds SessionLog* RPC types, Level enum, and ReasoningEffort support.
python/copilot/generated/session_events.py Adds system.notification + alreadyInUse and notification-kind metadata types.
python/e2e/test_session.py Adds E2E coverage for session.log event emission.
nodejs/src/generated/rpc.ts Adds session.log RPC and reasoningEffort param on model switch.
nodejs/src/generated/session-events.ts Adds alreadyInUse and system.notification event typing.
nodejs/src/session.ts Adds CopilotSession.log() convenience method.
nodejs/src/extension.ts Replaces prior extension entry behavior with joinSession() for CLI child-process extensions.
nodejs/src/client.ts Moves bundled-CLI helpers (no functional change).
nodejs/test/e2e/session.test.ts Adds E2E test for session logging events (uses vi.waitFor).
nodejs/package.json Ships docs/**/* in the published package.
nodejs/docs/extensions.md New extension architecture/usage documentation.
nodejs/docs/examples.md New, detailed extension examples (tools, hooks, events).
nodejs/docs/agent-author.md New agent-oriented extension authoring guide.
go/rpc/generated_rpc.go Adds SessionLog* RPC types, Level, and ReasoningEffort; wires request building.
go/session.go Adds Session.Log() convenience API and LogOptions.
go/generated_session_events.go Adds alreadyInUse, system.notification, and kind metadata structures.
go/internal/e2e/session_test.go Adds TestSessionLog E2E coverage with event polling helper.
dotnet/src/Generated/Rpc.cs Adds session.log RPC types + updates model switch signature for reasoningEffort.
dotnet/src/Session.cs Adds Session.LogAsync(...) convenience method.
dotnet/src/Generated/SessionEvents.cs Adds system.notification and alreadyInUse fields/types.
dotnet/test/SessionTests.cs Adds E2E coverage for LogAsync event emission.
Comments suppressed due to low confidence (2)

nodejs/docs/examples.md:293

  • The call to exec with filePath embedded inside a shell command string enables command injection if filePath contains characters like quotes and shell operators. If an attacker can control input.toolArgs.path for the edit tool (for example, via malicious tool arguments), they can cause this extension to run arbitrary shell commands when invoking the linter. Use a process-spawning API that does not invoke a shell and passes the path as a separate argument, or rigorously validate/escape the path before including it in any shell command string.
                    exec(`npx eslint "${filePath}"`, (err, stdout) => {

nodejs/docs/examples.md:615

  • In openInEditor, interpolating filePath directly into a shell command passed to exec creates a command injection risk if filePath contains quotes or shell metacharacters. An attacker controlling the path argument of the create/edit tools can supply a value that breaks out of the quoted path and appends arbitrary commands, which the extension will execute on Windows whenever it opens the editor. Prefer a non-shell API (or pass the path as a separate argument) and/or strictly validate and escape filePath before using it in any shell command.
    if (isWindows) exec(`code "${filePath}"`, () => {});

Comment on lines +413 to +430
var events = new List<SessionEvent>();
session.On(evt => events.Add(evt));

await session.LogAsync("Info message");
await session.LogAsync("Warning message", level: SessionLogRequestLevel.Warning);
await session.LogAsync("Error message", level: SessionLogRequestLevel.Error);
await session.LogAsync("Ephemeral message", ephemeral: true);

// Poll until all 4 notification events arrive
await WaitForAsync(() =>
{
var notifications = events.Where(e =>
e is SessionInfoEvent info && info.Data.InfoType == "notification" ||
e is SessionWarningEvent warn && warn.Data.WarningType == "notification" ||
e is SessionErrorEvent err && err.Data.ErrorType == "notification"
).ToList();
return notifications.Count >= 4;
}, timeout: TimeSpan.FromSeconds(10));
Copy link

Copilot AI Mar 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

session.On(evt => events.Add(evt)) appends to a List<SessionEvent> from the JSON-RPC event dispatch path while WaitForAsync concurrently enumerates events via LINQ. This can race and throw InvalidOperationException (collection modified) or produce flaky counts. Consider using a thread-safe collection (e.g., ConcurrentQueue<SessionEvent> / ConcurrentBag<SessionEvent>) or guarding both writes and reads with a lock (snapshot to array before filtering).

Copilot uses AI. Check for mistakes.
Comment on lines 5 to +39
import { CopilotClient } from "./client.js";
import type { CopilotSession } from "./session.js";
import type { ResumeSessionConfig } from "./types.js";

export const extension = new CopilotClient({ isChildProcess: true });
/**
* Joins the current foreground session.
*
* @param config - Configuration to add to the session
* @returns A promise that resolves with the joined session
*
* @example
* ```typescript
* import { approveAll } from "@github/copilot-sdk";
* import { joinSession } from "@github/copilot-sdk/extension";
*
* const session = await joinSession({
* onPermissionRequest: approveAll,
* tools: [myTool],
* });
* ```
*/
export async function joinSession(config: ResumeSessionConfig): Promise<CopilotSession> {
const sessionId = process.env.SESSION_ID;
if (!sessionId) {
throw new Error(
"joinSession() is intended for extensions running as child processes of the Copilot CLI."
);
}

const client = new CopilotClient({ isChildProcess: true });
return client.resumeSession(sessionId, {
...config,
disableResume: config.disableResume ?? true,
});
}
Copy link

Copilot AI Mar 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This changes the public API of the @github/copilot-sdk/extension entrypoint: the previous extension export is removed and replaced with joinSession(). If any consumers rely on import { extension } from "@github/copilot-sdk/extension", this is a breaking change. Consider keeping extension as a deprecated export (or providing a backwards-compatible alias) while introducing joinSession, or ensure the package versioning/release notes reflect the breaking change.

Copilot uses AI. Check for mistakes.
const filePath = input.toolArgs?.path;
if (filePath) {
// Open the file in VS Code
exec(`code "${filePath}"`, () => {});
Copy link

Copilot AI Mar 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using exec with filePath interpolated into a shell command allows command injection if filePath contains shell metacharacters or quotes. An attacker who can influence input.toolArgs.path (for example via the create/edit tools) can craft a value that closes the quoted path and appends arbitrary commands, which will then execute with the user's privileges whenever the extension opens the file in VS Code. To mitigate, avoid spawning a shell for this operation (use a non-shell API that takes the path as a separate argument) or strictly validate/escape the path before use.

This issue also appears in the following locations of the same file:

  • line 293
  • line 615

Copilot uses AI. Check for mistakes.
@github-actions
Copy link
Contributor

github-actions bot commented Mar 9, 2026

✅ Cross-SDK Consistency Review - PR #737

I've reviewed this PR for consistency across all four SDK implementations (Node.js, Python, Go, .NET). Here are my findings:


Consistently Implemented Features (All SDKs)

1. New RPC API: session.log()

All four SDKs correctly implement the session logging API:

SDK Method Signature Parameters
Node.js async log(message, options?) ✅ message, level, ephemeral
Python async def log(message, *, level, ephemeral) ✅ message, level, ephemeral
Go func Log(ctx, message, opts) ✅ message, level, ephemeral (via LogOptions)
.NET async Task LogAsync(message, level, ephemeral, cancellationToken) ✅ message, level, ephemeral

Assessment: ✅ Fully consistent - Minor API differences follow language conventions (e.g., Go context parameter, .NET cancellation token, Go's LogOptions struct).


2. RPC Update: reasoningEffort parameter on session.model.switchTo

All SDKs add the new optional parameter:

SDK Parameter Name Optional? Allowed Values
Node.js reasoningEffort? ✅ Yes "low" | "medium" | "high" | "xhigh"
Python reasoning_effort ✅ Yes (None default) ReasoningEffort enum
Go ReasoningEffort ✅ Yes (pointer) Low, Medium, High, Xhigh
.NET reasoningEffort ✅ Yes (nullable) SessionModelSwitchToRequestReasoningEffort enum

Assessment: ✅ Fully consistent - All SDKs properly expose this as optional with the same four allowed values.


3. New Session Event: system.notification

All SDKs include the new event type in generated session events:

SDK Event Type Constant Data Structure
Node.js "system.notification" ✅ Proper TypeScript types
Python SYSTEM_NOTIFICATION = "system.notification" ✅ Proper dataclasses
Go SystemNotification SessionEventType ✅ Proper struct definitions
.NET SystemNotificationEvent : SessionEvent ✅ Proper class hierarchy with JSON attributes

Assessment: ✅ Fully consistent - All SDKs support receiving and handling system notification events.


4. New Session Flag: alreadyInUse

All SDKs add this flag to session.created and session.resumed events:

SDK Property Name Type
Node.js alreadyInUse? boolean | undefined
Python already_in_use bool | None
Go AlreadyInUse *bool (pointer)
.NET AlreadyInUse bool? (nullable)

Assessment: ✅ Fully consistent - All SDKs use optional/nullable boolean typing.


⚠️ Potential Inconsistency: Extension Infrastructure

Issue: Extension support (extension.ts + documentation) appears to be Node.js-only.

Node.js:

  • nodejs/src/extension.ts - Provides joinSession() for extension processes
  • nodejs/docs/extensions.md - Complete extension authoring guide
  • ✅ Exported as @github/copilot-sdk/extension

Python, Go, .NET:

  • ❌ No extension modules or APIs found
  • ❌ No extension documentation

Question: Is extension support intentionally Node.js-only, or should Python/Go/.NET SDKs also provide extension APIs?

Recommendation:

  • If extensions are Node.js-only (e.g., because extensions run as Node.js processes), consider adding a note to the main README explaining this
  • If extensions should eventually support other languages, consider filing follow-up issues to track parity

📋 Summary

Overall Assessment: ✅ Excellent cross-SDK consistency

This PR maintains strong feature parity across all SDKs for the core runtime changes (session.log, reasoningEffort, system notifications, alreadyInUse flag). The only potential gap is extension infrastructure, which appears to be a deliberate Node.js-specific feature.

All generated types and RPC methods are properly synchronized across language implementations. Great work maintaining consistency! 🎉

Generated by SDK Consistency Review Agent for issue #737 ·

@github-actions
Copy link
Contributor

github-actions bot commented Mar 9, 2026

SDK Consistency Review

I've reviewed this PR for cross-language SDK consistency. Here's my analysis:

✅ Consistent Features (Implemented in All SDKs)

The following features are consistently implemented across all four SDKs (Node.js, Python, Go, .NET):

  1. New session.log RPC method - Allows logging messages at different severity levels (info/warning/error) with ephemeral option

    • Node.js: session.log(message, { level, ephemeral })
    • Python: session.log(message, level=..., ephemeral=...)
    • Go: session.Log(ctx, message, &LogOptions{Level, Ephemeral})
    • .NET: session.LogAsync(message, level, ephemeral)
  2. reasoningEffort parameter for model switching - New optional parameter on session.model.switchTo with values: "low", "medium", "high", "xhigh"

    • Consistently added to all SDKs' RPC definitions and method signatures
  3. system.notification event - New session event type added to generated session event types in all SDKs

  4. alreadyInUse flag - New optional boolean flag on session.start and session.resume event data, added to all SDKs

All of these features include comprehensive E2E tests across all languages. Great work maintaining parity! 👍

⚠️ Potential Inconsistency: Extension Infrastructure

The PR adds extension infrastructure that appears to be Node.js-only:

  • Node.js: New joinSession() function exported from @github/copilot-sdk/extension (in nodejs/src/extension.ts)
  • Node.js: Three new documentation files: extensions.md, examples.md, agent-author.md
  • Python, Go, .NET: No equivalent join_session() / JoinSession() functionality added

Is This Intentional?

This might be acceptable if:

  • Extensions are currently Node.js-only by design
  • The feature will be added to other SDKs in follow-up PRs
  • There's a technical limitation preventing this in other languages

However, if the goal is feature parity, the other SDKs would need:

  • A join_session() / JoinSession() function that reads SESSION_ID from environment
  • Calls resume_session() / ResumeSession() with disableResume=true by default
  • Similar documentation/examples

Recommendation

Could you clarify whether the extension infrastructure (joinSession) should be added to Python, Go, and .NET in this PR or a follow-up? If it's intentionally Node.js-only for now, consider adding a note to the PR description to document this scope decision.

Generated by SDK Consistency Review Agent for issue #737 ·

@SteveSandersonMS SteveSandersonMS merged commit 11dde6e into main Mar 9, 2026
33 checks passed
@SteveSandersonMS SteveSandersonMS deleted the stevesa/update-for-extensions branch March 9, 2026 16:08
@github-actions
Copy link
Contributor

github-actions bot commented Mar 9, 2026

✅ Cross-SDK Consistency Review: EXCELLENT

I've completed a comprehensive review of PR #737 for cross-language SDK consistency. This PR demonstrates exemplary multi-SDK development - all four language implementations have been updated in parallel with complete feature parity.

Summary of Changes

This PR adds four new runtime-related features across all SDKs:

  1. session.log() method - Log messages to the session timeline with severity levels
  2. system.notification event - New event type for system notifications
  3. alreadyInUse flag - Added to session.start and session.resume events
  4. reasoningEffort parameter - Added to session.model.switchTo() method

✅ Consistency Validation

1. session.log() Method

All four SDKs implement this with equivalent signatures:

  • TypeScript: async log(message: string, options?: { level?: "info" | "warning" | "error"; ephemeral?: boolean })
  • Python: async def log(self, message: str, *, level: str | None, ephemeral: bool | None)
  • Go: func (s *Session) Log(ctx context.Context, message string, opts *LogOptions) error
  • .NET: public async Task LogAsync(string message, SessionLogRequestLevel? level, bool? ephemeral, ...)

Consistent parameters: message, level (info/warning/error), ephemeral
Consistent naming: Uses language conventions (camelCase/snake_case/PascalCase)
Complete E2E test coverage in all four SDKs

2. system.notification Event

All SDKs have the system.notification event type in their generated session events with consistent structure (content + kind discriminated union).

3. alreadyInUse Flag

All SDKs have added the optional alreadyInUse?: boolean field to session start/resume event data, using language-appropriate naming (Python uses already_in_use).

4. reasoningEffort Parameter

All SDKs support the new parameter in session.model.switchTo() with the same enum values: "low", "medium", "high", "xhigh".


📚 Documentation

The PR includes new extension documentation (3 new/updated files in nodejs/docs/). This is intentionally Node.js-specific because the extension mechanism itself is JavaScript-based (.mjs files, joinSession() API). This is not a consistency gap - other SDKs don't need equivalent extension docs since extensions run as Node.js processes.


🎯 Conclusion

No consistency issues found. This PR is a model example of how to maintain feature parity across a multi-language SDK. The changes are well-structured, thoroughly tested, and maintain consistent API semantics while respecting each language's idioms.

Great work! 🚀

Generated by SDK Consistency Review Agent for issue #737 ·

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants