@@ -336,6 +336,14 @@ public async Task<CopilotSession> CreateSessionAsync(SessionConfig? config = nul
336336 {
337337 var connection = await EnsureConnectedAsync ( cancellationToken ) ;
338338
339+ var hasHooks = config ? . Hooks != null && (
340+ config . Hooks . OnPreToolUse != null ||
341+ config . Hooks . OnPostToolUse != null ||
342+ config . Hooks . OnUserPromptSubmitted != null ||
343+ config . Hooks . OnSessionStart != null ||
344+ config . Hooks . OnSessionEnd != null ||
345+ config . Hooks . OnErrorOccurred != null ) ;
346+
339347 var request = new CreateSessionRequest (
340348 config ? . Model ,
341349 config ? . SessionId ,
@@ -345,6 +353,9 @@ public async Task<CopilotSession> CreateSessionAsync(SessionConfig? config = nul
345353 config ? . ExcludedTools ,
346354 config ? . Provider ,
347355 config ? . OnPermissionRequest != null ? true : null ,
356+ config ? . OnUserInputRequest != null ? true : null ,
357+ hasHooks ? true : null ,
358+ config ? . WorkingDirectory ,
348359 config ? . Streaming == true ? true : null ,
349360 config ? . McpServers ,
350361 config ? . CustomAgents ,
@@ -362,6 +373,14 @@ public async Task<CopilotSession> CreateSessionAsync(SessionConfig? config = nul
362373 {
363374 session . RegisterPermissionHandler ( config . OnPermissionRequest ) ;
364375 }
376+ if ( config ? . OnUserInputRequest != null )
377+ {
378+ session . RegisterUserInputHandler ( config . OnUserInputRequest ) ;
379+ }
380+ if ( config ? . Hooks != null )
381+ {
382+ session . RegisterHooks ( config . Hooks ) ;
383+ }
365384
366385 if ( ! _sessions . TryAdd ( response . SessionId , session ) )
367386 {
@@ -399,11 +418,23 @@ public async Task<CopilotSession> ResumeSessionAsync(string sessionId, ResumeSes
399418 {
400419 var connection = await EnsureConnectedAsync ( cancellationToken ) ;
401420
421+ var hasHooks = config ? . Hooks != null && (
422+ config . Hooks . OnPreToolUse != null ||
423+ config . Hooks . OnPostToolUse != null ||
424+ config . Hooks . OnUserPromptSubmitted != null ||
425+ config . Hooks . OnSessionStart != null ||
426+ config . Hooks . OnSessionEnd != null ||
427+ config . Hooks . OnErrorOccurred != null ) ;
428+
402429 var request = new ResumeSessionRequest (
403430 sessionId ,
404431 config ? . Tools ? . Select ( ToolDefinition . FromAIFunction ) . ToList ( ) ,
405432 config ? . Provider ,
406433 config ? . OnPermissionRequest != null ? true : null ,
434+ config ? . OnUserInputRequest != null ? true : null ,
435+ hasHooks ? true : null ,
436+ config ? . WorkingDirectory ,
437+ config ? . DisableResume == true ? true : null ,
407438 config ? . Streaming == true ? true : null ,
408439 config ? . McpServers ,
409440 config ? . CustomAgents ,
@@ -419,6 +450,14 @@ public async Task<CopilotSession> ResumeSessionAsync(string sessionId, ResumeSes
419450 {
420451 session . RegisterPermissionHandler ( config . OnPermissionRequest ) ;
421452 }
453+ if ( config ? . OnUserInputRequest != null )
454+ {
455+ session . RegisterUserInputHandler ( config . OnUserInputRequest ) ;
456+ }
457+ if ( config ? . Hooks != null )
458+ {
459+ session . RegisterHooks ( config . Hooks ) ;
460+ }
422461
423462 // Replace any existing session entry to ensure new config (like permission handler) is used
424463 _sessions [ response . SessionId ] = session ;
@@ -804,6 +843,8 @@ private async Task<Connection> ConnectToServerAsync(Process? cliProcess, string?
804843 rpc . AddLocalRpcMethod ( "session.event" , handler . OnSessionEvent ) ;
805844 rpc . AddLocalRpcMethod ( "tool.call" , handler . OnToolCall ) ;
806845 rpc . AddLocalRpcMethod ( "permission.request" , handler . OnPermissionRequest ) ;
846+ rpc . AddLocalRpcMethod ( "userInput.request" , handler . OnUserInputRequest ) ;
847+ rpc . AddLocalRpcMethod ( "hooks.invoke" , handler . OnHooksInvoke ) ;
807848 rpc . StartListening ( ) ;
808849 return new Connection ( rpc , cliProcess , tcpClient , networkStream ) ;
809850 }
@@ -990,6 +1031,37 @@ public async Task<PermissionRequestResponse> OnPermissionRequest(string sessionI
9901031 } ) ;
9911032 }
9921033 }
1034+
1035+ public async Task < UserInputRequestResponse > OnUserInputRequest ( string sessionId , string question , List < string > ? choices = null , bool ? allowFreeform = null )
1036+ {
1037+ var session = client . GetSession ( sessionId ) ;
1038+ if ( session == null )
1039+ {
1040+ throw new ArgumentException ( $ "Unknown session { sessionId } ") ;
1041+ }
1042+
1043+ var request = new UserInputRequest
1044+ {
1045+ Question = question ,
1046+ Choices = choices ,
1047+ AllowFreeform = allowFreeform
1048+ } ;
1049+
1050+ var result = await session . HandleUserInputRequestAsync ( request ) ;
1051+ return new UserInputRequestResponse ( result . Answer , result . WasFreeform ) ;
1052+ }
1053+
1054+ public async Task < HooksInvokeResponse > OnHooksInvoke ( string sessionId , string hookType , JsonElement input )
1055+ {
1056+ var session = client . GetSession ( sessionId ) ;
1057+ if ( session == null )
1058+ {
1059+ throw new ArgumentException ( $ "Unknown session { sessionId } ") ;
1060+ }
1061+
1062+ var output = await session . HandleHooksInvokeAsync ( hookType , input ) ;
1063+ return new HooksInvokeResponse ( output ) ;
1064+ }
9931065 }
9941066
9951067 private class Connection (
@@ -1024,6 +1096,9 @@ internal record CreateSessionRequest(
10241096 List < string > ? ExcludedTools ,
10251097 ProviderConfig ? Provider ,
10261098 bool ? RequestPermission ,
1099+ bool ? RequestUserInput ,
1100+ bool ? Hooks ,
1101+ string ? WorkingDirectory ,
10271102 bool ? Streaming ,
10281103 Dictionary < string , object > ? McpServers ,
10291104 List < CustomAgentConfig > ? CustomAgents ,
@@ -1050,6 +1125,10 @@ internal record ResumeSessionRequest(
10501125 List < ToolDefinition > ? Tools ,
10511126 ProviderConfig ? Provider ,
10521127 bool ? RequestPermission ,
1128+ bool ? RequestUserInput ,
1129+ bool ? Hooks ,
1130+ string ? WorkingDirectory ,
1131+ bool ? DisableResume ,
10531132 bool ? Streaming ,
10541133 Dictionary < string , object > ? McpServers ,
10551134 List < CustomAgentConfig > ? CustomAgents ,
@@ -1079,6 +1158,13 @@ internal record ToolCallResponse(
10791158 internal record PermissionRequestResponse (
10801159 PermissionRequestResult Result ) ;
10811160
1161+ internal record UserInputRequestResponse (
1162+ string Answer ,
1163+ bool WasFreeform ) ;
1164+
1165+ internal record HooksInvokeResponse (
1166+ object ? Output ) ;
1167+
10821168 /// <summary>Trace source that forwards all logs to the ILogger.</summary>
10831169 internal sealed class LoggerTraceSource : TraceSource
10841170 {
@@ -1131,6 +1217,7 @@ public override void WriteLine(string? message) =>
11311217 [ JsonSerializable ( typeof ( DeleteSessionRequest ) ) ]
11321218 [ JsonSerializable ( typeof ( DeleteSessionResponse ) ) ]
11331219 [ JsonSerializable ( typeof ( GetLastSessionIdResponse ) ) ]
1220+ [ JsonSerializable ( typeof ( HooksInvokeResponse ) ) ]
11341221 [ JsonSerializable ( typeof ( ListSessionsResponse ) ) ]
11351222 [ JsonSerializable ( typeof ( PermissionRequestResponse ) ) ]
11361223 [ JsonSerializable ( typeof ( PermissionRequestResult ) ) ]
@@ -1143,6 +1230,9 @@ public override void WriteLine(string? message) =>
11431230 [ JsonSerializable ( typeof ( ToolDefinition ) ) ]
11441231 [ JsonSerializable ( typeof ( ToolResultAIContent ) ) ]
11451232 [ JsonSerializable ( typeof ( ToolResultObject ) ) ]
1233+ [ JsonSerializable ( typeof ( UserInputRequestResponse ) ) ]
1234+ [ JsonSerializable ( typeof ( UserInputRequest ) ) ]
1235+ [ JsonSerializable ( typeof ( UserInputResponse ) ) ]
11461236 internal partial class ClientJsonContext : JsonSerializerContext ;
11471237}
11481238
0 commit comments