{"meta":{"title":"Хук, присланный пользовательским запросом","intro":"onUserPromptSubmitted Крюк вызывается, когда пользователь отправляет сообщение. Он используется для следующих задач:","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/hooks","title":"Используйте крючки"},{"href":"/ru/copilot/how-tos/copilot-sdk/hooks/user-prompt-submitted","title":"Отправлен пользовательский запрос"}],"documentType":"article"},"body":"# Хук, присланный пользовательским запросом\n\nonUserPromptSubmitted Крюк вызывается, когда пользователь отправляет сообщение. Он используется для следующих задач:\n\n<!-- markdownlint-disable GHD046 GHD005 -->\n\n<!-- Suppressed: GHD046 (outdated release terminology), GHD005 (hardcoded data variable) -->\n\n* Изменять или улучшать пользовательские подсказки\n* Добавьте контекст перед обработкой\n* Фильтруйте или проверяйте ввод пользователя\n* Реализация шаблонов запросов\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\ntype UserPromptSubmittedHandler = (\n  input: UserPromptSubmittedHookInput,\n  invocation: HookInvocation\n) => Promise<UserPromptSubmittedHookOutput | null | undefined>;\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\nUserPromptSubmittedHandler = Callable[\n    [UserPromptSubmittedHookInput, dict[str, str]],\n    Awaitable[UserPromptSubmittedHookOutput | None]\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\ntype UserPromptSubmittedHandler func(\n    input UserPromptSubmittedHookInput,\n    invocation HookInvocation,\n) (*UserPromptSubmittedHookOutput, error)\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\npublic delegate Task<UserPromptSubmittedHookOutput?> UserPromptSubmittedHandler(\n    UserPromptSubmittedHookInput input,\n    HookInvocation invocation);\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\n@FunctionalInterface\npublic interface UserPromptSubmittedHandler {\n    CompletableFuture<UserPromptSubmittedHookOutput> handle(\n        UserPromptSubmittedHookInput input,\n        HookInvocation invocation);\n}\n```\n\n</div>\n\n</div>\n\n## Input\n\n| Поле        | Тип    | Description                                 |\n| ----------- | ------ | ------------------------------------------- |\n| `timestamp` | number | Временная метка Unix, когда срабатывал крюк |\n| `cwd`       | string | Текущий рабочий справочник                  |\n| `prompt`    | string | Запрос пользователя, отправленный           |\n\n## Выходные данные\n\nВернуть `null` или `undefined` использовать запрос без изменений. В противном случае верните объект с любым из следующих полей:\n\n| Поле                | Тип     | Description                                                 |\n| ------------------- | ------- | ----------------------------------------------------------- |\n| `modifiedPrompt`    | string  | Изменённая подсказка для использования вместо оригинального |\n| `additionalContext` | string  | Дополнительный контекст в разговор                          |\n| `suppressOutput`    | boolean | Если это верно, подавить ответный сигнал ассистента         |\n\n## Примеры\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\nconst session = await client.createSession({\n  hooks: {\n    onUserPromptSubmitted: async (input, invocation) => {\n      console.log(`[${invocation.sessionId}] User: ${input.prompt}`);\n      return null; // Pass through unchanged\n    },\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\nfrom copilot.session import PermissionHandler\n\nasync def on_user_prompt_submitted(input_data, invocation):\n    print(f\"[{invocation['session_id']}] User: {input_data['prompt']}\")\n    return None\n\nsession = await client.create_session(on_permission_request=PermissionHandler.approve_all, hooks={\"on_user_prompt_submitted\": on_user_prompt_submitted})\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\nsession, _ := client.CreateSession(context.Background(), &copilot.SessionConfig{\n    Hooks: &copilot.SessionHooks{\n        OnUserPromptSubmitted: func(input copilot.UserPromptSubmittedHookInput, inv copilot.HookInvocation) (*copilot.UserPromptSubmittedHookOutput, error) {\n            fmt.Printf(\"[%s] User: %s\\n\", inv.SessionID, input.Prompt)\n            return nil, nil\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\nvar session = await client.CreateSessionAsync(new SessionConfig\n{\n    Hooks = new SessionHooks\n    {\n        OnUserPromptSubmitted = (input, invocation) =>\n        {\n            Console.WriteLine($\"[{invocation.SessionId}] User: {input.Prompt}\");\n            return Task.FromResult<UserPromptSubmittedHookOutput?>(null);\n        },\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<!-- docs-validate: skip -->\n\n```java\nimport com.github.copilot.*;\nimport com.github.copilot.rpc.*;\nimport java.util.concurrent.CompletableFuture;\n\nvar hooks = new SessionHooks()\n    .setOnUserPromptSubmitted((input, invocation) -> {\n        System.out.println(\"[\" + invocation.getSessionId() + \"] User: \" + input.prompt());\n        return CompletableFuture.completedFuture(null);\n    });\n\nvar session = client.createSession(\n    new SessionConfig()\n        .setOnPermissionRequest(PermissionHandler.APPROVE_ALL)\n        .setHooks(hooks)\n).get();\n```\n\n</div>\n\n</div>\n\n### Добавить контекст проекта\n\n```typescript\nconst session = await client.createSession({\n  hooks: {\n    onUserPromptSubmitted: async (input) => {\n      const projectInfo = await getProjectInfo();\n      \n      return {\n        additionalContext: `\nProject: ${projectInfo.name}\nLanguage: ${projectInfo.language}\nFramework: ${projectInfo.framework}\n        `.trim(),\n      };\n    },\n  },\n});\n```\n\n### Расширение команд сокращения\n\n```typescript\nconst SHORTCUTS: Record<string, string> = {\n  \"/fix\": \"Please fix the errors in the code\",\n  \"/explain\": \"Please explain this code in detail\",\n  \"/test\": \"Please write unit tests for this code\",\n  \"/refactor\": \"Please refactor this code to improve readability and maintainability\",\n};\n\nconst session = await client.createSession({\n  hooks: {\n    onUserPromptSubmitted: async (input) => {\n      for (const [shortcut, expansion] of Object.entries(SHORTCUTS)) {\n        if (input.prompt.startsWith(shortcut)) {\n          const rest = input.prompt.slice(shortcut.length).trim();\n          return {\n            modifiedPrompt: `${expansion}${rest ? `: ${rest}` : \"\"}`,\n          };\n        }\n      }\n      return null;\n    },\n  },\n});\n```\n\n### Фильтрование содержимого\n\n```typescript\nconst BLOCKED_PATTERNS = [\n  /password\\s*[:=]/i,\n  /api[_-]?key\\s*[:=]/i,\n  /secret\\s*[:=]/i,\n];\n\nconst session = await client.createSession({\n  hooks: {\n    onUserPromptSubmitted: async (input) => {\n      for (const pattern of BLOCKED_PATTERNS) {\n        if (pattern.test(input.prompt)) {\n          // Replace the prompt with a warning message\n          return {\n            modifiedPrompt: \"[Content blocked: Please don't include sensitive credentials in your prompts. Use environment variables instead.]\",\n            suppressOutput: true,\n          };\n        }\n      }\n      return null;\n    },\n  },\n});\n```\n\n### Обеспечение ограничений по длине запросов\n\n```typescript\nconst MAX_PROMPT_LENGTH = 10000;\n\nconst session = await client.createSession({\n  hooks: {\n    onUserPromptSubmitted: async (input) => {\n      if (input.prompt.length > MAX_PROMPT_LENGTH) {\n        // Truncate the prompt and add context\n        return {\n          modifiedPrompt: input.prompt.substring(0, MAX_PROMPT_LENGTH),\n          additionalContext: `Note: The original prompt was ${input.prompt.length} characters and was truncated to ${MAX_PROMPT_LENGTH} characters.`,\n        };\n      }\n      return null;\n    },\n  },\n});\n```\n\n### Добавить пользовательские предпочтения\n\n```typescript\ninterface UserPreferences {\n  codeStyle: \"concise\" | \"verbose\";\n  preferredLanguage: string;\n  experienceLevel: \"beginner\" | \"intermediate\" | \"expert\";\n}\n\nconst session = await client.createSession({\n  hooks: {\n    onUserPromptSubmitted: async (input) => {\n      const prefs: UserPreferences = await loadUserPreferences();\n      \n      const contextParts = [];\n      \n      if (prefs.codeStyle === \"concise\") {\n        contextParts.push(\"User prefers concise code with minimal comments.\");\n      } else {\n        contextParts.push(\"User prefers verbose code with detailed comments.\");\n      }\n      \n      if (prefs.experienceLevel === \"beginner\") {\n        contextParts.push(\"Explain concepts in simple terms.\");\n      }\n      \n      return {\n        additionalContext: contextParts.join(\" \"),\n      };\n    },\n  },\n});\n```\n\n### Уведомления об пороговом значении использования\n\n```typescript\nconst promptTimestamps: number[] = [];\nconst NOTICE_THRESHOLD = 10; // prompts\nconst RATE_WINDOW = 60000; // 1 minute\n\nconst session = await client.createSession({\n  hooks: {\n    onUserPromptSubmitted: async (input) => {\n      const now = Date.now();\n      \n      // Remove timestamps outside the window\n      while (promptTimestamps.length > 0 && promptTimestamps[0] < now - RATE_WINDOW) {\n        promptTimestamps.shift();\n      }\n\n      promptTimestamps.push(now);\n      if (promptTimestamps.length >= NOTICE_THRESHOLD) {\n        // This is advisory context for the model, not an enforced rate limit.\n        // Enforce hard limits before calling session.send().\n        return {\n          additionalContext: `The user has sent ${promptTimestamps.length} prompts in the last minute. Suggest waiting before sending more.`,\n        };\n      }\n\n      return null;\n    },\n  },\n});\n```\n\n### Шаблоны запросов\n\n```typescript\nconst TEMPLATES: Record<string, (args: string) => string> = {\n  \"bug:\": (desc) => `I found a bug: ${desc}\n\nPlease help me:\n1. Understand why this is happening\n2. Suggest a fix\n3. Explain how to prevent similar bugs`,\n\n  \"feature:\": (desc) => `I want to implement this feature: ${desc}\n\nPlease:\n1. Outline the implementation approach\n2. Identify potential challenges\n3. Provide sample code`,\n};\n\nconst session = await client.createSession({\n  hooks: {\n    onUserPromptSubmitted: async (input) => {\n      for (const [prefix, template] of Object.entries(TEMPLATES)) {\n        if (input.prompt.toLowerCase().startsWith(prefix)) {\n          const args = input.prompt.slice(prefix.length).trim();\n          return {\n            modifiedPrompt: template(args),\n          };\n        }\n      }\n      return null;\n    },\n  },\n});\n```\n\n## Лучшие практики\n\n1. **Сохраняйте пользовательское намерение** — при изменении запросов убедитесь, что основное намерение остаётся ясным.\n\n2. **Будьте прозрачны в отношении изменений** — если вы значительно изменили запрос, подумайте о логировании или уведомлении пользователя.\n\n3. **Use `additionalContext` over `modifiedPrompt`** — добавление контекста менее навязчиво, чем переписывание запроса.\n\n4. **Использование `additionalContext` рекомендаций.** Этот перехватчик не может отклонить запрос или применить политику. Принудительное применение жестких ограничений перед вызовом `session.send()`.\n\n5. **Держите обработку быстро** — этот крючок работает на каждом пользовательском сообщении. Избегайте медленных операций.\n\n## См. также\n\n* [Используйте крючки](/ru/copilot/how-tos/copilot-sdk/hooks)\n* [Крючки жизненного цикла сессии](/ru/copilot/how-tos/copilot-sdk/hooks/session-lifecycle)\n* [Крючок для использования до использования инструмента](/ru/copilot/how-tos/copilot-sdk/hooks/pre-tool-use)"}