-
Notifications
You must be signed in to change notification settings - Fork 891
Description
Summary
session.rpc.fleet.start() is unusable for any non-trivial workload because it inherits the 30s default timeout from JsonRpcClient.request(), but the session.fleet.start RPC is a long-running blocking call that only responds once the fleet finishes and the session goes idle.
The problem
FleetApi.start() is a thin wrapper with no timeout parameter:
# copilot/generated/rpc.py
class FleetApi:
async def start(self, params: SessionFleetStartParams) -> SessionFleetStartResult:
params_dict = {k: v for k, v in params.to_dict().items() if v is not None}
params_dict["sessionId"] = self._session_id
return SessionFleetStartResult.from_dict(
await self._client.request("session.fleet.start", params_dict)
)It calls self._client.request(...) which defaults to 30s:
# copilot/jsonrpc.py
async def request(self, method, params=None, timeout=30.0):
...
return await asyncio.wait_for(future, timeout=timeout)Any fleet workload taking more than 30s raises asyncio.TimeoutError.
Observed behavior
I ran 5 tests against github-copilot-sdk==0.1.26rc0 / @github/copilot@0.0.411 to characterize fleet.start:
| Test | Result |
|---|---|
| Trivial plan (1 task) | fleet.start returned after 47.6s — same time as SESSION_IDLE |
| No plan at all | Returned after 8.3s with started=True (agent had nothing to do) |
| Typed API, trivial plan | Returned after 14.7s — happened to fit within 30s |
notify instead of request |
Fleet did not run at all — must be a request |
| Precise ordering (2 tasks) | FLEET_RETURNED and SESSION_IDLE at exactly the same time (23.04s) |
Key findings
fleet.startblocks until the fleet finishes — it returns at the same time asSESSION_IDLE, not when the fleet is "started"notifydoes not work —fleet.startmust be called as a JSON-RPC requeststarted=Trueis always returned (even with no plan), so the field doesn't convey meaningful status- The 30s default timeout causes
TimeoutErrorfor any real workload — forcing users to bypass the typed API
Current workaround
Bypass FleetApi.start() and call the raw RPC with a longer timeout:
params = SessionFleetStartParams(prompt="implement the plan")
params_dict = {k: v for k, v in params.to_dict().items() if v is not None}
params_dict["sessionId"] = session.session_id
raw_result = await session._client.request(
"session.fleet.start", params_dict, timeout=600.0
)
result = SessionFleetStartResult.from_dict(raw_result)Reproduction
import asyncio
from copilot import CopilotClient
from copilot.generated.rpc import Mode, SessionFleetStartParams, SessionModeSetParams
async def main():
client = CopilotClient()
await client.start()
session = await client.create_session(
{"model": "gpt-5-mini", "streaming": "true", "reasoning_effort": "low"}
)
await session.rpc.mode.set(SessionModeSetParams(mode=Mode.PLAN))
await session.send_and_wait(
{"prompt": "Create a plan with 4 independent tasks: write factorial.py, palindrome.py, fibonacci.py, bubblesort.py"},
timeout=120.0,
)
await session.rpc.mode.set(SessionModeSetParams(mode=Mode.AUTOPILOT))
# This will raise asyncio.TimeoutError for any plan that takes >30s
result = await session.rpc.fleet.start(
SessionFleetStartParams(prompt="implement the plan")
)
asyncio.run(main())Suggested fix
Make fleet.start return immediately with an acknowledgment (started=True) and let the caller wait via SESSION_IDLE events. This would match what the return type name (started) implies.
Environment
github-copilot-sdk: 0.1.26rc0@github/copilotCLI: 0.0.411- Python: 3.12
- macOS Darwin 25.2.0