diff --git a/src/main/java/com/github/copilot/sdk/CopilotSession.java b/src/main/java/com/github/copilot/sdk/CopilotSession.java
index 61ff49174..0a81cd719 100644
--- a/src/main/java/com/github/copilot/sdk/CopilotSession.java
+++ b/src/main/java/com/github/copilot/sdk/CopilotSession.java
@@ -98,6 +98,9 @@ public final class CopilotSession implements AutoCloseable {
private volatile EventErrorHandler eventErrorHandler;
private volatile EventErrorPolicy eventErrorPolicy = EventErrorPolicy.PROPAGATE_AND_LOG_ERRORS;
+ /** Tracks whether this session instance has been terminated via close(). */
+ private volatile boolean isTerminated = false;
+
/**
* Creates a new session with the given ID and RPC client.
*
@@ -186,11 +189,14 @@ public String getWorkspacePath() {
* @param handler
* the error handler, or {@code null} to use only the default logging
* behavior
+ * @throws IllegalStateException
+ * if this session has been terminated
* @see EventErrorHandler
* @see #setEventErrorPolicy(EventErrorPolicy)
* @since 1.0.8
*/
public void setEventErrorHandler(EventErrorHandler handler) {
+ ensureNotTerminated();
this.eventErrorHandler = handler;
}
@@ -224,11 +230,14 @@ public void setEventErrorHandler(EventErrorHandler handler) {
* @param policy
* the error policy (default is
* {@link EventErrorPolicy#PROPAGATE_AND_LOG_ERRORS})
+ * @throws IllegalStateException
+ * if this session has been terminated
* @see EventErrorPolicy
* @see #setEventErrorHandler(EventErrorHandler)
* @since 1.0.8
*/
public void setEventErrorPolicy(EventErrorPolicy policy) {
+ ensureNotTerminated();
if (policy == null) {
throw new NullPointerException("policy must not be null");
}
@@ -244,9 +253,12 @@ public void setEventErrorPolicy(EventErrorPolicy policy) {
* @param prompt
* the message text to send
* @return a future that resolves with the message ID assigned by the server
+ * @throws IllegalStateException
+ * if this session has been terminated
* @see #send(MessageOptions)
*/
public CompletableFuture send(String prompt) {
+ ensureNotTerminated();
return send(new MessageOptions().setPrompt(prompt));
}
@@ -260,9 +272,12 @@ public CompletableFuture send(String prompt) {
* the message text to send
* @return a future that resolves with the final assistant message event, or
* {@code null} if no assistant message was received
+ * @throws IllegalStateException
+ * if this session has been terminated
* @see #sendAndWait(MessageOptions)
*/
public CompletableFuture sendAndWait(String prompt) {
+ ensureNotTerminated();
return sendAndWait(new MessageOptions().setPrompt(prompt));
}
@@ -275,10 +290,13 @@ public CompletableFuture sendAndWait(String prompt) {
* @param options
* the message options containing the prompt and attachments
* @return a future that resolves with the message ID assigned by the server
+ * @throws IllegalStateException
+ * if this session has been terminated
* @see #sendAndWait(MessageOptions)
* @see #send(String)
*/
public CompletableFuture send(MessageOptions options) {
+ ensureNotTerminated();
var request = new SendMessageRequest();
request.setSessionId(sessionId);
request.setPrompt(options.getPrompt());
@@ -304,10 +322,13 @@ public CompletableFuture send(MessageOptions options) {
* {@code null} if no assistant message was received. The future
* completes exceptionally with a TimeoutException if the timeout
* expires.
+ * @throws IllegalStateException
+ * if this session has been terminated
* @see #sendAndWait(MessageOptions)
* @see #send(MessageOptions)
*/
public CompletableFuture sendAndWait(MessageOptions options, long timeoutMs) {
+ ensureNotTerminated();
var future = new CompletableFuture();
var lastAssistantMessage = new AtomicReference();
@@ -365,9 +386,12 @@ public CompletableFuture sendAndWait(MessageOptions optio
* the message options containing the prompt and attachments
* @return a future that resolves with the final assistant message event, or
* {@code null} if no assistant message was received
+ * @throws IllegalStateException
+ * if this session has been terminated
* @see #sendAndWait(MessageOptions, long)
*/
public CompletableFuture sendAndWait(MessageOptions options) {
+ ensureNotTerminated();
return sendAndWait(options, 60000);
}
@@ -397,11 +421,14 @@ public CompletableFuture sendAndWait(MessageOptions optio
* @param handler
* a callback to be invoked when a session event occurs
* @return a Closeable that, when closed, unsubscribes the handler
+ * @throws IllegalStateException
+ * if this session has been terminated
* @see #on(Class, Consumer)
* @see AbstractSessionEvent
* @see #setEventErrorPolicy(EventErrorPolicy)
*/
public Closeable on(Consumer handler) {
+ ensureNotTerminated();
eventHandlers.add(handler);
return () -> eventHandlers.remove(handler);
}
@@ -447,10 +474,13 @@ public Closeable on(Consumer handler) {
* @param handler
* a callback invoked when events of this type occur
* @return a Closeable that unsubscribes the handler when closed
+ * @throws IllegalStateException
+ * if this session has been terminated
* @see #on(Consumer)
* @see AbstractSessionEvent
*/
public Closeable on(Class eventType, Consumer handler) {
+ ensureNotTerminated();
Consumer wrapper = event -> {
if (eventType.isInstance(event)) {
handler.accept(eventType.cast(event));
@@ -708,9 +738,12 @@ CompletableFuture