1- /*---------------------------------------------------------------------------------------------
1+ /*---------------------------------------------------------------------------------------------
22 * Copyright (c) Microsoft Corporation. All rights reserved.
33 *--------------------------------------------------------------------------------------------*/
44
@@ -54,9 +54,9 @@ import {
5454
5555const execFileAsync = promisify ( execFile ) ;
5656
57- // ── C# type rename overrides ────────────────────────────────────────────────
57+ // ── C# type rename overrides ────────────────────────────────────────────────
5858// Map generated class names to shorter public-facing names.
59- // Applied to base classes AND their derived variants (e.g., FooBar → Bar, FooBazShell → BarShell).
59+ // Applied to base classes AND their derived variants (e.g., FooBar → Bar, FooBazShell → BarShell).
6060const TYPE_RENAMES : Record < string , string > = {
6161 PermissionRequestedDataPermissionRequest : "PermissionRequest" ,
6262} ;
@@ -72,7 +72,7 @@ function applyTypeRename(className: string): string {
7272 return className ;
7373}
7474
75- // ── C# utilities ────────────────────────────────────────────────────────────
75+ // ── C# utilities ────────────────────────────────────────────────────────────
7676
7777function escapeXml ( text : string ) : string {
7878 return text . replace ( / & / g, "&" ) . replace ( / < / g, "<" ) . replace ( / > / g, ">" ) ;
@@ -177,7 +177,7 @@ function pushRpcMethodXmlDocs(
177177const CANCELLATION_TOKEN_DESCRIPTION =
178178 'The <see cref="CancellationToken"/> to monitor for cancellation requests. The default is <see cref="CancellationToken.None"/>.' ;
179179
180- /** Like xmlDocComment but skips XML escaping — use only for codegen-controlled strings that already contain valid XML tags. */
180+ /** Like xmlDocComment but skips XML escaping — use only for codegen-controlled strings that already contain valid XML tags. */
181181function rawXmlDocSummary ( text : string , indent : string ) : string [ ] {
182182 const line = ensureTrailingPunctuation ( text . trim ( ) ) ;
183183 return [ `${ indent } /// <summary>${ line } </summary>` ] ;
@@ -297,7 +297,7 @@ function failUnmappable(context: string, schema: JSONSchema7): never {
297297 const summary = JSON . stringify ( schema , ( key , value ) => ( key === "description" ? undefined : value ) ) . slice ( 0 , 200 ) ;
298298 throw new Error (
299299 `C# codegen: cannot map schema to an idiomatic C# type (${ context } ). ` +
300- `On the runtime side, either tighten the Zod schema to a typed shape, or — if it is genuinely free-form JSON — ` +
300+ `On the runtime side, either tighten the Zod schema to a typed shape, or — if it is genuinely free-form JSON — ` +
301301 `mark it \`.asOpaqueJson()\` so the schema emits \`x-opaque-json: true\` and the codegen maps it to JsonElement. ` +
302302 `Offending schema (truncated): ${ summary } ` ,
303303 ) ;
@@ -311,7 +311,7 @@ async function formatCSharpFile(filePath: string): Promise<void> {
311311 try {
312312 const projectFile = path . join ( REPO_ROOT , "dotnet/src/GitHub.Copilot.SDK.csproj" ) ;
313313 await execFileAsync ( "dotnet" , [ "format" , projectFile , "--include" , filePath ] ) ;
314- console . log ( ` ✓ Formatted with dotnet format` ) ;
314+ console . log ( ` ✓ Formatted with dotnet format` ) ;
315315 } catch {
316316 // dotnet format not available, skip
317317 }
@@ -346,7 +346,7 @@ function schemaTypeToCSharp(schema: JSONSchema7, required: boolean, knownTypes:
346346 const refName = schema . $ref . split ( "/" ) . pop ( ) ! ;
347347 return knownTypes . get ( refName ) || refName ;
348348 }
349- // Titled union schemas (anyOf with a title) — use the title if it's a known generated type
349+ // Titled union schemas (anyOf with a title) — use the title if it's a known generated type
350350 if ( schema . title && schema . anyOf && knownTypes . has ( schema . title ) ) {
351351 return required ? schema . title : `${ schema . title } ?` ;
352352 }
@@ -518,9 +518,23 @@ function pushObsoleteAttributes(lines: string[], indent = ""): void {
518518 lines . push ( ...obsoleteAttributes ( indent ) ) ;
519519}
520520
521- // â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•
521+ /**
522+ * Emit the `[JsonInclude]` attribute for an internally-marked property and
523+ * return the C# access modifier to use for the property declaration.
524+ *
525+ * `[JsonInclude]` is required because System.Text.Json only auto-(de)serialises
526+ * public members by default; without it, the `internal` setter would silently
527+ * be skipped.
528+ */
529+ function pushCSharpInternalAttribute ( lines : string [ ] , schema : JSONSchema7 , indent = " " ) : "public" | "internal" {
530+ const propInternal = isSchemaInternal ( schema ) ;
531+ if ( propInternal ) lines . push ( `${ indent } [JsonInclude]` ) ;
532+ return propInternal ? "internal" : "public" ;
533+ }
534+
535+ // ══════════════════════════════════════════════════════════════════════════════
522536// SESSION EVENTS
523- // â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•
537+ // ══════════════════════════════════════════════════════════════════════════════
524538
525539interface EventVariant {
526540 typeName : string ;
@@ -779,12 +793,9 @@ function generateFlattenedBooleanDiscriminatedClass(
779793 if ( isSchemaExperimental ( info . schema ) ) pushExperimentalAttribute ( lines , " " ) ;
780794 if ( isMillisecondsDurationProperty ( propName , info . schema ) ) lines . push ( ` [JsonConverter(typeof(MillisecondsTimeSpanConverter))]` ) ;
781795 if ( ! isReq ) lines . push ( ` [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]` ) ;
782- const propInternal = isSchemaInternal ( info . schema ) ;
783- const propExperimental = isSchemaExperimental ( info . schema ) ;
784- if ( propInternal ) lines . push ( ` [JsonInclude]` ) ;
796+ const propVisibility = pushCSharpInternalAttribute ( lines , info . schema ) ;
785797 lines . push ( ` [JsonPropertyName("${ propName } ")]` ) ;
786798 const reqMod = isReq && ! csharpType . endsWith ( "?" ) ? "required " : "" ;
787- const propVisibility = propInternal ? "internal" : "public" ;
788799 lines . push ( ` ${ propVisibility } ${ reqMod } ${ csharpType } ${ csharpName } { get; set; }` ) ;
789800 }
790801
@@ -887,12 +898,9 @@ function generateDerivedClass(
887898 if ( isSchemaExperimental ( prop ) ) pushExperimentalAttribute ( lines , " " ) ;
888899 if ( isMillisecondsDurationProperty ( propName , prop ) ) lines . push ( ` [JsonConverter(typeof(MillisecondsTimeSpanConverter))]` ) ;
889900 if ( ! isReq ) lines . push ( ` [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]` ) ;
890- const propInternal = isSchemaInternal ( prop ) ;
891- const propExperimental = isSchemaExperimental ( prop ) ;
892- if ( propInternal ) lines . push ( ` [JsonInclude]` ) ;
901+ const propVisibility = pushCSharpInternalAttribute ( lines , prop ) ;
893902 lines . push ( ` [JsonPropertyName("${ propName } ")]` ) ;
894903 const reqMod = isReq && ! csharpType . endsWith ( "?" ) ? "required " : "" ;
895- const propVisibility = propInternal ? "internal" : "public" ;
896904 lines . push ( ` ${ propVisibility } ${ reqMod } ${ csharpType } ${ csharpName } { get; set; }` , "" ) ;
897905 }
898906 }
@@ -1117,12 +1125,9 @@ function generateNestedClass(
11171125 if ( isSchemaExperimental ( prop ) ) pushExperimentalAttribute ( lines , " " ) ;
11181126 if ( isMillisecondsDurationProperty ( propName , prop ) ) lines . push ( ` [JsonConverter(typeof(MillisecondsTimeSpanConverter))]` ) ;
11191127 if ( ! isReq ) lines . push ( ` [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]` ) ;
1120- const propInternal = isSchemaInternal ( prop ) ;
1121- const propExperimental = isSchemaExperimental ( prop ) ;
1122- if ( propInternal ) lines . push ( ` [JsonInclude]` ) ;
1128+ const propVisibility = pushCSharpInternalAttribute ( lines , prop ) ;
11231129 lines . push ( ` [JsonPropertyName("${ propName } ")]` ) ;
11241130 const reqMod = isReq && ! csharpType . endsWith ( "?" ) ? "required " : "" ;
1125- const propVisibility = propInternal ? "internal" : "public" ;
11261131 lines . push ( ` ${ propVisibility } ${ reqMod } ${ csharpType } ${ csharpName } { get; set; }` , "" ) ;
11271132 }
11281133 if ( lines [ lines . length - 1 ] === "" ) lines . pop ( ) ;
@@ -1268,12 +1273,9 @@ function generateDataClass(variant: EventVariant, knownTypes: Map<string, string
12681273 if ( isSchemaExperimental ( prop ) ) pushExperimentalAttribute ( lines , " " ) ;
12691274 if ( isMillisecondsDurationProperty ( propName , prop ) ) lines . push ( ` [JsonConverter(typeof(MillisecondsTimeSpanConverter))]` ) ;
12701275 if ( ! isReq ) lines . push ( ` [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]` ) ;
1271- const propInternal = isSchemaInternal ( prop ) ;
1272- const propExperimental = isSchemaExperimental ( prop ) ;
1273- if ( propInternal ) lines . push ( ` [JsonInclude]` ) ;
1276+ const propVisibility = pushCSharpInternalAttribute ( lines , prop ) ;
12741277 lines . push ( ` [JsonPropertyName("${ propName } ")]` ) ;
12751278 const reqMod = isReq && ! csharpType . endsWith ( "?" ) ? "required " : "" ;
1276- const propVisibility = propInternal ? "internal" : "public" ;
12771279 lines . push ( ` ${ propVisibility } ${ reqMod } ${ csharpType } ${ csharpName } { get; set; }` , "" ) ;
12781280 }
12791281 if ( lines [ lines . length - 1 ] === "" ) lines . pop ( ) ;
@@ -1305,10 +1307,8 @@ function emitSessionEventEnvelopeProperty(
13051307 if ( isSchemaExperimental ( property . schema ) ) pushExperimentalAttribute ( lines , " " ) ;
13061308 if ( isMillisecondsDurationProperty ( property . name , property . schema ) ) lines . push ( ` [JsonConverter(typeof(MillisecondsTimeSpanConverter))]` ) ;
13071309 if ( ! property . required ) lines . push ( ` [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]` ) ;
1308- const propInternal = isSchemaInternal ( property . schema ) ;
1309- if ( propInternal ) lines . push ( ` [JsonInclude]` ) ;
1310+ const propVisibility = pushCSharpInternalAttribute ( lines , property . schema ) ;
13101311 lines . push ( ` [JsonPropertyName("${ property . name } ")]` ) ;
1311- const propVisibility = propInternal ? "internal" : "public" ;
13121312 lines . push ( ` ${ propVisibility } ${ csharpType } ${ csharpName } { get; set; }` , "" ) ;
13131313
13141314 return lines ;
@@ -1411,13 +1411,13 @@ export async function generateSessionEvents(schemaPath?: string): Promise<void>
14111411 const processed = postProcessSchema ( schema ) ;
14121412 const code = generateSessionEventsCode ( processed ) ;
14131413 const outPath = await writeGeneratedFile ( "dotnet/src/Generated/SessionEvents.cs" , code ) ;
1414- console . log ( ` ✓ ${ outPath } ` ) ;
1414+ console . log ( ` ✓ ${ outPath } ` ) ;
14151415 await formatCSharpFile ( outPath ) ;
14161416}
14171417
1418- // â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•
1418+ // ══════════════════════════════════════════════════════════════════════════════
14191419// RPC TYPES
1420- // â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•
1420+ // ══════════════════════════════════════════════════════════════════════════════
14211421
14221422let emittedRpcClassSchemas = new Map < string , string > ( ) ;
14231423let emittedRpcEnumResultTypes = new Set < string > ( ) ;
@@ -1524,7 +1524,7 @@ function resolveRpcType(schema: JSONSchema7, isRequired: boolean, parentClassNam
15241524
15251525 return resolveRpcType ( refSchema , isRequired , parentClassName , propName , classes ) ;
15261526 }
1527- // Handle anyOf: [T, null/{not:{}}] → T? (nullable typed property)
1527+ // Handle anyOf: [T, null/{not:{}}] → T? (nullable typed property)
15281528 const nullableInner = getNullableInner ( schema ) ;
15291529 if ( nullableInner ) {
15301530 return resolveRpcType ( nullableInner , false , parentClassName , propName , classes ) ;
@@ -1661,8 +1661,7 @@ function emitRpcClass(
16611661 if ( isSchemaDeprecated ( prop ) ) pushObsoleteAttributes ( lines , " " ) ;
16621662 if ( isSchemaExperimental ( prop ) ) pushExperimentalAttribute ( lines , " " ) ;
16631663 if ( isMillisecondsDurationProperty ( propName , prop ) ) lines . push ( ` [JsonConverter(typeof(MillisecondsTimeSpanConverter))]` ) ;
1664- const propInternal = isSchemaInternal ( prop ) ;
1665- if ( propInternal ) lines . push ( ` [JsonInclude]` ) ;
1664+ const propVisibility = pushCSharpInternalAttribute ( lines , prop ) ;
16661665 lines . push ( ` [JsonPropertyName("${ propName } ")]` ) ;
16671666
16681667 let defaultVal = "" ;
@@ -1680,7 +1679,6 @@ function emitRpcClass(
16801679 defaultVal = " = null!;" ;
16811680 }
16821681 }
1683- const propVisibility = propInternal ? "internal" : "public" ;
16841682 lines . push ( ` ${ propVisibility } ${ csharpType } ${ csharpName } ${ propAccessors } ${ defaultVal } ` ) ;
16851683 if ( i < props . length - 1 ) lines . push ( "" ) ;
16861684 }
@@ -2403,13 +2401,13 @@ export async function generateRpc(schemaPath?: string, sessionEventsSchema?: JSO
24032401 }
24042402 const code = generateRpcCode ( schema , externalJsonSerializableRefs , externalValueTypes ) ;
24052403 const outPath = await writeGeneratedFile ( "dotnet/src/Generated/Rpc.cs" , code ) ;
2406- console . log ( ` ✓ ${ outPath } ` ) ;
2404+ console . log ( ` ✓ ${ outPath } ` ) ;
24072405 await formatCSharpFile ( outPath ) ;
24082406}
24092407
2410- // â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•
2408+ // ══════════════════════════════════════════════════════════════════════════════
24112409// MAIN
2412- // â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•
2410+ // ══════════════════════════════════════════════════════════════════════════════
24132411
24142412async function generate ( sessionSchemaPath ?: string , apiSchemaPath ?: string ) : Promise < void > {
24152413 await generateSessionEvents ( sessionSchemaPath ) ;
0 commit comments