Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
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
2 changes: 1 addition & 1 deletion .lastmerge
Original file line number Diff line number Diff line change
@@ -1 +1 @@
922959f4a7b83509c3620d4881733c6c5677f00c
dd2dcbc439256acfb9feb2cff07c0b9c820091b8
5 changes: 5 additions & 0 deletions src/main/java/com/github/copilot/sdk/CliServerManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,11 @@ ProcessInfo startCliServer() throws IOException, InterruptedException {
args.add("--no-auto-login");
}

if (options.getSessionIdleTimeoutSeconds() != null && options.getSessionIdleTimeoutSeconds() > 0) {
args.add("--session-idle-timeout");
args.add(String.valueOf(options.getSessionIdleTimeoutSeconds()));
}

List<String> command = resolveCliCommand(cliPath, args);

var pb = new ProcessBuilder(command);
Expand Down
6 changes: 3 additions & 3 deletions src/main/java/com/github/copilot/sdk/CopilotSession.java
Original file line number Diff line number Diff line change
Expand Up @@ -1224,7 +1224,7 @@ CompletableFuture<PermissionRequestResult> handlePermissionRequest(JsonNode perm
PermissionHandler handler = permissionHandler.get();
if (handler == null) {
PermissionRequestResult result = new PermissionRequestResult();
result.setKind("denied-no-approval-rule-and-could-not-request-from-user");
result.setKind(PermissionRequestResultKind.USER_NOT_AVAILABLE);
return CompletableFuture.completedFuture(result);
}

Expand All @@ -1235,13 +1235,13 @@ CompletableFuture<PermissionRequestResult> handlePermissionRequest(JsonNode perm
return handler.handle(request, invocation).exceptionally(ex -> {
LOG.log(Level.SEVERE, "Permission handler threw an exception", ex);
PermissionRequestResult result = new PermissionRequestResult();
result.setKind("denied-no-approval-rule-and-could-not-request-from-user");
result.setKind(PermissionRequestResultKind.USER_NOT_AVAILABLE);
return result;
});
} catch (Exception e) {
LOG.log(Level.SEVERE, "Failed to process permission request", e);
PermissionRequestResult result = new PermissionRequestResult();
result.setKind("denied-no-approval-rule-and-could-not-request-from-user");
result.setKind(PermissionRequestResultKind.USER_NOT_AVAILABLE);
return CompletableFuture.completedFuture(result);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ static CreateSessionRequest buildCreateRequest(SessionConfig config, String sess
request.setIncludeSubAgentStreamingEvents(config.getIncludeSubAgentStreamingEvents());
request.setMcpServers(config.getMcpServers());
request.setCustomAgents(config.getCustomAgents());
request.setDefaultAgent(config.getDefaultAgent());
request.setAgent(config.getAgent());
request.setInfiniteSessions(config.getInfiniteSessions());
request.setSkillDirectories(config.getSkillDirectories());
Expand All @@ -135,6 +136,7 @@ static CreateSessionRequest buildCreateRequest(SessionConfig config, String sess
if (config.getOnElicitationRequest() != null) {
request.setRequestElicitation(true);
}
request.setGitHubToken(config.getGitHubToken());

return request;
}
Expand Down Expand Up @@ -194,6 +196,7 @@ static ResumeSessionRequest buildResumeRequest(String sessionId, ResumeSessionCo
request.setIncludeSubAgentStreamingEvents(config.getIncludeSubAgentStreamingEvents());
request.setMcpServers(config.getMcpServers());
request.setCustomAgents(config.getCustomAgents());
request.setDefaultAgent(config.getDefaultAgent());
request.setAgent(config.getAgent());
request.setSkillDirectories(config.getSkillDirectories());
request.setDisabledSkills(config.getDisabledSkills());
Expand All @@ -209,6 +212,7 @@ static ResumeSessionRequest buildResumeRequest(String sessionId, ResumeSessionCo
if (config.getOnElicitationRequest() != null) {
request.setRequestElicitation(true);
}
request.setGitHubToken(config.getGitHubToken());

return request;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ public class CopilotClientOptions {
private Supplier<CompletableFuture<List<ModelInfo>>> onListModels;
private int port;
private TelemetryConfig telemetry;
private Integer sessionIdleTimeoutSeconds;
private Boolean useLoggedInUser;
private boolean useStdio = true;

Expand Down Expand Up @@ -430,6 +431,37 @@ public CopilotClientOptions setTelemetry(TelemetryConfig telemetry) {
return this;
}

/**
* Gets the server-wide idle timeout for sessions in seconds.
*
* @return the session idle timeout in seconds, or {@code null} to disable
* (sessions live indefinitely)
* @since 1.3.0
*/
public Integer getSessionIdleTimeoutSeconds() {
return sessionIdleTimeoutSeconds;
}

/**
* Sets the server-wide idle timeout for sessions in seconds.
* <p>
* Sessions without activity for this duration are automatically cleaned up. Set
* to {@code 0} or leave as {@code null} to disable (sessions live
* indefinitely).
* <p>
* This option is only used when the SDK spawns the CLI process; it is ignored
* when connecting to an external server via {@link #setCliUrl(String)}.
*
* @param sessionIdleTimeoutSeconds
* the idle timeout in seconds, or {@code null} to disable
* @return this options instance for method chaining
* @since 1.3.0
*/
public CopilotClientOptions setSessionIdleTimeoutSeconds(Integer sessionIdleTimeoutSeconds) {
this.sessionIdleTimeoutSeconds = sessionIdleTimeoutSeconds;
return this;
}

/**
* Returns whether to use the logged-in user for authentication.
*
Expand Down Expand Up @@ -508,6 +540,7 @@ public CopilotClientOptions clone() {
copy.logLevel = this.logLevel;
copy.onListModels = this.onListModels;
copy.port = this.port;
copy.sessionIdleTimeoutSeconds = this.sessionIdleTimeoutSeconds;
copy.telemetry = this.telemetry;
copy.useLoggedInUser = this.useLoggedInUser;
copy.useStdio = this.useStdio;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,9 @@ public final class CreateSessionRequest {
@JsonProperty("customAgents")
private List<CustomAgentConfig> customAgents;

@JsonProperty("defaultAgent")
private DefaultAgentConfig defaultAgent;

@JsonProperty("agent")
private String agent;

Expand Down Expand Up @@ -106,6 +109,9 @@ public final class CreateSessionRequest {
@JsonProperty("modelCapabilities")
private ModelCapabilitiesOverride modelCapabilities;

@JsonProperty("gitHubToken")
private String gitHubToken;

/** Gets the model name. @return the model */
public String getModel() {
return model;
Expand Down Expand Up @@ -278,6 +284,18 @@ public void setCustomAgents(List<CustomAgentConfig> customAgents) {
this.customAgents = customAgents;
}

/** Gets the default agent config. @return the default agent config */
public DefaultAgentConfig getDefaultAgent() {
return defaultAgent;
}

/**
* Sets the default agent config. @param defaultAgent the default agent config
*/
public void setDefaultAgent(DefaultAgentConfig defaultAgent) {
this.defaultAgent = defaultAgent;
}

/** Gets the pre-selected agent name. @return the agent name */
public String getAgent() {
return agent;
Expand Down Expand Up @@ -382,4 +400,17 @@ public ModelCapabilitiesOverride getModelCapabilities() {
public void setModelCapabilities(ModelCapabilitiesOverride modelCapabilities) {
this.modelCapabilities = modelCapabilities;
}

/** Gets the GitHub token for per-session authentication. @return the token */
public String getGitHubToken() {
return gitHubToken;
}

/**
* Sets the GitHub token for per-session authentication. @param gitHubToken the
* token
*/
public void setGitHubToken(String gitHubToken) {
this.gitHubToken = gitHubToken;
}
}
59 changes: 59 additions & 0 deletions src/main/java/com/github/copilot/sdk/json/DefaultAgentConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
*--------------------------------------------------------------------------------------------*/

package com.github.copilot.sdk.json;

import java.util.Collections;
import java.util.List;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;

/**
* Configuration for the default agent (the built-in agent that handles turns
* when no custom agent is selected).
* <p>
* Use {@link #setExcludedTools(List)} to hide specific tools from the default
* agent while keeping them available to custom sub-agents.
*
* <h2>Example Usage</h2>
*
* <pre>{@code
* var config = new SessionConfig().setTools(List.of(secretTool))
* .setDefaultAgent(new DefaultAgentConfig().setExcludedTools(List.of("secret_tool")));
* }</pre>
*
* @see SessionConfig#setDefaultAgent(DefaultAgentConfig)
* @since 1.3.0
*/
@JsonInclude(JsonInclude.Include.NON_NULL)
public class DefaultAgentConfig {

@JsonProperty("excludedTools")
private List<String> excludedTools;

/**
* Gets the list of tool names excluded from the default agent.
*
* @return the list of excluded tool names, or {@code null} if not set
*/
public List<String> getExcludedTools() {
return excludedTools == null ? null : Collections.unmodifiableList(excludedTools);
}

/**
* Sets the list of tool names to exclude from the default agent.
* <p>
* These tools remain available to custom sub-agents that reference them in
* their {@link CustomAgentConfig#setTools(List)} list.
*
* @param excludedTools
* the list of tool names to exclude from the default agent
* @return this config for method chaining
*/
public DefaultAgentConfig setExcludedTools(List<String> excludedTools) {
this.excludedTools = excludedTools;
return this;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,37 +19,29 @@
*
* <h2>Well-known kinds</h2>
* <ul>
* <li>{@link #APPROVED} — the permission was approved.</li>
* <li>{@link #DENIED_BY_RULES} — the permission was denied by policy
* rules.</li>
* <li>{@link #DENIED_COULD_NOT_REQUEST_FROM_USER} — the permission was denied
* because no approval rule was found and the user could not be prompted.</li>
* <li>{@link #DENIED_INTERACTIVELY_BY_USER} — the permission was denied
* interactively by the user.</li>
* <li>{@link #APPROVED} — the permission was approved for this one
* instance.</li>
* <li>{@link #REJECTED} — the permission was denied interactively by the
* user.</li>
* <li>{@link #USER_NOT_AVAILABLE} — the permission was denied because user
* confirmation was unavailable.</li>
* <li>{@link #NO_RESULT} — no permission decision was made.</li>
* </ul>
*
* @see PermissionRequestResult
* @since 1.1.0
*/
public final class PermissionRequestResultKind {

/** The permission was approved. */
public static final PermissionRequestResultKind APPROVED = new PermissionRequestResultKind("approved");

/** The permission was denied by policy rules. */
public static final PermissionRequestResultKind DENIED_BY_RULES = new PermissionRequestResultKind(
"denied-by-rules");

/**
* The permission was denied because no approval rule was found and the user
* could not be prompted.
*/
public static final PermissionRequestResultKind DENIED_COULD_NOT_REQUEST_FROM_USER = new PermissionRequestResultKind(
"denied-no-approval-rule-and-could-not-request-from-user");
/** The permission was approved for this one instance. */
public static final PermissionRequestResultKind APPROVED = new PermissionRequestResultKind("approve-once");

/** The permission was denied interactively by the user. */
public static final PermissionRequestResultKind DENIED_INTERACTIVELY_BY_USER = new PermissionRequestResultKind(
"denied-interactively-by-user");
public static final PermissionRequestResultKind REJECTED = new PermissionRequestResultKind("reject");

/** The permission was denied because user confirmation was unavailable. */
public static final PermissionRequestResultKind USER_NOT_AVAILABLE = new PermissionRequestResultKind(
"user-not-available");

/**
* Leaves the pending permission request unanswered.
Expand All @@ -66,6 +58,24 @@ public final class PermissionRequestResultKind {
*/
public static final PermissionRequestResultKind NO_RESULT = new PermissionRequestResultKind("no-result");

/**
* @deprecated Use {@link #REJECTED} instead.
*/
@Deprecated
public static final PermissionRequestResultKind DENIED_INTERACTIVELY_BY_USER = REJECTED;

/**
* @deprecated Use {@link #USER_NOT_AVAILABLE} instead.
*/
@Deprecated
public static final PermissionRequestResultKind DENIED_COULD_NOT_REQUEST_FROM_USER = USER_NOT_AVAILABLE;

/**
* @deprecated Use {@link #USER_NOT_AVAILABLE} instead.
*/
@Deprecated
public static final PermissionRequestResultKind DENIED_BY_RULES = USER_NOT_AVAILABLE;

private final String value;

/**
Expand Down
Loading
Loading