Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
On branch copilot-pr-20 Fix "2. **`SessionRequestBuilder.extractTrans…
…formCallbacks` returns `Object[]`** — MEDIUM-HIGH RISK"

Your branch is up to date with 'upstream/copilot/sync-upstream-39-new-commits'.

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
	modified:   .gitignore
	modified:   src/main/java/com/github/copilot/sdk/CopilotClient.java
	modified:   src/main/java/com/github/copilot/sdk/SessionRequestBuilder.java
	modified:   src/test/java/com/github/copilot/sdk/SessionRequestBuilderTest.java

Untracked files:
  (use "git add <file>..." to include in what will be committed)
	src/main/java/com/github/copilot/sdk/ExtractedTransforms.java

no changes added to commit (use "git add" and/or "git commit -a")
  • Loading branch information
edburns committed Mar 24, 2026
commit 13242227b35088bd2c0f426673db483c44f314b5
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ blog-copilotsdk/
.claude/worktrees
smoke-test
*job-logs.txt
temporary-prompts/
26 changes: 10 additions & 16 deletions src/main/java/com/github/copilot/sdk/CopilotClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -336,17 +336,14 @@ public CompletableFuture<CopilotSession> createSession(SessionConfig config) {
// Callbacks are registered with the session; a wire-safe copy of the
// system message (with transform sections replaced by action="transform")
// is used in the RPC request.
Object[] extracted = SessionRequestBuilder.extractTransformCallbacks(config.getSystemMessage());
var wireSystemMessage = (com.github.copilot.sdk.json.SystemMessageConfig) extracted[0];
@SuppressWarnings("unchecked")
var transformCallbacks = (java.util.Map<String, java.util.function.Function<String, java.util.concurrent.CompletableFuture<String>>>) extracted[1];
if (transformCallbacks != null) {
session.registerTransformCallbacks(transformCallbacks);
var extracted = SessionRequestBuilder.extractTransformCallbacks(config.getSystemMessage());
if (extracted.transformCallbacks() != null) {
session.registerTransformCallbacks(extracted.transformCallbacks());
}

var request = SessionRequestBuilder.buildCreateRequest(config, sessionId);
if (wireSystemMessage != config.getSystemMessage()) {
request.setSystemMessage(wireSystemMessage);
if (extracted.wireSystemMessage() != config.getSystemMessage()) {
request.setSystemMessage(extracted.wireSystemMessage());
}

return connection.rpc.invoke("session.create", request, CreateSessionResponse.class).thenApply(response -> {
Expand Down Expand Up @@ -406,17 +403,14 @@ public CompletableFuture<CopilotSession> resumeSession(String sessionId, ResumeS
sessions.put(sessionId, session);

// Extract transform callbacks from the system message config.
Object[] extracted = SessionRequestBuilder.extractTransformCallbacks(config.getSystemMessage());
var wireSystemMessage = (com.github.copilot.sdk.json.SystemMessageConfig) extracted[0];
@SuppressWarnings("unchecked")
var transformCallbacks = (java.util.Map<String, java.util.function.Function<String, java.util.concurrent.CompletableFuture<String>>>) extracted[1];
if (transformCallbacks != null) {
session.registerTransformCallbacks(transformCallbacks);
var extracted = SessionRequestBuilder.extractTransformCallbacks(config.getSystemMessage());
if (extracted.transformCallbacks() != null) {
session.registerTransformCallbacks(extracted.transformCallbacks());
}

var request = SessionRequestBuilder.buildResumeRequest(sessionId, config);
if (wireSystemMessage != config.getSystemMessage()) {
request.setSystemMessage(wireSystemMessage);
if (extracted.wireSystemMessage() != config.getSystemMessage()) {
request.setSystemMessage(extracted.wireSystemMessage());
}

return connection.rpc.invoke("session.resume", request, ResumeSessionResponse.class).thenApply(response -> {
Expand Down
30 changes: 30 additions & 0 deletions src/main/java/com/github/copilot/sdk/ExtractedTransforms.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
*--------------------------------------------------------------------------------------------*/

package com.github.copilot.sdk;

import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.function.Function;

import com.github.copilot.sdk.json.SystemMessageConfig;

/**
* Result of extracting transform callbacks from a {@link SystemMessageConfig}.
* <p>
* Holds a wire-safe copy of the system message config (with transform callbacks
* replaced by {@code action="transform"}) alongside the extracted callbacks
* that must be registered with the session.
*
* @param wireSystemMessage
* the system message config safe for JSON serialization; may be
* {@code null} when the input config was {@code null}
* @param transformCallbacks
* transform callbacks keyed by section identifier; {@code null} when
* no transforms were present
* @see SessionRequestBuilder#extractTransformCallbacks(SystemMessageConfig)
*/
record ExtractedTransforms(SystemMessageConfig wireSystemMessage,
Map<String, Function<String, CompletableFuture<String>>> transformCallbacks) {
}
12 changes: 6 additions & 6 deletions src/main/java/com/github/copilot/sdk/SessionRequestBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -47,13 +47,13 @@ private SessionRequestBuilder() {
*
* @param systemMessage
* the system message config, may be {@code null}
* @return a two-element array: {@code [wireConfig, callbacks]}; either may be
* {@code null} if not applicable
* @return an {@link ExtractedTransforms} containing the wire-safe config and
* any extracted callbacks
*/
static Object[] extractTransformCallbacks(SystemMessageConfig systemMessage) {
static ExtractedTransforms extractTransformCallbacks(SystemMessageConfig systemMessage) {
if (systemMessage == null || systemMessage.getMode() != SystemMessageMode.CUSTOMIZE
|| systemMessage.getSections() == null) {
return new Object[]{systemMessage, null};
return new ExtractedTransforms(systemMessage, null);
}

Map<String, Function<String, CompletableFuture<String>>> callbacks = new HashMap<>();
Expand All @@ -72,14 +72,14 @@ static Object[] extractTransformCallbacks(SystemMessageConfig systemMessage) {
}

if (callbacks.isEmpty()) {
return new Object[]{systemMessage, null};
return new ExtractedTransforms(systemMessage, null);
}

// Build a wire-safe copy of the system message with callbacks removed
var wireConfig = new SystemMessageConfig().setMode(systemMessage.getMode())
.setContent(systemMessage.getContent()).setSections(wireSections);

return new Object[]{wireConfig, callbacks};
return new ExtractedTransforms(wireConfig, callbacks);
}

/**
Expand Down
33 changes: 15 additions & 18 deletions src/test/java/com/github/copilot/sdk/SessionRequestBuilderTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -258,18 +258,18 @@ void testBuildResumeRequestWithAgent() {

@Test
void extractTransformCallbacks_nullSystemMessage_returnsNull() {
Object[] result = SessionRequestBuilder.extractTransformCallbacks(null);
assertNull(result[0]);
assertNull(result[1]);
ExtractedTransforms result = SessionRequestBuilder.extractTransformCallbacks(null);
assertNull(result.wireSystemMessage());
assertNull(result.transformCallbacks());
}

@Test
void extractTransformCallbacks_appendMode_returnsOriginalConfig() {
var config = new com.github.copilot.sdk.json.SystemMessageConfig()
.setMode(com.github.copilot.sdk.SystemMessageMode.APPEND).setContent("extra content");
Object[] result = SessionRequestBuilder.extractTransformCallbacks(config);
assertSame(config, result[0]);
assertNull(result[1]);
ExtractedTransforms result = SessionRequestBuilder.extractTransformCallbacks(config);
assertSame(config, result.wireSystemMessage());
assertNull(result.transformCallbacks());
}

@Test
Expand All @@ -278,32 +278,29 @@ void extractTransformCallbacks_customizeModeNoTransforms_returnsOriginalConfig()
.setAction(com.github.copilot.sdk.json.SectionOverrideAction.REMOVE));
var config = new com.github.copilot.sdk.json.SystemMessageConfig()
.setMode(com.github.copilot.sdk.SystemMessageMode.CUSTOMIZE).setSections(sections);
Object[] result = SessionRequestBuilder.extractTransformCallbacks(config);
assertSame(config, result[0]);
assertNull(result[1]);
ExtractedTransforms result = SessionRequestBuilder.extractTransformCallbacks(config);
assertSame(config, result.wireSystemMessage());
assertNull(result.transformCallbacks());
}

@Test
@SuppressWarnings("unchecked")
void extractTransformCallbacks_customizeModeWithTransform_extractsCallbacks() {
var transformFn = (java.util.function.Function<String, CompletableFuture<String>>) content -> CompletableFuture
.completedFuture(content + " modified");
var sections = Map.of("identity", new com.github.copilot.sdk.json.SectionOverride().setTransform(transformFn));
var config = new com.github.copilot.sdk.json.SystemMessageConfig()
.setMode(com.github.copilot.sdk.SystemMessageMode.CUSTOMIZE).setSections(sections);

Object[] result = SessionRequestBuilder.extractTransformCallbacks(config);
ExtractedTransforms result = SessionRequestBuilder.extractTransformCallbacks(config);

// Wire config should be different from original
assertNotSame(config, result[0]);
assertNotSame(config, result.wireSystemMessage());
// Callbacks should be extracted
assertNotNull(result[1]);
var callbacks = (java.util.Map<String, java.util.function.Function<String, CompletableFuture<String>>>) result[1];
assertTrue(callbacks.containsKey("identity"));
assertNotNull(result.transformCallbacks());
assertTrue(result.transformCallbacks().containsKey("identity"));
// Wire config should have transform action instead of callback
var wireConfig = (com.github.copilot.sdk.json.SystemMessageConfig) result[0];
assertNotNull(wireConfig.getSections());
var wireSection = wireConfig.getSections().get("identity");
assertNotNull(result.wireSystemMessage().getSections());
var wireSection = result.wireSystemMessage().getSections().get("identity");
assertNotNull(wireSection);
assertEquals(com.github.copilot.sdk.json.SectionOverrideAction.TRANSFORM, wireSection.getAction());
assertNull(wireSection.getTransform());
Expand Down
Loading