{"meta":{"title":"ユーザー プロンプト送信後フック","intro":"onUserPromptSubmitted フックは、ユーザーがメッセージを送信したときに呼び出されます。 これは次の目的で使用されます。","product":"GitHub Copilot","breadcrumbs":[{"href":"/ja/copilot","title":"GitHub Copilot"},{"href":"/ja/copilot/how-tos","title":"方法"},{"href":"/ja/copilot/how-tos/copilot-sdk","title":"Copilot SDK"},{"href":"/ja/copilot/how-tos/copilot-sdk/hooks","title":"フックを使用する"},{"href":"/ja/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## 入力\n\n| フィールド       | タイプ    | 説明                          |\n| ----------- | ------ | --------------------------- |\n| `timestamp` | number | フックがトリガーされたときの Unix タイムスタンプ |\n| `cwd`       | 文字列    | 現在の作業ディレクトリ                 |\n| `prompt`    | 文字列    | ユーザーが送信したプロンプト              |\n\n## アウトプット\n\nプロンプトを変更せずに使用するには、 `null` または `undefined` を返します。 それ以外の場合は、次のいずれかのフィールドを持つオブジェクトを返します。\n\n| フィールド               | タイプ     | 説明                            |\n| ------------------- | ------- | ----------------------------- |\n| `modifiedPrompt`    | 文字列     | 元のプロンプトの代わりに使用するように変更されたプロンプト |\n| `additionalContext` | 文字列     | 会話に追加された追加のコンテキスト             |\n| `suppressOutput`    | boolean | trueは、アシスタントの応答出力を抑制します       |\n\n## Examples\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. \\*\\*\n   `additionalContext`に対して`modifiedPrompt`を使用\\*\\*する - コンテキストの追加は、プロンプトを書き直すよりも侵入が少なくなります。\n\n4. **アドバイザリ ガイダンスに `additionalContext` を使用**する: このフックは、プロンプトを拒否したり、ポリシーを適用したりすることはできません。\n   `session.send()`を呼び出す前に、ハード制限を適用します。\n\n5. **高速な処理を続ける** - このフックは、すべてのユーザー メッセージで実行されます。 低速な操作は避けてください。\n\n## こちらも参照ください\n\n* [フックを使用する](/ja/copilot/how-tos/copilot-sdk/hooks)\n* [セッションライフサイクルフック](/ja/copilot/how-tos/copilot-sdk/hooks/session-lifecycle)\n* [ツール使用前のフック](/ja/copilot/how-tos/copilot-sdk/hooks/pre-tool-use)"}