{"meta":{"title":"Arbeiten mit Hooks","intro":"Hooks ermöglichen es Ihnen, benutzerdefinierte Logik in jede Phase einer Copilot-Sitzung zu integrieren – von dem Moment an, in dem sie gestartet wird, bis zu jedem Benutzereingabeaufforderungs- und Toolaufruf bis zum Ende. In diesem Leitfaden werden praktische Anwendungsfälle erläutert, damit Sie Berechtigungen, Überwachung, Benachrichtigungen und vieles mehr versenden können, ohne das Verhalten des Kern-Agents zu ändern.","product":"GitHub Copilot","breadcrumbs":[{"href":"/de/enterprise-cloud@latest/copilot","title":"GitHub Copilot"},{"href":"/de/enterprise-cloud@latest/copilot/how-tos","title":"Vorgehensweisen"},{"href":"/de/enterprise-cloud@latest/copilot/how-tos/copilot-sdk","title":"Copilot SDK"},{"href":"/de/enterprise-cloud@latest/copilot/how-tos/copilot-sdk/features","title":"Funktionen"},{"href":"/de/enterprise-cloud@latest/copilot/how-tos/copilot-sdk/features/hooks","title":"Hooks"}],"documentType":"article"},"body":"# Arbeiten mit Hooks\n\nHooks ermöglichen es Ihnen, benutzerdefinierte Logik in jede Phase einer Copilot-Sitzung zu integrieren – von dem Moment an, in dem sie gestartet wird, bis zu jedem Benutzereingabeaufforderungs- und Toolaufruf bis zum Ende. In diesem Leitfaden werden praktische Anwendungsfälle erläutert, damit Sie Berechtigungen, Überwachung, Benachrichtigungen und vieles mehr versenden können, ohne das Verhalten des Kern-Agents zu ändern.\n\n<!-- markdownlint-disable GHD046 GHD005 -->\n\n<!-- Suppressed: GHD046 (outdated release terminology), GHD005 (hardcoded data variable) -->\n\n## Übersicht\n\nEin 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.\n\n![Diagramm: Flussdiagramm mit dem beschriebenen Prozess.](/assets/images/help/copilot/copilot-sdk/features-hooks-diagram-0.png)\n\n| Hook                                                                                                                                              | Bei Aktivierung                                 | Mögliche Aktionen                                                             |\n| ------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------- | ----------------------------------------------------------------------------- |\n| [Session Lifecycle Hooks](/de/enterprise-cloud@latest/copilot/how-tos/copilot-sdk/hooks/session-lifecycle#session-start)                          | Sitzung beginnt (neu oder fortgesetzt)          | Kontext einfügen, Einstellungen laden                                         |\n| [Hook für gesendete Benutzer Prompt](/de/enterprise-cloud@latest/copilot/how-tos/copilot-sdk/hooks/user-prompt-submitted)                         | Der Benutzer sendet eine Nachricht.             | Umschreiben von Eingabeaufforderungen, Hinzufügen von Kontext, Filtereingaben |\n| [Hook „Benutzer-Prompt transformiert“](/de/enterprise-cloud@latest/copilot/how-tos/copilot-sdk/hooks/user-prompt-transformed)                     | Zur Laufzeit wird der Modell-Prompt erstellt.   | Überprüfen oder Ersetzen von modellbezogenen Inhalten                         |\n| [Pre-Tool Verwendungs-Hook](/de/enterprise-cloud@latest/copilot/how-tos/copilot-sdk/hooks/pre-tool-use)                                           | Bevor ein Tool ausgeführt wird                  | Zulassen/Verweigern/Ändern des Anrufs                                         |\n| [Hook für die Verwendung des Tools nach der Sitzung](/de/enterprise-cloud@latest/copilot/how-tos/copilot-sdk/hooks/post-tool-use)                 | Nach der Rückgabe eines Tools (nur bei Erfolg)  | Transformieren von Ergebnissen, Geheimnisse redigieren, auditieren            |\n| [Hook für die Verwendung des Tools nach der Sitzung](/de/enterprise-cloud@latest/copilot/how-tos/copilot-sdk/hooks/post-tool-use#failure-variant) | Nachdem ein Tool einen Fehler zurückgegeben hat | Hinweise für Wiederholungsversuche einfügen, Fehler protokollieren            |\n| [Session Lifecycle Hooks](/de/enterprise-cloud@latest/copilot/how-tos/copilot-sdk/hooks/session-lifecycle#session-end)                            | Sitzung endet                                   | Bereinigen, Aufzeichnen von Metriken                                          |\n| [Fehlerbehandlung Hook](/de/enterprise-cloud@latest/copilot/how-tos/copilot-sdk/hooks/error-handling)                                             | Ein Fehler wird ausgelöst.                      | Benutzerdefinierte Protokollierung, Wiederholungslogik, Warnungen             |\n\nAlle Hooks 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.\n\n## Registrieren von Hooks\n\nÜbergeben Sie ein `hooks` Objekt, wenn Sie eine Sitzung erstellen (oder fortsetzen). Jedes beispiel unten folgt diesem Muster.\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\nimport { CopilotClient } from \"@github/copilot-sdk\";\n\nconst client = new CopilotClient();\nawait client.start();\n\nconst session = await client.createSession({\n  hooks: {\n    onSessionStart: async (input, invocation) => {\n      /* ... */\n    },\n    onPreToolUse: async (input, invocation) => {\n      /* ... */\n    },\n    onPostToolUse: async (input, invocation) => {\n      /* ... */\n    },\n    // ... add only the hooks you need\n  },\n  onPermissionRequest: async () => ({ kind: \"approve-once\" }),\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 import CopilotClient, PermissionDecisionApproveOnce\n\nclient = CopilotClient()\nawait client.start()\n\nsession = await client.create_session(\n    on_permission_request=lambda req, inv: PermissionDecisionApproveOnce(),\n    hooks={\n        \"on_session_start\": on_session_start,\n        \"on_pre_tool_use\":  on_pre_tool_use,\n        \"on_post_tool_use\": on_post_tool_use,\n        # ... add only the hooks you need\n    },\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\nclient := copilot.NewClient(nil)\n\nsession, err := client.CreateSession(ctx, &copilot.SessionConfig{\n    Hooks: &copilot.SessionHooks{\n        OnSessionStart: onSessionStart,\n        OnPreToolUse:   onPreToolUse,\n        OnPostToolUse:  onPostToolUse,\n        // ... add only the hooks you need\n    },\n    OnPermissionRequest: func(req copilot.PermissionRequest, inv copilot.PermissionInvocation) (rpc.PermissionDecision, error) {\n        return &rpc.PermissionDecisionApproveOnce{}, nil\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 client = new CopilotClient();\n\nvar session = await client.CreateSessionAsync(new SessionConfig\n{\n    Hooks = new SessionHooks\n    {\n        OnSessionStart = onSessionStart,\n        OnPreToolUse   = onPreToolUse,\n        OnPostToolUse  = onPostToolUse,\n        // ... add only the hooks you need\n    },\n    OnPermissionRequest = (req, inv) =>\n        Task.FromResult(PermissionDecision.ApproveOnce()),\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```java\nimport com.github.copilot.CopilotClient;\nimport com.github.copilot.rpc.*;\nimport java.util.concurrent.CompletableFuture;\n\ntry (var client = new CopilotClient()) {\n    client.start().get();\n\n    var hooks = new SessionHooks()\n        .setOnSessionStart((input, inv) -> CompletableFuture.completedFuture(null))\n        .setOnPreToolUse((input, inv) -> CompletableFuture.completedFuture(null))\n        .setOnPostToolUse((input, inv) -> CompletableFuture.completedFuture(null));\n        // ... add only the hooks you need\n\n    var session = client.createSession(\n        new SessionConfig()\n            .setHooks(hooks)\n            .setOnPermissionRequest(PermissionHandler.APPROVE_ALL)\n    ).get();\n}\n```\n\n</div>\n\n</div>\n\n> \\[!TIP]\n> 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.\n\n## Anwendungsfall: Berechtigungssteuerung\n\nDient `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.\n\n### Zulassungsliste für einen sicheren Satz von Tools\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 READ_ONLY_TOOLS = [\"read_file\", \"glob\", \"grep\", \"view\"];\n\nconst session = await client.createSession({\n  hooks: {\n    onPreToolUse: async (input) => {\n      if (!READ_ONLY_TOOLS.includes(input.toolName)) {\n        return {\n          permissionDecision: \"deny\",\n          permissionDecisionReason: `Only read-only tools are allowed. \"${input.toolName}\" was blocked.`,\n        };\n      }\n      return { permissionDecision: \"allow\" };\n    },\n  },\n  onPermissionRequest: async () => ({ kind: \"approve-once\" }),\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 import PermissionDecisionApproveOnce\n\nREAD_ONLY_TOOLS = [\"read_file\", \"glob\", \"grep\", \"view\"]\n\nasync def on_pre_tool_use(input_data, invocation):\n    if input_data[\"toolName\"] not in READ_ONLY_TOOLS:\n        return {\n            \"permissionDecision\": \"deny\",\n            \"permissionDecisionReason\":\n                f'Only read-only tools are allowed. \"{input_data[\"toolName\"]}\" was blocked.',\n        }\n    return {\"permissionDecision\": \"allow\"}\n\nsession = await client.create_session(\n    on_permission_request=lambda req, inv: PermissionDecisionApproveOnce(),\n    hooks={\"on_pre_tool_use\": on_pre_tool_use},\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\nreadOnlyTools := map[string]bool{\"read_file\": true, \"glob\": true, \"grep\": true, \"view\": true}\n\nsession, _ := client.CreateSession(ctx, &copilot.SessionConfig{\n    Hooks: &copilot.SessionHooks{\n        OnPreToolUse: func(input copilot.PreToolUseHookInput, inv copilot.HookInvocation) (*copilot.PreToolUseHookOutput, error) {\n            if !readOnlyTools[input.ToolName] {\n                return &copilot.PreToolUseHookOutput{\n                    PermissionDecision:       \"deny\",\n                    PermissionDecisionReason: fmt.Sprintf(\"Only read-only tools are allowed. %q was blocked.\", input.ToolName),\n                }, nil\n            }\n            return &copilot.PreToolUseHookOutput{PermissionDecision: \"allow\"}, 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 readOnlyTools = new HashSet<string> { \"read_file\", \"glob\", \"grep\", \"view\" };\n\nvar session = await client.CreateSessionAsync(new SessionConfig\n{\n    Hooks = new SessionHooks\n    {\n        OnPreToolUse = (input, invocation) =>\n        {\n            if (!readOnlyTools.Contains(input.ToolName))\n            {\n                return Task.FromResult<PreToolUseHookOutput?>(new PreToolUseHookOutput\n                {\n                    PermissionDecision = \"deny\",\n                    PermissionDecisionReason = $\"Only read-only tools are allowed. \\\"{input.ToolName}\\\" was blocked.\",\n                });\n            }\n            return Task.FromResult<PreToolUseHookOutput?>(\n                new PreToolUseHookOutput { PermissionDecision = \"allow\" });\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 java.util.Set;\nimport java.util.concurrent.CompletableFuture;\n\nimport com.github.copilot.rpc.PermissionHandler;\nimport com.github.copilot.rpc.SessionConfig;\nimport com.github.copilot.rpc.SessionHooks;\nimport com.github.copilot.rpc.PreToolUseHookOutput;\nvar readOnlyTools = Set.of(\"read_file\", \"glob\", \"grep\", \"view\");\n\nvar hooks = new SessionHooks()\n    .setOnPreToolUse((input, invocation) -> {\n        if (!readOnlyTools.contains(input.getToolName())) {\n            return CompletableFuture.completedFuture(\n                PreToolUseHookOutput.deny(\n                    \"Only read-only tools are allowed. \\\"\" + input.getToolName() + \"\\\" was blocked.\")\n            );\n        }\n        return CompletableFuture.completedFuture(PreToolUseHookOutput.allow());\n    });\n\nvar session = client.createSession(\n    new SessionConfig()\n        .setHooks(hooks)\n        .setOnPermissionRequest(PermissionHandler.APPROVE_ALL)\n).get();\n```\n\n</div>\n\n</div>\n\n### Einschränken des Dateizugriffs auf bestimmte Verzeichnisse\n\n```typescript\nconst ALLOWED_DIRS = [\"/home/user/projects\", \"/tmp\"];\n\nconst session = await client.createSession({\n  hooks: {\n    onPreToolUse: async (input) => {\n      if ([\"read_file\", \"write_file\", \"edit\"].includes(input.toolName)) {\n        const filePath = (input.toolArgs as { path: string }).path;\n        const allowed = ALLOWED_DIRS.some((dir) => filePath.startsWith(dir));\n\n        if (!allowed) {\n          return {\n            permissionDecision: \"deny\",\n            permissionDecisionReason: `Access to \"${filePath}\" is outside the allowed directories.`,\n          };\n        }\n      }\n      return { permissionDecision: \"allow\" };\n    },\n  },\n  onPermissionRequest: async () => ({ kind: \"approve-once\" }),\n});\n```\n\n### Benutzer vor destruktiven Vorgängen fragen\n\n```typescript\nconst DESTRUCTIVE_TOOLS = [\"delete_file\", \"shell\", \"bash\"];\n\nconst session = await client.createSession({\n  hooks: {\n    onPreToolUse: async (input) => {\n      if (DESTRUCTIVE_TOOLS.includes(input.toolName)) {\n        return { permissionDecision: \"ask\" };\n      }\n      return { permissionDecision: \"allow\" };\n    },\n  },\n  onPermissionRequest: async () => ({ kind: \"approve-once\" }),\n});\n```\n\nDurch Rückgabe von `\"ask\"` wird die Entscheidung zur Laufzeit dem Benutzer überlassen – nützlich für destruktive Aktionen, bei denen menschliches Eingreifen erforderlich ist.\n\n## Anwendungsfall: Überwachung und Compliance\n\nKombinieren Sie `onPreToolUse`, `onPostToolUse`und die Sitzungslebenszyklus-Hooks, um einen vollständigen Überwachungspfad zu erstellen, der jede Aktion erfasst, die der Agent ausführt.\n\n### Strukturiertes Überwachungsprotokoll\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\ninterface AuditEntry {\n  timestamp: Date;\n  sessionId: string;\n  event: string;\n  toolName?: string;\n  toolArgs?: unknown;\n  toolResult?: unknown;\n  prompt?: string;\n}\n\nconst auditLog: AuditEntry[] = [];\n\nconst session = await client.createSession({\n  hooks: {\n    onSessionStart: async (input, invocation) => {\n      auditLog.push({\n        timestamp: input.timestamp,\n        sessionId: invocation.sessionId,\n        event: \"session_start\",\n      });\n      return null;\n    },\n    onUserPromptSubmitted: async (input, invocation) => {\n      auditLog.push({\n        timestamp: input.timestamp,\n        sessionId: invocation.sessionId,\n        event: \"user_prompt\",\n        prompt: input.prompt,\n      });\n      return null;\n    },\n    onPreToolUse: async (input, invocation) => {\n      auditLog.push({\n        timestamp: input.timestamp,\n        sessionId: invocation.sessionId,\n        event: \"tool_call\",\n        toolName: input.toolName,\n        toolArgs: input.toolArgs,\n      });\n      return { permissionDecision: \"allow\" };\n    },\n    onPostToolUse: async (input, invocation) => {\n      auditLog.push({\n        timestamp: input.timestamp,\n        sessionId: invocation.sessionId,\n        event: \"tool_result\",\n        toolName: input.toolName,\n        toolResult: input.toolResult,\n      });\n      return null;\n    },\n    onSessionEnd: async (input, invocation) => {\n      auditLog.push({\n        timestamp: input.timestamp,\n        sessionId: invocation.sessionId,\n        event: \"session_end\",\n      });\n\n      // Persist the log — swap this with your own storage backend\n      await fs.promises.writeFile(\n        `audit-${invocation.sessionId}.json`,\n        JSON.stringify(auditLog, null, 2),\n      );\n      return null;\n    },\n  },\n  onPermissionRequest: async () => ({ kind: \"approve-once\" }),\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<!-- docs-validate: skip -->\n\n```python\nimport json, aiofiles\nfrom copilot import PermissionDecisionApproveOnce\n\naudit_log = []\n\nasync def on_session_start(input_data, invocation):\n    audit_log.append({\n        \"timestamp\": input_data[\"timestamp\"].isoformat(),\n        \"session_id\": invocation[\"session_id\"],\n        \"event\": \"session_start\",\n    })\n    return None\n\nasync def on_user_prompt_submitted(input_data, invocation):\n    audit_log.append({\n        \"timestamp\": input_data[\"timestamp\"].isoformat(),\n        \"session_id\": invocation[\"session_id\"],\n        \"event\": \"user_prompt\",\n        \"prompt\": input_data[\"prompt\"],\n    })\n    return None\n\nasync def on_pre_tool_use(input_data, invocation):\n    audit_log.append({\n        \"timestamp\": input_data[\"timestamp\"].isoformat(),\n        \"session_id\": invocation[\"session_id\"],\n        \"event\": \"tool_call\",\n        \"tool_name\": input_data[\"toolName\"],\n        \"tool_args\": input_data[\"toolArgs\"],\n    })\n    return {\"permissionDecision\": \"allow\"}\n\nasync def on_post_tool_use(input_data, invocation):\n    audit_log.append({\n        \"timestamp\": input_data[\"timestamp\"].isoformat(),\n        \"session_id\": invocation[\"session_id\"],\n        \"event\": \"tool_result\",\n        \"tool_name\": input_data[\"toolName\"],\n        \"tool_result\": input_data[\"toolResult\"],\n    })\n    return None\n\nasync def on_session_end(input_data, invocation):\n    audit_log.append({\n        \"timestamp\": input_data[\"timestamp\"].isoformat(),\n        \"session_id\": invocation[\"session_id\"],\n        \"event\": \"session_end\",\n    })\n    async with aiofiles.open(f\"audit-{invocation['session_id']}.json\", \"w\") as f:\n        await f.write(json.dumps(audit_log, indent=2))\n    return None\n\nsession = await client.create_session(\n    on_permission_request=lambda req, inv: PermissionDecisionApproveOnce(),\n    hooks={\n        \"on_session_start\": on_session_start,\n        \"on_user_prompt_submitted\": on_user_prompt_submitted,\n        \"on_pre_tool_use\": on_pre_tool_use,\n        \"on_post_tool_use\": on_post_tool_use,\n        \"on_session_end\": on_session_end,\n    },\n)\n```\n\n</div>\n\n</div>\n\n### Geheime Daten aus Tool-Ergebnissen schwärzen\n\n```typescript\nconst SECRET_PATTERNS = [\n  /(?:api[_-]?key|token|secret|password)\\s*[:=]\\s*[\"']?[\\w\\-\\.]+[\"']?/gi,\n];\n\nconst session = await client.createSession({\n  hooks: {\n    onPostToolUse: async (input) => {\n      if (typeof input.toolResult !== \"string\") return null;\n\n      let redacted = input.toolResult;\n      for (const pattern of SECRET_PATTERNS) {\n        redacted = redacted.replace(pattern, \"[REDACTED]\");\n      }\n\n      return redacted !== input.toolResult\n        ? { modifiedResult: redacted }\n        : null;\n    },\n  },\n  onPermissionRequest: async () => ({ kind: \"approve-once\" }),\n});\n```\n\n## Anwendungsfall: Benachrichtigungen und Sounds\n\nHooks werden im Prozess Ihrer Anwendung ausgelöst, sodass Sie alle Nebeneffekte auslösen können– Desktopbenachrichtigungen, Sounds, Slack-Nachrichten oder Webhook-Aufrufe.\n\n### Desktopbenachrichtigung für Sitzungsereignisse\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\nimport notifier from \"node-notifier\"; // npm install node-notifier\n\nconst session = await client.createSession({\n  hooks: {\n    onSessionEnd: async (input, invocation) => {\n      notifier.notify({\n        title: \"Copilot Session Complete\",\n        message: `Session ${invocation.sessionId.slice(0, 8)} finished (${input.reason}).`,\n      });\n      return null;\n    },\n    onErrorOccurred: async (input) => {\n      notifier.notify({\n        title: \"Copilot Error\",\n        message: input.error.slice(0, 200),\n      });\n      return null;\n    },\n  },\n  onPermissionRequest: async () => ({ kind: \"approve-once\" }),\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\nimport subprocess\nfrom copilot import PermissionDecisionApproveOnce\n\nasync def on_session_end(input_data, invocation):\n    sid = invocation[\"session_id\"][:8]\n    reason = input_data[\"reason\"]\n    subprocess.Popen([\n        \"notify-send\", \"Copilot Session Complete\",\n        f\"Session {sid} finished ({reason}).\",\n    ])\n    return None\n\nasync def on_error_occurred(input_data, invocation):\n    subprocess.Popen([\n        \"notify-send\", \"Copilot Error\",\n        input_data[\"error\"][:200],\n    ])\n    return None\n\nsession = await client.create_session(\n    on_permission_request=lambda req, inv: PermissionDecisionApproveOnce(),\n    hooks={\n        \"on_session_end\": on_session_end,\n        \"on_error_occurred\": on_error_occurred,\n    },\n)\n```\n\n</div>\n\n</div>\n\n### Einen Ton abspielen, wenn ein Tool fertig ist\n\n```typescript\nimport { exec } from \"node:child_process\";\n\nconst session = await client.createSession({\n  hooks: {\n    onPostToolUse: async (input) => {\n      // macOS: play a system sound after every tool call\n      exec(\"afplay /System/Library/Sounds/Pop.aiff\");\n      return null;\n    },\n    onErrorOccurred: async () => {\n      exec(\"afplay /System/Library/Sounds/Basso.aiff\");\n      return null;\n    },\n  },\n  onPermissionRequest: async () => ({ kind: \"approve-once\" }),\n});\n```\n\n### Bei Fehlern auf Slack posten\n\n```typescript\nconst SLACK_WEBHOOK_URL = process.env.SLACK_WEBHOOK_URL!;\n\nconst session = await client.createSession({\n  hooks: {\n    onErrorOccurred: async (input, invocation) => {\n      if (!input.recoverable) {\n        await fetch(SLACK_WEBHOOK_URL, {\n          method: \"POST\",\n          headers: { \"Content-Type\": \"application/json\" },\n          body: JSON.stringify({\n            text: `🚨 Unrecoverable error in session \\`${invocation.sessionId.slice(0, 8)}\\`:\\n\\`\\`\\`${input.error}\\`\\`\\``,\n          }),\n        });\n      }\n      return null;\n    },\n  },\n  onPermissionRequest: async () => ({ kind: \"approve-once\" }),\n});\n```\n\n## Anwendungsfall: Prompt-Anreicherung\n\nVerwenden Sie `onSessionStart` und `onUserPromptSubmitted`, um den Kontext automatisch einzufügen, damit Benutzer sich nicht wiederholen müssen.\n\n### Einfügen von Projektmetadaten beim Sitzungsstart\n\n```typescript\nconst session = await client.createSession({\n  hooks: {\n    onSessionStart: async (input) => {\n      const pkg = JSON.parse(\n        await fs.promises.readFile(\"package.json\", \"utf-8\"),\n      );\n      return {\n        additionalContext: [\n          `Project: ${pkg.name} v${pkg.version}`,\n          `Node: ${process.version}`,\n          `Working directory: ${input.workingDirectory}`,\n        ].join(\"\\n\"),\n      };\n    },\n  },\n  onPermissionRequest: async () => ({ kind: \"approve-once\" }),\n});\n```\n\n### Erweitern von Kurzbefehlen in Eingabeaufforderungen\n\n```typescript\nconst SHORTCUTS: Record<string, string> = {\n  \"/fix\": \"Find and fix all errors in the current file\",\n  \"/test\": \"Write comprehensive unit tests for this code\",\n  \"/explain\": \"Explain this code in detail\",\n  \"/refactor\": \"Refactor this code to improve readability\",\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 { modifiedPrompt: rest ? `${expansion}: ${rest}` : expansion };\n        }\n      }\n      return null;\n    },\n  },\n  onPermissionRequest: async () => ({ kind: \"approve-once\" }),\n});\n```\n\n## Anwendungsfall: Fehlerbehandlung und Wiederherstellung\n\nDer `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.\n\n### Wiederholen von vorübergehenden Modellfehlern\n\n```typescript\nconst session = await client.createSession({\n  hooks: {\n    onErrorOccurred: async (input) => {\n      if (input.errorContext === \"model_call\" && input.recoverable) {\n        return {\n          errorHandling: \"retry\",\n          retryCount: 3,\n          userNotification: \"Temporary model issue — retrying…\",\n        };\n      }\n      return null;\n    },\n  },\n  onPermissionRequest: async () => ({ kind: \"approve-once\" }),\n});\n```\n\n### Benutzerfreundliche Fehlermeldungen\n\n```typescript\nconst FRIENDLY_MESSAGES: Record<string, string> = {\n  model_call: \"The AI model is temporarily unavailable. Please try again.\",\n  tool_execution: \"A tool encountered an error. Check inputs and try again.\",\n  system: \"A system error occurred. Please try again later.\",\n};\n\nconst session = await client.createSession({\n  hooks: {\n    onErrorOccurred: async (input) => {\n      return {\n        userNotification: FRIENDLY_MESSAGES[input.errorContext] ?? input.error,\n      };\n    },\n  },\n  onPermissionRequest: async () => ({ kind: \"approve-once\" }),\n});\n```\n\n## Anwendungsfall: Sitzungsmetriken\n\nVerfolgen Sie, wie lange Sitzungen ausgeführt werden, wie viele Tools aufgerufen werden und warum Sitzungen enden – nützlich für Dashboards und Kostenüberwachung.\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 metrics = new Map<\n  string,\n  { start: Date; toolCalls: number; prompts: number }\n>();\n\nconst session = await client.createSession({\n  hooks: {\n    onSessionStart: async (input, invocation) => {\n      metrics.set(invocation.sessionId, {\n        start: input.timestamp,\n        toolCalls: 0,\n        prompts: 0,\n      });\n      return null;\n    },\n    onUserPromptSubmitted: async (_input, invocation) => {\n      metrics.get(invocation.sessionId)!.prompts++;\n      return null;\n    },\n    onPreToolUse: async (_input, invocation) => {\n      metrics.get(invocation.sessionId)!.toolCalls++;\n      return { permissionDecision: \"allow\" };\n    },\n    onSessionEnd: async (input, invocation) => {\n      const m = metrics.get(invocation.sessionId)!;\n      const durationSec =\n        (input.timestamp.getTime() - m.start.getTime()) / 1000;\n\n      console.log(\n        `Session ${invocation.sessionId.slice(0, 8)}: ` +\n          `${durationSec.toFixed(1)}s, ${m.prompts} prompts, ` +\n          `${m.toolCalls} tool calls, ended: ${input.reason}`,\n      );\n\n      metrics.delete(invocation.sessionId);\n      return null;\n    },\n  },\n  onPermissionRequest: async () => ({ kind: \"approve-once\" }),\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 import PermissionDecisionApproveOnce\n\nsession_metrics = {}\n\nasync def on_session_start(input_data, invocation):\n    session_metrics[invocation[\"session_id\"]] = {\n        \"start\": input_data[\"timestamp\"],\n        \"tool_calls\": 0,\n        \"prompts\": 0,\n    }\n    return None\n\nasync def on_user_prompt_submitted(input_data, invocation):\n    session_metrics[invocation[\"session_id\"]][\"prompts\"] += 1\n    return None\n\nasync def on_pre_tool_use(input_data, invocation):\n    session_metrics[invocation[\"session_id\"]][\"tool_calls\"] += 1\n    return {\"permissionDecision\": \"allow\"}\n\nasync def on_session_end(input_data, invocation):\n    m = session_metrics.pop(invocation[\"session_id\"])\n    duration = (input_data[\"timestamp\"] - m[\"start\"]).total_seconds()\n    sid = invocation[\"session_id\"][:8]\n    print(\n        f\"Session {sid}: {duration:.1f}s, {m['prompts']} prompts, \"\n        f\"{m['tool_calls']} tool calls, ended: {input_data['reason']}\"\n    )\n    return None\n\nsession = await client.create_session(\n    on_permission_request=lambda req, inv: PermissionDecisionApproveOnce(),\n    hooks={\n        \"on_session_start\": on_session_start,\n        \"on_user_prompt_submitted\": on_user_prompt_submitted,\n        \"on_pre_tool_use\": on_pre_tool_use,\n        \"on_session_end\": on_session_end,\n    },\n)\n```\n\n</div>\n\n</div>\n\n## Kombinieren von Haken\n\nHooks entstehen von selbst. Ein einzelnes `hooks`-Objekt kann Berechtigungen **und** Überwachung **und** Benachrichtigungen bearbeiten — jeder Hook erfüllt seine eigene Aufgabe.\n\n```typescript\nconst session = await client.createSession({\n  hooks: {\n    onSessionStart: async (input) => {\n      console.log(`[audit] session started in ${input.workingDirectory}`);\n      return { additionalContext: \"Project uses TypeScript and Vitest.\" };\n    },\n    onPreToolUse: async (input) => {\n      console.log(`[audit] tool requested: ${input.toolName}`);\n      if (input.toolName === \"shell\") {\n        return { permissionDecision: \"ask\" };\n      }\n      return { permissionDecision: \"allow\" };\n    },\n    onPostToolUse: async (input) => {\n      console.log(`[audit] tool completed: ${input.toolName}`);\n      return null;\n    },\n    onErrorOccurred: async (input) => {\n      console.error(`[alert] ${input.errorContext}: ${input.error}`);\n      return null;\n    },\n    onSessionEnd: async (input, invocation) => {\n      console.log(\n        `[audit] session ${invocation.sessionId.slice(0, 8)} ended: ${input.reason}`,\n      );\n      return null;\n    },\n  },\n  onPermissionRequest: async () => ({ kind: \"approve-once\" }),\n});\n```\n\n## Bewährte Methoden\n\n1. **Halten Sie Hooks schnell.** Jeder Hook wird inline ausgeführt – langsame Hooks verzögern die Unterhaltung. Entladen Sie nach Möglichkeit schwere Arbeit (Datenbankschreibvorgänge, HTTP-Aufrufe) in eine Hintergrundwarteschlange.\n\n2. **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.\n\n3. **Seien Sie mit Berechtigungsentscheidungen explizit.** Die Rückgabe von `{ permissionDecision: \"allow\" }` ist klarer als die von `null`, obwohl beide das Tool zulassen.\n\n4. **Schlucken Sie keine kritischen Fehler.** Es ist in Ordnung, wiederherstellbare Toolfehler zu unterdrücken, aber nicht wiederherstellbare immer zu protokollieren oder darauf hinzuweisen.\n\n5. **Verwenden Sie `additionalContext` statt `modifiedPrompt` nach Möglichkeit.** Durch das Anfügen des Kontexts wird die ursprüngliche Absicht des Benutzers beibehalten, während das Modell weiterhin unterstützt wird.\n\n6. **Bereichsstatus nach Sitzungs-ID.** Wenn Sie Daten pro Sitzung nachverfolgen, schlüsseln Sie diese `invocation.sessionId` auf und bereinigen Sie sie in `onSessionEnd`.\n\n## Reference\n\nVollständige Typdefinitionen, Eingabe-/Ausgabefeldtabellen und zusätzliche Beispiele für jeden Hook finden Sie in der API-Referenz:\n\n* [Sitzungshaken](/de/enterprise-cloud@latest/copilot/how-tos/copilot-sdk/hooks/hooks-overview)\n* [Pre-Tool Verwendungs-Hook](/de/enterprise-cloud@latest/copilot/how-tos/copilot-sdk/hooks/pre-tool-use)\n* [Hook für die Verwendung des Tools nach der Sitzung](/de/enterprise-cloud@latest/copilot/how-tos/copilot-sdk/hooks/post-tool-use)\n* [Hook für gesendete Benutzer Prompt](/de/enterprise-cloud@latest/copilot/how-tos/copilot-sdk/hooks/user-prompt-submitted)\n* [Hook „Benutzer-Prompt transformiert“](/de/enterprise-cloud@latest/copilot/how-tos/copilot-sdk/hooks/user-prompt-transformed)\n* [Session Lifecycle Hooks](/de/enterprise-cloud@latest/copilot/how-tos/copilot-sdk/hooks/session-lifecycle)\n* [Fehlerbehandlung Hook](/de/enterprise-cloud@latest/copilot/how-tos/copilot-sdk/hooks/error-handling)\n\n## Siehe auch\n\n* [Erstellen Sie Ihre erste Copilot-gestützte App](/de/enterprise-cloud@latest/copilot/how-tos/copilot-sdk/getting-started)\n* [Angepasste Agents und Orchestrierung von Unteragenten](/de/enterprise-cloud@latest/copilot/how-tos/copilot-sdk/features/custom-agents)\n* [Ereignisse einer Streaming-Sitzung](/de/enterprise-cloud@latest/copilot/how-tos/copilot-sdk/features/streaming-events)\n* [Debughandbuch](/de/enterprise-cloud@latest/copilot/how-tos/copilot-sdk/troubleshooting/debugging)"}