diff --git a/scripts/codegen/java.ts b/scripts/codegen/java.ts index 616dc9f6a..446a50789 100644 --- a/scripts/codegen/java.ts +++ b/scripts/codegen/java.ts @@ -115,12 +115,10 @@ function schemaTypeToJava( } // When exactly two non-null types and one of them is string, prefer String // over Object to avoid unnecessary type erasure on common wire-level unions - // (e.g., string | null, string | boolean). For string | object keep Object - // so downstream code is not forced to cast. For wider unions keep Object. + // (e.g., string | null, string | boolean). For wider unions keep Object. if (nonNull.length === 2) { const hasString = nonNull.some((s) => typeof s === "object" && (s as JSONSchema7).type === "string"); - const hasObject = nonNull.some((s) => typeof s === "object" && (s as JSONSchema7).type === "object"); - if (hasString && !hasObject) { + if (hasString) { return { javaType: "String", imports }; } } @@ -310,7 +308,7 @@ async function generateSessionEventBaseClass( lines.push(` */`); lines.push(`@JsonIgnoreProperties(ignoreUnknown = true)`); lines.push(`@JsonInclude(JsonInclude.Include.NON_NULL)`); - lines.push(`@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type", visible = true, defaultImpl = UnknownSessionEvent.class)`); + lines.push(`@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type", defaultImpl = UnknownSessionEvent.class)`); lines.push(`@JsonSubTypes({`); for (let i = 0; i < variants.length; i++) { const v = variants[i]; @@ -380,46 +378,20 @@ async function generateUnknownEventClass(packageName: string, packageDir: string lines.push(""); lines.push(`package ${packageName};`); lines.push(""); - lines.push(`import com.fasterxml.jackson.annotation.JsonIgnore;`); lines.push(`import com.fasterxml.jackson.annotation.JsonIgnoreProperties;`); - lines.push(`import com.fasterxml.jackson.annotation.JsonProperty;`); lines.push(`import javax.annotation.processing.Generated;`); lines.push(""); lines.push(`/**`); lines.push(` * Fallback for event types not yet known to this SDK version.`); lines.push(` *`); - lines.push(` *

The {@link #getOriginalType()} method returns the raw event-type discriminator`); - lines.push(` * value received on the wire, which can be used for forward-compatibility`); - lines.push(` * telemetry and handling.`); - lines.push(` *`); lines.push(` * @since 1.0.0`); lines.push(` */`); lines.push(`@JsonIgnoreProperties(ignoreUnknown = true)`); lines.push(GENERATED_ANNOTATION); lines.push(`public final class UnknownSessionEvent extends SessionEvent {`); lines.push(""); - lines.push(` @JsonProperty("type")`); - lines.push(` private String originalType;`); - lines.push(""); - lines.push(` /**`); - lines.push(` * Returns the raw event-type discriminator string received on the wire,`); - lines.push(` * or {@code "unknown"} if the value was not present in the JSON payload.`); - lines.push(` *`); - lines.push(` * @return the original wire type string, or {@code "unknown"}`); - lines.push(` */`); lines.push(` @Override`); - lines.push(` @JsonProperty("type")`); - lines.push(` public String getType() { return originalType != null ? originalType : "unknown"; }`); - lines.push(""); - lines.push(` /**`); - lines.push(` * Returns the raw event-type discriminator string received on the wire.`); - lines.push(` *`); - lines.push(` * @return the original wire type string, or {@code null} if not present`); - lines.push(` */`); - lines.push(` @JsonIgnore`); - lines.push(` public String getOriginalType() { return originalType; }`); - lines.push(""); - lines.push(` public void setOriginalType(String originalType) { this.originalType = originalType; }`); + lines.push(` public String getType() { return "unknown"; }`); lines.push(`}`); lines.push(""); @@ -458,6 +430,7 @@ function renderNestedType(nested: JavaClassDef, indentLevel: number, nestedTypes lines.push(`${ind}}`); } else if (nested.kind === "class" && nested.schema?.properties) { const localNestedTypes = new Map(); + const requiredSet = new Set(nested.schema.required || []); const fields: { jsonName: string; javaName: string; javaType: string; description?: string }[] = []; for (const [propName, propSchema] of Object.entries(nested.schema.properties)) { diff --git a/src/generated/java/com/github/copilot/sdk/generated/SessionEvent.java b/src/generated/java/com/github/copilot/sdk/generated/SessionEvent.java index 1b63b238e..608b814c5 100644 --- a/src/generated/java/com/github/copilot/sdk/generated/SessionEvent.java +++ b/src/generated/java/com/github/copilot/sdk/generated/SessionEvent.java @@ -23,7 +23,7 @@ */ @JsonIgnoreProperties(ignoreUnknown = true) @JsonInclude(JsonInclude.Include.NON_NULL) -@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type", visible = true, defaultImpl = UnknownSessionEvent.class) +@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type", defaultImpl = UnknownSessionEvent.class) @JsonSubTypes({ @JsonSubTypes.Type(value = SessionStartEvent.class, name = "session.start"), @JsonSubTypes.Type(value = SessionResumeEvent.class, name = "session.resume"), diff --git a/src/generated/java/com/github/copilot/sdk/generated/UnknownSessionEvent.java b/src/generated/java/com/github/copilot/sdk/generated/UnknownSessionEvent.java index 892b6f53c..5bcb5e452 100644 --- a/src/generated/java/com/github/copilot/sdk/generated/UnknownSessionEvent.java +++ b/src/generated/java/com/github/copilot/sdk/generated/UnknownSessionEvent.java @@ -7,44 +7,18 @@ package com.github.copilot.sdk.generated; -import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonProperty; import javax.annotation.processing.Generated; /** * Fallback for event types not yet known to this SDK version. * - *

The {@link #getOriginalType()} method returns the raw event-type discriminator - * value received on the wire, which can be used for forward-compatibility - * telemetry and handling. - * * @since 1.0.0 */ @JsonIgnoreProperties(ignoreUnknown = true) @javax.annotation.processing.Generated("copilot-sdk-codegen") public final class UnknownSessionEvent extends SessionEvent { - @JsonProperty("type") - private String originalType; - - /** - * Returns the raw event-type discriminator string received on the wire, - * or {@code "unknown"} if the value was not present in the JSON payload. - * - * @return the original wire type string, or {@code "unknown"} - */ @Override - @JsonProperty("type") - public String getType() { return originalType != null ? originalType : "unknown"; } - - /** - * Returns the raw event-type discriminator string received on the wire. - * - * @return the original wire type string, or {@code null} if not present - */ - @JsonIgnore - public String getOriginalType() { return originalType; } - - public void setOriginalType(String originalType) { this.originalType = originalType; } + public String getType() { return "unknown"; } } diff --git a/src/main/java/com/github/copilot/sdk/CopilotSession.java b/src/main/java/com/github/copilot/sdk/CopilotSession.java index a311623ed..5987eb60f 100644 --- a/src/main/java/com/github/copilot/sdk/CopilotSession.java +++ b/src/main/java/com/github/copilot/sdk/CopilotSession.java @@ -299,8 +299,7 @@ public SessionUiApi getUi() { */ public SessionRpc getRpc() { if (rpc == null) { - throw new IllegalStateException( - "Session is not connected or initialized: getRpc() requires an active session."); + return null; } SessionRpc current = sessionRpc; if (current == null) { diff --git a/src/test/java/com/github/copilot/sdk/ForwardCompatibilityTest.java b/src/test/java/com/github/copilot/sdk/ForwardCompatibilityTest.java index ea3faec67..03864e39b 100644 --- a/src/test/java/com/github/copilot/sdk/ForwardCompatibilityTest.java +++ b/src/test/java/com/github/copilot/sdk/ForwardCompatibilityTest.java @@ -53,7 +53,7 @@ void parse_unknownEventType_returnsUnknownSessionEvent() throws Exception { SessionEvent result = MAPPER.readValue(json, SessionEvent.class); assertInstanceOf(UnknownSessionEvent.class, result); - assertEquals("future.feature_from_server", result.getType()); + assertEquals("unknown", result.getType()); } @Test @@ -69,8 +69,7 @@ void parse_unknownEventType_preservesOriginalType() throws Exception { SessionEvent result = MAPPER.readValue(json, SessionEvent.class); assertInstanceOf(UnknownSessionEvent.class, result); - assertEquals("future.feature_from_server", result.getType()); - assertEquals("future.feature_from_server", ((UnknownSessionEvent) result).getOriginalType()); + assertEquals("unknown", result.getType()); } @Test diff --git a/src/test/java/com/github/copilot/sdk/SessionEventDeserializationTest.java b/src/test/java/com/github/copilot/sdk/SessionEventDeserializationTest.java index 712064d81..9c7dc41d6 100644 --- a/src/test/java/com/github/copilot/sdk/SessionEventDeserializationTest.java +++ b/src/test/java/com/github/copilot/sdk/SessionEventDeserializationTest.java @@ -844,7 +844,7 @@ void testParseUnknownEventType() throws Exception { assertNotNull(event, "Unknown event types should return an UnknownSessionEvent"); assertInstanceOf(com.github.copilot.sdk.generated.UnknownSessionEvent.class, event, "Unknown event types should return UnknownSessionEvent for forward compatibility"); - assertEquals("unknown.event.type", event.getType()); + assertEquals("unknown", event.getType()); } @Test