Skip to content

Commit ef5b176

Browse files
committed
Add session lifecycle and user prompt hooks with corresponding handlers and inputs
1 parent 107185b commit ef5b176

12 files changed

Lines changed: 932 additions & 5 deletions

src/main/java/com/github/copilot/sdk/CopilotSession.java

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,12 +39,15 @@
3939
import com.github.copilot.sdk.json.PreToolUseHookInput;
4040
import com.github.copilot.sdk.json.SendMessageRequest;
4141
import com.github.copilot.sdk.json.SendMessageResponse;
42+
import com.github.copilot.sdk.json.SessionEndHookInput;
4243
import com.github.copilot.sdk.json.SessionHooks;
44+
import com.github.copilot.sdk.json.SessionStartHookInput;
4345
import com.github.copilot.sdk.json.ToolDefinition;
4446
import com.github.copilot.sdk.json.UserInputHandler;
4547
import com.github.copilot.sdk.json.UserInputInvocation;
4648
import com.github.copilot.sdk.json.UserInputRequest;
4749
import com.github.copilot.sdk.json.UserInputResponse;
50+
import com.github.copilot.sdk.json.UserPromptSubmittedHookInput;
4851

4952
/**
5053
* Represents a single conversation session with the Copilot CLI.
@@ -544,8 +547,30 @@ CompletableFuture<Object> handleHooksInvoke(String hookType, JsonNode input) {
544547
.thenApply(output -> (Object) output);
545548
}
546549
break;
550+
case "userPromptSubmitted" :
551+
if (hooks.getOnUserPromptSubmitted() != null) {
552+
UserPromptSubmittedHookInput promptInput =
553+
MAPPER.treeToValue(input, UserPromptSubmittedHookInput.class);
554+
return hooks.getOnUserPromptSubmitted().handle(promptInput, invocation)
555+
.thenApply(output -> (Object) output);
556+
}
557+
break;
558+
case "sessionStart" :
559+
if (hooks.getOnSessionStart() != null) {
560+
SessionStartHookInput startInput = MAPPER.treeToValue(input, SessionStartHookInput.class);
561+
return hooks.getOnSessionStart().handle(startInput, invocation)
562+
.thenApply(output -> (Object) output);
563+
}
564+
break;
565+
case "sessionEnd" :
566+
if (hooks.getOnSessionEnd() != null) {
567+
SessionEndHookInput endInput = MAPPER.treeToValue(input, SessionEndHookInput.class);
568+
return hooks.getOnSessionEnd().handle(endInput, invocation)
569+
.thenApply(output -> (Object) output);
570+
}
571+
break;
547572
default :
548-
LOG.warning("Unknown hook type: " + hookType);
573+
LOG.fine("Unhandled hook type: " + hookType);
549574
}
550575
} catch (Exception e) {
551576
LOG.log(Level.SEVERE, "Failed to process hook invocation", e);
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/*---------------------------------------------------------------------------------------------
2+
* Copyright (c) Microsoft Corporation. All rights reserved.
3+
*--------------------------------------------------------------------------------------------*/
4+
5+
package com.github.copilot.sdk.json;
6+
7+
import java.util.concurrent.CompletableFuture;
8+
9+
/**
10+
* Handler for session-end hooks.
11+
* <p>
12+
* This handler is invoked when a session ends, allowing you to perform cleanup
13+
* or logging.
14+
*
15+
* <h2>Example Usage</h2>
16+
*
17+
* <pre>{@code
18+
* SessionEndHandler handler = (input, invocation) -> {
19+
* System.out.println("Session ended: " + input.getReason());
20+
* return CompletableFuture.completedFuture(
21+
* new SessionEndHookOutput()
22+
* .setSessionSummary("Session completed successfully")
23+
* );
24+
* };
25+
* }</pre>
26+
*
27+
* @since 1.0.7
28+
*/
29+
@FunctionalInterface
30+
public interface SessionEndHandler {
31+
32+
/**
33+
* Handles a session end event.
34+
*
35+
* @param input
36+
* the hook input containing session end details
37+
* @param invocation
38+
* metadata about the hook invocation
39+
* @return a future that resolves with the hook output, or {@code null} to
40+
* proceed without modification
41+
*/
42+
CompletableFuture<SessionEndHookOutput> handle(SessionEndHookInput input, HookInvocation invocation);
43+
}
Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
/*---------------------------------------------------------------------------------------------
2+
* Copyright (c) Microsoft Corporation. All rights reserved.
3+
*--------------------------------------------------------------------------------------------*/
4+
5+
package com.github.copilot.sdk.json;
6+
7+
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
8+
import com.fasterxml.jackson.annotation.JsonProperty;
9+
10+
/**
11+
* Input for a session-end hook.
12+
* <p>
13+
* This hook is invoked when a session ends, allowing you to perform cleanup or
14+
* logging.
15+
*
16+
* @since 1.0.7
17+
*/
18+
@JsonIgnoreProperties(ignoreUnknown = true)
19+
public class SessionEndHookInput {
20+
21+
@JsonProperty("timestamp")
22+
private long timestamp;
23+
24+
@JsonProperty("cwd")
25+
private String cwd;
26+
27+
@JsonProperty("reason")
28+
private String reason;
29+
30+
@JsonProperty("finalMessage")
31+
private String finalMessage;
32+
33+
@JsonProperty("error")
34+
private String error;
35+
36+
/**
37+
* Gets the timestamp when the session ended.
38+
*
39+
* @return the timestamp in milliseconds since epoch
40+
*/
41+
public long getTimestamp() {
42+
return timestamp;
43+
}
44+
45+
/**
46+
* Sets the timestamp when the session ended.
47+
*
48+
* @param timestamp
49+
* the timestamp in milliseconds since epoch
50+
* @return this instance for method chaining
51+
*/
52+
public SessionEndHookInput setTimestamp(long timestamp) {
53+
this.timestamp = timestamp;
54+
return this;
55+
}
56+
57+
/**
58+
* Gets the current working directory.
59+
*
60+
* @return the current working directory
61+
*/
62+
public String getCwd() {
63+
return cwd;
64+
}
65+
66+
/**
67+
* Sets the current working directory.
68+
*
69+
* @param cwd
70+
* the current working directory
71+
* @return this instance for method chaining
72+
*/
73+
public SessionEndHookInput setCwd(String cwd) {
74+
this.cwd = cwd;
75+
return this;
76+
}
77+
78+
/**
79+
* Gets the reason for session end.
80+
*
81+
* @return the reason: "complete", "error", "abort", "timeout", or
82+
* "user_exit"
83+
*/
84+
public String getReason() {
85+
return reason;
86+
}
87+
88+
/**
89+
* Sets the reason for session end.
90+
*
91+
* @param reason
92+
* the reason: "complete", "error", "abort", "timeout", or
93+
* "user_exit"
94+
* @return this instance for method chaining
95+
*/
96+
public SessionEndHookInput setReason(String reason) {
97+
this.reason = reason;
98+
return this;
99+
}
100+
101+
/**
102+
* Gets the final message, if any.
103+
*
104+
* @return the final message, or {@code null}
105+
*/
106+
public String getFinalMessage() {
107+
return finalMessage;
108+
}
109+
110+
/**
111+
* Sets the final message.
112+
*
113+
* @param finalMessage
114+
* the final message
115+
* @return this instance for method chaining
116+
*/
117+
public SessionEndHookInput setFinalMessage(String finalMessage) {
118+
this.finalMessage = finalMessage;
119+
return this;
120+
}
121+
122+
/**
123+
* Gets the error message, if the session ended due to an error.
124+
*
125+
* @return the error message, or {@code null}
126+
*/
127+
public String getError() {
128+
return error;
129+
}
130+
131+
/**
132+
* Sets the error message.
133+
*
134+
* @param error
135+
* the error message
136+
* @return this instance for method chaining
137+
*/
138+
public SessionEndHookInput setError(String error) {
139+
this.error = error;
140+
return this;
141+
}
142+
}
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
/*---------------------------------------------------------------------------------------------
2+
* Copyright (c) Microsoft Corporation. All rights reserved.
3+
*--------------------------------------------------------------------------------------------*/
4+
5+
package com.github.copilot.sdk.json;
6+
7+
import java.util.List;
8+
9+
import com.fasterxml.jackson.annotation.JsonInclude;
10+
import com.fasterxml.jackson.annotation.JsonProperty;
11+
12+
/**
13+
* Output for a session-end hook.
14+
* <p>
15+
* Allows specifying cleanup actions and session summary.
16+
*
17+
* @since 1.0.7
18+
*/
19+
@JsonInclude(JsonInclude.Include.NON_NULL)
20+
public class SessionEndHookOutput {
21+
22+
@JsonProperty("suppressOutput")
23+
private Boolean suppressOutput;
24+
25+
@JsonProperty("cleanupActions")
26+
private List<String> cleanupActions;
27+
28+
@JsonProperty("sessionSummary")
29+
private String sessionSummary;
30+
31+
/**
32+
* Gets whether output should be suppressed.
33+
*
34+
* @return {@code true} to suppress output, or {@code null}
35+
*/
36+
public Boolean getSuppressOutput() {
37+
return suppressOutput;
38+
}
39+
40+
/**
41+
* Sets whether to suppress the output.
42+
*
43+
* @param suppressOutput
44+
* {@code true} to suppress output
45+
* @return this instance for method chaining
46+
*/
47+
public SessionEndHookOutput setSuppressOutput(Boolean suppressOutput) {
48+
this.suppressOutput = suppressOutput;
49+
return this;
50+
}
51+
52+
/**
53+
* Gets the list of cleanup actions.
54+
*
55+
* @return the cleanup actions, or {@code null}
56+
*/
57+
public List<String> getCleanupActions() {
58+
return cleanupActions;
59+
}
60+
61+
/**
62+
* Sets the cleanup actions to perform.
63+
*
64+
* @param cleanupActions
65+
* the cleanup actions
66+
* @return this instance for method chaining
67+
*/
68+
public SessionEndHookOutput setCleanupActions(List<String> cleanupActions) {
69+
this.cleanupActions = cleanupActions;
70+
return this;
71+
}
72+
73+
/**
74+
* Gets the session summary.
75+
*
76+
* @return the session summary, or {@code null}
77+
*/
78+
public String getSessionSummary() {
79+
return sessionSummary;
80+
}
81+
82+
/**
83+
* Sets a summary of the session.
84+
*
85+
* @param sessionSummary
86+
* the session summary
87+
* @return this instance for method chaining
88+
*/
89+
public SessionEndHookOutput setSessionSummary(String sessionSummary) {
90+
this.sessionSummary = sessionSummary;
91+
return this;
92+
}
93+
}

0 commit comments

Comments
 (0)