Skip to content

Commit c7c63ab

Browse files
stephentoubCopilot
andcommitted
Fix CI breaks from the CLI 1.0.78-2 schema update
The dependency bump regenerated types but left hand-written code and tests behind, breaking every language job. Five independent fixes: - Go: the CLI added a `factory` permission-request kind, so `PermissionRequestFactory` needed a `RequiresManagedApproval()` impl. Added it, and registered the new variant in the codegen shim (`PERMISSION_REQUEST_DEFINITION_NAMES`) so Go/Python/Rust all carry the `managedApprovalRequired` field consistently. - .NET: `session.start` gained `githubMcpToolConfig`, so codegen emitted a `GitHubMcpToolConfig` class colliding with the hand-written one in `dotnet/src/Types.cs` (15 CS0260/CS0102 errors). Taught the C# generator to skip nested classes whose names already exist hand-written under `dotnet/src`, mirroring the existing behavior in the Go generator. The `[JsonSerializable]` registrations are preserved. - Java: `SessionEventHandlingTest` calls generated record constructors positionally; `SessionStartEventData` gained `githubMcpToolConfig` and `AssistantMessageEventData` gained `chunkIndex`/`chunkCount`. Padded the three call sites. This also unblocks CodeQL's java-kotlin analysis. - Rust: `EventLogReadRequest` gained `agent_ids` and `direction`; added them to the three exhaustive struct literals in `tests/e2e/rpc_event_log.rs`. - Node.js: the CLI can now answer `session.factory.run`/`resume` before the run settles, so the e2e test saw `status: "running"`. `SessionFactoryApi` documents these as resolving with a terminal envelope, so both now route through a `settleFactoryRun` helper that waits for terminal state when the initial envelope is non-terminal. Correct under both old and new CLI behavior. Validated locally: go build/vet, dotnet build (src + test), mvn test-compile + spotless:check + SessionEventHandlingTest (29/29), cargo check --tests + cargo fmt --check, npm run typecheck/lint, and the full Node unit suite (363 tests). Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 820d6939-ba01-4044-8ee3-c5f5b4d3f443
1 parent 545a011 commit c7c63ab

10 files changed

Lines changed: 99 additions & 33 deletions

File tree

dotnet/src/Generated/SessionEvents.cs

Lines changed: 0 additions & 25 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

go/rpc/permission_request_managed_approval.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,12 @@ func (r PermissionRequestExtensionPermissionAccess) RequiresManagedApproval() bo
2626
return managedApprovalRequired(r.ManagedApprovalRequired)
2727
}
2828

29+
// RequiresManagedApproval reports whether managed policy requires an explicit
30+
// human decision for this request.
31+
func (r PermissionRequestFactory) RequiresManagedApproval() bool {
32+
return managedApprovalRequired(r.ManagedApprovalRequired)
33+
}
34+
2935
// RequiresManagedApproval reports whether managed policy requires an explicit
3036
// human decision for this request.
3137
func (r PermissionRequestHook) RequiresManagedApproval() bool {

go/rpc/zsession_events.go

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

java/src/test/java/com/github/copilot/SessionEventHandlingTest.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,7 @@ void testHandlerReceivesCorrectEventData() {
180180

181181
SessionStartEvent startEvent = createSessionStartEvent();
182182
startEvent.setData(new SessionStartEvent.SessionStartEventData("my-session-123", null, null, null, null, null,
183-
null, null, null, null, null, null, null, null, null));
183+
null, null, null, null, null, null, null, null, null, null));
184184
dispatchEvent(startEvent);
185185

186186
AssistantMessageEvent msgEvent = createAssistantMessageEvent("Test content");
@@ -857,15 +857,15 @@ private SessionStartEvent createSessionStartEvent() {
857857
private SessionStartEvent createSessionStartEvent(String sessionId) {
858858
var event = new SessionStartEvent();
859859
var data = new SessionStartEvent.SessionStartEventData(sessionId, null, null, null, null, null, null, null,
860-
null, null, null, null, null, null, null);
860+
null, null, null, null, null, null, null, null);
861861
event.setData(data);
862862
return event;
863863
}
864864

865865
private AssistantMessageEvent createAssistantMessageEvent(String content) {
866866
var event = new AssistantMessageEvent();
867867
var data = new AssistantMessageEvent.AssistantMessageEventData(null, null, content, null, null, null, null,
868-
null, null, null, null, null, null, null, null, null, null, null, null, null);
868+
null, null, null, null, null, null, null, null, null, null, null, null, null, null, null);
869869
event.setData(data);
870870
return event;
871871
}

nodejs/src/session.ts

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -461,7 +461,7 @@ export class CopilotSession {
461461
},
462462
});
463463

464-
return toPublicFactoryRunResult(envelope);
464+
return this.settleFactoryRun(envelope);
465465
}) as SessionFactoryApi["run"],
466466
resume: (async (runId: string, options?: Parameters<SessionFactoryApi["resume"]>[1]) => {
467467
let response;
@@ -483,7 +483,7 @@ export class CopilotSession {
483483
}
484484
throw error;
485485
}
486-
return toPublicFactoryRunResult(response.run);
486+
return this.settleFactoryRun(response.run);
487487
}) as SessionFactoryApi["resume"],
488488
getRun: async (runId) => toPublicFactoryRunResult(await this.rpc.factory.getRun({ runId })),
489489
waitForRun: (runId, options) => this.waitForFactoryRun(runId, options?.signal),
@@ -494,6 +494,20 @@ export class CopilotSession {
494494
cancel: async (runId) => toPublicFactoryRunResult(await this.rpc.factory.cancel({ runId })),
495495
};
496496

497+
/**
498+
* Resolve a start/resume envelope into the terminal envelope callers expect.
499+
*
500+
* The CLI may answer `session.factory.run` and `session.factory.resume`
501+
* before the run settles, so a non-terminal envelope is followed by a wait
502+
* on the run's terminal state.
503+
*/
504+
private settleFactoryRun(envelope: WireFactoryRunResult): Promise<FactoryRunResult> {
505+
if (isFactoryRunTerminal(envelope.status)) {
506+
return Promise.resolve(toPublicFactoryRunResult(envelope));
507+
}
508+
return this.waitForFactoryRun(envelope.runId);
509+
}
510+
497511
/**
498512
* Resolve when a factory run reaches a terminal status.
499513
*

python/copilot/generated/session_events.py

Lines changed: 5 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

rust/src/generated/session_events.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3322,6 +3322,9 @@ pub struct PermissionRequestFactory {
33223322
pub description: String,
33233323
/// Permission kind discriminator
33243324
pub kind: PermissionRequestFactoryKind,
3325+
/// When true, managed policy requires an explicit user decision and automatic approval must be bypassed.
3326+
#[serde(skip_serializing_if = "Option::is_none")]
3327+
pub managed_approval_required: Option<bool>,
33253328
/// Effective AI-credit limit; omitted means unlimited
33263329
#[serde(skip_serializing_if = "Option::is_none")]
33273330
pub max_ai_credits: Option<f64>,

rust/tests/e2e/rpc_event_log.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,10 @@ async fn should_read_persisted_events_from_beginning() {
4343
.rpc()
4444
.event_log()
4545
.read(EventLogReadRequest {
46+
agent_ids: None,
4647
agent_scope: None,
4748
cursor: None,
49+
direction: None,
4850
include_ephemeral: None,
4951
max: Some(100),
5052
types: Some(json!("*")),
@@ -89,8 +91,10 @@ async fn should_return_tail_cursor_and_read_empty_when_no_new_events() {
8991
.rpc()
9092
.event_log()
9193
.read(EventLogReadRequest {
94+
agent_ids: None,
9295
agent_scope: None,
9396
cursor: Some(tail.cursor),
97+
direction: None,
9498
include_ephemeral: None,
9599
max: Some(10),
96100
types: Some(json!("*")),
@@ -172,8 +176,10 @@ async fn should_longpoll_with_types_filter_for_titlechanged_event() {
172176
let tail = session.rpc().event_log().tail().await.expect("tail");
173177
let event_log = session.rpc().event_log();
174178
let read_future = event_log.read(EventLogReadRequest {
179+
agent_ids: None,
175180
agent_scope: None,
176181
cursor: Some(tail.cursor),
182+
direction: None,
177183
include_ephemeral: None,
178184
max: Some(10),
179185
types: Some(json!(["session.title_changed"])),

scripts/codegen/csharp.ts

Lines changed: 57 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,51 @@ const POLYMORPHIC_BASE_PROPERTIES: Record<string, readonly string[]> = {
7070
PermissionRequest: ["managedApprovalRequired"],
7171
};
7272

73+
/**
74+
* Public type names declared by hand-written C# sources under `dotnet/src`
75+
* (excluding `dotnet/src/Generated`). Generated session-event types share the
76+
* `GitHub.Copilot` namespace with those sources, so a schema definition whose
77+
* name collides with a hand-written declaration must reuse it — emitting a
78+
* second class of the same name fails the build (CS0260/CS0102).
79+
*
80+
* Populated by {@link collectHandWrittenCSharpTypeNames} before generation.
81+
*/
82+
let handWrittenCSharpTypeNames = new Set<string>();
83+
84+
/**
85+
* Scan hand-written `.cs` files under `dotnet/src` for top-level public type
86+
* declarations. The `Generated` directory is skipped so this scanner never
87+
* reads (or depends on the output of) its own emit.
88+
*/
89+
async function collectHandWrittenCSharpTypeNames(): Promise<Set<string>> {
90+
const names = new Set<string>();
91+
const srcDir = path.join(REPO_ROOT, "dotnet", "src");
92+
const declaration = /^\s*(?:public|internal)\s+(?:(?:abstract|sealed|static|partial|readonly|ref)\s+)*(?:class|record|struct|interface|enum)\s+([A-Za-z_]\w*)/gm;
93+
94+
const walk = async (dir: string): Promise<void> => {
95+
let entries;
96+
try {
97+
entries = await fs.readdir(dir, { withFileTypes: true });
98+
} catch {
99+
return;
100+
}
101+
for (const entry of entries) {
102+
const entryPath = path.join(dir, entry.name);
103+
if (entry.isDirectory()) {
104+
if (entry.name === "Generated" || entry.name === "bin" || entry.name === "obj") continue;
105+
await walk(entryPath);
106+
continue;
107+
}
108+
if (!entry.name.endsWith(".cs")) continue;
109+
const content = await fs.readFile(entryPath, "utf-8");
110+
for (const match of content.matchAll(declaration)) names.add(match[1]);
111+
}
112+
};
113+
114+
await walk(srcDir);
115+
return names;
116+
}
117+
73118
/** Apply rename to a generated class name, checking both exact match and prefix replacement for derived types. */
74119
function applyTypeRename(className: string): string {
75120
if (TYPE_RENAMES[className]) return TYPE_RENAMES[className];
@@ -1456,8 +1501,13 @@ namespace GitHub.Copilot;
14561501
lines.push(generateDataClass(variant, knownTypes, nestedClasses, enumOutput), "");
14571502
}
14581503

1459-
// Nested classes
1460-
for (const [, code] of nestedClasses) lines.push(code, "");
1504+
// Nested classes. A name already declared by a hand-written source is skipped:
1505+
// that declaration is the one the namespace keeps, and the generated property
1506+
// simply binds to it.
1507+
for (const [name, code] of nestedClasses) {
1508+
if (handWrittenCSharpTypeNames.has(name)) continue;
1509+
lines.push(code, "");
1510+
}
14611511

14621512
// Enums
14631513
for (const code of enumOutput) lines.push(code);
@@ -1477,6 +1527,7 @@ export async function generateSessionEvents(schemaPath?: string): Promise<void>
14771527
const resolvedPath = schemaPath ?? (await getSessionEventsSchemaPath());
14781528
const schema = cloneSchemaForCodegen((await loadSchemaJson(resolvedPath)) as JSONSchema7);
14791529
const processed = propagateInternalVisibility(postProcessSchema(schema));
1530+
handWrittenCSharpTypeNames = await collectHandWrittenCSharpTypeNames();
14801531
const code = generateSessionEventsCode(processed);
14811532
const outPath = await writeGeneratedFile("dotnet/src/Generated/SessionEvents.cs", code);
14821533
console.log(` ✓ ${outPath}`);
@@ -2629,6 +2680,7 @@ namespace GitHub.Copilot.Rpc;
26292680
export async function generateRpc(schemaPath?: string, sessionEventsSchema?: JSONSchema7): Promise<void> {
26302681
console.log("C#: generating RPC types...");
26312682
const resolvedPath = schemaPath ?? (await getApiSchemaPath());
2683+
handWrittenCSharpTypeNames = await collectHandWrittenCSharpTypeNames();
26322684
let schema = fixNullableRequiredRefsInApiSchema(cloneSchemaForCodegen((await loadSchemaJson(resolvedPath)) as ApiSchema));
26332685
if (sessionEventsSchema) {
26342686
const sharedDefinitions = findSharedSchemaDefinitions(
@@ -2658,7 +2710,9 @@ export async function generateRpc(schemaPath?: string, sessionEventsSchema?: JSO
26582710
for (const name of reachableDefinitions) {
26592711
const typeName = typeToClassName(name);
26602712
const declarationPattern = new RegExp(`\\bpublic\\s+(?:(?:sealed|abstract|partial|readonly)\\s+)*(?:class|struct)\\s+${typeName}\\b`);
2661-
if (declarationPattern.test(sessionEventsCode)) {
2713+
// A hand-written declaration also lives in `GitHub.Copilot`, so the
2714+
// reference resolves even though the generated file skipped it.
2715+
if (declarationPattern.test(sessionEventsCode) || handWrittenCSharpTypeNames.has(typeName)) {
26622716
emittedDefinitions.add(name);
26632717
}
26642718
const valueTypeDeclarationPattern = new RegExp(`\\bpublic\\s+(?:(?:readonly)\\s+)?struct\\s+${typeName}\\b`);

scripts/codegen/utils.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -457,6 +457,7 @@ const PERMISSION_REQUEST_DEFINITION_NAMES = [
457457
"PermissionRequestCustomTool",
458458
"PermissionRequestExtensionManagement",
459459
"PermissionRequestExtensionPermissionAccess",
460+
"PermissionRequestFactory",
460461
"PermissionRequestHook",
461462
"PermissionRequestMcp",
462463
"PermissionRequestMemory",

0 commit comments

Comments
 (0)