Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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 @@ -20,11 +20,13 @@
* // Check the permission kind
* if ("dangerous-action".equals(request.getKind())) {
* // Deny dangerous actions
* return CompletableFuture.completedFuture(new PermissionRequestResult().setKind("user-denied"));
* return CompletableFuture
* .completedFuture(new PermissionRequestResult().setKind(PermissionRequestResultKind.REJECTED));
* }
*
* // Approve other requests
* return CompletableFuture.completedFuture(new PermissionRequestResult().setKind("user-approved"));
* return CompletableFuture
* .completedFuture(new PermissionRequestResult().setKind(PermissionRequestResultKind.APPROVED));
* };
* }</pre>
*
Expand Down
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