feat: implement OpenClaw integration

Ports the OMX OpenClaw module into oh-my-openagent as a first-class integration.
This integration allows forwarding internal events (session lifecycle, tool execution) to external gateways (HTTP or command-based).

- Added `src/openclaw` directory with implementation:
  - `dispatcher.ts`: Handles HTTP/Command dispatching with interpolation
  - `types.ts`: TypeScript definitions
  - `client.ts`: Main entry point `wakeOpenClaw`
  - `index.ts`: Public API
- Added `src/config/schema/openclaw.ts` for Zod schema validation
- Updated `src/config/schema/oh-my-opencode-config.ts` to include `openclaw` config
- Added `src/hooks/openclaw-sender/index.ts` to listen for events
- Registered the hook in `src/plugin/hooks/create-session-hooks.ts`
- Added unit tests in `src/openclaw/__tests__`

Events handled:
- `session-start` (via `session.created`)
- `session-end` (via `session.deleted`)
- `session-idle` (via `session.idle`)
- `ask-user-question` (via `tool.execute.before` for `ask_user_question`)
- `stop` (via `tool.execute.before` for `stop-continuation` command)
This commit is contained in:
YeonGyu-Kim
2026-03-16 17:21:56 +09:00
parent 84fb1113f1
commit 03b346ba51
14 changed files with 1006 additions and 0 deletions
+1
View File
@@ -215,6 +215,7 @@ export function createEventHandler(args: {
await Promise.resolve(hooks.compactionTodoPreserver?.event?.(input));
await Promise.resolve(hooks.writeExistingFileGuard?.event?.(input));
await Promise.resolve(hooks.atlasHook?.handler?.(input));
await Promise.resolve(hooks.openclawSender?.event?.(input));
await Promise.resolve(hooks.autoSlashCommand?.event?.(input));
};
+8
View File
@@ -26,6 +26,7 @@ import {
createPreemptiveCompactionHook,
createRuntimeFallbackHook,
} from "../../hooks"
import { createOpenClawSenderHook } from "../../hooks/openclaw-sender"
import { createAnthropicEffortHook } from "../../hooks/anthropic-effort"
import {
detectExternalNotificationPlugin,
@@ -60,6 +61,7 @@ export type SessionHooks = {
taskResumeInfo: ReturnType<typeof createTaskResumeInfoHook> | null
anthropicEffort: ReturnType<typeof createAnthropicEffortHook> | null
runtimeFallback: ReturnType<typeof createRuntimeFallbackHook> | null
openclawSender: ReturnType<typeof createOpenClawSenderHook> | null
}
export function createSessionHooks(args: {
@@ -261,6 +263,11 @@ export function createSessionHooks(args: {
pluginConfig,
}))
: null
const openclawSender = isHookEnabled("openclaw-sender") && pluginConfig.openclaw?.enabled
? safeHook("openclaw-sender", () => createOpenClawSenderHook(ctx, pluginConfig.openclaw!))
: null
return {
contextWindowMonitor,
preemptiveCompaction,
@@ -285,5 +292,6 @@ export function createSessionHooks(args: {
taskResumeInfo,
anthropicEffort,
runtimeFallback,
openclawSender,
}
}
+1
View File
@@ -33,6 +33,7 @@ export function createToolExecuteBeforeHandler(args: {
await hooks.prometheusMdOnly?.["tool.execute.before"]?.(input, output)
await hooks.sisyphusJuniorNotepad?.["tool.execute.before"]?.(input, output)
await hooks.atlasHook?.["tool.execute.before"]?.(input, output)
await hooks.openclawSender?.["tool.execute.before"]?.(input, output)
const normalizedToolName = input.tool.toLowerCase()
if (