SessionRequestBuilder.java
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
*--------------------------------------------------------------------------------------------*/
package com.github.copilot.sdk;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.function.Function;
import com.github.copilot.sdk.json.CreateSessionRequest;
import com.github.copilot.sdk.json.ResumeSessionConfig;
import com.github.copilot.sdk.json.ResumeSessionRequest;
import com.github.copilot.sdk.json.SectionOverride;
import com.github.copilot.sdk.json.SectionOverrideAction;
import com.github.copilot.sdk.json.SessionConfig;
import com.github.copilot.sdk.json.SystemMessageConfig;
/**
* Builds JSON-RPC request objects from session configuration.
* <p>
* This class handles the conversion of SDK configuration objects
* ({@link SessionConfig}, {@link ResumeSessionConfig}) to JSON-RPC request
* objects for session creation and resumption.
*/
final class SessionRequestBuilder {
private SessionRequestBuilder() {
// Utility class
}
/**
* Extracts transform callbacks from a {@link SystemMessageConfig} and returns a
* wire-safe copy of the config alongside the extracted callbacks.
* <p>
* When the system message mode is {@link SystemMessageMode#CUSTOMIZE} and some
* sections have {@link SectionOverride#getTransform() transform} callbacks set,
* this method:
* <ol>
* <li>Removes the callbacks from the wire config (they must not be
* serialized).</li>
* <li>Replaces each transform section with
* {@link SectionOverrideAction#TRANSFORM} in the wire config.</li>
* <li>Returns the callbacks so they can be registered with the session.</li>
* </ol>
*
* @param systemMessage
* the system message config, may be {@code null}
* @return an {@link ExtractedTransforms} containing the wire-safe config and
* any extracted callbacks
*/
static ExtractedTransforms extractTransformCallbacks(SystemMessageConfig systemMessage) {
if (systemMessage == null || systemMessage.getMode() != SystemMessageMode.CUSTOMIZE
|| systemMessage.getSections() == null) {
return new ExtractedTransforms(systemMessage, null);
}
Map<String, Function<String, CompletableFuture<String>>> callbacks = new HashMap<>();
Map<String, SectionOverride> wireSections = new HashMap<>();
for (Map.Entry<String, SectionOverride> entry : systemMessage.getSections().entrySet()) {
String sectionId = entry.getKey();
SectionOverride override = entry.getValue();
if (override.getTransform() != null) {
callbacks.put(sectionId, override.getTransform());
wireSections.put(sectionId, new SectionOverride().setAction(SectionOverrideAction.TRANSFORM));
} else {
wireSections.put(sectionId, override);
}
}
if (callbacks.isEmpty()) {
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 ExtractedTransforms(wireConfig, callbacks);
}
/**
* Builds a CreateSessionRequest from the given configuration.
*
* @param config
* the session configuration (may be null)
* @param sessionId
* the pre-generated session ID to use
* @return the built request object
*/
static CreateSessionRequest buildCreateRequest(SessionConfig config, String sessionId) {
var request = new CreateSessionRequest();
// Always request permission callbacks to enable deny-by-default behavior
request.setRequestPermission(true);
// Always send envValueMode=direct for MCP servers
request.setEnvValueMode("direct");
request.setSessionId(sessionId);
if (config == null) {
return request;
}
request.setModel(config.getModel());
request.setClientName(config.getClientName());
request.setReasoningEffort(config.getReasoningEffort());
request.setTools(config.getTools());
request.setSystemMessage(config.getSystemMessage());
request.setAvailableTools(config.getAvailableTools());
request.setExcludedTools(config.getExcludedTools());
request.setProvider(config.getProvider());
request.setRequestUserInput(config.getOnUserInputRequest() != null ? true : null);
request.setHooks(config.getHooks() != null && config.getHooks().hasHooks() ? true : null);
request.setWorkingDirectory(config.getWorkingDirectory());
request.setStreaming(config.isStreaming() ? true : null);
request.setMcpServers(config.getMcpServers());
request.setCustomAgents(config.getCustomAgents());
request.setAgent(config.getAgent());
request.setInfiniteSessions(config.getInfiniteSessions());
request.setSkillDirectories(config.getSkillDirectories());
request.setDisabledSkills(config.getDisabledSkills());
request.setConfigDir(config.getConfigDir());
return request;
}
/**
* Builds a CreateSessionRequest from the given configuration.
*
* @param config
* the session configuration (may be null)
* @return the built request object
* @deprecated Use {@link #buildCreateRequest(SessionConfig, String)} instead.
*/
@Deprecated
static CreateSessionRequest buildCreateRequest(SessionConfig config) {
String sessionId = (config != null && config.getSessionId() != null)
? config.getSessionId()
: java.util.UUID.randomUUID().toString();
return buildCreateRequest(config, sessionId);
}
/**
* Builds a ResumeSessionRequest from the given session ID and configuration.
*
* @param sessionId
* the ID of the session to resume
* @param config
* the resume configuration (may be null)
* @return the built request object
*/
static ResumeSessionRequest buildResumeRequest(String sessionId, ResumeSessionConfig config) {
var request = new ResumeSessionRequest();
request.setSessionId(sessionId);
// Always request permission callbacks to enable deny-by-default behavior
request.setRequestPermission(true);
// Always send envValueMode=direct for MCP servers
request.setEnvValueMode("direct");
if (config == null) {
return request;
}
request.setModel(config.getModel());
request.setClientName(config.getClientName());
request.setReasoningEffort(config.getReasoningEffort());
request.setTools(config.getTools());
request.setSystemMessage(config.getSystemMessage());
request.setAvailableTools(config.getAvailableTools());
request.setExcludedTools(config.getExcludedTools());
request.setProvider(config.getProvider());
request.setRequestUserInput(config.getOnUserInputRequest() != null ? true : null);
request.setHooks(config.getHooks() != null && config.getHooks().hasHooks() ? true : null);
request.setWorkingDirectory(config.getWorkingDirectory());
request.setConfigDir(config.getConfigDir());
request.setDisableResume(config.isDisableResume() ? true : null);
request.setStreaming(config.isStreaming() ? true : null);
request.setMcpServers(config.getMcpServers());
request.setCustomAgents(config.getCustomAgents());
request.setAgent(config.getAgent());
request.setSkillDirectories(config.getSkillDirectories());
request.setDisabledSkills(config.getDisabledSkills());
request.setInfiniteSessions(config.getInfiniteSessions());
return request;
}
/**
* Configures a session with handlers from the given config.
*
* @param session
* the session to configure
* @param config
* the session configuration
*/
static void configureSession(CopilotSession session, SessionConfig config) {
if (config == null) {
return;
}
if (config.getTools() != null) {
session.registerTools(config.getTools());
}
if (config.getOnPermissionRequest() != null) {
session.registerPermissionHandler(config.getOnPermissionRequest());
}
if (config.getOnUserInputRequest() != null) {
session.registerUserInputHandler(config.getOnUserInputRequest());
}
if (config.getHooks() != null) {
session.registerHooks(config.getHooks());
}
if (config.getOnEvent() != null) {
session.on(config.getOnEvent());
}
}
/**
* Configures a resumed session with handlers from the given config.
*
* @param session
* the session to configure
* @param config
* the resume session configuration
*/
static void configureSession(CopilotSession session, ResumeSessionConfig config) {
if (config == null) {
return;
}
if (config.getTools() != null) {
session.registerTools(config.getTools());
}
if (config.getOnPermissionRequest() != null) {
session.registerPermissionHandler(config.getOnPermissionRequest());
}
if (config.getOnUserInputRequest() != null) {
session.registerUserInputHandler(config.getOnUserInputRequest());
}
if (config.getHooks() != null) {
session.registerHooks(config.getHooks());
}
if (config.getOnEvent() != null) {
session.on(config.getOnEvent());
}
}
}