@@ -37,9 +37,25 @@ function toJavaClassName(typeName: string): string {
3737 return typeName . split ( / [ . _ ] / ) . map ( ( p ) => p . charAt ( 0 ) . toUpperCase ( ) + p . slice ( 1 ) ) . join ( "" ) ;
3838}
3939
40+ /** Java reserved keywords and Object method names that cannot be used as record component names. */
41+ const JAVA_RESERVED_IDENTIFIERS = new Set ( [
42+ "abstract" , "assert" , "boolean" , "break" , "byte" , "case" , "catch" , "char" , "class" , "const" ,
43+ "continue" , "default" , "do" , "double" , "else" , "enum" , "extends" , "final" , "finally" , "float" ,
44+ "for" , "goto" , "if" , "implements" , "import" , "instanceof" , "int" , "interface" , "long" , "native" ,
45+ "new" , "package" , "private" , "protected" , "public" , "return" , "short" , "static" , "strictfp" ,
46+ "super" , "switch" , "synchronized" , "this" , "throw" , "throws" , "transient" , "try" , "void" ,
47+ "volatile" , "while" ,
48+ // Object methods that conflict with record component accessor names
49+ "wait" , "notify" , "notifyAll" , "getClass" , "clone" , "finalize" , "toString" , "hashCode" , "equals" ,
50+ ] ) ;
51+
4052function toCamelCase ( name : string ) : string {
4153 const pascal = toPascalCase ( name ) ;
42- return pascal . charAt ( 0 ) . toLowerCase ( ) + pascal . slice ( 1 ) ;
54+ let result = pascal . charAt ( 0 ) . toLowerCase ( ) + pascal . slice ( 1 ) ;
55+ if ( JAVA_RESERVED_IDENTIFIERS . has ( result ) ) {
56+ result = result + "_" ;
57+ }
58+ return result ;
4359}
4460
4561function toEnumConstant ( value : string ) : string {
@@ -102,6 +118,11 @@ interface JavaTypeResult {
102118let currentDefinitions : Record < string , JSONSchema7 > = { } ;
103119const pendingStandaloneTypes = new Map < string , JSONSchema7 > ( ) ;
104120
121+ // Cross-schema definitions: keyed by schema filename (e.g. "session-events.schema.json"),
122+ // value is the definitions map from that schema. Populated by generateRpcTypes so that
123+ // cross-schema $ref values like "session-events.schema.json#/definitions/Foo" can be resolved.
124+ const crossSchemaDefinitions = new Map < string , Record < string , JSONSchema7 > > ( ) ;
125+
105126/**
106127 * Resolve a $ref in a JSON Schema against the current definitions.
107128 * Returns the resolved schema, or the original if no $ref is present.
@@ -131,6 +152,28 @@ function schemaTypeToJava(
131152
132153 // Resolve $ref first — register standalone types for generation
133154 if ( schema . $ref ) {
155+ // Handle cross-schema $ref (e.g. "session-events.schema.json#/definitions/Foo")
156+ const crossSchemaMatch = schema . $ref . match ( / ^ ( [ ^ # ] + ) # \/ d e f i n i t i o n s \/ ( .+ ) $ / ) ;
157+ if ( crossSchemaMatch ) {
158+ const [ , schemaFile , typeName ] = crossSchemaMatch ;
159+ const externalDefs = crossSchemaDefinitions . get ( schemaFile ) ;
160+ if ( externalDefs ) {
161+ const resolved = externalDefs [ typeName ] ;
162+ if ( resolved ) {
163+ // Save and swap currentDefinitions so recursive calls resolve against
164+ // the external schema's definitions.
165+ const savedDefs = currentDefinitions ;
166+ currentDefinitions = externalDefs ;
167+ const result = schemaTypeToJava ( resolved , required , context , propName , nestedTypes ) ;
168+ currentDefinitions = savedDefs ;
169+ return result ;
170+ }
171+ }
172+ // Fallback: extract just the type name and warn
173+ console . warn ( `[codegen] Unresolved cross-schema $ref: ${ schema . $ref } ` ) ;
174+ return { javaType : typeName , imports } ;
175+ }
176+
134177 const name = schema . $ref . replace ( / ^ # \/ d e f i n i t i o n s \/ / , "" ) ;
135178 const resolved = currentDefinitions [ name ] ;
136179 if ( resolved ) {
@@ -883,6 +926,19 @@ async function generateRpcTypes(schemaPath: string): Promise<void> {
883926 // Set module-level definitions for $ref resolution
884927 currentDefinitions = ( schema . definitions ?? { } ) as Record < string , JSONSchema7 > ;
885928 pendingStandaloneTypes . clear ( ) ;
929+ crossSchemaDefinitions . clear ( ) ;
930+
931+ // Load cross-schema definitions (session-events) so that cross-schema $ref values
932+ // like "session-events.schema.json#/definitions/Foo" can be resolved.
933+ try {
934+ const sessionEventsSchemaPath = await getSessionEventsSchemaPath ( ) ;
935+ const sessionEventsContent = await fs . readFile ( sessionEventsSchemaPath , "utf-8" ) ;
936+ const sessionEventsSchema = JSON . parse ( sessionEventsContent ) as JSONSchema7 ;
937+ crossSchemaDefinitions . set ( "session-events.schema.json" ,
938+ ( sessionEventsSchema . definitions ?? { } ) as Record < string , JSONSchema7 > ) ;
939+ } catch ( e ) {
940+ console . warn ( `[codegen] Could not load session-events schema for cross-ref resolution: ${ e } ` ) ;
941+ }
886942
887943 const packageName = "com.github.copilot.sdk.generated.rpc" ;
888944 const packageDir = `src/generated/java/com/github/copilot/sdk/generated/rpc` ;
0 commit comments