Skip to content
Prev Previous commit
Next Next commit
Fix Big Risk 1. sendExpandedToolResult() bypasses the typed wrapper…
… (HIGH)

scripts/codegen/java.ts
- Remove the special-case `anyOf` rule that picked `String` when exactly 2 non-null branches included a string type. Multi-branch `anyOf` now falls through to `Object`, matching the C# reference generator's behavior.

src/generated/java/com/github/copilot/sdk/generated/rpc/SessionToolsHandlePendingToolCallParams.java
- Change the `result` field type from `String` to `Object` (regenerated output reflecting the `anyOf` rule fix in `java.ts`).

src/main/java/com/github/copilot/sdk/CopilotSession.java
- Replace the `sendExpandedToolResult(requestId, toolResult)` call with a direct typed-wrapper call: `getRpc().tools.handlePendingToolCall(new SessionToolsHandlePendingToolCallParams(...))`.
- Delete the `sendExpandedToolResult()` private method and its Javadoc block, which are no longer needed now that the `result` field accepts `Object`.

Signed-off-by: Ed Burns <edburns@microsoft.com>
  • Loading branch information
edburns committed Apr 17, 2026
commit 11da06aa3b0d87746ab549f3f0d92a34d4524f3c
12 changes: 3 additions & 9 deletions scripts/codegen/java.ts
Original file line number Diff line number Diff line change
Expand Up @@ -113,15 +113,9 @@ function schemaTypeToJava(
const result = schemaTypeToJava(nonNull[0] as JSONSchema7, required && !hasNull, context, propName, nestedTypes);
return result;
}
// 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 wider unions keep Object.
if (nonNull.length === 2) {
const hasString = nonNull.some((s) => typeof s === "object" && (s as JSONSchema7).type === "string");
if (hasString) {
return { javaType: "String", imports };
}
}
// Multi-branch anyOf: fall through to Object, matching the C# generator's
// behavior. Java has no union types, so Object is the correct erasure for
// anyOf[string, object] and similar multi-variant schemas.
console.warn(`[codegen] ${context}.${propName}: anyOf with ${nonNull.length} non-null branches — falling back to Object`);
return { javaType: "Object", imports };
}
Comment thread
edburns marked this conversation as resolved.
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

22 changes: 2 additions & 20 deletions src/main/java/com/github/copilot/sdk/CopilotSession.java
Original file line number Diff line number Diff line change
Expand Up @@ -822,7 +822,8 @@ private void executeToolAndRespondAsync(String requestId, String toolName, Strin
toolResult = ToolResultObject
.success(result instanceof String s ? s : MAPPER.writeValueAsString(result));
}
sendExpandedToolResult(requestId, toolResult);
getRpc().tools.handlePendingToolCall(
new SessionToolsHandlePendingToolCallParams(sessionId, requestId, toolResult, null));
} catch (Exception e) {
LOG.log(Level.WARNING, "Error sending tool result for requestId=" + requestId, e);
}
Expand Down Expand Up @@ -857,25 +858,6 @@ private void executeToolAndRespondAsync(String requestId, String toolName, Strin
}
}

/**
* Sends a {@code ToolResultObject} back via
* {@code session.tools.handlePendingToolCall} using an
* {@link com.fasterxml.jackson.databind.node.ObjectNode} payload.
* <p>
* {@link SessionToolsHandlePendingToolCallParams#result()} is typed as
* {@code String} by the code generator's {@code anyOf[string,object]}
* preference rule, but the protocol requires a JSON object for expanded
* {@code ToolResultObject} results. This helper bypasses the type constraint
* while keeping every other call site on the typed wrapper.
*/
private void sendExpandedToolResult(String requestId, ToolResultObject toolResult) {
var node = MAPPER.createObjectNode();
node.put("sessionId", sessionId);
node.put("requestId", requestId);
node.set("result", MAPPER.valueToTree(toolResult));
rpc.invoke("session.tools.handlePendingToolCall", node, Object.class);
}

/**
* Builds a {@link SessionUiHandlePendingElicitationParams} carrying a
* {@code cancel} action, used when an elicitation handler throws or the handler
Expand Down
Loading