For the complete documentation index, see llms.txt. Markdown versions of documentation pages are available by appending .md to the page URL.
Hauptnavigation

Hooks

Skripte oder MCP-Tools während des Codex-Lebenszyklus ausführen

Hooks sind ein Framework zur Erweiterung von Codex. Mit ihnen kannst du während der agentischen Schleife Skripte oder MCP-Tools ausführen und so unter anderem Folgendes ermöglichen:

  • Den Chat an ein eigenes System für Protokollierung oder Analyse senden
  • Die Prompts deines Teams prüfen, um das versehentliche Einfügen von API-Schlüsseln zu verhindern
  • Chats zusammenfassen, um automatisch dauerhafte Erinnerungen zu erstellen
  • Eine eigene Validierung ausführen, wenn eine Chat-Runde endet, um Standards durchzusetzen
  • Das Prompting in einem bestimmten Verzeichnis anpassen

Beachte Folgendes zum Laufzeitverhalten:

  • Alle passenden Hooks aus mehreren Dateien werden ausgeführt.
  • Mehrere passende Befehls-Hooks für dasselbe Ereignis werden gleichzeitig gestartet. Daher kann kein Hook den Start eines anderen passenden Hooks verhindern.
  • Nicht verwaltete Hooks müssen vor ihrer Ausführung überprüft und als vertrauenswürdig eingestuft werden.

Hooks werden zu verschiedenen Zeitpunkten einer Unterhaltung ausgeführt:

ZeitpunktHooks
Während einer RundePreToolUse, PermissionRequest, PostToolUse, PreCompact, PostCompact, UserPromptSubmit, SubagentStop, Stop
Beim Start einer Sitzung oder eines SubagentenSessionStart, SubagentStart
Wenn der Haupt-Thread endetSessionEnd (wird für Subagenten nicht ausgeführt)

Wo Codex nach Hooks sucht

Codex sucht neben aktiven Konfigurationsebenen in einer dieser Formen nach Hooks:

  • hooks.json
  • direkt in config.toml definierte [hooks]-Tabellen

Installierte Plug-ins können die Lebenszykluskonfiguration auch über ihr Plug-in-Manifest oder die Standarddatei hooks/hooks.json mitliefern. Unter Plug-ins erstellen findest du die Regeln für die Paketierung von Plug-ins.

In der Praxis sind die folgenden vier Speicherorte am nützlichsten:

  • ~/.codex/hooks.json
  • ~/.codex/config.toml
  • <repo>/.codex/hooks.json
  • <repo>/.codex/config.toml

Wenn mehrere Hook-Quellen vorhanden sind, lädt Codex alle passenden Hooks. Konfigurationsebenen mit höherer Priorität ersetzen keine Hooks aus Ebenen mit niedrigerer Priorität. Wenn eine einzelne Ebene sowohl hooks.json als auch direkt eingebettete [hooks]-Tabellen enthält, führt Codex sie zusammen und zeigt beim Start eine Warnung an. Verwende pro Ebene möglichst nur eine Darstellungsform.

Codex kann auch Hooks erkennen, die in aktivierten Plug-ins enthalten sind. Diese Hooks werden zusammen mit Hooks aus anderen Quellen geladen und durchlaufen dieselbe Vertrauensprüfung wie andere nicht verwaltete Hooks.

Projektlokale Hooks werden nur geladen, wenn die Projektebene .codex/ als vertrauenswürdig gilt. In nicht vertrauenswürdigen Projekten lädt Codex weiterhin Benutzer- und System-Hooks aus den jeweils eigenen aktiven Konfigurationsebenen.

Hooks überprüfen und als vertrauenswürdig einstufen

Codex listet die konfigurierten Hooks auf, bevor es entscheidet, welche ausgeführt werden dürfen. Bevor ein nicht verwalteter Hook ausgeführt werden kann, musst du seine genaue Definition überprüfen und als vertrauenswürdig einstufen. Codex bindet die Vertrauensentscheidung an den aktuellen Hash des Hooks. Neue oder geänderte Hooks werden deshalb zur Überprüfung markiert und übersprungen, bis du sie als vertrauenswürdig eingestuft hast.

Mit /hooks in der CLI kannst du Hook-Quellen einsehen, neue oder geänderte Hooks überprüfen, Hooks als vertrauenswürdig einstufen oder einzelne nicht verwaltete Hooks deaktivieren. Wenn beim Start Hooks überprüft werden müssen, gibt Codex eine Warnung aus, die dich auffordert, /hooks zu öffnen.

Verwaltete Hooks aus System-, MDM- oder Cloud-Quellen sowie aus requirements.toml werden als verwaltet gekennzeichnet, gelten aufgrund einer Richtlinie als vertrauenswürdig und können nicht über den benutzerseitigen Hook-Browser deaktiviert werden.

Für eine einmalige Automatisierung, die Hook-Quellen bereits außerhalb von Codex prüft, übergib --dangerously-bypass-hook-trust. So führst du aktivierte Hooks bei diesem Aufruf aus, ohne dass dafür eine dauerhaft gespeicherte Vertrauensentscheidung erforderlich ist.

Struktur der Konfiguration

Hooks sind in drei Ebenen organisiert:

  • Ein Hook-Ereignis wie PreToolUse, PostToolUse, PreCompact, SubagentStart oder Stop
  • Eine Matcher-Gruppe, die festlegt, wann sie für dieses Ereignis greift
  • Ein oder mehrere Hook-Handler, die ausgeführt werden, wenn die Matcher-Gruppe greift
{
  "description": "Optional lifecycle hooks for this workspace.",
  "hooks": {
    "SessionStart": [
      {
        "matcher": "startup|resume",
        "hooks": [
          {
            "type": "command",
            "command": "python3 ~/.codex/hooks/session_start.py",
            "statusMessage": "Loading session notes",
            "additionalContextLimit": 5000
          }
        ]
      }
    ],
    "SessionEnd": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "python3 ~/.codex/hooks/session_end.py",
            "timeout": 3
          }
        ]
      }
    ],
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "/usr/bin/python3 \"$(git rev-parse --show-toplevel)/.codex/hooks/pre_tool_use_policy.py\"",
            "statusMessage": "Checking Bash command"
          }
        ]
      }
    ],
    "PermissionRequest": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "/usr/bin/python3 \"$(git rev-parse --show-toplevel)/.codex/hooks/permission_request.py\"",
            "statusMessage": "Checking approval request"
          }
        ]
      }
    ],
    "PostToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "/usr/bin/python3 \"$(git rev-parse --show-toplevel)/.codex/hooks/post_tool_use_review.py\"",
            "statusMessage": "Reviewing Bash output"
          }
        ]
      }
    ],
    "UserPromptSubmit": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "/usr/bin/python3 \"$(git rev-parse --show-toplevel)/.codex/hooks/user_prompt_submit_data_flywheel.py\""
          }
        ]
      }
    ],
    "Stop": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "/usr/bin/python3 \"$(git rev-parse --show-toplevel)/.codex/hooks/stop_continue.py\"",
            "timeout": 30
          }
        ]
      }
    ]
  }
}

Hinweise:

  • description ist ein optionales Metadatenfeld auf oberster Ebene einer hooks.json-Datei. Es beeinflusst nicht, welche Hooks ausgeführt werden.
  • timeout wird in Sekunden angegeben.
  • Wenn timeout fehlt, verwendet Codex für die meisten Hooks 600 Sekunden.
    • Bei SessionEnd beträgt der Standardwert 1 Sekunde; unterstützt werden bis zu 3 Sekunden.
  • statusMessage ist optional.
  • Mit additionalContextLimit legst du fest, wie viel Kontext ein Befehls-Hook über additionalContext an das Modell senden kann, bevor Codex den vollständigen Text auf dem Datenträger speichert und stattdessen eine kürzere Vorschau sendet. Siehe Große Hook-Ausgaben.
  • Mit commandWindows kannst du optional einen abweichenden Befehl festlegen, der nur unter Windows gilt. Verwende in TOML command_windows oder commandWindows.
  • Setze async auf true, um einen Befehls-Hook im Hintergrund auszuführen.
  • Handler vom Typ command und mcp_tool werden unterstützt. Handler vom Typ prompt und agent werden geparst, aber übersprungen.
  • Befehle werden mit dem cwd der Sitzung als Arbeitsverzeichnis ausgeführt.
  • Bei Hooks im Repository solltest du Pfade vom Git-Stammverzeichnis aus auflösen, statt einen relativen Pfad wie .codex/hooks/... zu verwenden. Codex kann aus einem Unterverzeichnis gestartet werden. Ein Pfad auf Basis des Git-Stammverzeichnisses verweist trotzdem auf denselben Speicherort des Hooks.

Die entsprechende Inline-TOML-Konfiguration in config.toml:

[[hooks.SessionStart]]
matcher = "^compact$"

[[hooks.SessionStart.hooks]]
type = "command"
command = '/usr/bin/python3 "$(git rev-parse --show-toplevel)/.codex/hooks/session_start.py"'
additionalContextLimit = 5000

[[hooks.PreToolUse]]
matcher = "^Bash$"

[[hooks.PreToolUse.hooks]]
type = "command"
command = '/usr/bin/python3 "$(git rev-parse --show-toplevel)/.codex/hooks/pre_tool_use_policy.py"'
timeout = 30
statusMessage = "Checking Bash command"

[[hooks.PostToolUse]]
matcher = "^Bash$"

[[hooks.PostToolUse.hooks]]
type = "command"
command = '/usr/bin/python3 "$(git rev-parse --show-toplevel)/.codex/hooks/post_tool_use_review.py"'
timeout = 30
statusMessage = "Reviewing Bash output"

MCP-Tool-Hooks

Mit einem MCP-Tool-Hook kann ein Lebenszyklusereignis ein Tool auf einem bereits verbundenen MCP-Server aufrufen. Der Hook sendet strukturierte Argumente direkt an das Tool. Für ihn gelten dieselbe Vertrauensprüfung und dieselben Ausgabevorgaben wie für einen Befehls-Hook.

Einen MCP-Tool-Hook konfigurieren

Dieser Hook fordert den MCP-Server scanner auf, jeden Patch zu scannen, nachdem Codex Dateien geschrieben oder bearbeitet hat:

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          {
            "type": "mcp_tool",
            "server": "scanner",
            "tool": "scan_patch",
            "input": { "patch": "${tool_input.command}" },
            "timeout": 30,
            "statusMessage": "Scanning edited files"
          }
        ]
      }
    ]
  }
}
FeldBedeutung
typeMuss mcp_tool sein.
serverErforderlicher Name eines bereits verbundenen MCP-Servers.
toolErforderlicher Name eines Tools, das dieser Server bereitstellt.
inputOptionales JSON-Objekt mit Vorlagen für Argumente. Standardwert: {}.
timeoutOptionales Zeitlimit für die aktive Ausführung in Sekunden. Standardwert: 600.
statusMessageOptionale Nachricht, die während der Ausführung des Hooks angezeigt wird.

Argumente mit Daten aus dem Hook-Ereignis füllen

Verwende ${field.nested}, um ein Feld im Hook-Ereignis über einen Pfad in Punktnotation auszulesen. Ein Platzhalter, der den gesamten Wert bildet, behält seinen JSON-Typ. Ein Platzhalter innerhalb einer längeren Zeichenfolge wird als Text ausgegeben. Codex löst Platzhalter in Objekten und Arrays rekursiv auf.

Bei einem Ereignis mit {"tool_input":{"file_path":"src/main.rs","count":3}} und dieser Argumentvorlage:

{
  "path": "${tool_input.file_path}",
  "count": "${tool_input.count}",
  "message": "Scanning ${tool_input.file_path}"
}

ergibt sich:

{
  "path": "src/main.rs",
  "count": 3,
  "message": "Scanning src/main.rs"
}

Ausführung und Lebenszyklus

  • Hooks nutzen eine bestehende MCP-Verbindung. Sie starten keine Server und verbinden sich nicht erneut mit ihnen.
  • Ein Hook kann einen Vorgang blockieren, wenn das Tool eine Entscheidung zum Blockieren zurückgibt. Fehler, fehlende Server und nicht verfügbare Tools blockieren den Vorgang nicht.
  • MCP-Tool-Hooks laufen synchron. Sie fordern keine Genehmigung für Tool-Aufrufe an und lösen keine anderen Hooks aus.
  • Es gilt das kürzere der beiden Zeitlimits für Hook und Server. Die Wartezeit auf eine Antwort auf eine MCP-Elizitation wird nicht auf das Zeitlimit angerechnet.
  • Hooks für SessionStart können ausgeführt werden, bevor ein MCP-Server bereit ist. In diesem Fall blockieren sie die Sitzung nicht.
  • SessionEnd unterstützt keine MCP-Tool-Hooks.

Hooks deaktivieren

Hooks sind standardmäßig aktiviert. Um sie in config.toml zu deaktivieren, lege Folgendes fest:

[features]
hooks = false

Verwende hooks als kanonischen Schlüssel für die Funktion. codex_hooks funktioniert weiterhin als veralteter Alias. Admins können Hooks auf dieselbe Weise in requirements.toml mit [features].hooks = false verbindlich deaktivieren.

Verwaltete Hooks aus requirements.toml

Auch vom Unternehmen verwaltete Vorgaben können Hooks direkt unter [hooks] definieren. Das ist nützlich, wenn Admins die Hook-Konfiguration verbindlich vorgeben und dabei die eigentlichen Skripte über MDM oder ein anderes System zur Geräteverwaltung verteilen möchten. Um verwaltete Hooks auch für Nutzende durchzusetzen, die Hooks lokal deaktiviert haben, lege [features].hooks = true in requirements.toml zusätzlich zu [hooks] verbindlich fest. Um Hooks aus Benutzer-, Projekt- und Sitzungsquellen sowie Plug-ins zu ignorieren, aber weiterhin von Admins verwaltete Hooks zuzulassen, setze allow_managed_hooks_only = true.

allow_managed_hooks_only = true

[features]
hooks = true

[hooks]
managed_dir = "/enterprise/hooks"
windows_managed_dir = 'C:\enterprise\hooks'

[[hooks.PreToolUse]]
matcher = "^Bash$"

[[hooks.PreToolUse.hooks]]
type = "command"
command = "python3 /enterprise/hooks/pre_tool_use_policy.py"
command_windows = 'py -3 C:\enterprise\hooks\pre_tool_use_policy.py'
timeout = 30
statusMessage = "Checking managed Bash command"

Hinweise zu verwalteten Hooks:

  • managed_dir wird unter macOS und Linux verwendet.
  • windows_managed_dir wird unter Windows verwendet.
  • Codex verteilt die Skripte in managed_dir nicht. Die Tools deines Unternehmens müssen sie separat installieren und aktualisieren.
  • Befehle für verwaltete Hooks sollten absolute Pfade zu Skripten innerhalb des konfigurierten verwalteten Verzeichnisses verwenden.
  • Mit allow_managed_hooks_only = true überspringt Codex Hooks aus Benutzer-, Projekt- und Sitzungsquellen sowie Plug-ins, lädt aber weiterhin verwaltete Hooks aus requirements.toml und anderen verwalteten Konfigurationsebenen.

In Plug-ins enthaltene Hooks

Wenn ein Plug-in aktiviert ist, kann Codex dessen Lebenszyklus-Hooks zusammen mit benutzer- und projektspezifischen sowie verwalteten Hooks laden.

Standardmäßig sucht Codex im Stammverzeichnis des Plug-ins nach hooks/hooks.json. Das Manifest des Plug-ins kann diese Voreinstellung mit einem hooks-Eintrag in .codex-plugin/plugin.json überschreiben. Dieser Eintrag kann ein Pfad mit dem Präfix ./, ein Array aus Pfaden mit dem Präfix ./, ein direkt eingebettetes Hooks-Objekt oder ein Array aus direkt eingebetteten Hooks-Objekten sein.

{
  "name": "repo-policy",
  "hooks": "./hooks/hooks.json"
}

Hook-Pfade im Manifest werden relativ zum Stammverzeichnis des Plug-ins aufgelöst und müssen innerhalb dieses Verzeichnisses bleiben. Wenn ein Manifest hooks definiert, verwendet Codex diese Manifest-Einträge anstelle der Standarddatei hooks/hooks.json.

Befehle von Plug-in-Hooks erhalten diese Umgebungsvariablen:

  • PLUGIN_ROOT ist eine Codex-spezifische Erweiterung, die auf das Stammverzeichnis des installierten Plug-ins verweist.
  • PLUGIN_DATA ist eine Codex-spezifische Erweiterung, die auf das beschreibbare Datenverzeichnis des Plug-ins verweist.
  • Codex setzt außerdem CLAUDE_PLUGIN_ROOT und CLAUDE_PLUGIN_DATA, um die Kompatibilität mit bestehenden Plug-in-Hooks zu gewährleisten.

Plug-in-Hooks verwenden dasselbe Ereignisschema wie andere Hooks. Wenn du ein Plug-in installierst oder aktivierst, gelten dessen Hooks nicht automatisch als vertrauenswürdig. Codex überspringt die im Plug-in enthaltenen Hooks, bis du die aktuelle Hook-Definition geprüft und als vertrauenswürdig eingestuft hast.

Matcher-Muster

Das Feld matcher enthält einen regulären Ausdruck als Zeichenfolge und legt fest, wann Hooks ausgelöst werden. Verwende "*", "" oder lass matcher ganz weg, um jedes Auftreten eines unterstützten Ereignisses zu erfassen.

Nur einige der aktuellen Codex-Ereignisse berücksichtigen matcher:

EreignisWas matcher filtertHinweise
PermissionRequestTool-NameUnterstützt werden unter anderem Bash, apply_patch* und MCP-Tool-Namen
PostToolUseTool-NameSiehe Unterstützte Tools
PostCompactAuslöser für Compaction (Kontextverdichtung)Die Werte sind manual oder auto
PreCompactAuslöser für Compaction (Kontextverdichtung)Die Werte sind manual oder auto
PreToolUseTool-NameSiehe Unterstützte Tools
SessionEndBeendigungsgrundDerzeit nur other
SessionStartAuslöser des StartsDie Werte sind startup, resume, clear und compact
SubagentStartTyp des SubagentenDie Werte richten sich nach dem Subagenten, der startet
SubagentStopTyp des SubagentenDie Werte richten sich nach dem Subagenten, der stoppt
UserPromptSubmitNicht unterstütztDie Einstellung matcher wird bei diesem Ereignis immer ignoriert
StopNicht unterstütztDie Einstellung matcher wird bei diesem Ereignis immer ignoriert

*Bei apply_patch kannst du für matcher auch Edit oder Write verwenden.

Beispiele:

  • Bash
  • ^apply_patch$
  • Edit|Write
  • mcp__filesystem__read_file
  • mcp__filesystem__.*
  • startup|resume|clear|compact
  • manual|auto

Unterstützte Tools

PreToolUse und PostToolUse können mehr als nur Shell- und MCP-Aufrufe erfassen. Die meisten lokalen Funktionstools nutzen denselben Hook-Pfad. So kannst du nach ihrem Tool-Namen filtern, ihre JSON-Argumente prüfen und bei PreToolUse den Aufruf blockieren oder umschreiben.

Tool-PfadPreToolUsePostToolUseHinweise
Shell-BefehleJaJaVerwende Bash zum Abgleich.
Einheitliche Befehlsausführung (exec_command)JaJaVerwende Bash zum Abgleich. Eine spätere Abfrage mit write_stdin kann das PostToolUse-Ereignis des ursprünglichen Befehls liefern, sobald dieser abgeschlossen ist.
apply_patchJaJaVerwende apply_patch, Edit oder Write zum Abgleich.
MCP-ToolsJaJaVerwende zum Abgleich den Namen des MCP-Tools, etwa mcp__filesystem__read_file.
Andere lokale FunktionstoolsJaJaVerwende zum Abgleich den Namen des Funktionstools, etwa update_plan. Für spawn_agent kannst du auch Agent zum Abgleich verwenden.
Gehostete Tools, etwa WebSearchNeinNeinDiese verwenden nicht den Hook-Pfad für lokale Funktionstools.

write_stdin überträgt Daten für eine bestehende Sitzung zur einheitlichen Befehlsausführung. Es führt PreToolUse nicht erneut aus, wenn es Eingaben sendet oder einen Befehl abfragt, der PreToolUse bereits durchlaufen hat.

Einige spezialisierte Tool-Pfade können den standardmäßigen Hook-Pfad überspringen. Tool-Hooks sind eine nützliche Schutzmaßnahme, gewährleisten aber keine lückenlose Durchsetzung.

Gemeinsame Eingabefelder

Jeder Befehls-Hook erhält ein JSON-Objekt über stdin.

Diese gemeinsamen Felder verwendest du in der Regel:

FeldTypBedeutung
session_idstringID der aktuellen Codex-Sitzung. Hooks für Subagenten verwenden die ID der übergeordneten Sitzung.
transcript_pathstring | nullPfad zur Transkriptdatei der Sitzung, sofern vorhanden
cwdstringArbeitsverzeichnis der Sitzung
hook_event_namestringName des aktuellen Hook-Ereignisses
modelstringCodex-spezifische Erweiterung. Slug des aktiven Modells

Hooks für einzelne Gesprächsrunden führen turn_id in ihren ereignisspezifischen Tabellen als Codex-spezifische Erweiterung auf.

SessionStart, PreToolUse, PermissionRequest, PostToolUse, UserPromptSubmit, SubagentStart, SubagentStop und Stop enthalten außerdem permission_mode. Dieses Feld gibt den aktuellen Berechtigungsmodus als default, acceptEdits, plan, dontAsk oder bypassPermissions an.

transcript_path verweist der Einfachheit halber auf ein Chat-Transkript. Das Transkriptformat ist jedoch keine stabile Schnittstelle für Hooks und kann sich im Laufe der Zeit ändern.

Das vollständige Übertragungsformat findest du unter Schemata.

Gemeinsame Ausgabefelder

SessionStart, PreCompact, PostCompact, UserPromptSubmit, SubagentStop und Stop unterstützen diese gemeinsamen JSON-Felder. SubagentStart akzeptiert dieselbe Struktur für systemMessage und Hook-spezifischen Kontext, aber continue: false stoppt den Subagenten nicht:

{
  "continue": true,
  "stopReason": "optional",
  "systemMessage": "optional",
  "suppressOutput": false
}
FeldWirkung
continueBei false wird diese Hook-Ausführung als gestoppt markiert
stopReasonWird als Grund für den Stopp erfasst
systemMessageWird in der Benutzeroberfläche oder im Ereignisstream als Warnung ausgegeben
suppressOutputWird derzeit geparst, ist aber noch nicht implementiert

Ein Exit-Code von 0 ohne Ausgabe gilt als Erfolg und Codex fährt fort.

PreToolUse und PermissionRequest unterstützen systemMessage, aber continue, stopReason und suppressOutput werden für diese Ereignisse derzeit nicht unterstützt. Gibt ein Hook für PreToolUse eines dieser nicht unterstützten Felder zurück, markiert Codex diese Hook-Ausführung als fehlgeschlagen, meldet den Fehler und setzt den Tool-Aufruf fort.

PostToolUse unterstützt systemMessage, continue: false und stopReason. suppressOutput wird geparst, für dieses Ereignis derzeit aber nicht unterstützt.

Umfangreiche Hook-Ausgaben

Standardmäßig begrenzt Codex jede für das Modell sichtbare Nachricht mit Hook-Ausgabe auf etwa 2.500 Token. Gibt ein Hook mehr zurück, speichert Codex den vollständigen Text unter <temp_dir>/hook_outputs/<session_id>/<uuid>.txt und stellt dem Modell eine Vorschau mit Anfang und Ende des Textes sowie dem Pfad zur gespeicherten Datei bereit. Dies wird als Auslagern bezeichnet: Codex speichert zu umfangreiche Ausgaben auf dem Datenträger und ersetzt sie durch eine kürzere, für das Modell sichtbare Vorschau. Kann die Datei nicht geschrieben werden, erhält das Modell trotzdem eine gekürzte Vorschau.

Halte den Kontext von Hooks und Plug-ins knapp. Der Kontext mehrerer Hooks und Plug-ins summiert sich und kann die Modellleistung beeinträchtigen. Ein höherer Wert für additionalContextLimit erhöht dieses Risiko. Setze den Grenzwert nur dann auf 0, wenn der Hook eine strikte Obergrenze für die Ausgabe erzwingt. Andernfalls kann ein einzelner Hook das gesamte Kontextfenster belegen.

Wenn ein Befehls-Hook additionalContext zurückgibt, lege additionalContextLimit im Handler fest, um den ungefähren Token-Schwellenwert anzupassen:

{
  "type": "command",
  "command": "python3 ~/.codex/hooks/session_start.py",
  "additionalContextLimit": 5000
}

Lass additionalContextLimit weg, um den Standardschwellenwert von 2500 Token zu verwenden. Verwende eine positive Ganzzahl für einen anderen Schwellenwert oder 0, um den gesamten zusätzlichen Kontext des Handlers direkt an das Modell zu übergeben. Codex wertet jeden passenden Handler unabhängig aus. Bei Ereignissen, die keinen zusätzlichen Kontext erzeugen können, ignoriert Codex additionalContextLimit und gibt eine Konfigurationswarnung aus.

Die Einstellung gilt nur für additionalContext. Für Tool-Feedback und Prompts zur Fortsetzung gilt weiterhin der Standardgrenzwert.

Da zu umfangreiche Ausgaben auf dem Datenträger gespeichert werden können, solltest du in Hook-Ausgaben keine vertraulichen Informationen oder anderen sensiblen Daten zurückgeben.

Hooks im Hintergrund ausführen

Standardmäßig wartet Codex, bis ein Befehls-Hook abgeschlossen ist, bevor es den Vorgang fortsetzt, der ihn ausgelöst hat. Setze async auf true, um einen Befehls-Hook im Hintergrund auszuführen, während Codex fortfährt.

Einen Hintergrund-Hook konfigurieren

Füge "async": true einem Befehlshandler in hooks.json hinzu:

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "python3 ~/.codex/hooks/post_tool_use.py",
            "async": true,
            "timeout": 120
          }
        ]
      }
    ]
  }
}

Trage für einen Inline-Hook in config.toml die Einstellung async = true ein:

[[hooks.PostToolUse]]
matcher = "Bash"

[[hooks.PostToolUse.hooks]]
type = "command"
command = "python3 ~/.codex/hooks/post_tool_use.py"
async = true
timeout = 120

Bei Hintergrund-Hooks funktionieren Eingabe, Matcher, Vertrauensprüfung, Zeitlimit und Verarbeitung großer Ausgaben genauso wie bei synchronen Befehls-Hooks. Wie bei anderen Befehls-Hooks wird timeout in Sekunden angegeben und hat standardmäßig den Wert 600.

So werden Hintergrund-Hooks ausgeführt

Sobald ein Hintergrund-Hook abgeschlossen ist, stellt Codex die unterstützten Informationsausgaben zum nächsten sicheren Zeitpunkt in der Unterhaltung bereit:

  • Wenn ein Durchlauf aktiv ist, wartet Codex, bis die aktuelle Modellanfrage und die Toolaufrufe abgeschlossen sind. Anschließend stellt Codex die Ausgabe für die nächste Modellanfrage in diesem Durchlauf bereit.
  • Wenn kein Durchlauf aktiv ist, wartet Codex bis zum nächsten Durchlauf mit Nutzereingabe. Der Abschluss eines Hintergrund-Hooks startet keinen neuen Durchlauf.

Verwende dieselbe ereignisspezifische JSON-Ausgabe wie bei einem synchronen Hook. Codex fügt additionalContext zum Kontext des Modells hinzu und zeigt systemMessage als Warnung an.

Hintergrund-Hooks können den Vorgang, der sie ausgelöst hat, weder blockieren noch genehmigen, ändern oder anderweitig steuern. Verwende synchrone Hooks für Tool-Richtlinien, Entscheidungen über Berechtigungen, das Ablehnen von Prompts oder das Fortsetzen eines Durchlaufs.

Einschränkungen

  • Codex führt pro Sitzung bis zu acht Hintergrund-Hooks gleichzeitig aus. Weitere Hooks warten, bis ein laufender Hook abgeschlossen ist.
  • Jeder passende Aufruf wird unabhängig ausgeführt. Hintergrund-Hooks können in einer anderen Reihenfolge enden, als sie gestartet wurden.
  • Wenn die Sitzung endet, bricht Codex noch nicht abgeschlossene Hintergrund-Hooks ab und verwirft Ausgaben, die noch nicht bereitgestellt wurden.
  • Hooks vom Typ SessionEnd werden immer synchron ausgeführt.

Hooks

SessionStart

matcher wird bei diesem Ereignis auf source angewendet.

Felder zusätzlich zu den allgemeinen Eingabefeldern:

FeldTypBedeutung
sourcestringWie die Sitzung gestartet wurde: startup, resume, clear oder compact

Klartext auf stdout wird als zusätzlicher Entwicklerkontext hinzugefügt.

JSON auf stdout unterstützt die allgemeinen Ausgabefelder und die folgende Hook-spezifische Struktur:

{
  "hookSpecificOutput": {
    "hookEventName": "SessionStart",
    "additionalContext": "Load the workspace conventions before editing."
  }
}

Der Text aus additionalContext wird als zusätzlicher Entwicklerkontext hinzugefügt.

Nachdem Codex den Kontext einer Root-Sitzung verdichtet hat, werden Hooks vom Typ SessionStart, die der Bedingung source: "compact" entsprechen, vor der nächsten Modellanfrage ausgeführt. Das gilt auch, wenn die automatische Compaction (Kontextverdichtung) mitten in einem Durchlauf stattfindet: Codex stellt den zusätzlichen Kontext des Hooks für die unmittelbare Fortsetzung bereit, statt auf einen späteren Durchlauf mit Nutzereingabe zu warten. Gibt der Hook continue: false zurück, beendet Codex den Durchlauf, ohne eine weitere Modellanfrage zu senden.

SessionEnd

Mit SessionEnd kannst du beim Ende einer Sitzung einen Befehl ausführen, etwa um abschließende Notizen zu speichern oder Dateien zu bereinigen. Der Hook wird für den Haupt-Thread ausgeführt, wenn du eine noch offene Unterhaltung archivierst oder löschst, wenn Codex regulär beendet wird oder wenn eine Unterhaltung seit 30 Minuten inaktiv und in keinem verbundenen Client geöffnet ist. Für Subagenten wird er nicht ausgeführt.

Wenn du eine Unterhaltung verlässt oder thread/unsubscribe aufrufst, wird die Sitzung nicht sofort beendet. Deshalb wird SessionEnd nicht unmittelbar ausgeführt. Dein Hook kann während seiner Ausführung weiterhin das Sitzungstranskript lesen.

Bei diesem Ereignis filtert matcher nach reason. Derzeit hat reason immer den Wert other. Du kannst matcher weglassen oder other verwenden, damit der Hook bei jedem Ereignis vom Typ SessionEnd ausgeführt wird.

Felder zusätzlich zu den allgemeinen Eingabefeldern:

FeldTypBedeutung
reasonstringGrund für das Ende der Sitzung: other

Ein Befehl für SessionEnd erhält beispielsweise:

{
  "session_id": "thr_123",
  "transcript_path": "/workspace/.codex/rollout.jsonl",
  "cwd": "/workspace",
  "hook_event_name": "SessionEnd",
  "reason": "other"
}

Hooks vom Typ SessionEnd werden immer synchron ausgeführt, auch wenn async den Wert true hat. Ihre Ausgaben dienen nur als Hinweis: Sie steuern Codex nicht und halten den Thread nicht offen. Wenn bei einem Befehl eine Zeitüberschreitung auftritt oder er mit einem Fehler beendet wird, meldet Codex dies als Hook-Fehler.

SubagentStart

matcher wird bei diesem Ereignis auf agent_type angewendet.

Felder zusätzlich zu den allgemeinen Eingabefeldern:

FeldTypBedeutung
turn_idstringCodex-spezifische Erweiterung. ID des aktiven Codex-Durchlaufs
agent_idstringKennung des Subagenten
agent_typestringTyp oder Profil des Subagenten
permission_modestringAktueller Berechtigungsmodus

Klartext auf stdout wird dem Subagenten als zusätzlicher Entwicklerkontext hinzugefügt.

JSON auf stdout unterstützt systemMessage und die folgende Hook-spezifische Struktur:

{
  "hookSpecificOutput": {
    "hookEventName": "SubagentStart",
    "additionalContext": "Review the repository test conventions first."
  }
}

Der Text aus additionalContext wird dem Subagenten als zusätzlicher Entwicklerkontext hinzugefügt. continue: false wird aus Kompatibilitätsgründen geparst, verhindert jedoch nicht, dass der Subagent gestartet wird.

PreToolUse

PreToolUse kann Bash, über apply_patch vorgenommene Dateibearbeitungen, MCP-Toolaufrufe und andere lokale Funktionstools abfangen. Unter Unterstützung für Tools findest du die unterstützten Ausführungspfade und Ausnahmen.

matcher wird auf tool_name und Matcher-Aliasse angewendet. Bei Dateibearbeitungen über apply_patch kannst du für matcher die Werte apply_patch, Edit oder Write verwenden. Die Hook-Eingabe gibt weiterhin tool_name: "apply_patch" an.

Felder zusätzlich zu den allgemeinen Eingabefeldern:

FeldTypBedeutung
turn_idstringCodex-spezifische Erweiterung. ID des aktiven Codex-Durchlaufs
tool_namestringKanonischer Toolname für den Hook, etwa Bash, apply_patch oder ein MCP-Name wie mcp__fs__read
tool_use_idstringID des Toolaufrufs für diese Ausführung
tool_inputJSON valueTool-spezifische Eingabe. Bash und apply_patch verwenden tool_input.command. MCP-Tools und andere lokale Funktionstools senden ihre Argumente.

Klartext auf stdout wird ignoriert.

JSON auf stdout kann systemMessage verwenden. Um einen unterstützten Toolaufruf abzulehnen, gib die folgende Hook-spezifische Struktur zurück:

{
  "hookSpecificOutput": {
    "hookEventName": "PreToolUse",
    "permissionDecision": "deny",
    "permissionDecisionReason": "Destructive command blocked by hook."
  }
}

Codex akzeptiert zum Blockieren auch diese ältere Struktur:

{
  "decision": "block",
  "reason": "Destructive command blocked by hook."
}

Du kannst auch den Exitcode 2 verwenden und den Grund für die Blockierung auf stderr ausgeben.

Um für das Modell sichtbaren Kontext hinzuzufügen, ohne zu blockieren, gib hookSpecificOutput.additionalContext zurück:

{
  "hookSpecificOutput": {
    "hookEventName": "PreToolUse",
    "additionalContext": "The pending command touches generated files."
  }
}

Um einen unterstützten Tool-Aufruf umzuschreiben, ohne ihn zu blockieren, gib permissionDecision: "allow" mit updatedInput zurück:

{
  "hookSpecificOutput": {
    "hookEventName": "PreToolUse",
    "permissionDecision": "allow",
    "updatedInput": {
      "command": "echo rewritten"
    }
  }
}

Für Bash-Befehle und apply_patch muss updatedInput ein Feld command vom Typ String enthalten. Bei MCP-Tools und anderen lokalen Funktionstools ist updatedInput das neue Argumentobjekt. Gib updatedInput nur zusammen mit permissionDecision: "allow" zurück. Andere Strukturen für updatedInput werden als Fehler gemeldet.

permissionDecision: "ask", die ältere Form decision: "approve", continue: false, stopReason und suppressOutput werden geparst, aber noch nicht unterstützt. Codex markiert die Hook-Ausführung als fehlgeschlagen, meldet den Fehler und setzt den Tool-Aufruf fort.

PermissionRequest

PermissionRequest wird ausgeführt, wenn Codex eine Genehmigung anfordern will, etwa für erweiterte Shell-Berechtigungen oder verwalteten Netzwerkzugriff. Der Hook kann die Anfrage genehmigen, ablehnen oder keine Entscheidung treffen und die normale Genehmigungsabfrage fortsetzen lassen. Bei Befehlen, die keine Genehmigung benötigen, wird er nicht ausgeführt.

matcher wird auf tool_name und Matcher-Aliase angewendet. Zu den aktuellen kanonischen Werten gehören Bash, apply_patch und MCP-Toolnamen wie mcp__server__tool. apply_patch lässt sich auch über Edit und Write abgleichen.

Zusätzliche Felder zu den allgemeinen Eingabefeldern:

FeldTypBedeutung
turn_idstringCodex-spezifische Erweiterung. ID des aktiven Codex-Interaktionsschritts
tool_namestringKanonischer Toolname für Hooks, zum Beispiel Bash, apply_patch oder ein MCP-Name wie mcp__fs__read
tool_inputJSON valueToolspezifische Eingabe. Bash und apply_patch verwenden tool_input.command, während MCP-Tools alle Argumente senden.
tool_input.descriptionstring | nullVerständliche Begründung für die Genehmigungsanfrage, sofern Codex eine bereitstellt

Nur-Text-Ausgabe auf stdout wird ignoriert.

Manche Tool-Eingaben können eine verständliche Beschreibung enthalten. Verlass dich aber nicht darauf, dass jedes Tool ein Feld tool_input.description bereitstellt.

Um die Anfrage zu genehmigen, gib Folgendes zurück:

{
  "hookSpecificOutput": {
    "hookEventName": "PermissionRequest",
    "decision": {
      "behavior": "allow"
    }
  }
}

Um die Anfrage abzulehnen, gib Folgendes zurück:

{
  "hookSpecificOutput": {
    "hookEventName": "PermissionRequest",
    "decision": {
      "behavior": "deny",
      "message": "Blocked by repository policy."
    }
  }
}

Wenn mehrere passende Hooks Entscheidungen zurückgeben, hat deny immer Vorrang. Andernfalls wird die Anfrage bei allow ohne Genehmigungsabfrage fortgesetzt. Wenn kein passender Hook entscheidet, verwendet Codex den normalen Genehmigungsablauf.

Gib weder updatedInput noch updatedPermissions noch interrupt für PermissionRequest zurück. Diese Felder sind für künftiges Verhalten reserviert und führen derzeit zur Ablehnung der Anfrage.

PostToolUse

PostToolUse wird ausgeführt, nachdem unterstützte Tools eine Ausgabe erzeugt haben. Dazu gehören Bash, apply_patch, MCP-Tool-Aufrufe und andere lokale Funktionstools. Bei Bash wird der Hook auch nach Befehlen ausgeführt, die mit einem Status ungleich null enden. Er kann die Seiteneffekte eines bereits ausgeführten Tools nicht rückgängig machen. Unter Unterstützte Tools findest du die unterstützten Ausführungspfade und Ausnahmen.

matcher wird auf tool_name und Matcher-Aliase angewendet. Für Dateiänderungen über apply_patch kannst du für matcher die Werte apply_patch, Edit oder Write verwenden. Die Hook-Eingabe enthält weiterhin tool_name: "apply_patch".

Zusätzliche Felder zu den allgemeinen Eingabefeldern:

FeldTypBedeutung
turn_idstringCodex-spezifische Erweiterung. ID des aktiven Codex-Interaktionsschritts
tool_namestringKanonischer Toolname für Hooks, zum Beispiel Bash, apply_patch oder ein MCP-Name wie mcp__fs__read
tool_use_idstringID des Tool-Aufrufs für diese Ausführung
tool_inputJSON valueToolspezifische Eingabe. Bash und apply_patch verwenden tool_input.command. MCP-Tools und andere lokale Funktionstools senden ihre Argumente.
tool_responseJSON valueToolspezifische Ausgabe. MCP-Tools senden das Ergebnis des MCP-Aufrufs. Andere lokale Funktionstools senden in der Regel ihre für das Modell bestimmte Ausgabe.

Nur-Text-Ausgabe auf stdout wird ignoriert.

In JSON auf stdout kannst du systemMessage und die folgende Hook-spezifische Struktur verwenden:

{
  "decision": "block",
  "reason": "The Bash output needs review before continuing.",
  "hookSpecificOutput": {
    "hookEventName": "PostToolUse",
    "additionalContext": "The command updated generated files."
  }
}

Der Text aus additionalContext wird als zusätzlicher Entwicklerkontext hinzugefügt.

Bei diesem Ereignis macht decision: "block" den bereits ausgeführten Bash-Befehl nicht rückgängig. Stattdessen zeichnet Codex das Feedback auf, ersetzt das Tool-Ergebnis durch dieses Feedback und setzt die Modellausführung mit der vom Hook bereitgestellten Nachricht fort.

Du kannst auch den Exit-Code 2 verwenden und die Begründung für das Feedback auf stderr ausgeben.

Um die normale Verarbeitung des ursprünglichen Tool-Ergebnisses zu stoppen, nachdem der Befehl bereits ausgeführt wurde, gib continue: false zurück. Codex ersetzt das Tool-Ergebnis durch dein Feedback oder deine Stoppmeldung und setzt die Verarbeitung damit fort.

updatedMCPToolOutput und suppressOutput werden geparst, aber noch nicht unterstützt. Codex markiert die Hook-Ausführung als fehlgeschlagen, meldet den Fehler und setzt die normale Verarbeitung des Tool-Ergebnisses fort.

Tool-Aufrufe im Codemodus

Wenn ein Modell im Codemodus über JavaScript ein Tool aufruft, gelten die Hook-Entscheidungen für diesen verschachtelten Aufruf. PreToolUse kann die Ausführung des Tools verhindern oder dessen Eingabe umschreiben. Ein blockierender PostToolUse-Hook kann die Seiteneffekte des Tools nicht rückgängig machen, aber verhindern, dass das ursprüngliche Ergebnis das laufende Skript erreicht.

Hook-ErgebnisWas im Codemodus sichtbar ist
PreToolUse blockiertDas Promise des Tools wird abgelehnt, bevor das Tool ausgeführt wird.
PreToolUse gibt updatedInput zurückDas Tool wird mit der umgeschriebenen Eingabe ausgeführt, und das Promise wird mit diesem Ergebnis aufgelöst.
PostToolUse gibt decision: "block" zurück oder endet mit Exit-Code 2Das Tool wird ausgeführt. Anschließend wird das Promise mit der Begründung des Hooks abgelehnt.
PostToolUse gibt continue: false zurückCodex verwendet das Hook-Feedback als für das Modell sichtbares Ergebnis, lehnt das Promise des verschachtelten Tool-Aufrufs aber nicht ab.

PreCompact

PreCompact wird ausgeführt, bevor Codex den Chat verdichtet. matcher wird auf trigger mit den möglichen Werten manual und auto angewendet.

Zusätzliche Felder zu den allgemeinen Eingabefeldern:

FeldTypBedeutung
turn_idstringCodex-spezifische Erweiterung. ID des aktiven Codex-Interaktionsschritts
triggerstringAuslöser der Compaction (Kontextverdichtung): manual oder auto

Nur-Text-Ausgabe auf stdout wird ignoriert.

JSON auf stdout unterstützt die allgemeinen Ausgabefelder. Wenn ein passender PreCompact-Hook continue: false zurückgibt, stoppt Codex vor der Compaction (Kontextverdichtung).

PostCompact

PostCompact wird ausgeführt, nachdem Codex den Chat verdichtet hat. matcher wird auf trigger mit den möglichen Werten manual und auto angewendet.

Zusätzliche Felder zu den allgemeinen Eingabefeldern:

FeldTypBedeutung
turn_idstringCodex-spezifische Erweiterung. ID des aktiven Codex-Turns
triggerstringAuslöser der Compaction (Kontextverdichtung): manual oder auto

Reiner Text auf stdout wird ignoriert.

JSON auf stdout unterstützt die gemeinsamen Ausgabefelder. Gibt ein zutreffender PostCompact-Hook continue: false zurück, stoppt Codex nach der Compaction (Kontextverdichtung).

UserPromptSubmit

matcher wird derzeit für dieses Ereignis nicht verwendet.

Neben den gemeinsamen Eingabefeldern gibt es folgende Felder:

FeldTypBedeutung
turn_idstringCodex-spezifische Erweiterung. ID des aktiven Codex-Turns
promptstringBenutzer-Prompt, der gleich gesendet wird

Reiner Text auf stdout wird als zusätzlicher Entwicklerkontext hinzugefügt.

JSON auf stdout unterstützt die gemeinsamen Ausgabefelder und folgende Struktur für diesen Hook:

{
  "hookSpecificOutput": {
    "hookEventName": "UserPromptSubmit",
    "additionalContext": "Ask for a clearer reproduction before editing files."
  }
}

Der Text in additionalContext wird als zusätzlicher Entwicklerkontext hinzugefügt.

Gib Folgendes zurück, um den Prompt zu blockieren:

{
  "decision": "block",
  "reason": "Ask for confirmation before doing that."
}

Du kannst auch den Exit-Code 2 verwenden und den Grund für die Blockierung auf stderr ausgeben.

SubagentStop

Bei diesem Ereignis wird matcher auf agent_type angewendet.

Neben den gemeinsamen Eingabefeldern gibt es folgende Felder:

FeldTypBedeutung
turn_idstringCodex-spezifische Erweiterung. ID des aktiven Codex-Turns
agent_idstringKennung des Subagenten
agent_typestringTyp oder Profil des Subagenten
agent_transcript_pathstring | nullPfad zur Transkriptdatei des Subagenten, falls vorhanden
stop_hook_activebooleanOb die Ausführung dieses Subagenten bereits fortgesetzt wurde
last_assistant_messagestring | nullLetzte Assistentennachricht des Subagenten, sofern verfügbar

SubagentStop erwartet JSON auf stdout, wenn der Hook mit Exit-Code 0 endet. Eine reine Textausgabe ist für dieses Ereignis ungültig.

JSON auf stdout unterstützt die gemeinsamen Ausgabefelder. Gib Folgendes zurück, um Codex aufzufordern, den Ablauf des Subagenten fortzusetzen:

{
  "decision": "block",
  "reason": "Run one more focused pass inside the subagent."
}

Du kannst auch den Exit-Code 2 verwenden und den Grund für die Fortsetzung auf stderr ausgeben.

Wenn ein zutreffender SubagentStop-Hook continue: false zurückgibt, hat das Vorrang vor Entscheidungen anderer zutreffender SubagentStop-Hooks zur Fortsetzung.

Stop

matcher wird derzeit für dieses Ereignis nicht verwendet.

Neben den gemeinsamen Eingabefeldern gibt es folgende Felder:

FeldTypBedeutung
turn_idstringCodex-spezifische Erweiterung. ID des aktiven Codex-Turns
stop_hook_activebooleanOb dieser Turn bereits durch Stop fortgesetzt wurde
last_assistant_messagestring | nullText der letzten Assistentennachricht, sofern verfügbar

Stop erwartet JSON auf stdout, wenn der Hook mit Exit-Code 0 endet. Eine reine Textausgabe ist für dieses Ereignis ungültig.

JSON auf stdout unterstützt die gemeinsamen Ausgabefelder. Gib Folgendes zurück, damit Codex fortfährt:

{
  "decision": "block",
  "reason": "Run one more pass over the failing tests."
}

Du kannst auch den Exit-Code 2 verwenden und den Grund für die Fortsetzung auf stderr ausgeben.

Bei diesem Ereignis lehnt decision: "block" den Turn nicht ab. Stattdessen weist es Codex an, fortzufahren, und erstellt automatisch einen neuen Prompt zur Fortsetzung. Dieser dient als neuer Benutzer-Prompt, wobei der von dir in reason angegebene Grund als Prompttext verwendet wird.

Wenn ein zutreffender Stop-Hook continue: false zurückgibt, hat das Vorrang vor Entscheidungen anderer zutreffender Stop-Hooks zur Fortsetzung.

Schemas

Die verlinkten Schemas des Branches main können Hook-Felder enthalten, die nicht Teil der aktuellen Version sind. Nutze diese Seite als Referenz für das Verhalten der aktuellen Version.

Das genaue aktuelle Übertragungsformat findest du in den generierten Schemas im Codex-Repository auf GitHub.