@@ -76,6 +76,7 @@ from copilot import CopilotClient
7676from copilot.session_events import AssistantMessageData, SessionIdleData
7777from copilot.session import PermissionHandler
7878
79+
7980async def main ():
8081 # Client automatically starts on enter and cleans up on exit
8182 async with CopilotClient() as client:
@@ -100,6 +101,7 @@ async def main():
100101 await session.send(" What is 2+2?" )
101102 await done.wait()
102103
104+
103105asyncio.run(main())
104106```
105107
@@ -114,6 +116,7 @@ from copilot import CopilotClient
114116from copilot.session_events import AssistantMessageData, SessionIdleData
115117from copilot.session import PermissionHandler
116118
119+
117120async def main ():
118121 client = CopilotClient()
119122 await client.start()
@@ -141,6 +144,7 @@ async def main():
141144 await session.disconnect()
142145 await client.stop()
143146
147+
144148asyncio.run(main())
145149```
146150
@@ -167,6 +171,7 @@ async with CopilotClient() as client:
167171 on_permission_request = PermissionHandler.approve_all,
168172 model = " gpt-5" ,
169173 ) as session:
174+
170175 def on_event (event ):
171176 print (f " Event: { event.type} " )
172177
@@ -287,14 +292,18 @@ session_id = await client.get_foreground_session_id()
287292# Request TUI to display a specific session (TUI+server mode only)
288293await client.set_foreground_session_id(" session-123" )
289294
295+
290296# Subscribe to all lifecycle events
291297def on_lifecycle (event ):
292298 print (f " { event.type} : { event.session_id} " )
293299
300+
294301unsubscribe = client.on_lifecycle(on_lifecycle)
295302
296303# Subscribe to specific event type
297- unsubscribe = client.on_lifecycle(" session.foreground" , lambda e : print (f " Foreground: { e.session_id} " ))
304+ unsubscribe = client.on_lifecycle(
305+ " session.foreground" , lambda e : print (f " Foreground: { e.session_id} " )
306+ )
298307
299308# Later, to stop receiving events:
300309unsubscribe()
@@ -316,14 +325,17 @@ Define tools with automatic JSON schema generation using the `@define_tool` deco
316325from pydantic import BaseModel, Field
317326from copilot import CopilotClient, define_tool
318327
328+
319329class LookupIssueParams (BaseModel ):
320330 id : str = Field(description = " Issue identifier" )
321331
332+
322333@define_tool (description = " Fetch issue details from our tracker" )
323334async def lookup_issue (params : LookupIssueParams) -> str :
324335 issue = await fetch_issue(params.id)
325336 return issue.summary
326337
338+
327339async with await client.create_session(
328340 on_permission_request = PermissionHandler.approve_all,
329341 model = " gpt-5" ,
@@ -343,6 +355,7 @@ from copilot import CopilotClient
343355from copilot.tools import Tool, ToolInvocation, ToolResult
344356from copilot.session import PermissionHandler
345357
358+
346359async def lookup_issue (invocation : ToolInvocation) -> ToolResult:
347360 issue_id = invocation.arguments[" id" ]
348361 issue = await fetch_issue(issue_id)
@@ -352,6 +365,7 @@ async def lookup_issue(invocation: ToolInvocation) -> ToolResult:
352365 session_log = f " Fetched issue { issue_id} " ,
353366 )
354367
368+
355369async with await client.create_session(
356370 on_permission_request = PermissionHandler.approve_all,
357371 model = " gpt-5" ,
@@ -471,6 +485,7 @@ from copilot.session_events import (
471485)
472486from copilot.session import PermissionHandler
473487
488+
474489async def main ():
475490 async with CopilotClient() as client:
476491 async with await client.create_session(
@@ -507,6 +522,7 @@ async def main():
507522 await session.send(" Tell me a short story" )
508523 await done.wait() # Wait for streaming to complete
509524
525+
510526asyncio.run(main())
511527```
512528
@@ -678,7 +694,10 @@ async with await client.create_session(
678694 system_message = {
679695 " mode" : " customize" ,
680696 " sections" : {
681- " tone" : {" action" : " replace" , " content" : " Respond in a warm, professional tone. Be thorough in explanations." },
697+ " tone" : {
698+ " action" : " replace" ,
699+ " content" : " Respond in a warm, professional tone. Be thorough in explanations." ,
700+ },
682701 " code_change_rules" : {" action" : " remove" },
683702 " guidelines" : {" action" : " append" , " content" : " \n * Always cite data sources" },
684703 },
@@ -698,6 +717,7 @@ You can also pass a transform callback as the `action` instead of a string. The
698717def redact_paths (content : str ) -> str :
699718 return content.replace(" /home/user" , " /***" )
700719
720+
701721async with await client.create_session(
702722 on_permission_request = PermissionHandler.approve_all,
703723 model = " gpt-5" ,
@@ -785,9 +805,7 @@ from copilot.rpc import (
785805from copilot.session_events import PermissionRequestShell
786806
787807
788- def on_permission_request (
789- request : PermissionRequest, invocation : dict
790- ) -> PermissionRequestResult:
808+ def on_permission_request (request : PermissionRequest, invocation : dict ) -> PermissionRequestResult:
791809 # ``PermissionRequest`` is a discriminated union — pattern-match on
792810 # the variant class to access the per-kind fields.
793811 match request:
@@ -871,6 +889,7 @@ async def handle_user_input(request, invocation):
871889 " wasFreeform" : True , # Whether the answer was freeform (not from choices)
872890 }
873891
892+
874893async with await client.create_session(
875894 on_permission_request = PermissionHandler.approve_all,
876895 model = " gpt-5" ,
@@ -893,12 +912,14 @@ async def on_pre_tool_use(input, invocation):
893912 " additionalContext" : " Extra context for the model" ,
894913 }
895914
915+
896916async def on_post_tool_use (input , invocation ):
897917 print (f " Tool { input [' toolName' ]} completed " )
898918 return {
899919 " additionalContext" : " Post-execution notes" ,
900920 }
901921
922+
902923async def on_post_tool_use_failure (input , invocation ):
903924 # Fires when a tool's result was a failure. `on_post_tool_use` only fires
904925 # on success, so register this handler to observe failed tool calls. The
@@ -908,27 +929,32 @@ async def on_post_tool_use_failure(input, invocation):
908929 " additionalContext" : f " Retry guidance for { input [' toolName' ]} " ,
909930 }
910931
932+
911933async def on_user_prompt_submitted (input , invocation ):
912934 print (f " User prompt: { input [' prompt' ]} " )
913935 return {
914936 " modifiedPrompt" : input [" prompt" ], # Optionally modify the prompt
915937 }
916938
939+
917940async def on_session_start (input , invocation ):
918941 print (f " Session started from: { input [' source' ]} " ) # "startup", "resume", "new"
919942 return {
920943 " additionalContext" : " Session initialization context" ,
921944 }
922945
946+
923947async def on_session_end (input , invocation ):
924948 print (f " Session ended: { input [' reason' ]} " )
925949
950+
926951async def on_error_occurred (input , invocation ):
927952 print (f " Error in { input [' errorContext' ]} : { input [' error' ]} " )
928953 return {
929954 " errorHandling" : " retry" , # "retry", "skip", or "abort"
930955 }
931956
957+
932958async with await client.create_session(
933959 on_permission_request = PermissionHandler.approve_all,
934960 model = " gpt-5" ,
@@ -962,13 +988,15 @@ Register slash commands that users can invoke from the CLI TUI. When the user ty
962988``` python
963989from copilot.session import CommandDefinition, CommandContext, PermissionHandler
964990
991+
965992async def handle_deploy (ctx : CommandContext) -> None :
966993 print (f " Deploying with args: { ctx.args} " )
967994 # ctx.session_id — the session where the command was invoked
968995 # ctx.command — full command text (e.g. "/deploy production")
969996 # ctx.command_name — command name without leading / (e.g. "deploy")
970997 # ctx.args — raw argument string (e.g. "production")
971998
999+
9721000async with await client.create_session(
9731001 on_permission_request = PermissionHandler.approve_all,
9741002 commands = [
@@ -1030,29 +1058,34 @@ Shows a text input dialog with optional constraints:
10301058name = await session.ui.input(" Enter your name:" )
10311059
10321060# With options
1033- email = await session.ui.input(" Enter email:" , {
1034- " title" : " Email Address" ,
1035- " description" : " We'll use this for notifications" ,
1036- " format" : " email" ,
1037- })
1061+ email = await session.ui.input(
1062+ " Enter email:" ,
1063+ {
1064+ " title" : " Email Address" ,
1065+ " description" : " We'll use this for notifications" ,
1066+ " format" : " email" ,
1067+ },
1068+ )
10381069```
10391070
10401071### Custom Elicitation
10411072
10421073For full control, use the ` elicitation() ` method with a custom JSON schema:
10431074
10441075``` python
1045- result = await session.ui.elicitation({
1046- " message" : " Configure deployment" ,
1047- " requestedSchema" : {
1048- " type" : " object" ,
1049- " properties" : {
1050- " region" : {" type" : " string" , " enum" : [" us-east-1" , " eu-west-1" ]},
1051- " replicas" : {" type" : " number" , " minimum" : 1 , " maximum" : 10 },
1076+ result = await session.ui.elicitation(
1077+ {
1078+ " message" : " Configure deployment" ,
1079+ " requestedSchema" : {
1080+ " type" : " object" ,
1081+ " properties" : {
1082+ " region" : {" type" : " string" , " enum" : [" us-east-1" , " eu-west-1" ]},
1083+ " replicas" : {" type" : " number" , " minimum" : 1 , " maximum" : 10 },
1084+ },
1085+ " required" : [" region" ],
10521086 },
1053- " required" : [" region" ],
1054- },
1055- })
1087+ }
1088+ )
10561089
10571090if result[" action" ] == " accept" :
10581091 region = result[" content" ][" region" ]
@@ -1066,6 +1099,7 @@ When the server (or an MCP tool) needs to ask the end-user a question, it sends
10661099``` python
10671100from copilot.session import ElicitationContext, ElicitationResult, PermissionHandler
10681101
1102+
10691103async def handle_elicitation (
10701104 context : ElicitationContext,
10711105) -> ElicitationResult:
@@ -1082,6 +1116,7 @@ async def handle_elicitation(
10821116 " content" : {" answer" : " yes" },
10831117 }
10841118
1119+
10851120async with await client.create_session(
10861121 on_permission_request = PermissionHandler.approve_all,
10871122 on_elicitation_request = handle_elicitation,
0 commit comments