- Initialiser le contexte lorsque les sessions commencent
- Nettoyer les ressources lorsque les sessions se terminent
- Suivre les métriques de session et l'analyse
- Configurer dynamiquement le comportement de session
Point d'ancrage pour le démarrage de session
Le onSessionStart hook est appelé lorsqu’une session commence (nouvelle ou reprise).
Signature du hook
Langages de code navigation
type SessionStartHandler = (
input: SessionStartHookInput,
invocation: HookInvocation
) => Promise<SessionStartHookOutput | null | undefined>;
SessionStartHandler = Callable[
[SessionStartHookInput, dict[str, str]],
Awaitable[SessionStartHookOutput | None]
]
type SessionStartHandler func(
input SessionStartHookInput,
invocation HookInvocation,
) (*SessionStartHookOutput, error)
public delegate Task<SessionStartHookOutput?> SessionStartHandler(
SessionStartHookInput input,
HookInvocation invocation);
@FunctionalInterface
public interface SessionStartHandler {
CompletableFuture<SessionStartHookOutput> handle(
SessionStartHookInput input,
HookInvocation invocation);
}
Input
| Champ | Catégorie | Description |
|---|---|---|
timestamp | number | Horodatage Unix lorsque le hook a été déclenché |
cwd | string | Répertoire de travail actuel |
source | ||
"startup" | ||
| | | ||
"resume" | ||
| | | ||
"new" | ||
| Démarrage de la session | ||
initialPrompt | chaîne | non définie | Invitation initiale si disponible |
Sortie
| Champ | Catégorie | Description |
|---|---|---|
additionalContext | string | Contexte à ajouter au démarrage de la session |
modifiedConfig | Objet | Remplacer la configuration de session |
Exemples
Ajouter un contexte de projet au début
Langages de code navigation
const session = await client.createSession({
hooks: {
onSessionStart: async (input, invocation) => {
console.log(`Session ${invocation.sessionId} started (${input.source})`);
const projectInfo = await detectProjectType(input.cwd);
return {
additionalContext: `
This is a ${projectInfo.type} project.
Main language: ${projectInfo.language}
Package manager: ${projectInfo.packageManager}
`.trim(),
};
},
},
});
from copilot.session import PermissionHandler
async def on_session_start(input_data, invocation):
print(f"Session {invocation['session_id']} started ({input_data['source']})")
project_info = await detect_project_type(input_data["cwd"])
return {
"additionalContext": f"""
This is a {project_info['type']} project.
Main language: {project_info['language']}
Package manager: {project_info['packageManager']}
""".strip()
}
session = await client.create_session(on_permission_request=PermissionHandler.approve_all, hooks={"on_session_start": on_session_start})
Gérer la reprise de session
const session = await client.createSession({
hooks: {
onSessionStart: async (input, invocation) => {
if (input.source === "resume") {
// Load previous session state
const previousState = await loadSessionState(invocation.sessionId);
return {
additionalContext: `
Session resumed. Previous context:
- Last topic: ${previousState.lastTopic}
- Open files: ${previousState.openFiles.join(", ")}
`.trim(),
};
}
return null;
},
},
});
Charger les préférences utilisateur
const session = await client.createSession({
hooks: {
onSessionStart: async () => {
const preferences = await loadUserPreferences();
const contextParts = [];
if (preferences.language) {
contextParts.push(`Preferred language: ${preferences.language}`);
}
if (preferences.codeStyle) {
contextParts.push(`Code style: ${preferences.codeStyle}`);
}
if (preferences.verbosity === "concise") {
contextParts.push("Keep responses brief and to the point.");
}
return {
additionalContext: contextParts.join("\n"),
};
},
},
});
Hook de fin de session
Le onSessionEnd hook est appelé lorsqu’une session se termine.
Signature du hook
Langages de code navigation
type SessionEndHandler = (
input: SessionEndHookInput,
invocation: HookInvocation
) => Promise<SessionEndHookOutput | null | undefined>;
SessionEndHandler = Callable[
[SessionEndHookInput, dict[str, str]],
Awaitable[SessionEndHookOutput | None]
]
type SessionEndHandler func(
input SessionEndHookInput,
invocation HookInvocation,
) (*SessionEndHookOutput, error)
public delegate Task<SessionEndHookOutput?> SessionEndHandler(
SessionEndHookInput input,
HookInvocation invocation);
@FunctionalInterface
public interface SessionEndHandler {
CompletableFuture<SessionEndHookOutput> handle(
SessionEndHookInput input,
HookInvocation invocation);
}
Input
| Champ | Catégorie | Description |
|---|---|---|
timestamp | number | Horodatage Unix lorsque le hook a été déclenché |
cwd | string | Répertoire de travail actuel |
reason | string | Pourquoi la session s’est terminée (voir ci-dessous) |
finalMessage | chaîne | non définie | Dernier message de la session |
error | chaîne | non définie | Message d’erreur si la session s’est terminée en raison d’une erreur |
Raisons de fin
| Reason | Description |
|---|---|
"complete" | Session terminée normalement |
"error" | Session terminée en raison d’une erreur |
"abort" | La session a été abandonnée par l’utilisateur ou le code |
"timeout" | La session a expiré |
"user_exit" | L’utilisateur a explicitement terminé la session |
Sortie
| Champ | Catégorie | Description |
|---|---|---|
suppressOutput | booléen | ** |
| Masquer la sortie finale de session | ||
cleanupActions | chaîne de caractères[] | Liste des actions de nettoyage à effectuer |
sessionSummary | string | Résumé de la session pour la journalisation des événements et les analyses |
Exemples
Suivre les métriques de session
Langages de code navigation
const sessionStartTimes = new Map<string, number>();
const session = await client.createSession({
hooks: {
onSessionStart: async (input, invocation) => {
sessionStartTimes.set(invocation.sessionId, input.timestamp);
return null;
},
onSessionEnd: async (input, invocation) => {
const startTime = sessionStartTimes.get(invocation.sessionId);
const duration = startTime ? input.timestamp - startTime : 0;
await recordMetrics({
sessionId: invocation.sessionId,
duration,
endReason: input.reason,
});
sessionStartTimes.delete(invocation.sessionId);
return null;
},
},
});
from copilot.session import PermissionHandler
session_start_times = {}
async def on_session_start(input_data, invocation):
session_start_times[invocation["session_id"]] = input_data["timestamp"]
return None
async def on_session_end(input_data, invocation):
start_time = session_start_times.get(invocation["session_id"])
duration = input_data["timestamp"] - start_time if start_time else 0
await record_metrics({
"session_id": invocation["session_id"],
"duration": duration,
"end_reason": input_data["reason"],
})
session_start_times.pop(invocation["session_id"], None)
return None
session = await client.create_session(on_permission_request=PermissionHandler.approve_all, hooks={
"on_session_start": on_session_start,
"on_session_end": on_session_end,
})
Nettoyer les ressources
const sessionResources = new Map<string, { tempFiles: string[] }>();
const session = await client.createSession({
hooks: {
onSessionStart: async (input, invocation) => {
sessionResources.set(invocation.sessionId, { tempFiles: [] });
return null;
},
onSessionEnd: async (input, invocation) => {
const resources = sessionResources.get(invocation.sessionId);
if (resources) {
// Clean up temp files
for (const file of resources.tempFiles) {
await fs.unlink(file).catch(() => {});
}
sessionResources.delete(invocation.sessionId);
}
console.log(`Session ${invocation.sessionId} ended: ${input.reason}`);
return null;
},
},
});
Enregistrer l’état de session pour reprendre
const session = await client.createSession({
hooks: {
onSessionEnd: async (input, invocation) => {
if (input.reason !== "error") {
// Save state for potential resume
await saveSessionState(invocation.sessionId, {
endTime: input.timestamp,
cwd: input.cwd,
reason: input.reason,
});
}
return null;
},
},
});
Résumé de la session de journalisation
const sessionData: Record<string, { prompts: number; tools: number; startTime: number }> = {};
const session = await client.createSession({
hooks: {
onSessionStart: async (input, invocation) => {
sessionData[invocation.sessionId] = {
prompts: 0,
tools: 0,
startTime: input.timestamp
};
return null;
},
onUserPromptSubmitted: async (_, invocation) => {
sessionData[invocation.sessionId].prompts++;
return null;
},
onPreToolUse: async (_, invocation) => {
sessionData[invocation.sessionId].tools++;
return { permissionDecision: "allow" };
},
onSessionEnd: async (input, invocation) => {
const data = sessionData[invocation.sessionId];
console.log(`
Session Summary:
ID: ${invocation.sessionId}
Duration: ${(input.timestamp - data.startTime) / 1000}s
Prompts: ${data.prompts}
Tool calls: ${data.tools}
End reason: ${input.reason}
`.trim());
delete sessionData[invocation.sessionId];
return null;
},
},
});
Crochet d’arrêt de l’agent
Le hook d’arrêt de l’agent s’exécute lorsque l’agent de niveau supérieur atteint naturellement la fin d’un tour. Il est distinct de onSessionEnd : la session reste active et le hook peut demander le tour d'un autre agent.
| Language | Handler |
|---|---|
| Node.js / TypeScript | onAgentStop |
| Python | on_agent_stop |
| Go | OnAgentStop |
| .NET | OnAgentStop |
| Rust | on_agent_stop |
| Java | setOnAgentStop |
Input
Les noms des membres publics suivent les conventions de casse de chaque langue :
| Sens | Node.js / Python | Go / .NET | Rust | Java |
|---|---|---|---|---|
Pourquoi l’agent s’est arrêté, par exemple end_turn | stopReason | StopReason | stop_reason | getStopReason() |
| Chemin d’accès à la transcription de session sur disque | transcriptPath | TranscriptPath | transcript_path | get |
| Si une décision de bloc antérieure a déjà forcé cette continuation | stopHookActive | StopHookActive | stop_hook_active | get |
Sortie
Ne renvoyez rien afin que l’agent s’arrête. Renvoyer une décision de blocage pour placer un autre message utilisateur en file d’attente et continuer :
{
"decision": "block",
"reason": "Run the final validation and fix any failures."
}
Utilisez le membre « active-stop » indiqué ci-dessus pour éviter de bloquer de manière répétée un agent qui a déjà poursuivi son exécution à cause de ce hook. Le runtime limite également les décisions de bloc consécutives.
Bonnes pratiques
-
Assurez
onSessionStartla rapidité - Les utilisateurs attendent que la session soit prête. -
Gérer toutes les raisons de fin - Ne partez pas du principe que les sessions se terminent correctement ; gérer les erreurs et les abandons.
-
Libérer les ressources - Utilisez
onSessionEndpour libérer les ressources allouées pendant la session. -
Stocker un état minimal : si vous effectuez le suivi des données de session, veillez à les limiter au strict nécessaire.
-
Rendez le nettoyage idempotent -
onSessionEndpeut ne pas être appelé si le processus se bloque.