The onUserPromptSubmitted hook is called when a user submits a message. Use it to:
- Modify or enhance user prompts
- Add context before processing
- Filter or validate user input
- Implement prompt templates
Node.js / TypeScript
import type { UserPromptSubmittedHookInput, HookInvocation, UserPromptSubmittedHookOutput } from "@github/copilot-sdk";
type UserPromptSubmittedHandler = (
input: UserPromptSubmittedHookInput,
invocation: HookInvocation
) => Promise<UserPromptSubmittedHookOutput | null | undefined>;type UserPromptSubmittedHandler = (
input: UserPromptSubmittedHookInput,
invocation: HookInvocation
) => Promise<UserPromptSubmittedHookOutput | null | undefined>;Python
from copilot.session import UserPromptSubmittedHookInput, UserPromptSubmittedHookOutput
from typing import Callable, Awaitable
UserPromptSubmittedHandler = Callable[
[UserPromptSubmittedHookInput, dict[str, str]],
Awaitable[UserPromptSubmittedHookOutput | None]
]UserPromptSubmittedHandler = Callable[
[UserPromptSubmittedHookInput, dict[str, str]],
Awaitable[UserPromptSubmittedHookOutput | None]
]Go
package main
import copilot "github.com/github/copilot-sdk/go"
type UserPromptSubmittedHandler func(
input copilot.UserPromptSubmittedHookInput,
invocation copilot.HookInvocation,
) (*copilot.UserPromptSubmittedHookOutput, error)
func main() {}type UserPromptSubmittedHandler func(
input UserPromptSubmittedHookInput,
invocation HookInvocation,
) (*UserPromptSubmittedHookOutput, error).NET
using GitHub.Copilot.SDK;
public delegate Task<UserPromptSubmittedHookOutput?> UserPromptSubmittedHandler(
UserPromptSubmittedHookInput input,
HookInvocation invocation);public delegate Task<UserPromptSubmittedHookOutput?> UserPromptSubmittedHandler(
UserPromptSubmittedHookInput input,
HookInvocation invocation);Java
import com.github.copilot.sdk.json.*;
UserPromptSubmittedHandler userPromptSubmittedHandler;| Field | Type | Description |
|---|---|---|
timestamp |
number | Unix timestamp when the hook was triggered |
cwd |
string | Current working directory |
prompt |
string | The user's submitted prompt |
Return null or undefined to use the prompt unchanged. Otherwise, return an object with any of these fields:
| Field | Type | Description |
|---|---|---|
modifiedPrompt |
string | Modified prompt to use instead of original |
additionalContext |
string | Extra context added to the conversation |
suppressOutput |
boolean | If true, suppress the assistant's response output |
Node.js / TypeScript
const session = await client.createSession({
hooks: {
onUserPromptSubmitted: async (input, invocation) => {
console.log(`[${invocation.sessionId}] User: ${input.prompt}`);
return null; // Pass through unchanged
},
},
});Python
from copilot.session import PermissionHandler
async def on_user_prompt_submitted(input_data, invocation):
print(f"[{invocation['session_id']}] User: {input_data['prompt']}")
return None
session = await client.create_session(on_permission_request=PermissionHandler.approve_all, hooks={"on_user_prompt_submitted": on_user_prompt_submitted})Go
package main
import (
"context"
"fmt"
copilot "github.com/github/copilot-sdk/go"
)
func main() {
client := copilot.NewClient(nil)
session, _ := client.CreateSession(context.Background(), &copilot.SessionConfig{
OnPermissionRequest: copilot.PermissionHandler.ApproveAll,
Hooks: &copilot.SessionHooks{
OnUserPromptSubmitted: func(input copilot.UserPromptSubmittedHookInput, inv copilot.HookInvocation) (*copilot.UserPromptSubmittedHookOutput, error) {
fmt.Printf("[%s] User: %s\n", inv.SessionID, input.Prompt)
return nil, nil
},
},
})
_ = session
}session, _ := client.CreateSession(context.Background(), &copilot.SessionConfig{
Hooks: &copilot.SessionHooks{
OnUserPromptSubmitted: func(input copilot.UserPromptSubmittedHookInput, inv copilot.HookInvocation) (*copilot.UserPromptSubmittedHookOutput, error) {
fmt.Printf("[%s] User: %s\n", inv.SessionID, input.Prompt)
return nil, nil
},
},
}).NET
using GitHub.Copilot.SDK;
public static class UserPromptSubmittedExample
{
public static async Task Main()
{
await using var client = new CopilotClient();
var session = await client.CreateSessionAsync(new SessionConfig
{
Hooks = new SessionHooks
{
OnUserPromptSubmitted = (input, invocation) =>
{
Console.WriteLine($"[{invocation.SessionId}] User: {input.Prompt}");
return Task.FromResult<UserPromptSubmittedHookOutput?>(null);
},
},
});
}
}var session = await client.CreateSessionAsync(new SessionConfig
{
Hooks = new SessionHooks
{
OnUserPromptSubmitted = (input, invocation) =>
{
Console.WriteLine($"[{invocation.SessionId}] User: {input.Prompt}");
return Task.FromResult<UserPromptSubmittedHookOutput?>(null);
},
},
});Java
import com.github.copilot.sdk.*;
import com.github.copilot.sdk.json.*;
import java.util.concurrent.CompletableFuture;
var hooks = new SessionHooks()
.setOnUserPromptSubmitted((input, invocation) -> {
System.out.println("[" + invocation.getSessionId() + "] User: " + input.prompt());
return CompletableFuture.completedFuture(null);
});
var session = client.createSession(
new SessionConfig()
.setOnPermissionRequest(PermissionHandler.APPROVE_ALL)
.setHooks(hooks)
).get();const session = await client.createSession({
hooks: {
onUserPromptSubmitted: async (input) => {
const projectInfo = await getProjectInfo();
return {
additionalContext: `
Project: ${projectInfo.name}
Language: ${projectInfo.language}
Framework: ${projectInfo.framework}
`.trim(),
};
},
},
});const SHORTCUTS: Record<string, string> = {
"/fix": "Please fix the errors in the code",
"/explain": "Please explain this code in detail",
"/test": "Please write unit tests for this code",
"/refactor": "Please refactor this code to improve readability and maintainability",
};
const session = await client.createSession({
hooks: {
onUserPromptSubmitted: async (input) => {
for (const [shortcut, expansion] of Object.entries(SHORTCUTS)) {
if (input.prompt.startsWith(shortcut)) {
const rest = input.prompt.slice(shortcut.length).trim();
return {
modifiedPrompt: `${expansion}${rest ? `: ${rest}` : ""}`,
};
}
}
return null;
},
},
});const BLOCKED_PATTERNS = [
/password\s*[:=]/i,
/api[_-]?key\s*[:=]/i,
/secret\s*[:=]/i,
];
const session = await client.createSession({
hooks: {
onUserPromptSubmitted: async (input) => {
for (const pattern of BLOCKED_PATTERNS) {
if (pattern.test(input.prompt)) {
// Replace the prompt with a warning message
return {
modifiedPrompt: "[Content blocked: Please don't include sensitive credentials in your prompts. Use environment variables instead.]",
suppressOutput: true,
};
}
}
return null;
},
},
});const MAX_PROMPT_LENGTH = 10000;
const session = await client.createSession({
hooks: {
onUserPromptSubmitted: async (input) => {
if (input.prompt.length > MAX_PROMPT_LENGTH) {
// Truncate the prompt and add context
return {
modifiedPrompt: input.prompt.substring(0, MAX_PROMPT_LENGTH),
additionalContext: `Note: The original prompt was ${input.prompt.length} characters and was truncated to ${MAX_PROMPT_LENGTH} characters.`,
};
}
return null;
},
},
});interface UserPreferences {
codeStyle: "concise" | "verbose";
preferredLanguage: string;
experienceLevel: "beginner" | "intermediate" | "expert";
}
const session = await client.createSession({
hooks: {
onUserPromptSubmitted: async (input) => {
const prefs: UserPreferences = await loadUserPreferences();
const contextParts = [];
if (prefs.codeStyle === "concise") {
contextParts.push("User prefers concise code with minimal comments.");
} else {
contextParts.push("User prefers verbose code with detailed comments.");
}
if (prefs.experienceLevel === "beginner") {
contextParts.push("Explain concepts in simple terms.");
}
return {
additionalContext: contextParts.join(" "),
};
},
},
});const promptTimestamps: number[] = [];
const RATE_LIMIT = 10; // prompts
const RATE_WINDOW = 60000; // 1 minute
const session = await client.createSession({
hooks: {
onUserPromptSubmitted: async (input) => {
const now = Date.now();
// Remove timestamps outside the window
while (promptTimestamps.length > 0 && promptTimestamps[0] < now - RATE_WINDOW) {
promptTimestamps.shift();
}
if (promptTimestamps.length >= RATE_LIMIT) {
return {
reject: true,
rejectReason: `Rate limit exceeded. Please wait before sending more prompts.`,
};
}
promptTimestamps.push(now);
return null;
},
},
});const TEMPLATES: Record<string, (args: string) => string> = {
"bug:": (desc) => `I found a bug: ${desc}
Please help me:
1. Understand why this is happening
2. Suggest a fix
3. Explain how to prevent similar bugs`,
"feature:": (desc) => `I want to implement this feature: ${desc}
Please:
1. Outline the implementation approach
2. Identify potential challenges
3. Provide sample code`,
};
const session = await client.createSession({
hooks: {
onUserPromptSubmitted: async (input) => {
for (const [prefix, template] of Object.entries(TEMPLATES)) {
if (input.prompt.toLowerCase().startsWith(prefix)) {
const args = input.prompt.slice(prefix.length).trim();
return {
modifiedPrompt: template(args),
};
}
}
return null;
},
},
});-
Preserve user intent - When modifying prompts, ensure the core intent remains clear.
-
Be transparent about modifications - If you significantly change a prompt, consider logging or notifying the user.
-
Use
additionalContextovermodifiedPrompt- Adding context is less intrusive than rewriting the prompt. -
Provide clear rejection reasons - When rejecting prompts, explain why and how to fix it.
-
Keep processing fast - This hook runs on every user message. Avoid slow operations.