{"meta":{"title":"Fleet mode","intro":"Use fleet mode to split work across multiple sub-agents and combine their results in one parent session.","product":"GitHub Copilot","breadcrumbs":[{"href":"/zh/copilot","title":"GitHub Copilot"},{"href":"/zh/copilot/how-tos","title":"操作方法"},{"href":"/zh/copilot/how-tos/copilot-sdk","title":"Copilot SDK"},{"href":"/zh/copilot/how-tos/copilot-sdk/features","title":"功能"},{"href":"/zh/copilot/how-tos/copilot-sdk/features/fleet-mode","title":"Fleet Mode"}],"documentType":"article"},"body":"# Fleet mode\n\nUse fleet mode to split work across multiple sub-agents and combine their results in one parent session.\n\n<!-- markdownlint-disable GHD046 GHD005 -->\n\n<!-- Suppressed: GHD046 (outdated release terminology), GHD005 (hardcoded data variable) -->\n\n## When to use fleet mode\n\nFleet mode is useful when the work can be decomposed before execution and each unit can run without waiting for the others.\n\nGood fits include:\n\n* Multi-file refactors where each worker owns a file, package, or language SDK.\n* Batch reviews where each worker checks a separate diff, module, or alert group.\n* Parallel research across independent repositories, services, or feature areas.\n* Documentation refreshes where each worker owns a page or topic.\n* Migration tasks where each worker can validate its own slice and report back.\n\nAvoid fleet mode for:\n\n* Sequential tasks where step 2 needs the concrete output from step 1.\n* Tightly coupled edits where workers would contend for the same files.\n* Small tasks that one synchronous sub-agent or the parent agent can finish quickly.\n* Tasks that require continuous shared reasoning rather than clear ownership.\n\nFleet mode works best when the parent session can create clear units of work, assign one owner per unit, and define what each worker must return.\n\n## Starting fleet mode\n\nThe SDK exposes fleet mode through the session RPC namespace in several languages. The binding is experimental in the generated RPC surface; pin both the SDK and the Copilot CLI runtime if your application depends on it.\n\n### From within a session\n\nThe wire method is `session.fleet.start`. The optional `prompt` is combined with the runtime's fleet orchestration instructions.\n\n<div class=\"ghd-codetabs\">\n<div class=\"ghd-codetab\" data-lang=\"typescript\" data-label=\"TypeScript\"><div class=\"ghd-codetab-fallback-label\" role=\"heading\" aria-level=\"3\">TypeScript</div>\n\n```typescript\nconst result = await session.rpc.fleet.start({\n    prompt: \"Refactor each SDK package independently, then summarize the changes.\",\n});\n\nif (result.started) {\n    console.log(\"Fleet mode started\");\n}\n```\n\n</div>\n\n<div class=\"ghd-codetab\" data-lang=\"python\" data-label=\"Python\"><div class=\"ghd-codetab-fallback-label\" role=\"heading\" aria-level=\"3\">Python</div>\n\n```python\nfrom copilot.generated.rpc import FleetStartRequest\n\nresult = await session.rpc.fleet.start(\n    FleetStartRequest(\n        prompt=\"Review each service independently, then summarize the risks.\"\n    )\n)\n\nif result.started:\n    print(\"Fleet mode started\")\n```\n\n</div>\n\n<div class=\"ghd-codetab\" data-lang=\"go\" data-label=\"Go\"><div class=\"ghd-codetab-fallback-label\" role=\"heading\" aria-level=\"3\">Go</div>\n\n```golang\npackage main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\tcopilot \"github.com/github/copilot-sdk/go\"\n\t\"github.com/github/copilot-sdk/go/rpc\"\n)\n\nfunc main() {\n\tctx := context.Background()\n\tclient := copilot.NewClient(nil)\n\tsession, err := client.CreateSession(ctx, &copilot.SessionConfig{})\n\tif err != nil {\n\t\treturn\n\t}\n\n\tprompt := \"Update each package independently, then report validation results.\"\n\tresult, err := session.RPC.Fleet.Start(ctx, &rpc.FleetStartRequest{\n\t\tPrompt: &prompt,\n\t})\n\tif err != nil {\n\t\treturn\n\t}\n\tif result.Started {\n\t\tfmt.Println(\"Fleet mode started\")\n\t}\n}\n```\n\n```golang\nprompt := \"Update each package independently, then report validation results.\"\nresult, err := session.RPC.Fleet.Start(ctx, &rpc.FleetStartRequest{\n    Prompt: &prompt,\n})\nif err != nil {\n    return err\n}\nif result.Started {\n    fmt.Println(\"Fleet mode started\")\n}\n```\n\n</div>\n\n<div class=\"ghd-codetab\" data-lang=\"dotnet\" data-label=\".NET\"><div class=\"ghd-codetab-fallback-label\" role=\"heading\" aria-level=\"3\">.NET</div>\n\n```csharp\nusing GitHub.Copilot;\n\nawait using var client = new CopilotClient();\nawait using var session = await client.CreateSessionAsync(new SessionConfig());\n\nvar result = await session.Rpc.Fleet.StartAsync(\n    \"Audit each project independently, then summarize the findings.\");\n\nif (result.Started)\n{\n    Console.WriteLine(\"Fleet mode started\");\n}\n```\n\n```csharp\nvar result = await session.Rpc.Fleet.StartAsync(\n    \"Audit each project independently, then summarize the findings.\");\n\nif (result.Started)\n{\n    Console.WriteLine(\"Fleet mode started\");\n}\n```\n\n</div>\n\n<div class=\"ghd-codetab\" data-lang=\"rust\" data-label=\"Rust\"><div class=\"ghd-codetab-fallback-label\" role=\"heading\" aria-level=\"3\">Rust</div>\n\n```rust\nuse github_copilot_sdk::generated::api_types::FleetStartRequest;\n\nlet result = session\n    .rpc()\n    .fleet()\n    .start(FleetStartRequest {\n        prompt: Some(\"Research each crate independently, then summarize the plan.\".into()),\n    })\n    .await?;\n\nif result.started {\n    println!(\"Fleet mode started\");\n}\n```\n\n</div>\n\n</div>\n\nNative typed bindings for fleet mode were verified in Node.js/TypeScript, Python, Go, .NET, and Rust. A Java binding was not found in `java/src/main/java` on this branch, so Java examples are omitted until that surface is available.\n\n### From plan mode\n\nPlan-mode UIs can start fleet deployment by returning the `autopilot_fleet` exit action. The generated session event types describe it as:\n\n```typescript\ntype PlanModeExitAction =\n  | \"exit_only\"\n  | \"interactive\"\n  | \"autopilot\"\n  /** Exit plan mode and continue with parallel autonomous workers. */\n  | \"autopilot_fleet\";\n```\n\nUse this when a user approves a plan that already contains independent work items. Use `autopilot` for a single autonomous worker and `interactive` when the user should stay in the loop.\n\n## How sub-agents coordinate\n\nFleet mode relies on explicit coordination state instead of implicit shared memory. The parent agent decomposes the work into todos, each sub-agent owns one todo, and the orchestrator dispatches workers whose dependencies are already complete.\n\nThe canonical schema is:\n\n```sql\nCREATE TABLE todos (\n    id TEXT PRIMARY KEY,\n    title TEXT NOT NULL,\n    description TEXT,\n    status TEXT DEFAULT 'pending'\n);\n\nCREATE TABLE todo_deps (\n    todo_id TEXT,\n    depends_on TEXT,\n    PRIMARY KEY (todo_id, depends_on)\n);\n```\n\nEach todo moves through a small state machine:\n\n```text\npending -> in_progress -> done\n                       \\-> blocked\n```\n\nA sub-agent should:\n\n1. Claim exactly one ready todo by setting `status = 'in_progress'`.\n2. Work only on that todo's scope.\n3. Store its result in the conversation or relevant task output.\n4. Set `status = 'done'` when complete.\n5. Set `status = 'blocked'` when it cannot proceed, and include the reason.\n\nThe orchestrator can find work whose dependencies are satisfied with a query like:\n\n```sql\nSELECT t.*\nFROM todos t\nWHERE t.status = 'pending'\n  AND NOT EXISTS (\n      SELECT 1\n      FROM todo_deps td\n      JOIN todos dep ON td.depends_on = dep.id\n      WHERE td.todo_id = t.id\n        AND dep.status != 'done'\n  );\n```\n\nThis pattern gives every worker a clear owner and lets the parent session reason about what is ready, running, complete, or blocked.\n\n## Lifecycle hooks\n\nFleet mode invokes sub-agents through the runtime's task mechanism. The runtime emits hook activity for sub-agent tool calls: the runtime 1.0.52 changelog notes that `preToolUse`, `postToolUse`, `subagentStart`, and `subagentStop` fire correctly for sub-agent tool calls.\n\nA dedicated SDK hook callback for `subagentStart` or `subagentStop` was not found in the public SDK surface on this branch. SDK consumers can observe sub-agent activity through the generic session event stream, which includes events such as `subagent.started`, `subagent.completed`, `subagent.failed`, `subagent.selected`, and `subagent.deselected`.\n\n<div class=\"ghd-codetabs\">\n<div class=\"ghd-codetab\" data-lang=\"typescript\" data-label=\"TypeScript\"><div class=\"ghd-codetab-fallback-label\" role=\"heading\" aria-level=\"3\">TypeScript</div>\n\n```typescript\nsession.on((event) => {\n    if (event.type === \"subagent.started\") {\n        console.log(`Started ${event.data.agentDisplayName}`);\n    }\n\n    if (event.type === \"subagent.completed\") {\n        console.log(`Completed ${event.data.agentDisplayName}`);\n    }\n});\n```\n\n</div>\n\n<div class=\"ghd-codetab\" data-lang=\"python\" data-label=\"Python\"><div class=\"ghd-codetab-fallback-label\" role=\"heading\" aria-level=\"3\">Python</div>\n\n```python\nimport asyncio\nfrom copilot import CopilotClient\nfrom copilot.session import PermissionHandler\n\nasync def main():\n    client = CopilotClient()\n    await client.start()\n    session = await client.create_session(\n        on_permission_request=PermissionHandler.approve_all,\n    )\n\n    def handle_event(event):\n        if event.type == \"subagent.started\":\n            print(f\"Started {event.data.agent_display_name}\")\n        elif event.type == \"subagent.completed\":\n            print(f\"Completed {event.data.agent_display_name}\")\n\n    unsubscribe = session.on(handle_event)\n\nasyncio.run(main())\n```\n\n```python\ndef handle_event(event):\n    if event.type == \"subagent.started\":\n        print(f\"Started {event.data.agent_display_name}\")\n    elif event.type == \"subagent.completed\":\n        print(f\"Completed {event.data.agent_display_name}\")\n\nunsubscribe = session.on(handle_event)\n```\n\n</div>\n\n</div>\n\nFor hook configuration that is already exposed at the SDK layer, see [使用挂钩](/zh/copilot/how-tos/copilot-sdk/features/hooks). For sub-agent event payloads, see [自定义代理和子代理编排](/zh/copilot/how-tos/copilot-sdk/features/custom-agents).\n\n## Plugin sub-agents\n\nThe runtime can load plugins with `--plugin-dir`. Plugins loaded this way can register their agents as available `task(agent_type=...)` sub-agent types in prompt mode, which means fleet mode can dispatch to those plugin-provided worker types.\n\nThis is currently a runtime-level configuration pattern rather than a documented SDK-level registration API. Configure the Copilot CLI runtime with the plugin directory, then connect the SDK client to that runtime. Native SDK helpers for registering plugin sub-agent types may be added in the future.\n\nConceptually, a fleet prompt can then ask for a specific worker type:\n\n```text\nUse task(agent_type=\"security-review\") for each independent package.\nRun the workers in parallel and summarize only high-confidence findings.\n```\n\nKeep plugin-provided sub-agent types narrow and descriptive so the orchestrator can choose them reliably.\n\n## Best practices\n\n* Decompose the work into independent units before starting fleet mode.\n* Minimize dependencies between todos; dependencies reduce parallelism.\n* Give each todo a durable ID, a clear title, and a complete description.\n* Make each sub-agent own exactly one todo at a time.\n* Use background sub-agents for truly parallel work.\n* Use synchronous sub-agent calls for serialized steps or validation gates.\n* Provide each sub-agent with complete context; sub-agents are stateless across calls.\n* Include file paths, commands, expected outputs, and constraints in each worker prompt.\n* Do not dispatch a single background sub-agent; prefer a synchronous call or batch multiple workers in parallel.\n* Avoid assigning overlapping files to different workers unless the parent agent will reconcile conflicts explicitly.\n* Require every worker to report what it changed, how it validated the change, and what remains blocked.\n* Have the parent agent verify the combined result after workers finish.\n\n## Limitations and open questions\n\n* Fleet mode is exposed through generated session RPC bindings and is marked experimental in several SDKs.\n* The SQL todos pattern is the canonical coordination model in the runtime guidance, but whether it is a stable extensibility contract for SDK consumers is still an open question.\n* `subagentStart` and `subagentStop` are runtime hook names; this branch exposes sub-agent lifecycle to SDK consumers through the generic session event stream, not dedicated hook callbacks.\n* Plugin sub-agent registration is configured at the runtime layer through `--plugin-dir`; no SDK-level plugin registration helper was verified on this branch.\n* Java native typed bindings for `session.fleet.start` were not found in the Java SDK source on this branch.\n* Fleet mode does not remove the need for parent-agent review. Parallel workers can produce inconsistent assumptions that the orchestrator must reconcile.\n\n## See also\n\n* [自定义代理和子代理编排](/zh/copilot/how-tos/copilot-sdk/features/custom-agents)\n* [使用挂钩](/zh/copilot/how-tos/copilot-sdk/features/hooks)"}