How OpenClaw Integration Forwards Session Events to External Gateways in oh-my-claudecode
OpenClaw integration in oh-my-claudecode uses a fire-and-forget _openclaw.wake wrapper that lazily loads a dispatcher to read user configuration, build whitelisted JSON payloads, and forward session events to external command or HTTP gateways without blocking the main hook execution.
The oh-my-claudecode project implements a lightweight event forwarding system called OpenClaw that streams Claude Code session lifecycle events to external services. This integration allows users to configure custom gateways—either shell commands or HTTP endpoints—that receive structured payloads whenever hooks like session-start or stop trigger. Understanding how OpenClaw integration forwards session events to external gateways reveals a non-blocking architecture designed for reliability and extensibility.
Architecture Overview
The OpenClaw system operates through a layered dispatch mechanism that remains dormant until explicitly enabled. At the core, the _openclaw.wake wrapper in src/hooks/bridge.ts (lines 1303-1308) serves as the single entry point for all hook implementations. This wrapper performs a lazy dynamic import of the OpenClaw module only when the OMC_OPENCLAW=1 environment variable is set, ensuring zero overhead when the feature is disabled.
The actual dispatch logic resides in src/openclaw/index.ts, where the wakeOpenClaw function (lines 72-84 and 119-180) orchestrates configuration loading, gateway resolution, payload construction, and final transmission. Gateway resolution logic in src/openclaw/dispatcher.ts determines whether to invoke a shell command via wakeCommandGateway or an HTTP request via wakeGateway based on the user-defined configuration stored in ~/.claude/omc_config.openclaw.json.
The Event Forwarding Pipeline
Hook Entry Points and the Wake Wrapper
Every relevant session hook—session-start, session-end, stop, keyword-detector, and ask-user-question—invokes the same forwarding interface. The wrapper checks the environment switch before proceeding:
// src/hooks/bridge.ts (lines 1296-1310)
export const _openclaw = {
wake: (event: string, ctx: OpenClawContext) => {
if (!process.env.OMC_OPENCLAW) return; // Environment gate
import("../openclaw/index.js")
.then((m) => m.wakeOpenClaw(event, ctx))
.catch(() => {}); // Fire-and-forget: errors swallowed
},
};
This pattern ensures that hooks at src/hooks/session-start/index.ts (lines 41-53) and similar entry points can trigger external notifications without awaiting network I/O or risking runtime exceptions.
Configuration Loading and Gateway Resolution
Upon activation, wakeOpenClaw calls getOpenClawConfig() from src/openclaw/config.ts to read the JSON configuration. If the file does not exist at the default location, the function returns null and forwarding aborts silently. Assuming configuration exists, resolveGateway(config, event) in src/openclaw/dispatcher.ts (line 46) maps the event name to a specific gateway definition:
// Conceptual flow from src/openclaw/index.ts (lines 72-84)
const config = await getOpenClawConfig();
if (!config) return null;
const resolved = resolveGateway(config, event);
if (!resolved) return null; // No gateway mapped for this event
const { gatewayName, gateway, instruction } = resolved;
Payload Construction and Variable Interpolation
Before transmission, the system builds a whitelisted context object via buildWhitelistedContext (lines 44-59 in index.ts) to prevent leaking unintended session data. The dispatcher then prepares template variables including sessionId, projectPath, timestamp, and derived values like tmuxSession or projectName.
If the configuration instruction contains template variables (e.g., {{replyChannel}}), interpolateInstruction (lines 149-150) substitutes them using the variables map. The final OpenClawPayload object (lines 151-165) assembles the event name, interpolated instruction, timestamp, session metadata, a cryptographic signal from buildOpenClawSignal, and the whitelisted context:
{
"event": "session-start",
"instruction": "notify-start",
"timestamp": 1699999999999,
"signal": "...",
"context": { "sessionId": "abc123", "projectPath": "/home/user/project" }
}
Dispatch to Command or HTTP Gateways
The system supports two gateway types determined by isCommandGateway in src/openclaw/dispatcher.ts. The decision block at lines 170-176 in index.ts routes accordingly:
- Command gateways:
wakeCommandGateway(gatewayName, gateway, variables, payload)executes shell commands with proper escaping, useful for local scripts orcurlinvocations. - HTTP gateways:
wakeGateway(gatewayName, gateway, payload)performs afetchPOST with JSON content.
Both methods operate asynchronously with discarded promises, ensuring the hook continues regardless of gateway health.
Configuring External Gateways
Users define gateway mappings in omc_config.openclaw.json. The configuration specifies event-to-gateway mappings, authentication headers, and template variables:
{
"gateways": {
"session-start": {
"type": "http",
"url": "https://example.com/openclaw/hooks",
"method": "POST",
"headers": { "Authorization": "Bearer {{replyChannel}}" }
},
"stop": {
"type": "command",
"command": "curl -X POST -H \"Content-Type: application/json\" -d '{{payloadJson}}' https://example.com/openclaw/stop"
}
}
}
The {{payloadJson}} variable inserts the entire serialized payload object, while {{replyChannel}} and other custom variables interpolate user-defined values from the context.
Implementation Details and Error Handling
Debug visibility requires setting OMC_OPENCLAW_DEBUG=1, which enables result logging to stderr (lines 79-82 in index.ts). By design, all errors are caught and suppressed at the wrapper level (.catch(() => {}) in bridge.ts line 1308), preventing network timeouts or configuration errors from disrupting the Claude Code session lifecycle.
The wakeOpenClaw function returns an OpenClawResult object or null on failure (lines 183-190), though the wrapper discards this return value to maintain the fire-and-forget contract.
Summary
- Entry point: The
_openclaw.wakewrapper insrc/hooks/bridge.tsprovides a fire-and-forget interface that checksOMC_OPENCLAW=1before lazily loading the OpenClaw dispatcher. - Configuration:
getOpenClawConfig()reads~/.claude/omc_config.openclaw.jsonto map events to specific command or HTTP gateways. - Payload building: The system constructs whitelisted contexts, interpolates template variables like
{{payloadJson}}, and generates cryptographic signals for verification. - Non-blocking dispatch: Gateway resolution in
src/openclaw/dispatcher.tsroutes to eitherwakeCommandGatewayorwakeGateway, with all promises discarded to prevent hook blocking. - Silent failure: Errors are swallowed by design, with optional debug logging available via
OMC_OPENCLAW_DEBUG=1.
Frequently Asked Questions
What environment variables are required to enable OpenClaw event forwarding?
The OMC_OPENCLAW=1 environment variable must be set to activate the integration. Without this flag, the _openclaw.wake wrapper returns immediately without loading the dispatcher. For troubleshooting, set OMC_OPENCLAW_DEBUG=1 to print gateway results to stderr.
How does oh-my-claudecode prevent session data leakage when forwarding events?
The dispatcher uses buildWhitelistedContext (lines 44-59 in src/openclaw/index.ts) to filter the incoming OpenClawContext object before adding it to the payload. This whitelist approach ensures only explicitly defined fields (such as sessionId and projectPath) are transmitted, preventing accidental exposure of sensitive session internals.
Can I use shell scripts instead of HTTP endpoints for OpenClaw gateways?
Yes. Set the gateway type to command in your omc_config.openclaw.json configuration. The system will invoke wakeCommandGateway from src/openclaw/dispatcher.ts, which executes the specified shell command with the payload available via template variables like {{payloadJson}}. This enables integration with local notification scripts, custom logging tools, or alternative HTTP clients.
Where does the OpenClaw configuration file live by default?
The system looks for omc_config.openclaw.json at ~/.claude/omc_config.openclaw.json unless overridden. The getOpenClawConfig() function in src/openclaw/config.ts reads this location; if the file is missing, the forwarding operation aborts silently without throwing errors.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →