Übersicht
Ein Hook ist ein Callback, den Sie beim Erstellen einer Sitzung nur einmal registrieren. Das SDK ruft sie an einem gut definierten Punkt im Unterhaltungslebenszyklus auf, übergibt kontextbezogene Eingaben und akzeptiert optional die Ausgabe, die das Verhalten der Sitzung ändert.

| Hook | Bei Aktivierung | Mögliche Aktionen |
|---|---|---|
| Session Lifecycle Hooks | Sitzung beginnt (neu oder fortgesetzt) | Kontext einfügen, Einstellungen laden |
| Hook für gesendete Benutzer Prompt | Der Benutzer sendet eine Nachricht. | Umschreiben von Eingabeaufforderungen, Hinzufügen von Kontext, Filtereingaben |
| Hook „Benutzer-Prompt transformiert“ | Zur Laufzeit wird der Modell-Prompt erstellt. | Überprüfen oder Ersetzen von modellbezogenen Inhalten |
| Pre-Tool Verwendungs-Hook | Bevor ein Tool ausgeführt wird | Zulassen/Verweigern/Ändern des Anrufs |
| Hook für die Verwendung des Tools nach der Sitzung | Nach der Rückgabe eines Tools (nur bei Erfolg) | Transformieren von Ergebnissen, Geheimnisse redigieren, auditieren |
| Hook für die Verwendung des Tools nach der Sitzung | Nachdem ein Tool einen Fehler zurückgegeben hat | Hinweise für Wiederholungsversuche einfügen, Fehler protokollieren |
| Session Lifecycle Hooks | Sitzung endet | Bereinigen, Aufzeichnen von Metriken |
| Fehlerbehandlung Hook | Ein Fehler wird ausgelöst. | Benutzerdefinierte Protokollierung, Wiederholungslogik, Warnungen |
Alle Haken sind optional – registrieren Sie nur die, die Sie benötigen. Indem null (oder die entsprechende Sprache) von einem Hook zurückgegeben wird, wird dem SDK angezeigt, dass es mit dem Standardverhalten fortfahren soll.
Dateibasierte Repository-Haken und Repository-Hook-Einstellungen werden erst geladen, nachdem der Arbeitsbereich vertrauenswürdig ist. Benutzer-Haken bleiben verfügbar, bevor dem Arbeitsbereich vertraut wird.
Repository-Haken aus zusätzlichen Verzeichnissen
Repository-Dateisystem-Haken sind von den in diesem Handbuch beschriebenen SDK-Callbacks getrennt. Um sie aus zusätzlichen Verzeichnissen während der Erstellung lokaler Sitzungen oder der Kaltstart-Wiederaufnahme zu laden, geben Sie additionalDirectories an; CLI --add-dir nimmt ebenfalls standardmäßig teil.
DISABLE_ADDITIONAL_DIRECTORY_HOOKS=true stellt den ausschließlich primären Geltungsbereich wieder her; enableFileHooks kann Repository-Hooks durch session.rpc.options.update aktivieren oder deaktivieren; explizite SDK-Hook-Verzeichnisse und Callbacks sind unabhängig.
Discovery verwendet das Git-Stammverzeichnis jedes akzeptierten Verzeichnisses oder das Verzeichnis selbst, wenn es sich außerhalb eines Git-Repositorys befindet. Die Bereitstellung von B\packages\core innerhalb des Repositorys B ermöglicht beispielsweise sowohl B\.github\hooks als auch Bs Repository-Hook-Einstellungen, nicht nur die Konfiguration innerhalb von B\packages\core.
Warnung
Geben Sie nur vertrauenswürdige Startverzeichnisse an. Dadurch werden Hookbefehle und hook-bereitgestellte Prompts von allen berechtigten Startverzeichnissen ohne zusätzliche Genehmigung pro Verzeichnis oder pro Hook autorisiert. Später gewährte Dateizugriffe berechtigen nicht zur Verwendung von Haken. Vorhandene verwaltete Richtlinien, Hook-Disable-Steuerelemente und Sandkastenregeln gelten weiterhin; sie machen schädliche Hook-Inhalte nicht sicher.
Spätere Verzeichnisberechtigungen fügen keine Hookquellen hinzu. Ein Kaltstart muss die Verzeichnisse erneut bereitstellen; spätere Berechtigungen oder Escape-Hasch-Updates können keine beibehaltene Berechtigung ändern.
Registrieren von Haken
hooks Der Rückruf bleibt von dateibasierten hookDirectories getrennt. Informationen zu verpackten Hookkonfigurationen und angrenzenden Ressourcen finden Sie unter Ermittlungsorte für Anpassungen.
Agentbezogene deklarative Haken
Benutzerdefinierte Agents können eine hooks Ereigniszuordnung im .agent.md Frontmatter oder in der Benutzerdefinierten Agent-Konfiguration des SDK deklarieren. Hierbei handelt es sich um deklarative Befehle oder HTTP-Aktionen, nicht um SDK-Rückruffunktionen. Sie werden nur ausgeführt, während dieser Agent ausgewählt ist, zusätzlich zu den anwendbaren Sitzungs-, Benutzer-, Repository-, Plug-In- und Managed-Policy-Haken. Wenn Sie den ausgewählten Agenten ändern oder die Auswahl des ausgewählten Agenten aufheben, werden die bereichsbezogenen Haken für den nächsten Aufruf geändert, ohne andere Sitzungen oder gleichzeitige Subagenten zu ändern.
---
name: formatter
description: Formats changed files
hooks:
PostToolUse:
- type: command
command: ./scripts/format-changed-files.sh
windows: ./scripts/format-changed-files.ps1
---
Format the files you change.
Sowohl Laufzeitereignisnamen wie postToolUse als auch unterstützte VS-Code-Aliasse wie PostToolUse werden akzeptiert.
command liefert den Fallback-Shellbefehl; windows, linux und osx überschreiben ihn auf den jeweiligen Plattformen. Hook-Befehle sind relativ zum Repositorystamm der ausführungssitzung, es sei denn, sie legen ein explizites Arbeitsverzeichnis fest. Hook-Nutzlasten und Entscheidungen folgen den vorhandenen Hook-Verträgen der Laufzeit.
Wenn ein lokaler Agent als Subagent ausgeführt wird, werden seine bereichsbezogenen Stop (agentStop) Aktionen bei SubagentStop statt beim normalen Turnstopp ausgeführt. Eine explizite SubagentStop Aktion für diesen Agenten beobachtet weiterhin seine eigenen untergeordneten Elemente. Stop-Callbacks erhalten stop_hook_active: true nach einem vorherigen Block. Nach acht aufeinander folgenden bereichsbezogenen Stop-Fortsetzungen ignoriert die Laufzeit weitere bereichsbezogene Blöcke und akzeptiert die endgültige Antwort des untergeordneten Prozesses, es sei denn, ein übergeordneter Lebenszyklus-Hook blockiert den Abschluss noch.
Repository- und geerbte Agent-Haken erfordern die Zulassung zum Arbeitsbereich. Die Agentermittlung oder -auswahl begründet für sich genommen kein Vertrauen. Vorhandene Hook-Disable-Einstellungen, Unternehmenseinschränkungen, Sandbox-Berechtigungen und Timeouts gelten auch für bereichsbezogene Aktionen. Vom SDK bereitgestellte Agenten können keine Privilegien für Managed Plugins erhalten. Remote- oder organisationsgehostete Agentdefinitionen führen keine bereichsbezogenen Haken aus, und der bereichsbezogene Stop-Dispatch wird nur für lokale Runtime-Subagenten, nicht für externe AHP-Teilnehmer, unterstützt.
Nicht unterstützte Ereignisnamen werden vor der Ereignisauswahl gemeldet. Aktionsvorbereitungsfehler wirken sich nur auf das jeweils deklarierte Ereignis aus; Freigegebene und verwaltete Richtlinien-Haken werden weiterhin ausgeführt.
Übergeben Sie ein hooks Objekt, wenn Sie eine Sitzung erstellen (oder fortsetzen). Jedes beispiel unten folgt diesem Muster.
Codesprachen navigation
import { CopilotClient } from "@github/copilot-sdk";
const client = new CopilotClient();
await client.start();
const session = await client.createSession({
hooks: {
onSessionStart: async (input, invocation) => {
/* ... */
},
onPreToolUse: async (input, invocation) => {
/* ... */
},
onPostToolUse: async (input, invocation) => {
/* ... */
},
// ... add only the hooks you need
},
onPermissionRequest: async () => ({ kind: "approve-once" }),
});
from copilot import CopilotClient, PermissionDecisionApproveOnce
client = CopilotClient()
await client.start()
session = await client.create_session(
on_permission_request=lambda req, inv: PermissionDecisionApproveOnce(),
hooks={
"on_session_start": on_session_start,
"on_pre_tool_use": on_pre_tool_use,
"on_post_tool_use": on_post_tool_use,
# ... add only the hooks you need
},
)
client := copilot.NewClient(nil)
session, err := client.CreateSession(ctx, &copilot.SessionConfig{
Hooks: &copilot.SessionHooks{
OnSessionStart: onSessionStart,
OnPreToolUse: onPreToolUse,
OnPostToolUse: onPostToolUse,
// ... add only the hooks you need
},
OnPermissionRequest: func(req copilot.PermissionRequest, inv copilot.PermissionInvocation) (rpc.PermissionDecision, error) {
return &rpc.PermissionDecisionApproveOnce{}, nil
},
})
var client = new CopilotClient();
var session = await client.CreateSessionAsync(new SessionConfig
{
Hooks = new SessionHooks
{
OnSessionStart = onSessionStart,
OnPreToolUse = onPreToolUse,
OnPostToolUse = onPostToolUse,
// ... add only the hooks you need
},
OnPermissionRequest = (req, inv) =>
Task.FromResult(PermissionDecision.ApproveOnce()),
});
import com.github.copilot.CopilotClient;
import com.github.copilot.rpc.*;
import java.util.concurrent.CompletableFuture;
try (var client = new CopilotClient()) {
client.start().get();
var hooks = new SessionHooks()
.setOnSessionStart((input, inv) -> CompletableFuture.completedFuture(null))
.setOnPreToolUse((input, inv) -> CompletableFuture.completedFuture(null))
.setOnPostToolUse((input, inv) -> CompletableFuture.completedFuture(null));
// ... add only the hooks you need
var session = client.createSession(
new SessionConfig()
.setHooks(hooks)
.setOnPermissionRequest(PermissionHandler.APPROVE_ALL)
).get();
}
Tipp
Jeder Hook-Handler empfängt einen invocation Parameter mit dem sessionId, der nützlich ist, um Protokolle zu korrelieren und den Zustand pro Sitzung zu verwalten.
Anwendungsfall: Berechtigungssteuerung
Dient onPreToolUse zum Erstellen einer Berechtigungsschicht, die entscheidet, welche Tools der Agent ausführen kann, welche Argumente zulässig sind und ob der Benutzer vor der Ausführung aufgefordert werden soll.
Zulassungsliste für einen sicheren Satz von Tools
Codesprachen navigation
const READ_ONLY_TOOLS = ["read_file", "glob", "grep", "view"];
const session = await client.createSession({
hooks: {
onPreToolUse: async (input) => {
if (!READ_ONLY_TOOLS.includes(input.toolName)) {
return {
permissionDecision: "deny",
permissionDecisionReason: `Only read-only tools are allowed. "${input.toolName}" was blocked.`,
};
}
return { permissionDecision: "allow" };
},
},
onPermissionRequest: async () => ({ kind: "approve-once" }),
});
from copilot import PermissionDecisionApproveOnce
READ_ONLY_TOOLS = ["read_file", "glob", "grep", "view"]
async def on_pre_tool_use(input_data, invocation):
if input_data["toolName"] not in READ_ONLY_TOOLS:
return {
"permissionDecision": "deny",
"permissionDecisionReason":
f'Only read-only tools are allowed. "{input_data["toolName"]}" was blocked.',
}
return {"permissionDecision": "allow"}
session = await client.create_session(
on_permission_request=lambda req, inv: PermissionDecisionApproveOnce(),
hooks={"on_pre_tool_use": on_pre_tool_use},
)
readOnlyTools := map[string]bool{"read_file": true, "glob": true, "grep": true, "view": true}
session, _ := client.CreateSession(ctx, &copilot.SessionConfig{
Hooks: &copilot.SessionHooks{
OnPreToolUse: func(input copilot.PreToolUseHookInput, inv copilot.HookInvocation) (*copilot.PreToolUseHookOutput, error) {
if !readOnlyTools[input.ToolName] {
return &copilot.PreToolUseHookOutput{
PermissionDecision: "deny",
PermissionDecisionReason: fmt.Sprintf("Only read-only tools are allowed. %q was blocked.", input.ToolName),
}, nil
}
return &copilot.PreToolUseHookOutput{PermissionDecision: "allow"}, nil
},
},
})
var readOnlyTools = new HashSet<string> { "read_file", "glob", "grep", "view" };
var session = await client.CreateSessionAsync(new SessionConfig
{
Hooks = new SessionHooks
{
OnPreToolUse = (input, invocation) =>
{
if (!readOnlyTools.Contains(input.ToolName))
{
return Task.FromResult<PreToolUseHookOutput?>(new PreToolUseHookOutput
{
PermissionDecision = "deny",
PermissionDecisionReason = $"Only read-only tools are allowed. \"{input.ToolName}\" was blocked.",
});
}
return Task.FromResult<PreToolUseHookOutput?>(
new PreToolUseHookOutput { PermissionDecision = "allow" });
},
},
});
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import com.github.copilot.rpc.PermissionHandler;
import com.github.copilot.rpc.SessionConfig;
import com.github.copilot.rpc.SessionHooks;
import com.github.copilot.rpc.PreToolUseHookOutput;
var readOnlyTools = Set.of("read_file", "glob", "grep", "view");
var hooks = new SessionHooks()
.setOnPreToolUse((input, invocation) -> {
if (!readOnlyTools.contains(input.getToolName())) {
return CompletableFuture.completedFuture(
PreToolUseHookOutput.deny(
"Only read-only tools are allowed. \"" + input.getToolName() + "\" was blocked.")
);
}
return CompletableFuture.completedFuture(PreToolUseHookOutput.allow());
});
var session = client.createSession(
new SessionConfig()
.setHooks(hooks)
.setOnPermissionRequest(PermissionHandler.APPROVE_ALL)
).get();
Einschränken des Dateizugriffs auf bestimmte Verzeichnisse
const ALLOWED_DIRS = ["/home/user/projects", "/tmp"];
const session = await client.createSession({
hooks: {
onPreToolUse: async (input) => {
if (["read_file", "write_file", "edit"].includes(input.toolName)) {
const filePath = (input.toolArgs as { path: string }).path;
const allowed = ALLOWED_DIRS.some((dir) => filePath.startsWith(dir));
if (!allowed) {
return {
permissionDecision: "deny",
permissionDecisionReason: `Access to "${filePath}" is outside the allowed directories.`,
};
}
}
return { permissionDecision: "allow" };
},
},
onPermissionRequest: async () => ({ kind: "approve-once" }),
});
Benutzer vor destruktiven Vorgängen fragen
const DESTRUCTIVE_TOOLS = ["delete_file", "shell", "bash"];
const session = await client.createSession({
hooks: {
onPreToolUse: async (input) => {
if (DESTRUCTIVE_TOOLS.includes(input.toolName)) {
return { permissionDecision: "ask" };
}
return { permissionDecision: "allow" };
},
},
onPermissionRequest: async () => ({ kind: "approve-once" }),
});
Durch Rückgabe von "ask" wird die Entscheidung zur Laufzeit dem Benutzer überlassen – nützlich für destruktive Aktionen, bei denen menschliches Eingreifen erforderlich ist.
Anwendungsfall: Überwachung und Compliance
Kombinieren Sie onPreToolUse, onPostToolUseund die Sitzungslebenszyklus-Haken, um einen vollständigen Überwachungspfad zu erstellen, der jede Aktion erfasst, die der Agent ausführt.
Strukturiertes Überwachungsprotokoll
Codesprachen navigation
interface AuditEntry {
timestamp: Date;
sessionId: string;
event: string;
toolName?: string;
toolArgs?: unknown;
toolResult?: unknown;
prompt?: string;
}
const auditLog: AuditEntry[] = [];
const session = await client.createSession({
hooks: {
onSessionStart: async (input, invocation) => {
auditLog.push({
timestamp: input.timestamp,
sessionId: invocation.sessionId,
event: "session_start",
});
return null;
},
onUserPromptSubmitted: async (input, invocation) => {
auditLog.push({
timestamp: input.timestamp,
sessionId: invocation.sessionId,
event: "user_prompt",
prompt: input.prompt,
});
return null;
},
onPreToolUse: async (input, invocation) => {
auditLog.push({
timestamp: input.timestamp,
sessionId: invocation.sessionId,
event: "tool_call",
toolName: input.toolName,
toolArgs: input.toolArgs,
});
return { permissionDecision: "allow" };
},
onPostToolUse: async (input, invocation) => {
auditLog.push({
timestamp: input.timestamp,
sessionId: invocation.sessionId,
event: "tool_result",
toolName: input.toolName,
toolResult: input.toolResult,
});
return null;
},
onSessionEnd: async (input, invocation) => {
auditLog.push({
timestamp: input.timestamp,
sessionId: invocation.sessionId,
event: "session_end",
});
// Persist the log — swap this with your own storage backend
await fs.promises.writeFile(
`audit-${invocation.sessionId}.json`,
JSON.stringify(auditLog, null, 2),
);
return null;
},
},
onPermissionRequest: async () => ({ kind: "approve-once" }),
});
import json, aiofiles
from copilot import PermissionDecisionApproveOnce
audit_log = []
async def on_session_start(input_data, invocation):
audit_log.append({
"timestamp": input_data["timestamp"].isoformat(),
"session_id": invocation["session_id"],
"event": "session_start",
})
return None
async def on_user_prompt_submitted(input_data, invocation):
audit_log.append({
"timestamp": input_data["timestamp"].isoformat(),
"session_id": invocation["session_id"],
"event": "user_prompt",
"prompt": input_data["prompt"],
})
return None
async def on_pre_tool_use(input_data, invocation):
audit_log.append({
"timestamp": input_data["timestamp"].isoformat(),
"session_id": invocation["session_id"],
"event": "tool_call",
"tool_name": input_data["toolName"],
"tool_args": input_data["toolArgs"],
})
return {"permissionDecision": "allow"}
async def on_post_tool_use(input_data, invocation):
audit_log.append({
"timestamp": input_data["timestamp"].isoformat(),
"session_id": invocation["session_id"],
"event": "tool_result",
"tool_name": input_data["toolName"],
"tool_result": input_data["toolResult"],
})
return None
async def on_session_end(input_data, invocation):
audit_log.append({
"timestamp": input_data["timestamp"].isoformat(),
"session_id": invocation["session_id"],
"event": "session_end",
})
async with aiofiles.open(f"audit-{invocation['session_id']}.json", "w") as f:
await f.write(json.dumps(audit_log, indent=2))
return None
session = await client.create_session(
on_permission_request=lambda req, inv: PermissionDecisionApproveOnce(),
hooks={
"on_session_start": on_session_start,
"on_user_prompt_submitted": on_user_prompt_submitted,
"on_pre_tool_use": on_pre_tool_use,
"on_post_tool_use": on_post_tool_use,
"on_session_end": on_session_end,
},
)
Geheime Daten aus Tool-Ergebnissen schwärzen
const SECRET_PATTERNS = [
/(?:api[_-]?key|token|secret|password)\s*[:=]\s*["']?[\w\-\.]+["']?/gi,
];
const session = await client.createSession({
hooks: {
onPostToolUse: async (input) => {
if (typeof input.toolResult !== "string") return null;
let redacted = input.toolResult;
for (const pattern of SECRET_PATTERNS) {
redacted = redacted.replace(pattern, "[REDACTED]");
}
return redacted !== input.toolResult
? { modifiedResult: redacted }
: null;
},
},
onPermissionRequest: async () => ({ kind: "approve-once" }),
});
Anwendungsfall: Benachrichtigungen und Sounds
Haken werden im Prozess Ihrer Anwendung ausgelöst, sodass Sie alle Nebeneffekte auslösen können– Desktopbenachrichtigungen, Sounds, Slack-Nachrichten oder Webhook-Aufrufe.
Desktopbenachrichtigung für Sitzungsereignisse
Codesprachen navigation
import notifier from "node-notifier"; // npm install node-notifier
const session = await client.createSession({
hooks: {
onSessionEnd: async (input, invocation) => {
notifier.notify({
title: "Copilot Session Complete",
message: `Session ${invocation.sessionId.slice(0, 8)} finished (${input.reason}).`,
});
return null;
},
onErrorOccurred: async (input) => {
notifier.notify({
title: "Copilot Error",
message: input.error.slice(0, 200),
});
return null;
},
},
onPermissionRequest: async () => ({ kind: "approve-once" }),
});
import subprocess
from copilot import PermissionDecisionApproveOnce
async def on_session_end(input_data, invocation):
sid = invocation["session_id"][:8]
reason = input_data["reason"]
subprocess.Popen([
"notify-send", "Copilot Session Complete",
f"Session {sid} finished ({reason}).",
])
return None
async def on_error_occurred(input_data, invocation):
subprocess.Popen([
"notify-send", "Copilot Error",
input_data["error"][:200],
])
return None
session = await client.create_session(
on_permission_request=lambda req, inv: PermissionDecisionApproveOnce(),
hooks={
"on_session_end": on_session_end,
"on_error_occurred": on_error_occurred,
},
)
Einen Ton abspielen, wenn ein Tool fertig ist
import { exec } from "node:child_process";
const session = await client.createSession({
hooks: {
onPostToolUse: async (input) => {
// macOS: play a system sound after every tool call
exec("afplay /System/Library/Sounds/Pop.aiff");
return null;
},
onErrorOccurred: async () => {
exec("afplay /System/Library/Sounds/Basso.aiff");
return null;
},
},
onPermissionRequest: async () => ({ kind: "approve-once" }),
});
Bei Fehlern auf Slack posten
const SLACK_WEBHOOK_URL = process.env.SLACK_WEBHOOK_URL!;
const session = await client.createSession({
hooks: {
onErrorOccurred: async (input, invocation) => {
if (!input.recoverable) {
await fetch(SLACK_WEBHOOK_URL, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
text: `🚨 Unrecoverable error in session \`${invocation.sessionId.slice(0, 8)}\`:\n\`\`\`${input.error}\`\`\``,
}),
});
}
return null;
},
},
onPermissionRequest: async () => ({ kind: "approve-once" }),
});
Anwendungsfall: Prompt-Anreicherung
Verwenden Sie onSessionStart und onUserPromptSubmitted, um den Kontext automatisch einzufügen, damit Benutzer sich nicht wiederholen müssen.
Einfügen von Projektmetadaten beim Sitzungsstart
const session = await client.createSession({
hooks: {
onSessionStart: async (input) => {
const pkg = JSON.parse(
await fs.promises.readFile("package.json", "utf-8"),
);
return {
additionalContext: [
`Project: ${pkg.name} v${pkg.version}`,
`Node: ${process.version}`,
`Working directory: ${input.workingDirectory}`,
].join("\n"),
};
},
},
onPermissionRequest: async () => ({ kind: "approve-once" }),
});
Erweitern von Kurzbefehlen in Eingabeaufforderungen
const SHORTCUTS: Record<string, string> = {
"/fix": "Find and fix all errors in the current file",
"/test": "Write comprehensive unit tests for this code",
"/explain": "Explain this code in detail",
"/refactor": "Refactor this code to improve readability",
};
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: rest ? `${expansion}: ${rest}` : expansion };
}
}
return null;
},
},
onPermissionRequest: async () => ({ kind: "approve-once" }),
});
Anwendungsfall: Fehlerbehandlung und Wiederherstellung
Der onErrorOccurred Hook gibt Ihnen die Möglichkeit, auf Fehler zu reagieren – sei es, erneut zu versuchen, einen Menschen zu benachrichtigen oder auf ordnungsgemäße Weise herunterzufahren.
Wiederholen von vorübergehenden Modellfehlern
const session = await client.createSession({
hooks: {
onErrorOccurred: async (input) => {
if (input.errorContext === "model_call" && input.recoverable) {
return {
errorHandling: "retry",
retryCount: 3,
userNotification: "Temporary model issue — retrying…",
};
}
return null;
},
},
onPermissionRequest: async () => ({ kind: "approve-once" }),
});
Benutzerfreundliche Fehlermeldungen
const FRIENDLY_MESSAGES: Record<string, string> = {
model_call: "The AI model is temporarily unavailable. Please try again.",
tool_execution: "A tool encountered an error. Check inputs and try again.",
system: "A system error occurred. Please try again later.",
};
const session = await client.createSession({
hooks: {
onErrorOccurred: async (input) => {
return {
userNotification: FRIENDLY_MESSAGES[input.errorContext] ?? input.error,
};
},
},
onPermissionRequest: async () => ({ kind: "approve-once" }),
});
Anwendungsfall: Sitzungsmetriken
Verfolgen Sie, wie lange Sitzungen ausgeführt werden, wie viele Tools aufgerufen werden und warum Sitzungen enden – nützlich für Dashboards und Kostenüberwachung.
Codesprachen navigation
const metrics = new Map<
string,
{ start: Date; toolCalls: number; prompts: number }
>();
const session = await client.createSession({
hooks: {
onSessionStart: async (input, invocation) => {
metrics.set(invocation.sessionId, {
start: input.timestamp,
toolCalls: 0,
prompts: 0,
});
return null;
},
onUserPromptSubmitted: async (_input, invocation) => {
metrics.get(invocation.sessionId)!.prompts++;
return null;
},
onPreToolUse: async (_input, invocation) => {
metrics.get(invocation.sessionId)!.toolCalls++;
return { permissionDecision: "allow" };
},
onSessionEnd: async (input, invocation) => {
const m = metrics.get(invocation.sessionId)!;
const durationSec =
(input.timestamp.getTime() - m.start.getTime()) / 1000;
console.log(
`Session ${invocation.sessionId.slice(0, 8)}: ` +
`${durationSec.toFixed(1)}s, ${m.prompts} prompts, ` +
`${m.toolCalls} tool calls, ended: ${input.reason}`,
);
metrics.delete(invocation.sessionId);
return null;
},
},
onPermissionRequest: async () => ({ kind: "approve-once" }),
});
from copilot import PermissionDecisionApproveOnce
session_metrics = {}
async def on_session_start(input_data, invocation):
session_metrics[invocation["session_id"]] = {
"start": input_data["timestamp"],
"tool_calls": 0,
"prompts": 0,
}
return None
async def on_user_prompt_submitted(input_data, invocation):
session_metrics[invocation["session_id"]]["prompts"] += 1
return None
async def on_pre_tool_use(input_data, invocation):
session_metrics[invocation["session_id"]]["tool_calls"] += 1
return {"permissionDecision": "allow"}
async def on_session_end(input_data, invocation):
m = session_metrics.pop(invocation["session_id"])
duration = (input_data["timestamp"] - m["start"]).total_seconds()
sid = invocation["session_id"][:8]
print(
f"Session {sid}: {duration:.1f}s, {m['prompts']} prompts, "
f"{m['tool_calls']} tool calls, ended: {input_data['reason']}"
)
return None
session = await client.create_session(
on_permission_request=lambda req, inv: PermissionDecisionApproveOnce(),
hooks={
"on_session_start": on_session_start,
"on_user_prompt_submitted": on_user_prompt_submitted,
"on_pre_tool_use": on_pre_tool_use,
"on_session_end": on_session_end,
},
)
Kombinieren von Haken
Haken entstehen von selbst. Ein einzelnes hooks-Objekt kann Berechtigungen und Überwachung und Benachrichtigungen bearbeiten — jeder Hook erfüllt seine eigene Aufgabe.
const session = await client.createSession({
hooks: {
onSessionStart: async (input) => {
console.log(`[audit] session started in ${input.workingDirectory}`);
return { additionalContext: "Project uses TypeScript and Vitest." };
},
onPreToolUse: async (input) => {
console.log(`[audit] tool requested: ${input.toolName}`);
if (input.toolName === "shell") {
return { permissionDecision: "ask" };
}
return { permissionDecision: "allow" };
},
onPostToolUse: async (input) => {
console.log(`[audit] tool completed: ${input.toolName}`);
return null;
},
onErrorOccurred: async (input) => {
console.error(`[alert] ${input.errorContext}: ${input.error}`);
return null;
},
onSessionEnd: async (input, invocation) => {
console.log(
`[audit] session ${invocation.sessionId.slice(0, 8)} ended: ${input.reason}`,
);
return null;
},
},
onPermissionRequest: async () => ({ kind: "approve-once" }),
});
Bewährte Methoden
-
Halten Sie Haken schnell. Jeder Hook wird inline ausgeführt – langsame Haken verzögern die Unterhaltung. Entladen Sie nach Möglichkeit schwere Arbeit (Datenbankschreibvorgänge, HTTP-Aufrufe) in eine Hintergrundwarteschlange.
-
Kehren Sie zurück
null, wenn Sie nichts ändern müssen. Dadurch wird das SDK aufgefordert, mit den Standardwerten fortzufahren und unnötige Objektzuordnungen zu vermeiden. -
Seien Sie mit Berechtigungsentscheidungen explizit. Die Rückgabe von
{ permissionDecision: "allow" }ist klarer als die vonnull, obwohl beide das Tool zulassen. -
Schlucken Sie keine kritischen Fehler. Es ist in Ordnung, wiederherstellbare Toolfehler zu unterdrücken, aber nicht wiederherstellbare immer zu protokollieren oder darauf hinzuweisen.
-
Verwenden Sie
additionalContextstattmodifiedPromptnach Möglichkeit. Durch das Anfügen des Kontexts wird die ursprüngliche Absicht des Benutzers beibehalten, während das Modell weiterhin unterstützt wird. -
Bereichsstatus nach Sitzungs-ID. Wenn Sie Daten pro Sitzung nachverfolgen, schlüsseln Sie diese
invocation.sessionIdauf und bereinigen Sie sie inonSessionEnd.
Reference
Vollständige Typdefinitionen, Eingabe-/Ausgabefeldtabellen und zusätzliche Beispiele für jeden Hook finden Sie in der API-Referenz:
- Sitzungshaken
- Pre-Tool Verwendungs-Hook
- Hook für die Verwendung des Tools nach der Sitzung
- Hook für gesendete Benutzer Prompt
- Hook „Benutzer-Prompt transformiert“
- Session Lifecycle Hooks
- Fehlerbehandlung Hook