Zum Inhalt springen

OpenClaw Hooks konfigurieren: Automatisierung in Minuten

Hooks sind kleine Skripte, die ausgeführt werden, wenn ein bestimmtes Ereignis eintritt. Es gibt zwei Arten:

  • Hooks (diese Seite): Diese laufen innerhalb des Gateways ab, wenn Agent-Events ausgelöst werden, wie zum Beispiel /new, /reset, /stop oder Lifecycle-Events.
  • Webhooks: Externe HTTP-Webhooks, mit denen andere Systeme Aktionen in OpenClaw auslösen können. Schau dir dazu Webhook Hooks an oder nutze openclaw webhooks für Gmail-Hilfsbefehle.

Hooks können auch innerhalb von Plugins gebündelt sein; Details dazu findest du unter Plugin hooks. Der Befehl openclaw hooks list zeigt dir sowohl eigenständige Hooks als auch von Plugins verwaltete Hooks an.

Typische Anwendungsfälle:

  • Speichere einen Memory-Snapshot, wenn du eine Session zurücksetzt.
  • Erstelle einen Audit-Trail von Befehlen zur Fehlerbehebung oder für Compliance-Zwecke.
  • Trigger Follow-up-Automatisierungen, wenn eine Session startet oder endet.
  • Schreibe Dateien in den Agent-Workspace oder rufe externe APIs auf, wenn Events gefeuert werden.

Wenn du eine kleine TypeScript-Funktion schreiben kannst, kannst du auch einen Hook schreiben. Managed und Bundled Hooks gelten als vertrauenswürdiger lokaler Code. Workspace-Hooks werden automatisch erkannt, aber OpenClaw lässt sie deaktiviert, bis du sie explizit über das CLI oder die Konfiguration aktivierst.

Das Hooks-System erlaubt dir:

  • Session-Kontext im Memory zu speichern, wenn /new aufgerufen wird.
  • Alle Befehle für Auditing-Zwecke zu protokollieren.
  • Eigene Automatisierungen bei Agent-Lifecycle-Events auszulösen.
  • Das Verhalten von OpenClaw zu erweitern, ohne den Core-Code zu ändern.

OpenClaw wird mit vier Bundled Hooks ausgeliefert, die automatisch erkannt werden:

  • 💾 session-memory: Speichert den Session-Kontext in deinem Agent-Workspace (Standard: ~/.openclaw/workspace/memory/), wenn du /new oder /reset nutzt.
  • 📎 bootstrap-extra-files: Fügt während agent:bootstrap zusätzliche Workspace-Bootstrap-Dateien aus konfigurierten Glob/Path-Mustern ein.
  • 📝 command-logger: Protokolliert alle Befehls-Events in ~/.openclaw/logs/commands.log.
  • 🚀 boot-md: Führt BOOT.md aus, wenn das Gateway startet (erfordert aktivierte interne Hooks).

Verfügbare Hooks auflisten:

Terminal-Fenster
openclaw hooks list

Einen Hook aktivieren:

Terminal-Fenster
openclaw hooks enable session-memory

Hook-Status prüfen:

Terminal-Fenster
openclaw hooks check

Detaillierte Informationen abrufen:

Terminal-Fenster
openclaw hooks info session-memory

Während des Onboardings (openclaw onboard) wirst du gefragt, ob du empfohlene Hooks aktivieren möchtest. Der Wizard erkennt automatisch passende Hooks und bietet sie dir zur Auswahl an.

Hooks laufen innerhalb des Gateway-Prozesses. Betrachte Bundled Hooks, Managed Hooks und hooks.internal.load.extraDirs als vertrauenswürdigen lokalen Code. Workspace-Hooks unter <workspace>/hooks/ sind Repo-lokaler Code. Daher verlangt OpenClaw einen expliziten Aktivierungsschritt, bevor sie geladen werden.

Hooks werden automatisch in den folgenden Verzeichnissen gesucht (in der Reihenfolge steigender Priorität bei Überschreibungen):

  1. Bundled hooks: Werden mit OpenClaw ausgeliefert; sie befinden sich bei npm-Installationen unter <openclaw>/dist/hooks/bundled/ (oder in einem Geschwisterverzeichnis hooks/bundled/ bei kompilierten Binärdateien).
  2. Plugin hooks: Hooks, die in installierten Plugins enthalten sind (siehe Plugin hooks).
  3. Managed hooks: ~/.openclaw/hooks/ (vom Benutzer installiert, über Workspaces hinweg geteilt; können Bundled und Plugin Hooks überschreiben). Zusätzliche Hook-Verzeichnisse, die über hooks.internal.load.extraDirs konfiguriert wurden, werden ebenfalls als Managed Hooks behandelt und haben dieselbe Priorität.
  4. Workspace hooks: <workspace>/hooks/ (pro Agent, standardmäßig deaktiviert; können keine Hooks aus anderen Quellen überschreiben).

Workspace-Hooks können neue Hook-Namen für ein Repo hinzufügen, aber sie können keine Bundled, Managed oder Plugin-Hooks mit demselben Namen überschreiben.

Managed Hook-Verzeichnisse können entweder ein einzelner Hook oder ein Hook-Pack (Package-Verzeichnis) sein.

Jeder Hook ist ein Verzeichnis, das Folgendes enthält:

my-hook/
├── HOOK.md # Metadata + documentation
└── handler.ts # Handler implementation

Hook Packs sind Standard-npm-Pakete, die einen oder mehrere Hooks über openclaw.hooks in der package.json exportieren. Installiere sie mit:

Terminal-Fenster
openclaw plugins install <path-or-spec>

Npm-Specs sind Registry-only (Paketname + optionale exakte Version oder Dist-Tag). Git/URL/File-Specs und Semver-Bereiche werden abgelehnt.

Bare Specs und @latest bleiben auf dem Stable-Track. Wenn npm eine dieser Versionen zu einem Prerelease auflöst, stoppt OpenClaw und bittet dich, explizit per Prerelease-Tag wie @beta/@rc oder einer exakten Prerelease-Version zuzustimmen.

Beispiel für eine package.json:

{
"name": "@acme/my-hooks",
"version": "0.1.0",
"openclaw": {
"hooks": ["./hooks/my-hook", "./hooks/other-hook"]
}
}

Jeder Eintrag zeigt auf ein Hook-Verzeichnis, das eine HOOK.md und eine handler.ts (oder index.ts) enthält. Hook Packs können Dependencies mitliefern; diese werden unter ~/.openclaw/hooks/<id> installiert. Jeder openclaw.hooks-Eintrag muss nach der Symlink-Auflösung innerhalb des Paketverzeichnisses bleiben; Einträge, die nach außen führen, werden abgelehnt.

Sicherheitshinweis: openclaw plugins install installiert Hook-Pack-Dependencies mit npm install --ignore-scripts (keine Lifecycle-Scripts). Halte die Dependency-Trees von Hook Packs “pure JS/TS” und vermeide Pakete, die auf postinstall-Builds angewiesen sind.

Die HOOK.md-Datei enthält Metadaten im YAML-Frontmatter sowie eine Markdown-Dokumentation:

---
name: my-hook
description: "Short description of what this hook does"
homepage: https://docs.openclaw.ai/automation/hooks#my-hook
metadata:
{ "openclaw": { "emoji": "🔗", "events": ["command:new"], "requires": { "bins": ["node"] } } }
---
# My Hook
Detailed documentation goes here...
## What It Does
- Listens for `/new` commands
- Performs some action
- Logs the result
## Requirements
- Node.js must be installed
## Configuration
No configuration needed.

Das metadata.openclaw-Objekt unterstützt:

  • emoji: Anzeige-Emoji für das CLI (z. B. "💾")
  • events: Array von Events, auf die gehört werden soll (z. B. ["command:new", "command:reset"])
  • export: Benannter Export, der verwendet werden soll (Standard ist "default")
  • homepage: URL zur Dokumentation
  • os: Erforderliche Plattformen (z. B. ["darwin", "linux"])
  • requires: Optionale Anforderungen
    • bins: Erforderliche Binaries im PATH (z. B. ["git", "node"])
    • anyBins: Mindestens eine dieser Binaries muss vorhanden sein
    • env: Erforderliche Umgebungsvariablen
    • config: Erforderliche Konfigurationspfade (z. B. ["workspace.dir"])
  • always: Eligibility-Checks umgehen (Boolean)
  • install: Installationsmethoden (für gebündelte Hooks: [{"id":"bundled","kind":"bundled"}])

Die Datei handler.ts exportiert eine HookHandler-Funktion:

const myHandler = async (event) => {
// Only trigger on 'new' command
if (event.type !== "command" || event.action !== "new") {
return;
}
console.log(`[my-hook] New command triggered`);
console.log(` Session: ${event.sessionKey}`);
console.log(` Timestamp: ${event.timestamp.toISOString()}`);
// Your custom logic here
// Optionally send message to user
event.messages.push("✨ My hook executed!");
};
export default myHandler;

Jedes Event enthält:

{
type: 'command' | 'session' | 'agent' | 'gateway' | 'message',
action: string, // e.g., 'new', 'reset', 'stop', 'received', 'sent'
sessionKey: string, // Session identifier
timestamp: Date, // When the event occurred
messages: string[], // Push messages here to send to user
context: {
// Command events (command:new, command:reset):
sessionEntry?: SessionEntry, // current session entry
previousSessionEntry?: SessionEntry, // pre-reset entry (preferred for session-memory)
commandSource?: string, // e.g., 'whatsapp', 'telegram'
senderId?: string,
workspaceDir?: string,
cfg?: OpenClawConfig,
// Command events (command:stop only):
sessionId?: string,
// Agent bootstrap events (agent:bootstrap):
bootstrapFiles?: WorkspaceBootstrapFile[],
// Message events (see Message Events section for full details):
from?: string, // message:received
to?: string, // message:sent
content?: string,
channelId?: string,
success?: boolean, // message:sent
}
}

Werden ausgelöst, wenn Agent-Commands erteilt werden:

  • command: Alle Command-Events (allgemeiner Listener)
  • command:new: Wenn der /new-Befehl erteilt wird
  • command:reset: Wenn der /reset-Befehl erteilt wird
  • command:stop: Wenn der /stop-Befehl erteilt wird
  • session:compact:before: Direkt bevor die Compaction den Verlauf zusammenfasst
  • session:compact:after: Nachdem die Compaction mit Zusammenfassungs-Metadaten abgeschlossen wurde

Interne Hook-Payloads senden diese als type: "session" mit action: "compact:before" / action: "compact:after"; Listener abonnieren sie mit den oben genannten kombinierten Keys. Die spezifische Handler-Registrierung nutzt das Format ${type}:${action}. Registriere für diese Events session:compact:before und session:compact:after.

  • agent:bootstrap: Bevor Workspace-Bootstrap-Dateien injiziert werden (Hooks können context.bootstrapFiles verändern)

Werden beim Start des Gateways ausgelöst:

  • gateway:startup: Nachdem Channels gestartet und Hooks geladen wurden

Werden ausgelöst, wenn Session-Eigenschaften geändert werden:

  • session:patch: Wenn eine Session aktualisiert wird

Session-Events enthalten einen detaillierten Kontext über die Session und die Änderungen:

{
sessionEntry: SessionEntry, // The complete updated session entry
patch: { // The patch object (only changed fields)
// Session identity & labeling
label?: string | null, // Human-readable session label
// AI model configuration
model?: string | null, // Model override (e.g., "claude-opus-4-5")
thinkingLevel?: string | null, // Thinking level ("off"|"low"|"med"|"high")
verboseLevel?: string | null, // Verbose output level
reasoningLevel?: string | null, // Reasoning mode override
elevatedLevel?: string | null, // Elevated mode override
responseUsage?: "off" | "tokens" | "full" | null, // Usage display mode
// Tool execution settings
execHost?: string | null, // Exec host (sandbox|gateway|node)
execSecurity?: string | null, // Security mode (deny|allowlist|full)
execAsk?: string | null, // Approval mode (off|on-miss|always)
execNode?: string | null, // Node ID for host=node
// Subagent coordination
spawnedBy?: string | null, // Parent session key (for subagents)
spawnDepth?: number | null, // Nesting depth (0 = root)
// Communication policies
sendPolicy?: "allow" | "deny" | null, // Message send policy
groupActivation?: "mention" | "always" | null, // Group chat activation
},
cfg: OpenClawConfig // Current gateway config
}

Sicherheitshinweis: Nur privilegierte Clients (einschließlich der Control UI) können session:patch-Events auslösen. Standard-WebChat-Clients sind für das Patchen von Sessions gesperrt (siehe PR #20800), daher wird der Hook bei diesen Verbindungen nicht ausgelöst.

Die vollständige Typdefinition findest du unter SessionsPatchParamsSchema in src/gateway/protocol/schema/sessions.ts.

const handler = async (event) => {
if (event.type !== "session" || event.action !== "patch") {
return;
}
const { patch } = event.context;
console.log(`[session-patch] Session updated: ${event.sessionKey}`);
console.log(`[session-patch] Changes:`, patch);
};
export default handler;

Werden ausgelöst, wenn Nachrichten empfangen oder gesendet werden:

  • message: Alle Message-Events (allgemeiner Listener)
  • message:received: Wenn eine eingehende Nachricht von einem beliebigen Channel empfangen wird. Wird früh in der Verarbeitung ausgelöst, noch vor dem Media-Understanding. Der Inhalt kann rohe Platzhalter wie <media:audio> für noch nicht verarbeitete Anhänge enthalten.
  • message:transcribed: Wenn eine Nachricht vollständig verarbeitet wurde, inklusive Audio-Transkription und Link-Understanding. Zu diesem Zeitpunkt enthält transcript den vollständigen Text für Audio-Nachrichten. Nutze diesen Hook, wenn du Zugriff auf transkribierte Audio-Inhalte benötigst.
  • message:preprocessed: Wird für jede Nachricht ausgelöst, nachdem das Media- und Link-Understanding abgeschlossen ist. Hooks erhalten Zugriff auf den vollständig angereicherten Body (Transkripte, Bildbeschreibungen, Link-Zusammenfassungen), bevor der Agent ihn sieht.
  • message:sent: Wenn eine ausgehende Nachricht erfolgreich gesendet wurde

Message-Events enthalten einen detaillierten Kontext zur Nachricht:

// message:received context
{
from: string, // Sender identifier (phone number, user ID, etc.)
content: string, // Message content
timestamp?: number, // Unix timestamp when received
channelId: string, // Channel (e.g., "whatsapp", "telegram", "discord")
accountId?: string, // Provider account ID for multi-account setups
conversationId?: string, // Chat/conversation ID
messageId?: string, // Message ID from the provider
metadata?: { // Additional provider-specific data
to?: string,
provider?: string,
surface?: string,
threadId?: string | number,
senderId?: string,
senderName?: string,
senderUsername?: string,
senderE164?: string,
guildId?: string, // Discord guild / server ID
channelName?: string, // Channel name (e.g., Discord channel name)
}
}
// message:sent context
{
to: string, // Recipient identifier
content: string, // Message content that was sent
success: boolean, // Whether the send succeeded
error?: string, // Error message if sending failed
channelId: string, // Channel (e.g., "whatsapp", "telegram", "discord")
accountId?: string, // Provider account ID
conversationId?: string, // Chat/conversation ID
messageId?: string, // Message ID returned by the provider
isGroup?: boolean, // Whether this outbound message belongs to a group/channel context
groupId?: string, // Group/channel identifier for correlation with message:received
}
// message:transcribed context
{
from?: string, // Sender identifier
to?: string, // Recipient identifier
body?: string, // Raw inbound body before enrichment
bodyForAgent?: string, // Enriched body visible to the agent
transcript: string, // Audio transcript text
timestamp?: number, // Unix timestamp when received
channelId: string, // Channel (e.g., "telegram", "whatsapp")
conversationId?: string,
messageId?: string,
senderId?: string, // Sender user ID
senderName?: string, // Sender display name
senderUsername?: string,
provider?: string, // Provider name
surface?: string, // Surface name
mediaPath?: string, // Path to the media file that was transcribed
mediaType?: string, // MIME type of the media
}
// message:preprocessed context
{
from?: string, // Sender identifier
to?: string, // Recipient identifier
body?: string, // Raw inbound body
bodyForAgent?: string, // Final enriched body after media/link understanding
transcript?: string, // Transcript when audio was present
timestamp?: number, // Unix timestamp when received
channelId: string, // Channel (e.g., "telegram", "whatsapp")
conversationId?: string,
messageId?: string,
senderId?: string, // Sender user ID
senderName?: string, // Sender display name
senderUsername?: string,
provider?: string, // Provider name
surface?: string, // Surface name
mediaPath?: string, // Path to the media file
mediaType?: string, // MIME type of the media
isGroup?: boolean,
groupId?: string,
}
const isMessageReceivedEvent = (event: { type: string; action: string }) =>
event.type === "message" && event.action === "received";
const isMessageSentEvent = (event: { type: string; action: string }) =>
event.type === "message" && event.action === "sent";
const handler = async (event) => {
if (isMessageReceivedEvent(event as { type: string; action: string })) {
console.log(`[message-logger] Received from ${event.context.from}: ${event.context.content}`);
} else if (isMessageSentEvent(event as { type: string; action: string })) {
console.log(`[message-logger] Sent to ${event.context.to}: ${event.context.content}`);
}
};
export default handler;

Diese Hooks sind keine Event-Stream-Listener; sie ermöglichen es Plugins, Tool-Ergebnisse synchron anzupassen, bevor OpenClaw sie speichert.

  • tool_result_persist: Transformiert Tool-Ergebnisse, bevor sie in das Session-Transkript geschrieben werden. Muss synchron sein; gib das aktualisierte Tool-Result-Payload zurück oder undefined, um es unverändert zu lassen. Siehe Agent Loop.

Compaction-Lifecycle-Hooks, die über den Plugin-Hook-Runner bereitgestellt werden:

  • before_compaction: Läuft vor der Compaction mit Count/Token-Metadaten
  • after_compaction: Läuft nach der Compaction mit Compaction-Summary-Metadaten

Geplante Event-Typen:

  • session:start: Wenn eine neue Session beginnt
  • session:end: Wenn eine Session endet
  • agent:error: Wenn ein Agent auf einen Fehler stößt
  • Workspace-Hooks (<workspace>/hooks/): Pro Agent; können neue Hook-Namen hinzufügen, aber keine gebündelten, verwalteten oder Plugin-Hooks mit demselben Namen überschreiben
  • Managed-Hooks (~/.openclaw/hooks/): Werden über Workspaces hinweg geteilt; können gebündelte und Plugin-Hooks überschreiben
Terminal-Fenster
mkdir -p ~/.openclaw/hooks/my-hook
cd ~/.openclaw/hooks/my-hook
---
name: my-hook
description: "Does something useful"
metadata: { "openclaw": { "emoji": "🎯", "events": ["command:new"] } }
---
# My Custom Hook
This hook does something useful when you issue `/new`.
const handler = async (event) => {
if (event.type !== "command" || event.action !== "new") {
return;
}
console.log("[my-hook] Running!");
// Your logic here
};
export default handler;
Terminal-Fenster
# Verify hook is discovered
openclaw hooks list
# Enable it
openclaw hooks enable my-hook
# Restart your gateway process (menu bar app restart on macOS, or restart your dev process)
# Trigger the event
# Send /new via your messaging channel
{
"hooks": {
"internal": {
"enabled": true,
"entries": {
"session-memory": { "enabled": true },
"command-logger": { "enabled": false }
}
}
}
}

Hooks können eine eigene Konfiguration haben:

{
"hooks": {
"internal": {
"enabled": true,
"entries": {
"my-hook": {
"enabled": true,
"env": {
"MY_CUSTOM_VAR": "value"
}
}
}
}
}
}

Du kannst Hooks aus weiteren Verzeichnissen laden. Diese werden wie verwaltete Hooks behandelt und folgen derselben Priorität beim Überschreiben:

{
"hooks": {
"internal": {
"enabled": true,
"load": {
"extraDirs": ["/path/to/more/hooks"]
}
}
}
}

Altes Konfigurationsformat (Wird weiterhin unterstützt)

Abschnitt betitelt „Altes Konfigurationsformat (Wird weiterhin unterstützt)“

Das alte Format funktioniert aus Gründen der Abwärtskompatibilität immer noch:

{
"hooks": {
"internal": {
"enabled": true,
"handlers": [
{
"event": "command:new",
"module": "./hooks/handlers/my-handler.ts",
"export": "default"
}
]
}
}
}

Hinweis: module muss ein Pfad relativ zum Workspace sein. Absolute Pfade oder Pfade außerhalb des Workspace werden abgelehnt.

Migration: Nutze für neue Hooks das neue, auf Discovery basierende System. Legacy-Handler werden nach den verzeichnisbasierten Hooks geladen.

Terminal-Fenster
# List all hooks
openclaw hooks list
# Show only eligible hooks
openclaw hooks list --eligible
# Verbose output (show missing requirements)
openclaw hooks list --verbose
# JSON output
openclaw hooks list --json
Terminal-Fenster
# Show detailed info about a hook
openclaw hooks info session-memory
# JSON output
openclaw hooks info session-memory --json
Terminal-Fenster
# Show eligibility summary
openclaw hooks check
# JSON output
openclaw hooks check --json
Terminal-Fenster
# Enable a hook
openclaw hooks enable session-memory
# Disable a hook
openclaw hooks disable command-logger

Speichert den Session-Kontext im Speicher, wenn du /new oder /reset ausführst.

Events: command:new, command:reset

Voraussetzungen: workspace.dir muss konfiguriert sein

Output: <workspace>/memory/YYYY-MM-DD-slug.md (Standard ist ~/.openclaw/workspace)

Was der Hook macht:

  1. Er nutzt den Session-Eintrag vor dem Reset, um das richtige Transkript zu finden.
  2. Er extrahiert die letzten 15 Nachrichten von User und Assistant aus der Konversation (konfigurierbar).
  3. Er verwendet ein LLM, um einen aussagekräftigen Slug für den Dateinamen zu erstellen.
  4. Er speichert die Session-Metadaten in einer datierten Memory-Datei.

Beispiel-Output:

# Session: 2026-01-16 14:30:00 UTC
- **Session Key**: agent:main:main
- **Session ID**: abc123def456
- **Source**: telegram
## Conversation Summary
user: Can you help me design the API?
assistant: Sure! Let's start with the endpoints...

Beispiele für Dateinamen:

  • 2026-01-16-vendor-pitch.md
  • 2026-01-16-api-design.md
  • 2026-01-16-1430.md (Fallback-Zeitstempel, falls die Slug-Generierung fehlschlägt)

Aktivieren:

Terminal-Fenster
openclaw hooks enable session-memory

Fügt während agent:bootstrap zusätzliche Bootstrap-Dateien ein (zum Beispiel lokale AGENTS.md oder TOOLS.md aus einem Monorepo).

Events: agent:bootstrap

Voraussetzungen: workspace.dir muss konfiguriert sein

Output: Es werden keine Dateien geschrieben; der Bootstrap-Kontext wird nur im Arbeitsspeicher angepasst.

Konfiguration:

{
"hooks": {
"internal": {
"enabled": true,
"entries": {
"bootstrap-extra-files": {
"enabled": true,
"paths": ["packages/*/AGENTS.md", "packages/*/TOOLS.md"]
}
}
}
}
}

Konfigurationsoptionen:

  • paths (string[]): Glob- oder Pfadmuster, die vom Workspace aus aufgelöst werden.
  • patterns / files (string[]): Aliase für paths.

Hinweise:

  1. Pfade werden relativ zum Workspace aufgelöst.
  2. Dateien müssen innerhalb des Workspace bleiben (Prüfung via realpath).
  3. Nur bekannte Bootstrap-Dateinamen werden geladen (AGENTS.md, SOUL.md, TOOLS.md, IDENTITY.md, USER.md, HEARTBEAT.md, BOOTSTRAP.md, MEMORY.md, memory.md).
  4. Für Subagent- oder Cron-Sessions gilt eine eingeschränkte Allowlist (AGENTS.md, TOOLS.md, SOUL.md, IDENTITY.md, USER.md).

Aktivieren:

Terminal-Fenster
openclaw hooks enable bootstrap-extra-files

Loggt alle Command-Events in eine zentrale Audit-Datei.

Events: command

Voraussetzungen: Keine

Output: ~/.openclaw/logs/commands.log

Was der Hook macht:

  1. Er erfasst Event-Details wie Command-Aktion, Zeitstempel, Session Key, Sender-ID und Quelle.
  2. Er hängt diese Daten im JSONL-Format an die Log-Datei an.
  3. Er läuft lautlos im Hintergrund.
  4. Er ermöglicht eine einfache spätere Analyse der Systemnutzung.

Beispiel für Log-Einträge:

{"timestamp":"2026-01-16T14:30:00.000Z","action":"new","sessionKey":"agent:main:main","senderId":"+1234567890","source":"telegram"}
{"timestamp":"2026-01-16T15:45:22.000Z","action":"stop","sessionKey":"agent:main:main","senderId":"user@example.com","source":"whatsapp"}

Logs ansehen:

Terminal-Fenster
# View recent commands
tail -n 20 ~/.openclaw/logs/commands.log
# Pretty-print with jq
cat ~/.openclaw/logs/commands.log | jq .
# Filter by action
grep '"action":"new"' ~/.openclaw/logs/commands.log | jq .

Aktivieren:

Terminal-Fenster
openclaw hooks enable command-logger

Führt die BOOT.md aus, wenn das Gateway startet (nachdem die Channels bereit sind). Damit dies funktioniert, müssen interne Hooks aktiviert sein.

Events: gateway:startup

Voraussetzungen: workspace.dir muss konfiguriert sein

Was der Hook macht:

  1. Er liest die BOOT.md aus deinem Workspace und führt die Anweisungen über den Agent Runner aus.
  2. Er sendet alle angeforderten ausgehenden Nachrichten über das Message Tool.

Aktivieren:

Terminal-Fenster
openclaw hooks enable boot-md

Hooks werden während der Befehlsverarbeitung ausgeführt. Halte sie leichtgewichtig:

// ✓ Good - async work, returns immediately
const handler: HookHandler = async (event) => {
void processInBackground(event); // Fire and forget
};
// ✗ Bad - blocks command processing
const handler: HookHandler = async (event) => {
await slowDatabaseQuery(event);
await evenSlowerAPICall(event);
};

Umschließe riskante Operationen immer mit Try-Catch:

const handler: HookHandler = async (event) => {
try {
await riskyOperation(event);
} catch (err) {
console.error("[my-handler] Failed:", err instanceof Error ? err.message : String(err));
// Don't throw - let other handlers run
}
};

Beende den Handler frühzeitig, wenn das Event nicht relevant ist:

const handler: HookHandler = async (event) => {
// Only handle 'new' commands
if (event.type !== "command" || event.action !== "new") {
return;
}
// Deine Logik hier
};

Gib in den Metadaten nach Möglichkeit exakte Events an:

metadata: { "openclaw": { "events": ["command:new"] } } # Spezifisch

Stattdessen solltest du dies vermeiden:

metadata: { "openclaw": { "events": ["command"] } } # General - more overhead

Das Gateway loggt das Laden von Hooks direkt beim Startup. So siehst du sofort, was registriert wurde:

Registered hook: session-memory -> command:new
Registered hook: bootstrap-extra-files -> agent:bootstrap
Registered hook: command-logger -> command
Registered hook: boot-md -> gateway:startup

Falls ein Hook nicht auftaucht, kannst du alle entdeckten Hooks mit diesem Befehl auflisten:

Terminal-Fenster
openclaw hooks list --verbose

Um sicherzugehen, dass dein Handler wirklich läuft, kannst du einen einfachen Log-Befehl einbauen:

const handler: HookHandler = async (event) => {
console.log("[my-handler] Triggered:", event.type, event.action);
// Your logic
};

Wenn ein Hook zwar da ist, aber nicht ausgeführt wird, hilft dieser Befehl:

Terminal-Fenster
openclaw hooks info my-hook

Suche in der Ausgabe nach fehlenden Anforderungen (Requirements), die die Ausführung verhindern könnten.

Du kannst die Gateway-Logs in Echtzeit überwachen, um die Hook-Ausführung zu verfolgen:

Terminal-Fenster
# macOS
./scripts/clawlog.sh -f
# Other platforms
tail -f ~/.openclaw/gateway.log

Am besten testest du deine Handler isoliert. So stellst du sicher, dass die Logik stimmt, ohne das gesamte System zu starten:

import { test } from "vitest";
import myHandler from "./hooks/my-hook/handler.js";
test("my handler works", async () => {
const event = {
type: "command",
action: "new",
sessionKey: "test-session",
timestamp: new Date(),
messages: [],
context: { foo: "bar" },
};
await myHandler(event);
// Assert side effects
});

Hier erfährst du, wie die Hooks unter der Haube funktionieren. Das System ist modular aufgebaut, damit die Entdeckung und Ausführung deiner Erweiterungen stabil funktioniert.

  • src/hooks/types.ts: Typdefinitionen
  • src/hooks/workspace.ts: Verzeichnisse scannen und laden
  • src/hooks/frontmatter.ts: HOOK.md Metadaten-Parsing
  • src/hooks/config.ts: Berechtigungsprüfung
  • src/hooks/hooks-status.ts: Statusberichte
  • src/hooks/loader.ts: Dynamischer Module-Loader
  • src/cli/hooks-cli.ts: CLI-Befehle
  • src/gateway/server-startup.ts: Lädt Hooks beim Gateway-Start
  • src/auto-reply/reply/commands-core.ts: Löst Command-Events aus
Gateway startup
↓
Scan directories (bundled → plugin → managed + extra dirs → workspace)
↓
Parse HOOK.md files
↓
Sort by override precedence (bundled < plugin < managed < workspace)
↓
Check eligibility (bins, env, config, os)
↓
Load handlers from eligible hooks
↓
Register handlers for events
User sends /new
↓
Command validation
↓
Create hook event
↓
Trigger hook (all registered handlers)
↓
Command processing continues
↓
Session reset

Wenn ein Hook nicht so funktioniert, wie du es erwartest, helfen dir diese Schritte dabei, das Problem schnell zu finden.

  1. Prüf die Verzeichnisstruktur:

    Terminal-Fenster
    ls -la ~/.openclaw/hooks/my-hook/
    # Should show: HOOK.md, handler.ts
  2. Verifizier das HOOK.md Format:

    Terminal-Fenster
    cat ~/.openclaw/hooks/my-hook/HOOK.md
    # Should have YAML frontmatter with name and metadata
  3. Liste alle gefundenen Hooks auf:

    Terminal-Fenster
    openclaw hooks list

Prüf die Anforderungen:

Terminal-Fenster
openclaw hooks info my-hook

Achte auf fehlende:

  • Binaries (prüf den PATH)
  • Umgebungsvariablen
  • Config-Werte
  • OS-Kompatibilität
  1. Verifizier, dass der Hook aktiviert ist:

    Terminal-Fenster
    openclaw hooks list
    # Should show ✓ next to enabled hooks
  2. Starte deinen Gateway-Prozess neu, damit die Hooks neu geladen werden.

  3. Prüf die Gateway-Logs auf Fehler:

    Terminal-Fenster
    ./scripts/clawlog.sh | grep hook

Prüf auf TypeScript- oder Import-Fehler:

Terminal-Fenster
# Test import directly
node -e "import('./path/to/handler.ts').then(console.log)"

Vorher:

{
"hooks": {
"internal": {
"enabled": true,
"handlers": [
{
"event": "command:new",
"module": "./hooks/handlers/my-handler.ts"
}
]
}
}
}

Nachher:

  1. Erstelle das Hook-Verzeichnis:

    Terminal-Fenster
    mkdir -p ~/.openclaw/hooks/my-hook
    mv ./hooks/handlers/my-handler.ts ~/.openclaw/hooks/my-hook/handler.ts
  2. Erstelle die HOOK.md:

    ---
    name: my-hook
    description: "My custom hook"
    metadata: { "openclaw": { "emoji": "🎯", "events": ["command:new"] } }
    ---
    # My Hook
    Does something useful.
  3. Aktualisiere die Konfiguration:

    {
    "hooks": {
    "internal": {
    "enabled": true,
    "entries": {
    "my-hook": { "enabled": true }
    }
    }
    }
    }
  4. Überprüfe die Hooks und starte deinen Gateway-Prozess neu:

    Terminal-Fenster
    openclaw hooks list
    # Should show: 🎯 my-hook ✓

Vorteile der Migration:

  • Automatische Discovery
  • CLI-Management
  • Prüfung der Voraussetzungen
  • Bessere Dokumentation
  • Konsistente Struktur

AI Setup Assistant

OpenClaw

OpenClaw Expert

Noch festgefahren?

Wenn diese Seite nicht hilft, frage OpenClaw Expert nach Schritt-fuer-Schritt-Loesungen.