{"meta":{"title":"Создайте своё первое приложение на базе Copilot","intro":"В этом уроке вы будете использовать Copilot SDK для создания помощника командной строки. Вы начнёте с основ, добавляете потоковые ответы, затем добавляете пользовательские инструменты — давая Copilot возможность вызывать ваш код.","product":"GitHub Copilot","breadcrumbs":[{"href":"/ru/copilot","title":"GitHub Copilot"},{"href":"/ru/copilot/how-tos","title":"Инструкции"},{"href":"/ru/copilot/how-tos/copilot-sdk","title":"Второй пилот SDK"},{"href":"/ru/copilot/how-tos/copilot-sdk/getting-started","title":"Getting Started"}],"documentType":"article"},"body":"# Создайте своё первое приложение на базе Copilot\n\nВ этом уроке вы будете использовать Copilot SDK для создания помощника командной строки. Вы начнёте с основ, добавляете потоковые ответы, затем добавляете пользовательские инструменты — давая Copilot возможность вызывать ваш код.\n\n<!-- markdownlint-disable GHD046 GHD005 -->\n\n<!-- Suppressed: GHD046 (outdated release terminology), GHD005 (hardcoded data variable) -->\n\n**Что вы создадите:**\n\n```text\nYou: What's the weather like in Seattle?\nCopilot: Let me check the weather for Seattle...\n         Currently 62°F and cloudy with a chance of rain.\n         Typical Seattle weather!\n\nYou: How about Tokyo?\nCopilot: In Tokyo it's 75°F and sunny. Great day to be outside!\n```\n\n## Необходимые условия\n\nПрежде чем начать, убедитесь, что у вас есть:\n\n* **GitHub Copilot ИНТЕРФЕЙС командной строки** установлен и прошел проверку подлинности (пакеты SDK Node.js, Python и .NET предоставляют интерфейс командной строки автоматически— см. раздел [Настройка по умолчанию (в комплекте CLI)](/ru/copilot/how-tos/copilot-sdk/setup/bundled-cli). Требуется для Go, Java и Rust, если только не используется их функции интерфейса командной строки на уровне приложения.)\n* Ваш предпочтительный языковой режим выполнения:\n  * **Node.js** 20+ или **Python** 3.11+ или **Go** 1.24+ или **Rust** 1.94+ или **Java** 17+ или **.NET** 8.0+\n\nУбедитесь, что CLI работает:\n\n```bash\ncopilot --version\n```\n\n## Шаг 1: установка SDK\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Сначала создайте новый каталог и инициализуйте проект:\n\n```bash\nmkdir copilot-demo && cd copilot-demo\nnpm init -y --init-type module\n```\n\nЗатем установите SDK и TypeScript runner:\n\n```bash\nnpm install @github/copilot-sdk tsx\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```bash\npip install github-copilot-sdk\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Во-первых, создайте новую директорию и инициализуйте модуль:\n\n```bash\nmkdir copilot-demo && cd copilot-demo\ngo mod init copilot-demo\n```\n\nЗатем установите SDK:\n\n```bash\ngo get github.com/github/copilot-sdk/go\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Сначала создайте новый двоичный ящик:\n\n```bash\ncargo new copilot-demo && cd copilot-demo\n```\n\nЗатем установите SDK и прямые зависимости, используемые в примерах:\n\n```bash\ncargo add github-copilot-sdk --features derive\n# Used by #[tokio::main] and tokio::spawn\ncargo add tokio --features rt-multi-thread,macros\n# Used by custom-tool parameter derives later in this guide\ncargo add serde --features derive\ncargo add schemars\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Во-первых, создайте новый консольный проект:\n\n```bash\ndotnet new console -n CopilotDemo && cd CopilotDemo\n```\n\nЗатем добавьте SDK:\n\n```bash\ndotnet add package GitHub.Copilot.SDK\n```\n\n</div>\n\n<div class=\"ghd-codetab\" data-lang=\"java\" data-label=\"Java\"><div class=\"ghd-codetab-fallback-label\" role=\"heading\" aria-level=\"3\">Java</div>\n\nСначала создайте новый каталог и инициализуйте свой проект.\n\n**Maven** — добавьте к вашему `pom.xml`:\n\n```xml\n<dependency>\n    <groupId>com.github</groupId>\n    <artifactId>copilot-sdk-java</artifactId>\n    <version>${copilot.sdk.version}</version>\n</dependency>\n```\n\n**Gradle** — добавьте к вашему `build.gradle`:\n\n```groovy\nimplementation 'com.github:copilot-sdk-java:${copilotSdkVersion}'\n```\n\n</div>\n\n</div>\n\n## Шаг 2: отправьте первое сообщение\n\nСоздайте новый файл и добавьте следующий код. Это самый простой способ использовать SDK — около 5 строк кода.\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Создайте `index.ts`:\n\n```typescript\nimport { CopilotClient } from \"@github/copilot-sdk\";\n\nconst client = new CopilotClient();\nconst session = await client.createSession({ model: \"auto\" });\n\nconst response = await session.sendAndWait({ prompt: \"What is 2 + 2?\" });\nconsole.log(response?.data.content);\n\nawait client.stop();\nprocess.exit(0);\n```\n\nЗапустите его:\n\n```bash\nnpx tsx index.ts\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Создайте `main.py`:\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\n    session = await client.create_session(on_permission_request=PermissionHandler.approve_all, model=\"auto\")\n    response = await session.send_and_wait(\"What is 2 + 2?\")\n    print(response.data.content)\n\n    await client.stop()\n\nasyncio.run(main())\n```\n\nЗапустите его:\n\n```bash\npython main.py\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Создайте `main.go`:\n\n```golang\npackage main\n\nimport (\n    \"context\"\n    \"fmt\"\n    \"log\"\n    \"os\"\n\n    copilot \"github.com/github/copilot-sdk/go\"\n)\n\nfunc main() {\n    ctx := context.Background()\n    client := copilot.NewClient(nil)\n    if err := client.Start(ctx); err != nil {\n        log.Fatal(err)\n    }\n    defer client.Stop()\n\n    session, err := client.CreateSession(ctx, &copilot.SessionConfig{Model: \"auto\"})\n    if err != nil {\n        log.Fatal(err)\n    }\n\n    response, err := session.SendAndWait(ctx, copilot.MessageOptions{Prompt: \"What is 2 + 2?\"})\n    if err != nil {\n        log.Fatal(err)\n    }\n\n    if d, ok := response.Data.(*copilot.AssistantMessageData); ok {\n        fmt.Println(d.Content)\n    }\n    os.Exit(0)\n}\n```\n\nЗапустите его:\n\n```bash\ngo run main.go\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Создайте `src/main.rs`:\n\n```rust\nuse std::sync::Arc;\nuse std::time::Duration;\n\nuse github_copilot_sdk::handler::ApproveAllHandler;\nuse github_copilot_sdk::{Client, ClientOptions, MessageOptions, SessionConfig};\n\n#[tokio::main]\nasync fn main() -> Result<(), Box<dyn std::error::Error>> {\n    let client = Client::start(ClientOptions::default()).await?;\n    let session = client\n        .create_session(SessionConfig::default().with_permission_handler(Arc::new(ApproveAllHandler)))\n        .await?;\n\n    let response = session\n        .send_and_wait(\n            MessageOptions::new(\"What is 2 + 2?\").with_wait_timeout(Duration::from_secs(120)),\n        )\n        .await?;\n\n    if let Some(event) = response {\n        if let Some(content) = event.data.get(\"content\").and_then(|value| value.as_str()) {\n            println!(\"{content}\");\n        }\n    }\n\n    session.disconnect().await?;\n    client.stop().await?;\n    Ok(())\n}\n```\n\nЗапустите его:\n\n```bash\ncargo run\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Создайте новый консольный проект и добавьте его в `Program.cs`:\n\n```csharp\nusing GitHub.Copilot;\n\nawait using var client = new CopilotClient();\nawait using var session = await client.CreateSessionAsync(new SessionConfig\n{\n    Model = \"auto\",\n    OnPermissionRequest = PermissionHandler.ApproveAll\n});\n\nvar response = await session.SendAndWaitAsync(new MessageOptions { Prompt = \"What is 2 + 2?\" });\nConsole.WriteLine(response?.Data.Content);\n```\n\nЗапустите его:\n\n```bash\ndotnet run\n```\n\n</div>\n\n<div class=\"ghd-codetab\" data-lang=\"java\" data-label=\"Java\"><div class=\"ghd-codetab-fallback-label\" role=\"heading\" aria-level=\"3\">Java</div>\n\nСоздайте `HelloCopilot.java`:\n\n<!-- docs-validate: skip -->\n\n```java\nimport com.github.copilot.CopilotClient;\nimport com.github.copilot.rpc.*;\n\npublic class HelloCopilot {\n    public static void main(String[] args) throws Exception {\n        try (var client = new CopilotClient()) {\n            client.start().get();\n\n            var session = client.createSession(\n                new SessionConfig()\n                    .setModel(\"auto\")\n                    .setOnPermissionRequest(PermissionHandler.APPROVE_ALL)\n            ).get();\n\n            var response = session.sendAndWait(\n                new MessageOptions().setPrompt(\"What is 2 + 2?\")\n            ).get();\n\n            System.out.println(response.getData().content());\n\n            client.stop().get();\n        }\n    }\n}\n```\n\nЗапустите его:\n\n```bash\njavac -cp copilot-sdk.jar HelloCopilot.java && java -cp .:copilot-sdk.jar HelloCopilot\n```\n\n</div>\n\n</div>\n\n**Вам стоит увидеть:**\n\n```text\n4\n```\n\nПоздравляем! Вы только что создали своё первое приложение на базе Copilot.\n\n## Шаг 3: добавляйте ответы на поток\n\nСейчас вы ждёте полного ответа, прежде чем что-либо увидеть. Давайте сделаем это интерактивным, транслируя ответы по мере их генерации.\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Обновление `index.ts`:\n\n```typescript\nimport { CopilotClient } from \"@github/copilot-sdk\";\n\nconst client = new CopilotClient();\nconst session = await client.createSession({\n    model: \"auto\",\n    streaming: true,\n});\n\n// Listen for response chunks\nsession.on(\"assistant.message_delta\", (event) => {\n    process.stdout.write(event.data.deltaContent);\n});\nsession.on(\"session.idle\", () => {\n    console.log(); // New line when done\n});\n\nawait session.sendAndWait({ prompt: \"Tell me a short joke\" });\n\nawait client.stop();\nprocess.exit(0);\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Обновление `main.py`:\n\n```python\nimport asyncio\nimport sys\nfrom copilot import CopilotClient\nfrom copilot.session import PermissionHandler\nfrom copilot.session_events import SessionEventType\n\nasync def main():\n    client = CopilotClient()\n    await client.start()\n\n    session = await client.create_session(on_permission_request=PermissionHandler.approve_all, model=\"auto\", streaming=True)\n\n    # Listen for response chunks\n    def handle_event(event):\n        if event.type == SessionEventType.ASSISTANT_MESSAGE_DELTA:\n            sys.stdout.write(event.data.delta_content)\n            sys.stdout.flush()\n        if event.type == SessionEventType.SESSION_IDLE:\n            print()  # New line when done\n\n    session.on(handle_event)\n\n    await session.send_and_wait(\"Tell me a short joke\")\n\n    await client.stop()\n\nasyncio.run(main())\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Обновление `main.go`:\n\n```golang\npackage main\n\nimport (\n    \"context\"\n    \"fmt\"\n    \"log\"\n    \"os\"\n\n    copilot \"github.com/github/copilot-sdk/go\"\n)\n\nfunc main() {\n    ctx := context.Background()\n    client := copilot.NewClient(nil)\n    if err := client.Start(ctx); err != nil {\n        log.Fatal(err)\n    }\n    defer client.Stop()\n\n    session, err := client.CreateSession(ctx, &copilot.SessionConfig{\n        Model:     \"auto\",\n        Streaming: copilot.Bool(true),\n    })\n    if err != nil {\n        log.Fatal(err)\n    }\n\n    // Listen for response chunks\n    session.On(func(event copilot.SessionEvent) {\n        switch d := event.Data.(type) {\n        case *copilot.AssistantMessageDeltaData:\n            fmt.Print(d.DeltaContent)\n        case *copilot.SessionIdleData:\n            _ = d\n            fmt.Println()\n        }\n    })\n\n    _, err = session.SendAndWait(ctx, copilot.MessageOptions{Prompt: \"Tell me a short joke\"})\n    if err != nil {\n        log.Fatal(err)\n    }\n    os.Exit(0)\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Обновление `src/main.rs`:\n\n```rust\nuse std::io::{self, Write};\nuse std::sync::Arc;\nuse std::time::Duration;\n\nuse github_copilot_sdk::handler::ApproveAllHandler;\nuse github_copilot_sdk::{Client, ClientOptions, MessageOptions, SessionConfig};\n\n#[tokio::main]\nasync fn main() -> Result<(), Box<dyn std::error::Error>> {\n    let client = Client::start(ClientOptions::default()).await?;\n\n    let mut config = SessionConfig::default();\n    config.streaming = Some(true);\n    let session = client\n        .create_session(config.with_permission_handler(Arc::new(ApproveAllHandler)))\n        .await?;\n\n    // Listen for response chunks\n    let mut events = session.subscribe();\n    tokio::spawn(async move {\n        while let Ok(event) = events.recv().await {\n            match event.event_type.as_str() {\n                \"assistant.message_delta\" => {\n                    if let Some(text) =\n                        event.data.get(\"deltaContent\").and_then(|value| value.as_str())\n                    {\n                        print!(\"{text}\");\n                        io::stdout().flush().ok();\n                    }\n                }\n                \"assistant.message\" => println!(),\n                _ => {}\n            }\n        }\n    });\n\n    session\n        .send_and_wait(\n            MessageOptions::new(\"Tell me a short joke\")\n                .with_wait_timeout(Duration::from_secs(120)),\n        )\n        .await?;\n\n    session.disconnect().await?;\n    client.stop().await?;\n    Ok(())\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Обновление `Program.cs`:\n\n```csharp\nusing GitHub.Copilot;\n\nawait using var client = new CopilotClient();\nawait using var session = await client.CreateSessionAsync(new SessionConfig\n{\n    Model = \"auto\",\n    OnPermissionRequest = PermissionHandler.ApproveAll,\n    Streaming = true,\n});\n\n// Listen for response chunks\nsession.On<SessionEvent>(ev =>\n{\n    if (ev is AssistantMessageDeltaEvent deltaEvent)\n    {\n        Console.Write(deltaEvent.Data.DeltaContent);\n    }\n    if (ev is SessionIdleEvent)\n    {\n        Console.WriteLine();\n    }\n});\n\nawait session.SendAndWaitAsync(new MessageOptions { Prompt = \"Tell me a short joke\" });\n```\n\n</div>\n\n<div class=\"ghd-codetab\" data-lang=\"java\" data-label=\"Java\"><div class=\"ghd-codetab-fallback-label\" role=\"heading\" aria-level=\"3\">Java</div>\n\nОбновление `HelloCopilot.java`:\n\n<!-- docs-validate: skip -->\n\n```java\nimport com.github.copilot.CopilotClient;\nimport com.github.copilot.rpc.*;\n\npublic class HelloCopilot {\n    public static void main(String[] args) throws Exception {\n        try (var client = new CopilotClient()) {\n            client.start().get();\n\n            var session = client.createSession(\n                new SessionConfig()\n                    .setModel(\"auto\")\n                    .setStreaming(true)\n                    .setOnPermissionRequest(PermissionHandler.APPROVE_ALL)\n            ).get();\n\n            // Listen for response chunks\n            session.on(AssistantMessageDeltaEvent.class, delta -> {\n                System.out.print(delta.getData().deltaContent());\n            });\n            session.on(SessionIdleEvent.class, idle -> {\n                System.out.println(); // New line when done\n            });\n\n            session.sendAndWait(\n                new MessageOptions().setPrompt(\"Tell me a short joke\")\n            ).get();\n\n            client.stop().get();\n        }\n    }\n}\n```\n\n</div>\n\n</div>\n\nЗапусти код ещё раз. Вы увидите, как ответ будет появляться слово за словом.\n\n### Методы подписки на события\n\nSDK предоставляет методы подписки на события сессии:\n\n| Метод                    | Description                                                                                  |\n| ------------------------ | -------------------------------------------------------------------------------------------- |\n| `on(handler)`            | Подписывайтесь на все события; Возвращает функцию отписки                                    |\n| `on(eventType, handler)` | Оформите подписку на конкретный тип события (Node.js/TypeScript); Возвращает функцию отписки |\n| `subscribe()`            | Подписывайтесь на все события (Rust); Фильтр по `event_type`                                 |\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\n// Subscribe to all events\nconst unsubscribeAll = session.on((event) => {\n    console.log(\"Event:\", event.type);\n});\n\n// Subscribe to specific event type\nconst unsubscribeIdle = session.on(\"session.idle\", (event) => {\n    console.log(\"Session is idle\");\n});\n\n// Later, to unsubscribe:\nunsubscribeAll();\nunsubscribeIdle();\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\n# Subscribe to all events\nunsubscribe = session.on(lambda event: print(f\"Event: {event.type}\"))\n\n# Filter by event type in your handler\ndef handle_event(event):\n    if event.type == SessionEventType.SESSION_IDLE:\n        print(\"Session is idle\")\n    elif event.type == SessionEventType.ASSISTANT_MESSAGE:\n        print(f\"Message: {event.data.content}\")\n\nunsubscribe = session.on(handle_event)\n\n# Later, to unsubscribe:\nunsubscribe()\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\n// Subscribe to all events\nunsubscribe := session.On(func(event copilot.SessionEvent) {\n    fmt.Println(\"Event:\", event.Type)\n})\n\n// Filter by event type in your handler\nsession.On(func(event copilot.SessionEvent) {\n    switch d := event.Data.(type) {\n    case *copilot.SessionIdleData:\n        _ = d\n        fmt.Println(\"Session is idle\")\n    case *copilot.AssistantMessageData:\n        fmt.Println(\"Message:\", d.Content)\n    }\n})\n\n// Later, to unsubscribe:\nunsubscribe()\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\nlet mut events = session.subscribe();\n\ntokio::spawn(async move {\n    while let Ok(event) = events.recv().await {\n        println!(\"Event: {}\", event.event_type);\n\n        match event.event_type.as_str() {\n            \"session.idle\" => println!(\"Session is idle\"),\n            \"assistant.message\" => {\n                if let Some(content) = event.data.get(\"content\").and_then(|value| value.as_str()) {\n                    println!(\"Message: {content}\");\n                }\n            }\n            _ => {}\n        }\n    }\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\n// Subscribe to all events\nvar unsubscribe = session.On<SessionEvent>(ev => Console.WriteLine($\"Event: {ev.Type}\"));\n\n// Filter by event type using pattern matching\nsession.On<SessionEvent>(ev =>\n{\n    switch (ev)\n    {\n        case SessionIdleEvent:\n            Console.WriteLine(\"Session is idle\");\n            break;\n        case AssistantMessageEvent msg:\n            Console.WriteLine($\"Message: {msg.Data.Content}\");\n            break;\n    }\n});\n\n// Later, to unsubscribe:\nunsubscribe.Dispose();\n```\n\n</div>\n\n<div class=\"ghd-codetab\" data-lang=\"java\" data-label=\"Java\"><div class=\"ghd-codetab-fallback-label\" role=\"heading\" aria-level=\"3\">Java</div>\n\n<!-- docs-validate: skip -->\n\n```java\n// Subscribe to all events\nvar unsubscribe = session.on(event -> {\n    System.out.println(\"Event: \" + event.getType());\n});\n\n// Subscribe to a specific event type\nsession.on(AssistantMessageEvent.class, msg -> {\n    System.out.println(\"Message: \" + msg.getData().content());\n});\n\nsession.on(SessionIdleEvent.class, idle -> {\n    System.out.println(\"Session is idle\");\n});\n\n// Later, to unsubscribe:\nunsubscribe.close();\n```\n\n</div>\n\n</div>\n\n## Шаг 4: добавить кастомный инструмент\n\nА теперь о сильной части. Давайте дадим Copilot возможность вызывать ваш код, определив кастомный инструмент. Мы создадим простой инструмент для поиска погоды.\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Обновление `index.ts`:\n\n```typescript\nimport { CopilotClient, defineTool } from \"@github/copilot-sdk\";\n\n// Define a tool that Copilot can call\nconst getWeather = defineTool(\"get_weather\", {\n    description: \"Get the current weather for a city\",\n    parameters: {\n        type: \"object\",\n        properties: {\n            city: { type: \"string\", description: \"The city name\" },\n        },\n        required: [\"city\"],\n    },\n    handler: async (args: { city: string }) => {\n        const { city } = args;\n        // In a real app, you'd call a weather API here\n        const conditions = [\"sunny\", \"cloudy\", \"rainy\", \"partly cloudy\"];\n        const temp = Math.floor(Math.random() * 30) + 50;\n        const condition = conditions[Math.floor(Math.random() * conditions.length)];\n        return { city, temperature: `${temp}°F`, condition };\n    },\n});\n\nconst client = new CopilotClient();\nconst session = await client.createSession({\n    model: \"auto\",\n    streaming: true,\n    tools: [getWeather],\n});\n\nsession.on(\"assistant.message_delta\", (event) => {\n    process.stdout.write(event.data.deltaContent);\n});\n\nsession.on(\"session.idle\", () => {\n    console.log(); // New line when done\n});\n\nawait session.sendAndWait({\n    prompt: \"What's the weather like in Seattle and Tokyo?\",\n});\n\nawait client.stop();\nprocess.exit(0);\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Обновление `main.py`:\n\n```python\nimport asyncio\nimport random\nimport sys\nfrom copilot import CopilotClient\nfrom copilot.session import PermissionHandler\nfrom copilot.tools import define_tool\nfrom copilot.session_events import SessionEventType\nfrom pydantic import BaseModel, Field\n\n# Define the parameters for the tool using Pydantic\nclass GetWeatherParams(BaseModel):\n    city: str = Field(description=\"The name of the city to get weather for\")\n\n# Define a tool that Copilot can call\n@define_tool(description=\"Get the current weather for a city\")\nasync def get_weather(params: GetWeatherParams) -> dict:\n    city = params.city\n    # In a real app, you'd call a weather API here\n    conditions = [\"sunny\", \"cloudy\", \"rainy\", \"partly cloudy\"]\n    temp = random.randint(50, 80)\n    condition = random.choice(conditions)\n    return {\"city\": city, \"temperature\": f\"{temp}°F\", \"condition\": condition}\n\nasync def main():\n    client = CopilotClient()\n    await client.start()\n\n    session = await client.create_session(on_permission_request=PermissionHandler.approve_all, model=\"auto\", streaming=True, tools=[get_weather])\n\n    def handle_event(event):\n        if event.type == SessionEventType.ASSISTANT_MESSAGE_DELTA:\n            sys.stdout.write(event.data.delta_content)\n            sys.stdout.flush()\n        if event.type == SessionEventType.SESSION_IDLE:\n            print()\n\n    session.on(handle_event)\n\n    await session.send_and_wait(\"What's the weather like in Seattle and Tokyo?\")\n\n    await client.stop()\n\nasyncio.run(main())\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Обновление `main.go`:\n\n```golang\npackage main\n\nimport (\n    \"context\"\n    \"fmt\"\n    \"log\"\n    \"math/rand\"\n    \"os\"\n\n    copilot \"github.com/github/copilot-sdk/go\"\n)\n\n// Define the parameter type\ntype WeatherParams struct {\n    City string `json:\"city\" jsonschema:\"The city name\"`\n}\n\n// Define the return type\ntype WeatherResult struct {\n    City        string `json:\"city\"`\n    Temperature string `json:\"temperature\"`\n    Condition   string `json:\"condition\"`\n}\n\nfunc main() {\n    ctx := context.Background()\n\n    // Define a tool that Copilot can call\n    getWeather := copilot.DefineTool(\n        \"get_weather\",\n        \"Get the current weather for a city\",\n        func(params WeatherParams, inv copilot.ToolInvocation) (WeatherResult, error) {\n            // In a real app, you'd call a weather API here\n            conditions := []string{\"sunny\", \"cloudy\", \"rainy\", \"partly cloudy\"}\n            temp := rand.Intn(30) + 50\n            condition := conditions[rand.Intn(len(conditions))]\n            return WeatherResult{\n                City:        params.City,\n                Temperature: fmt.Sprintf(\"%d°F\", temp),\n                Condition:   condition,\n            }, nil\n        },\n    )\n\n    client := copilot.NewClient(nil)\n    if err := client.Start(ctx); err != nil {\n        log.Fatal(err)\n    }\n    defer client.Stop()\n\n    session, err := client.CreateSession(ctx, &copilot.SessionConfig{\n        Model:     \"auto\",\n        Streaming: copilot.Bool(true),\n        Tools:     []copilot.Tool{getWeather},\n    })\n    if err != nil {\n        log.Fatal(err)\n    }\n\n    session.On(func(event copilot.SessionEvent) {\n        switch d := event.Data.(type) {\n        case *copilot.AssistantMessageDeltaData:\n            fmt.Print(d.DeltaContent)\n        case *copilot.SessionIdleData:\n            _ = d\n            fmt.Println()\n        }\n    })\n\n    _, err = session.SendAndWait(ctx, copilot.MessageOptions{\n        Prompt: \"What's the weather like in Seattle and Tokyo?\",\n    })\n    if err != nil {\n        log.Fatal(err)\n    }\n    os.Exit(0)\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Обновление `src/main.rs`:\n\n```rust\nuse std::io::{self, Write};\nuse std::sync::Arc;\nuse std::time::Duration;\n\nuse github_copilot_sdk::handler::ApproveAllHandler;\nuse github_copilot_sdk::tool::{define_tool, JsonSchema};\nuse github_copilot_sdk::{Client, ClientOptions, MessageOptions, SessionConfig, ToolResult};\nuse serde::Deserialize;\n\n#[derive(Deserialize, JsonSchema)]\nstruct GetWeatherParams {\n    city: String,\n}\n\n#[tokio::main]\nasync fn main() -> Result<(), Box<dyn std::error::Error>> {\n    // Define a tool that Copilot can call\n    let tools = vec![define_tool(\n        \"get_weather\",\n        \"Get the current weather for a city\",\n        |_inv, params: GetWeatherParams| async move {\n            Ok(ToolResult::Text(format!(\n                \"{}: 62°F and sunny\",\n                params.city\n            )))\n        },\n    )];\n\n    let client = Client::start(ClientOptions::default()).await?;\n\n    let mut config = SessionConfig::default();\n    config.streaming = Some(true);\n    let session = client\n        .create_session(\n            config\n                .with_tools(tools)\n                .with_permission_handler(Arc::new(ApproveAllHandler)),\n        )\n        .await?;\n\n    let mut events = session.subscribe();\n    tokio::spawn(async move {\n        while let Ok(event) = events.recv().await {\n            match event.event_type.as_str() {\n                \"assistant.message_delta\" => {\n                    if let Some(text) =\n                        event.data.get(\"deltaContent\").and_then(|value| value.as_str())\n                    {\n                        print!(\"{text}\");\n                        io::stdout().flush().ok();\n                    }\n                }\n                \"assistant.message\" => println!(),\n                _ => {}\n            }\n        }\n    });\n\n    session\n        .send_and_wait(\n            MessageOptions::new(\"What's the weather like in Seattle and Tokyo?\")\n                .with_wait_timeout(Duration::from_secs(120)),\n        )\n        .await?;\n\n    session.disconnect().await?;\n    client.stop().await?;\n    Ok(())\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Обновление `Program.cs`:\n\n```csharp\nusing GitHub.Copilot;\nusing Microsoft.Extensions.AI;\nusing System.ComponentModel;\n\nawait using var client = new CopilotClient();\n\n// Define a tool that Copilot can call\nvar getWeather = CopilotTool.DefineTool(\n    ([Description(\"The city name\")] string city) =>\n    {\n        // In a real app, you'd call a weather API here\n        var conditions = new[] { \"sunny\", \"cloudy\", \"rainy\", \"partly cloudy\" };\n        var temp = Random.Shared.Next(50, 80);\n        var condition = conditions[Random.Shared.Next(conditions.Length)];\n        return new { city, temperature = $\"{temp}°F\", condition };\n    },\n    factoryOptions: new AIFunctionFactoryOptions\n    {\n        Name = \"get_weather\",\n        Description = \"Get the current weather for a city\",\n    }\n);\n\nawait using var session = await client.CreateSessionAsync(new SessionConfig\n{\n    Model = \"auto\",\n    OnPermissionRequest = PermissionHandler.ApproveAll,\n    Streaming = true,\n    Tools = [getWeather],\n});\n\nsession.On<SessionEvent>(ev =>\n{\n    if (ev is AssistantMessageDeltaEvent deltaEvent)\n    {\n        Console.Write(deltaEvent.Data.DeltaContent);\n    }\n    if (ev is SessionIdleEvent)\n    {\n        Console.WriteLine();\n    }\n});\n\nawait session.SendAndWaitAsync(new MessageOptions\n{\n    Prompt = \"What's the weather like in Seattle and Tokyo?\",\n});\n```\n\n</div>\n\n<div class=\"ghd-codetab\" data-lang=\"java\" data-label=\"Java\"><div class=\"ghd-codetab-fallback-label\" role=\"heading\" aria-level=\"3\">Java</div>\n\nОбновление `HelloCopilot.java`:\n\n<!-- docs-validate: skip -->\n\n```java\nimport com.github.copilot.CopilotClient;\nimport com.github.copilot.rpc.*;\n\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Random;\nimport java.util.concurrent.CompletableFuture;\n\npublic class HelloCopilot {\n    public static void main(String[] args) throws Exception {\n        var random = new Random();\n        var conditions = List.of(\"sunny\", \"cloudy\", \"rainy\", \"partly cloudy\");\n\n        // Define a tool that Copilot can call\n        var getWeather = ToolDefinition.create(\n            \"get_weather\",\n            \"Get the current weather for a city\",\n            Map.of(\n                \"type\", \"object\",\n                \"properties\", Map.of(\n                    \"city\", Map.of(\"type\", \"string\", \"description\", \"The city name\")\n                ),\n                \"required\", List.of(\"city\")\n            ),\n            invocation -> {\n                var city = (String) invocation.getArguments().get(\"city\");\n                var temp = random.nextInt(30) + 50;\n                var condition = conditions.get(random.nextInt(conditions.size()));\n                return CompletableFuture.completedFuture(Map.of(\n                    \"city\", city,\n                    \"temperature\", temp + \"°F\",\n                    \"condition\", condition\n                ));\n            }\n        );\n\n        try (var client = new CopilotClient()) {\n            client.start().get();\n\n            var session = client.createSession(\n                new SessionConfig()\n                    .setModel(\"auto\")\n                    .setStreaming(true)\n                    .setTools(List.of(getWeather))\n                    .setOnPermissionRequest(PermissionHandler.APPROVE_ALL)\n            ).get();\n\n            session.on(AssistantMessageDeltaEvent.class, delta -> {\n                System.out.print(delta.getData().deltaContent());\n            });\n            session.on(SessionIdleEvent.class, idle -> {\n                System.out.println();\n            });\n\n            session.sendAndWait(\n                new MessageOptions().setPrompt(\"What's the weather like in Seattle and Tokyo?\")\n            ).get();\n\n            client.stop().get();\n        }\n    }\n}\n```\n\n</div>\n\n</div>\n\nЗапустите его, и вы увидите, как Copilot позвонит в ваш инструмент для получения погодных данных, а затем ответит с результатами!\n\n## Шаг 5: создайте интерактивного помощника\n\nДавайте объединим всё это в полезного интерактивного помощника:\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\nimport { CopilotClient, defineTool } from \"@github/copilot-sdk\";\nimport * as readline from \"readline\";\n\nconst getWeather = defineTool(\"get_weather\", {\n    description: \"Get the current weather for a city\",\n    parameters: {\n        type: \"object\",\n        properties: {\n            city: { type: \"string\", description: \"The city name\" },\n        },\n        required: [\"city\"],\n    },\n    handler: async ({ city }) => {\n        const conditions = [\"sunny\", \"cloudy\", \"rainy\", \"partly cloudy\"];\n        const temp = Math.floor(Math.random() * 30) + 50;\n        const condition = conditions[Math.floor(Math.random() * conditions.length)];\n        return { city, temperature: `${temp}°F`, condition };\n    },\n});\n\nconst client = new CopilotClient();\nconst session = await client.createSession({\n    model: \"auto\",\n    streaming: true,\n    tools: [getWeather],\n});\n\nsession.on(\"assistant.message_delta\", (event) => {\n    process.stdout.write(event.data.deltaContent);\n});\n\nconst rl = readline.createInterface({\n    input: process.stdin,\n    output: process.stdout,\n});\n\nconsole.log(\"🌤️  Weather Assistant (type 'exit' to quit)\");\nconsole.log(\"   Try: 'What's the weather in Paris?'\\n\");\n\nconst prompt = () => {\n    rl.question(\"You: \", async (input) => {\n        if (input.toLowerCase() === \"exit\") {\n            await client.stop();\n            rl.close();\n            return;\n        }\n\n        process.stdout.write(\"Assistant: \");\n        await session.sendAndWait({ prompt: input });\n        console.log(\"\\n\");\n        prompt();\n    });\n};\n\nprompt();\n```\n\nПроведите с:\n\n```bash\nnpx tsx weather-assistant.ts\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Создайте `weather_assistant.py`:\n\n```python\nimport asyncio\nimport random\nimport sys\nfrom copilot import CopilotClient\nfrom copilot.session import PermissionHandler\nfrom copilot.tools import define_tool\nfrom copilot.session_events import SessionEventType\nfrom pydantic import BaseModel, Field\n\nclass GetWeatherParams(BaseModel):\n    city: str = Field(description=\"The name of the city to get weather for\")\n\n@define_tool(description=\"Get the current weather for a city\")\nasync def get_weather(params: GetWeatherParams) -> dict:\n    city = params.city\n    conditions = [\"sunny\", \"cloudy\", \"rainy\", \"partly cloudy\"]\n    temp = random.randint(50, 80)\n    condition = random.choice(conditions)\n    return {\"city\": city, \"temperature\": f\"{temp}°F\", \"condition\": condition}\n\nasync def main():\n    client = CopilotClient()\n    await client.start()\n\n    session = await client.create_session(on_permission_request=PermissionHandler.approve_all, model=\"auto\", streaming=True, tools=[get_weather])\n\n    def handle_event(event):\n        if event.type == SessionEventType.ASSISTANT_MESSAGE_DELTA:\n            sys.stdout.write(event.data.delta_content)\n            sys.stdout.flush()\n\n    session.on(handle_event)\n\n    print(\"🌤️  Weather Assistant (type 'exit' to quit)\")\n    print(\"   Try: 'What's the weather in Paris?' or 'Compare weather in NYC and LA'\\n\")\n\n    while True:\n        try:\n            user_input = input(\"You: \")\n        except EOFError:\n            break\n\n        if user_input.lower() == \"exit\":\n            break\n\n        sys.stdout.write(\"Assistant: \")\n        await session.send_and_wait(user_input)\n        print(\"\\n\")\n\n    await client.stop()\n\nasyncio.run(main())\n```\n\nПроведите с:\n\n```bash\npython weather_assistant.py\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Создайте `weather-assistant.go`:\n\n```golang\npackage main\n\nimport (\n    \"bufio\"\n    \"context\"\n    \"fmt\"\n    \"log\"\n    \"math/rand\"\n    \"os\"\n    \"strings\"\n\n    copilot \"github.com/github/copilot-sdk/go\"\n)\n\ntype WeatherParams struct {\n    City string `json:\"city\" jsonschema:\"The city name\"`\n}\n\ntype WeatherResult struct {\n    City        string `json:\"city\"`\n    Temperature string `json:\"temperature\"`\n    Condition   string `json:\"condition\"`\n}\n\nfunc main() {\n    ctx := context.Background()\n\n    getWeather := copilot.DefineTool(\n        \"get_weather\",\n        \"Get the current weather for a city\",\n        func(params WeatherParams, inv copilot.ToolInvocation) (WeatherResult, error) {\n            conditions := []string{\"sunny\", \"cloudy\", \"rainy\", \"partly cloudy\"}\n            temp := rand.Intn(30) + 50\n            condition := conditions[rand.Intn(len(conditions))]\n            return WeatherResult{\n                City:        params.City,\n                Temperature: fmt.Sprintf(\"%d°F\", temp),\n                Condition:   condition,\n            }, nil\n        },\n    )\n\n    client := copilot.NewClient(nil)\n    if err := client.Start(ctx); err != nil {\n        log.Fatal(err)\n    }\n    defer client.Stop()\n\n    session, err := client.CreateSession(ctx, &copilot.SessionConfig{\n        Model:     \"auto\",\n        Streaming: copilot.Bool(true),\n        Tools:     []copilot.Tool{getWeather},\n    })\n    if err != nil {\n        log.Fatal(err)\n    }\n\n    session.On(func(event copilot.SessionEvent) {\n        switch d := event.Data.(type) {\n        case *copilot.AssistantMessageDeltaData:\n            fmt.Print(d.DeltaContent)\n        case *copilot.SessionIdleData:\n            _ = d\n            fmt.Println()\n        }\n    })\n\n    fmt.Println(\"🌤️  Weather Assistant (type 'exit' to quit)\")\n    fmt.Println(\"   Try: 'What's the weather in Paris?' or 'Compare weather in NYC and LA'\\n\")\n\n    scanner := bufio.NewScanner(os.Stdin)\n    for {\n        fmt.Print(\"You: \")\n        if !scanner.Scan() {\n            break\n        }\n        input := scanner.Text()\n        if strings.ToLower(input) == \"exit\" {\n            break\n        }\n\n        fmt.Print(\"Assistant: \")\n        _, err = session.SendAndWait(ctx, copilot.MessageOptions{Prompt: input})\n        if err != nil {\n            fmt.Fprintf(os.Stderr, \"Error: %v\\n\", err)\n            break\n        }\n        fmt.Println()\n    }\n    if err := scanner.Err(); err != nil {\n        fmt.Fprintf(os.Stderr, \"Input error: %v\\n\", err)\n    }\n}\n```\n\nПроведите с:\n\n```bash\ngo run weather-assistant.go\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Создайте `src/main.rs`:\n\n```rust\nuse std::io::{self, BufRead, Write};\nuse std::sync::Arc;\nuse std::time::Duration;\n\nuse github_copilot_sdk::handler::ApproveAllHandler;\nuse github_copilot_sdk::tool::{define_tool, JsonSchema};\nuse github_copilot_sdk::{Client, ClientOptions, MessageOptions, SessionConfig, ToolResult};\nuse serde::Deserialize;\n\n#[derive(Deserialize, JsonSchema)]\nstruct GetWeatherParams {\n    city: String,\n}\n\nfn read_line() -> Option<String> {\n    let stdin = io::stdin();\n    let mut line = String::new();\n    stdin.lock().read_line(&mut line).ok()?;\n    if line.is_empty() {\n        return None;\n    }\n    Some(line.trim_end_matches(&['\\n', '\\r'][..]).to_string())\n}\n\n#[tokio::main]\nasync fn main() -> Result<(), Box<dyn std::error::Error>> {\n    let tools = vec![define_tool(\n        \"get_weather\",\n        \"Get the current weather for a city\",\n        |_inv, params: GetWeatherParams| async move {\n            Ok(ToolResult::Text(format!(\n                \"{}: 62°F and sunny\",\n                params.city\n            )))\n        },\n    )];\n\n    let client = Client::start(ClientOptions::default()).await?;\n\n    let mut config = SessionConfig::default();\n    config.streaming = Some(true);\n    let session = client\n        .create_session(\n            config\n                .with_tools(tools)\n                .with_permission_handler(Arc::new(ApproveAllHandler)),\n        )\n        .await?;\n\n    let mut events = session.subscribe();\n    tokio::spawn(async move {\n        while let Ok(event) = events.recv().await {\n            match event.event_type.as_str() {\n                \"assistant.message_delta\" => {\n                    if let Some(text) =\n                        event.data.get(\"deltaContent\").and_then(|value| value.as_str())\n                    {\n                        print!(\"{text}\");\n                        io::stdout().flush().ok();\n                    }\n                }\n                \"assistant.message\" => println!(),\n                _ => {}\n            }\n        }\n    });\n\n    println!(\"Weather Assistant (type 'exit' to quit)\");\n    println!(\"Try: 'What's the weather in Paris?' or 'Compare weather in NYC and LA'\\n\");\n\n    loop {\n        print!(\"You: \");\n        io::stdout().flush().ok();\n\n        let Some(input) = read_line() else { break };\n        if input.eq_ignore_ascii_case(\"exit\") {\n            break;\n        }\n\n        print!(\"Assistant: \");\n        io::stdout().flush().ok();\n        session\n            .send_and_wait(MessageOptions::new(input).with_wait_timeout(Duration::from_secs(120)))\n            .await?;\n        println!();\n    }\n\n    session.disconnect().await?;\n    client.stop().await?;\n    Ok(())\n}\n```\n\nПроведите с:\n\n```bash\ncargo run\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Создайте новый консольный проект и обновите `Program.cs`его:\n\n```csharp\nusing GitHub.Copilot;\nusing Microsoft.Extensions.AI;\nusing System.ComponentModel;\n\n// Define the weather tool\nvar getWeather = CopilotTool.DefineTool(\n    ([Description(\"The city name\")] string city) =>\n    {\n        var conditions = new[] { \"sunny\", \"cloudy\", \"rainy\", \"partly cloudy\" };\n        var temp = Random.Shared.Next(50, 80);\n        var condition = conditions[Random.Shared.Next(conditions.Length)];\n        return new { city, temperature = $\"{temp}°F\", condition };\n    },\n    factoryOptions: new AIFunctionFactoryOptions\n    {\n        Name = \"get_weather\",\n        Description = \"Get the current weather for a city\",\n    });\n\nawait using var client = new CopilotClient();\nawait using var session = await client.CreateSessionAsync(new SessionConfig\n{\n    Model = \"auto\",\n    OnPermissionRequest = PermissionHandler.ApproveAll,\n    Streaming = true,\n    Tools = [getWeather]\n});\n\n// Listen for response chunks\nsession.On<SessionEvent>(ev =>\n{\n    if (ev is AssistantMessageDeltaEvent deltaEvent)\n    {\n        Console.Write(deltaEvent.Data.DeltaContent);\n    }\n    if (ev is SessionIdleEvent)\n    {\n        Console.WriteLine();\n    }\n});\n\nConsole.WriteLine(\"🌤️  Weather Assistant (type 'exit' to quit)\");\nConsole.WriteLine(\"   Try: 'What's the weather in Paris?' or 'Compare weather in NYC and LA'\\n\");\n\nwhile (true)\n{\n    Console.Write(\"You: \");\n    var input = Console.ReadLine();\n\n    if (string.IsNullOrEmpty(input) || input.Equals(\"exit\", StringComparison.OrdinalIgnoreCase))\n    {\n        break;\n    }\n\n    Console.Write(\"Assistant: \");\n    await session.SendAndWaitAsync(new MessageOptions { Prompt = input });\n    Console.WriteLine(\"\\n\");\n}\n```\n\nПроведите с:\n\n```bash\ndotnet run\n```\n\n</div>\n\n<div class=\"ghd-codetab\" data-lang=\"java\" data-label=\"Java\"><div class=\"ghd-codetab-fallback-label\" role=\"heading\" aria-level=\"3\">Java</div>\n\nСоздайте `WeatherAssistant.java`:\n\n<!-- docs-validate: skip -->\n\n```java\nimport com.github.copilot.CopilotClient;\nimport com.github.copilot.rpc.*;\n\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Random;\nimport java.util.Scanner;\nimport java.util.concurrent.CompletableFuture;\n\npublic class WeatherAssistant {\n    public static void main(String[] args) throws Exception {\n        var random = new Random();\n        var conditions = List.of(\"sunny\", \"cloudy\", \"rainy\", \"partly cloudy\");\n\n        var getWeather = ToolDefinition.create(\n            \"get_weather\",\n            \"Get the current weather for a city\",\n            Map.of(\n                \"type\", \"object\",\n                \"properties\", Map.of(\n                    \"city\", Map.of(\"type\", \"string\", \"description\", \"The city name\")\n                ),\n                \"required\", List.of(\"city\")\n            ),\n            invocation -> {\n                var city = (String) invocation.getArguments().get(\"city\");\n                var temp = random.nextInt(30) + 50;\n                var condition = conditions.get(random.nextInt(conditions.size()));\n                return CompletableFuture.completedFuture(Map.of(\n                    \"city\", city,\n                    \"temperature\", temp + \"°F\",\n                    \"condition\", condition\n                ));\n            }\n        );\n\n        try (var client = new CopilotClient()) {\n            client.start().get();\n\n            var session = client.createSession(\n                new SessionConfig()\n                    .setModel(\"auto\")\n                    .setStreaming(true)\n                    .setOnPermissionRequest(request ->\n                        CompletableFuture.completedFuture(PermissionDecision.allow())\n                    )\n                    .setTools(List.of(getWeather))\n            ).get();\n\n            session.on(AssistantMessageDeltaEvent.class, delta -> {\n                System.out.print(delta.getData().deltaContent());\n            });\n            session.on(SessionIdleEvent.class, idle -> {\n                System.out.println();\n            });\n\n            System.out.println(\"🌤️  Weather Assistant (type 'exit' to quit)\");\n            System.out.println(\"   Try: 'What's the weather in Paris?' or 'Compare weather in NYC and LA'\\n\");\n\n            var scanner = new Scanner(System.in);\n            while (true) {\n                System.out.print(\"You: \");\n                if (!scanner.hasNextLine()) break;\n                var input = scanner.nextLine();\n                if (input.equalsIgnoreCase(\"exit\")) break;\n\n                System.out.print(\"Assistant: \");\n                session.sendAndWait(\n                    new MessageOptions().setPrompt(input)\n                ).get();\n                System.out.println(\"\\n\");\n            }\n\n            client.stop().get();\n        }\n    }\n}\n```\n\nПроведите с:\n\n```bash\njavac -cp copilot-sdk.jar WeatherAssistant.java && java -cp .:copilot-sdk.jar WeatherAssistant\n```\n\n</div>\n\n</div>\n\n**Пример сеанса:**\n\n```text\n🌤️  Weather Assistant (type 'exit' to quit)\n   Try: 'What's the weather in Paris?' or 'Compare weather in NYC and LA'\n\nYou: What's the weather in Seattle?\nAssistant: Let me check the weather for Seattle...\nIt's currently 62°F and cloudy in Seattle.\n\nYou: How about Tokyo and London?\nAssistant: I'll check both cities for you:\n- Tokyo: 75°F and sunny\n- London: 58°F and rainy\n\nYou: exit\n```\n\nВы создали ассистента с помощью кастомного инструмента, который Copilot может вызвать!\n\n## Как работают средства\n\nКогда вы определяете инструмент, вы говорите Copilot:\n\n1. **Что делает инструмент** (описание)\n2. **Какие параметры нужны** (схема)\n3. **Какой код запускать** (обработчик)\n\nCopilot решает, когда вызвать ваш инструмент, исходя из вопроса пользователя. Когда это происходит:\n\n1. Copilot отправляет запрос на вызов инструмента с параметрами\n2. SDK запускает функцию обработчика\n3. Результат отправляется обратно в Copilot\n4. Copilot включает результат в свой ответ\n\n## Что дальше?\n\nТеперь, когда у вас есть основы, вот более мощные возможности для изучения:\n\n### Подключение к серверам MCP\n\nСерверы MCP (Model Context Protocol) предоставляют готовые инструменты. Подключитесь к MCP-серверу GitHub, чтобы дать Copilot доступ к репозиториям, проблемам и pull requests:\n\n```typescript\nconst session = await client.createSession({\n    mcpServers: {\n        github: {\n            type: \"http\",\n            url: \"https://api.githubcopilot.com/mcp/\",\n        },\n    },\n});\n```\n\n📖\n\\*\\*\n[Использование MCP-серверов с SDK GitHub Copilot](/ru/copilot/how-tos/copilot-sdk/features/mcp)\\*\\* — Узнайте о локальных и удалённых серверах, всех настройках и решении неполадок.\n\n### Создание настраиваемых агентов\n\nОпределите специализированные ИИ-персоны для конкретных задач:\n\n```typescript\nconst session = await client.createSession({\n    customAgents: [{\n        name: \"pr-reviewer\",\n        displayName: \"PR Reviewer\",\n        description: \"Reviews pull requests for best practices\",\n        prompt: \"You are an expert code reviewer. Focus on security, performance, and maintainability.\",\n    }],\n});\n```\n\n> \\[!TIP]\n> Вы также можете настроить `agent: \"pr-reviewer\"` в конфигурации сессии предвыбор этого агента с самого начала. Подробности смотрите [в АВТОЗАГОЛОВКЕ](/ru/copilot/how-tos/copilot-sdk/features/custom-agents#selecting-an-agent-at-session-creation) .\n\n### Настройка системного сообщения\n\nКонтролируйте поведение и личность ИИ, добавляя инструкции:\n\n```typescript\nconst session = await client.createSession({\n    systemMessage: {\n        content: \"You are a helpful assistant for our engineering team. Always be concise.\",\n    },\n});\n```\n\nДля более тонкого управления используйте `mode: \"customize\"` переобозначение отдельных разделов системного запроса, сохраняя остальные:\n\n```typescript\nconst session = await client.createSession({\n    systemMessage: {\n        mode: \"customize\",\n        sections: {\n            tone: { action: \"replace\", content: \"Respond in a warm, professional tone. Be thorough in explanations.\" },\n            code_change_rules: { action: \"remove\" },\n            guidelines: { action: \"append\", content: \"\\n* Always cite data sources\" },\n        },\n        content: \"Focus on financial analysis and reporting.\",\n    },\n});\n```\n\nДоступные идентификаторы разделов: `preamble`, `identity``tone``tool_efficiency``environment_context``code_change_rules``guidelines``safety``tool_instructions`, . `custom_instructions``runtime_instructions``last_instructions`\n\n`identity` и `tool_instructions` являются *группами* разделов: они нацелены на коллекцию связанных подразделов в виде единицы. Используйте `preamble` только предварительную версию удостоверения, не затрагивая его дочерние подразделы.\n\nКаждая переопределение поддерживает пять действий: `replace`, `remove`, `append`, `prepend`и `preserve`. Действие `preserve` представляет собой no-op, который выбирает отдельный адресируемый раздел из уровня группы `remove` (например, сохраняйте `tone` при удалении `identity` группы). Неизвестные идентификаторы разделов обрабатываются корректно: содержимое из `replace`/`append`/`prepend` переопределений добавляется к дополнительным инструкциям, а `remove` переопределения автоматически игнорируются.\n\nСм. языковые READMEs SDK для примеров в [TypeScript](https://github.com/github/copilot-sdk/tree/main/nodejs/README.md), [Python](https://github.com/github/copilot-sdk/tree/main/python/README.md), [Go](https://github.com/github/copilot-sdk/tree/main/go/README.md), [Rust](https://github.com/github/copilot-sdk/tree/main/rust/README.md), [Java](https://github.com/github/copilot-sdk/tree/main/java/README.md) и [C#](https://github.com/github/copilot-sdk/tree/main/dotnet/README.md).\n\n## Подключение к внешнему CLI-серверу\n\nПо умолчанию SDK автоматически управляет жизненным циклом процесса Copilot CLI, запуская и останавливая CLI по мере необходимости. Однако можно отдельно запустить CLI в серверном режиме и подключить к нему SDK. Это может быть полезно для:\n\n* **Отладка**: Оставляйте CLI включённым между перезагрузками SDK для проверки логов\n* **Совместное использование ресурсов**: несколько SDK-клиентов могут подключаться к одному и тому же CLI-серверу\n* **Разработка**: запускайте CLI с пользовательскими настройками или в другой среде\n\n### Запуск CLI в серверном режиме\n\nЗапустите CLI в серверном режиме с помощью `--headless` флага и, по желанию, укажите порт:\n\n```bash\ncopilot --headless --port 4321\n```\n\nЕсли вы не указываете порт, CLI выберет случайный доступный порт.\n\nПо умолчанию безголовый сервер принимает соединения только через loopback (`127.0.0.1`), поэтому SDK должен работать на той же машине. Чтобы принять соединения от других хостов (например, при запуске CLI в контейнере или на отдельном сервере), привяжьте к адресу без обратной связи с `--host`:\n\n```bash\n# Listen on all interfaces\ncopilot --headless --host 0.0.0.0 --port 4321\n```\n\n> \\[!WARNING]\n> Открытие безголового сервера на не-лупбэк-адресе делает его доступным для любого, кто может маршрутизироваться по этому адресу. Сочетайте его с сетевыми контролями (файрвол, частная сеть, обратный прокси) и аутентификацией, подходящей для вашей среды.\n\n### Подключение SDK к внешнему серверу\n\nКогда CLI заработает в серверном режиме, настройте SDK-клиент для подключения к нему через опцию «cli url»:\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\nimport { CopilotClient, approveAll } from \"@github/copilot-sdk\";\n\nconst client = new CopilotClient({\n    cliUrl: \"localhost:4321\"\n});\n\n// Use the client normally\nconst session = await client.createSession({ onPermissionRequest: approveAll });\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 import CopilotClient, RuntimeConnection\nfrom copilot.session import PermissionHandler\n\nclient = CopilotClient(connection=RuntimeConnection.for_uri(\"localhost:4321\"))\nawait client.start()\n\n# Use the client normally\nsession = await client.create_session(on_permission_request=PermissionHandler.approve_all)\n# ...\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\nimport copilot \"github.com/github/copilot-sdk/go\"\n\nclient := copilot.NewClient(&copilot.ClientOptions{\n    Connection: copilot.URIConnection{URL: \"localhost:4321\"},\n})\n\nif err := client.Start(ctx); err != nil {\n    log.Fatal(err)\n}\ndefer client.Stop()\n\n// Use the client normally\nsession, err := client.CreateSession(ctx, &copilot.SessionConfig{\n    OnPermissionRequest: copilot.PermissionHandler.ApproveAll,\n})\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 std::sync::Arc;\n\nuse github_copilot_sdk::handler::ApproveAllHandler;\nuse github_copilot_sdk::{Client, ClientOptions, SessionConfig, Transport};\n\nlet mut options = ClientOptions::default();\noptions.transport = Transport::External {\n    host: \"localhost\".to_string(),\n    port: 4321,\n    connection_token: None,\n};\nlet client = Client::start(options).await?;\n\n// Use the client normally\nlet session = client\n    .create_session(SessionConfig::default().with_permission_handler(Arc::new(ApproveAllHandler)))\n    .await?;\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\nusing var client = new CopilotClient(new CopilotClientOptions\n{\n    Connection = RuntimeConnection.ForUri(\"localhost:4321\"),\n});\n\n// Use the client normally\nawait using var session = await client.CreateSessionAsync(new()\n{\n    OnPermissionRequest = PermissionHandler.ApproveAll\n});\n// ...\n```\n\n</div>\n\n<div class=\"ghd-codetab\" data-lang=\"java\" data-label=\"Java\"><div class=\"ghd-codetab-fallback-label\" role=\"heading\" aria-level=\"3\">Java</div>\n\n```java\nimport com.github.copilot.CopilotClient;\nimport com.github.copilot.rpc.*;\n\nvar client = new CopilotClient(\n    new CopilotClientOptions().setCliUrl(\"localhost:4321\")\n);\nclient.start().get();\n\n// Use the client normally\nvar session = client.createSession(\n    new SessionConfig().setOnPermissionRequest(PermissionHandler.APPROVE_ALL)\n).get();\n// ...\n```\n\n</div>\n\n</div>\n\n**Примечание:** Когда `cli_url` / `cliUrl` предоставляется `URIConnection` / Go, или использует Rust `Transport::External`, SDK не создаёт и не управляет CLI-процессом — он подключается только к существующему серверу по указанному URL.\n\n## Телеметрия и наблюдаемость\n\nSDK Copilot поддерживает [OpenTelemetry](https://opentelemetry.io/) для распределённого трассировки. Предоставить клиенту `telemetry` конфигурацию для включения экспорта трасс из процесса CLI и автоматического распространения [контекста W3C Trace](https://www.w3.org/TR/trace-context/) между SDK и CLI.\n\n### Включение телеметрии\n\nПередайте конфигурацию `telemetry` (или `Telemetry`) при создании клиента. Это опция для включения — отдельный флаг «включено» не требуется.\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<!-- docs-validate: skip -->\n\n```typescript\nimport { CopilotClient } from \"@github/copilot-sdk\";\n\nconst client = new CopilotClient({\n  telemetry: {\n    otlpEndpoint: \"http://localhost:4318\",\n  },\n});\n```\n\nОпциональная зависимость от пиров: `@opentelemetry/api`\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<!-- docs-validate: skip -->\n\n```python\nfrom copilot import CopilotClient, CopilotClientOptions\n\nclient = CopilotClient(CopilotClientOptions(\n    telemetry={\n        \"otlp_endpoint\": \"http://localhost:4318\",\n    },\n))\n```\n\nУстановка с дополнительными элементами телеметрии: `pip install copilot-sdk[telemetry]` (предоставляет `opentelemetry-api`)\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<!-- docs-validate: skip -->\n\n```golang\nclient := copilot.NewClient(&copilot.ClientOptions{\n    Telemetry: &copilot.TelemetryConfig{\n        OTLPEndpoint: \"http://localhost:4318\",\n    },\n})\n```\n\nЗависимость: `go.opentelemetry.io/otel`\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<!-- docs-validate: skip -->\n\n```rust\nuse github_copilot_sdk::{Client, ClientOptions, OtelExporterType, TelemetryConfig};\n\nlet mut options = ClientOptions::default();\noptions.telemetry = Some(\n    TelemetryConfig::new()\n        .with_exporter_type(OtelExporterType::OtlpHttp)\n        .with_otlp_endpoint(\"http://localhost:4318\"),\n);\nlet client = Client::start(options).await?;\n```\n\nНикаких дополнительных зависимостей — SDK вводит переменные среды телеметрии для сгенерированного CLI-процесса.\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<!-- docs-validate: skip -->\n\n```csharp\nvar client = new CopilotClient(new CopilotClientOptions\n{\n    Telemetry = new TelemetryConfig\n    {\n        OtlpEndpoint = \"http://localhost:4318\",\n    },\n});\n```\n\nНет дополнительных зависимостей — использует встроенные `System.Diagnostics.Activity`.\n\n</div>\n\n<div class=\"ghd-codetab\" data-lang=\"java\" data-label=\"Java\"><div class=\"ghd-codetab-fallback-label\" role=\"heading\" aria-level=\"3\">Java</div>\n\n<!-- docs-validate: skip -->\n\n```java\nimport com.github.copilot.CopilotClient;\nimport com.github.copilot.rpc.*;\n\nvar client = new CopilotClient(new CopilotClientOptions()\n    .setTelemetry(new TelemetryConfig()\n        .setOtlpEndpoint(\"http://localhost:4318\")));\n```\n\nЗависимость: `io.opentelemetry:opentelemetry-api`\n\n</div>\n\n</div>\n\n### Опции TelemetryConfig\n\n| Опция                      | Node.js          | Python            | Go               | Rust              | Java             | .NET             | Description                                                               |\n| -------------------------- | ---------------- | ----------------- | ---------------- | ----------------- | ---------------- | ---------------- | ------------------------------------------------------------------------- |\n| Конечная точка OTLP        | `otlpEndpoint`   | `otlp_endpoint`   | `OTLPEndpoint`   | `otlp_endpoint`   | `otlpEndpoint`   | `OtlpEndpoint`   | URL конечной точки OTLP HTTP                                              |\n| Протокол OTLP              | `otlpProtocol`   | `otlp_protocol`   | `OTLPProtocol`   | `otlp_protocol`   | `otlpProtocol`   | `OtlpProtocol`   | Протокол OTLP HTTP для всех сигналов: `\"http/json\"` или `\"http/protobuf\"` |\n| Путь к файлу               | `filePath`       | `file_path`       | `FilePath`       | `file_path`       | `filePath`       | `FilePath`       | Путь файла для вывода JSON-lines trace                                    |\n| Тип экспортера             | `exporterType`   | `exporter_type`   | `ExporterType`   | `exporter_type`   | `exporterType`   | `ExporterType`   |                                                                           |\n| `\"otlp-http\"` или `\"file\"` |                  |                   |                  |                   |                  |                  |                                                                           |\n| Имя источника              | `sourceName`     | `source_name`     | `SourceName`     | `source_name`     | `sourceName`     | `SourceName`     | Название области приборов                                                 |\n| Захват контента            | `captureContent` | `capture_content` | `CaptureContent` | `capture_content` | `captureContent` | `CaptureContent` | Нужно ли фиксировать содержимое сообщений                                 |\n\nПоле протокола OTLP настраивает `\"otlp-http\"` экспортера CLI для всех сигналов. Оставьте его неустановленным, чтобы использовать CLI по умолчанию, или поставьте на `\"http/protobuf\"` экспорт protobuf через HTTP.\n\n### Экспорт файлов\n\nЧтобы записать трассы в локальный файл вместо конечной точки OTLP:\n\n<!-- docs-validate: skip -->\n\n```typescript\nconst client = new CopilotClient({\n  telemetry: {\n    filePath: \"./traces.jsonl\",\n    exporterType: \"file\",\n  },\n});\n```\n\n### Распространение контекста следов\n\nTrace context распространяется автоматически — не требуется ручное инструментирование:\n\n* **SDK → CLI**: `traceparent` и `tracestate` заголовки текущего диапазона/активности включены в `session.create`, `session.resume`и `session.send` RPC-вызовы.\n* **CLI → SDK**: Когда CLI вызывает обработчики инструментов, контекст трассировки из span-а CLI распространяется, чтобы ваш инструмент работал под правильным родительским span.\n\n📖\n\\*\\*\n[Инструментация OpenTelemetry для Copilot SDK](/ru/copilot/how-tos/copilot-sdk/observability/opentelemetry)\\*\\*—Опции TelemetryConfig, распространение контекста трассировки и зависимости по языкам.\n\n## Узнать больше\n\n* [Проверка подлинности](/ru/copilot/how-tos/copilot-sdk/auth/authenticate) - GitHub OAuth, переменные окружения и BYOK\n* [БЁК (принеси свой ключ)](/ru/copilot/how-tos/copilot-sdk/auth/byok) — используйте собственные ключи API из Microsoft Foundry, OpenAI и т. д.\n* [Node.js Справочник SDK](https://github.com/github/copilot-sdk/tree/main/nodejs/README.md)\n* [Справочник по пакету SDK Python](https://github.com/github/copilot-sdk/tree/main/python/README.md)\n* [Справочник Go SDK](https://github.com/github/copilot-sdk/tree/main/go/README.md)\n* [Справочник по пакету SDK Rust](https://github.com/github/copilot-sdk/tree/main/rust/README.md)\n* [.NET Ссылка на SDK](https://github.com/github/copilot-sdk/tree/main/dotnet/README.md)\n* [Java Ссылка на SDK](https://github.com/github/copilot-sdk/tree/main/java/README.md)\n* [Использование MCP-серверов с SDK GitHub Copilot](/ru/copilot/how-tos/copilot-sdk/features/mcp) — Интеграция внешних инструментов через протокол Model Context Protocol\n* [GitHub Документация MCP сервера](https://github.com/github/github-mcp-server)\n* [Каталог серверов MCP](https://github.com/modelcontextprotocol/servers) — Изучите больше серверов MCP\n* [Инструментация OpenTelemetry для Copilot SDK](/ru/copilot/how-tos/copilot-sdk/observability/opentelemetry) — TelemetryConfig, распространение контекста трассировки и зависимости по языкам\n\n**Ты сделал это!** Вы изучили основные концепции SDK GitHub Copilot:\n\n* ✅ Создание клиента и сессии\n* ✅ Отправка сообщений и получение ответов\n* ✅ Стриминг для выхода в реальном времени\n* ✅ Определение пользовательских инструментов, которые Copilot может вызывать\n\nА теперь иди и построй что-то потрясающее!\n🚀"}