- Run
copilot auth loginif this machine is not already authenticated. - Create an environment in the included starter before the timed session:
cd start-intro/python
python -m venv .venv
.\.venv\Scripts\python.exe -m pip install -r requirements.txtOn macOS/Linux, use .venv/bin/python instead of .\.venv\Scripts\python.exe.
Say: "We are building a Podcast Agent for The GitHub Podcast. It will let us choose a real episode, retrieve verified metadata from the official RSS feed, and turn those facts into sponsor-safe social copy."
Say: "We will begin with the smallest possible Copilot SDK conversation, then give it a purpose, an identity, and application-owned tools."
Start with main.py. It deliberately has named placeholders for client, is_authenticated, and session. Leave the streaming event handler and completion wait in place.
Replace client: CopilotClient with:
client = CopilotClient()
await client.start()Say: "The client is my connection to the Copilot runtime. I start it explicitly, so the application owns its lifecycle."
Replace is_authenticated = False and its if block with:
is_authenticated = (await client.get_auth_status()).isAuthenticated
if not is_authenticated:
await client.stop()
raise RuntimeError("Run 'copilot auth login' before continuing.")Say: "Before creating a session, I can ask the runtime whether this machine is signed in."
Replace session = None with:
session = await client.create_session(
model=MODEL,
streaming=True,
available_tools=[],
on_permission_request=PermissionHandler.approve_all,
)Widen the SDK import at the top of the file:
from copilot import CopilotClient, PermissionHandlerSay: "The session is the conversation. I chose the model, enabled streaming, and the event handler below already prints each text fragment as it arrives."
Say: "The handler answers permission requests. The empty tool allowlist removes tool capabilities for this exercise; approve-all alone is not a safety boundary."
Replace everything from # Step 5: Send the first message. to the end of
main() with this indented block. Keep the module's
if __name__ == "__main__": block:
try:
await session.send(
"Hello world! In one sentence, say what the Copilot SDK helps a Python app do."
)
await asyncio.wait_for(done.wait(), timeout=60)
if error is not None:
raise error
print()
finally:
await session.disconnect()
await client.stop()Say: "That is the basic shape: start a client, create a session, listen for events, and send a message. Once this loop works, we can evolve it into our Podcast Agent."
Run this Hello World checkpoint from start-intro/python:
.\.venv\Scripts\python.exe main.pyOn macOS/Linux:
.venv/bin/python main.pyExpected output: a streamed one-sentence answer, followed by SessionIdleData completing the program.
After Hello World, use the prewritten helpers to turn the same session into a grounded podcast workflow.
Say: "The conversation works. Now we will turn it into our Podcast Agent: a focused assistant that can research a selected GitHub Podcast episode and prepare launch copy without inventing facts."
Add imports:
from github_podcast_tools import (
get_latest,
get_latest_github_podcast_episodes,
get_github_podcast_episode,
pick_episode,
)
from model_selector import select_model
from permission_prompt import permission_promptNarrow the SDK import back, since permission_prompt replaces PermissionHandler below:
from copilot import CopilotClientAfter the auth check:
model = await select_model(client, MODEL)
latest_episodes = get_latest()
selected_episode = pick_episode(latest_episodes)Say: "This keeps the demo live. I can choose a model in the room, then choose from the real ten newest GitHub Podcast episodes. That selection becomes the Podcast Agent's assignment."
Create the session with tools:
session = await client.create_session(
model=model,
streaming=True,
tools=[get_github_podcast_episode, get_latest_github_podcast_episodes],
available_tools=["get_github_podcast_episode", "get_latest_github_podcast_episodes"],
on_permission_request=permission_prompt,
system_message={
"mode": "replace",
"content": "You are the launch assistant for The GitHub Podcast. Use supplied episode facts only.",
},
)Say: "The model does not get arbitrary access to my application. I grant two narrow, typed capabilities, allowlist them by name, and swap Hello World's approve-all handler for one that prompts me, so I remain the approval point before a tool executes."
Say: "These tools are what make this an agent rather than a generic chatbot: it can take action against a trusted data source that my application controls."
Say: "The system message uses replace, not append. My application supplies the complete agent identity and grounding rule for this session instead of inheriting the default prompt."
Replace the prompt with:
await session.send(
f"Use get_github_podcast_episode for the episode titled \"{selected_episode.title}\". "
"Return exactly a social headline and a sponsor-safe post under 280 characters. "
"Use only facts returned by the tool; do not invent guests, sponsors, topics, or links."
)Say: "The agent decides to call the episode tool, I approve the read-only lookup, and its response is grounded in the official feed rather than invented details."
Keep the event handler, completion wait, and cleanup unchanged. Replace only
the session.send(...) call inside the existing try block. Update the
model-status line to print the selected model.
Run the completed Podcast Agent from the same folder:
.\.venv\Scripts\python.exe main.pyOn macOS/Linux:
.venv/bin/python main.pyExpected milestones: model selection, ten-episode selection, [Tool call started], approval prompt, [Tool call complete], then streamed launch copy.