Code Reader
首页
帮助
设计文档
首页
帮助
设计文档
  • Hooks 系统架构

Hooks 系统架构

Moltbot 的事件驱动钩子系统

概述

Hooks 系统是 Moltbot 的事件驱动扩展机制,允许在关键事件点(如命令执行、Agent 运行、工具调用等)注入自定义逻辑。支持目录发现和动态加载。

设计目标

  • 事件驱动: 在关键生命周期点触发钩子
  • 目录发现: 从 bundled、managed、workspace 目录自动发现钩子
  • 动态加载: 支持热重载和缓存清理
  • 类型安全: 钩子处理器有明确的类型定义
  • 优雅降级: 钩子错误不影响主流程

钩子系统组成

Internal Hooks

文件: src/hooks/internal-hooks.ts

Internal Hooks 是通过代码注册的同步/异步处理器,用于核心系统扩展。

注册机制

export type InternalHookEventType = "command" | "session" | "agent" | "gateway";

export type InternalHookEvent = {
  /** 事件类型 (command, session, agent, gateway) */
  type: InternalHookEventType;
  /** 特定动作 (e.g., 'new', 'reset', 'stop') */
  action: string;
  /** 关联的 session key */
  sessionKey: string;
  /** 事件特定的上下文 */
  context: Record<string, unknown>;
  /** 发生时间戳 */
  timestamp: Date;
  /** 钩子可以推送消息的数组 */
  messages: string[];
};

export type InternalHookHandler = (event: InternalHookEvent) => Promise<void> | void;

注册函数

// 注册通用事件处理器
registerInternalHook('command', async (event) => {
  console.log('Command:', event.action);
});

// 注册特定事件处理器
registerInternalHook('command:new', async (event) => {
  await saveSessionToMemory(event);
});

// 触发钩子
await triggerInternalHook({
  type: 'command',
  action: 'new',
  sessionKey: 'agent:main',
  context: { ... },
  timestamp: new Date(),
  messages: [],
});

Directory-Based Hooks

钩子类型定义

文件: src/hooks/types.ts

export type Hook = {
  name: string;
  description: string;
  source: "moltbot-bundled" | "moltbot-managed" | "moltbot-workspace" | "moltbot-plugin";
  pluginId?: string;
  filePath: string;  // HOOK.md 路径
  baseDir: string;   // 包含钩子的目录
  handlerPath: string; // handler 模块路径
};

export type MoltbotHookMetadata = {
  always?: boolean;              // 总是触发
  hookKey?: string;             // 钩子唯一键
  emoji?: string;                // Emoji 图标
  homepage?: string;             // 主页 URL
  /** 处理的事件列表 */
  events: string[];
  /** 导出名称 (默认: "default") */
  export?: string;
  /** 支持的操作系统 */
  os?: string[];
  /** 依赖要求 */
  requires?: {
    bins?: string[];
    anyBins?: string[];
    env?: string[];
    config?: string[];
  };
  /** 安装规范 */
  install?: HookInstallSpec[];
};

钩子目录结构

钩子通过目录结构自动发现:

~/.clawdbot/
├── hooks/bundled/       # 内置钩子 (moltbot-bundled)
│   ├── BOOTSTRAP_HOOK.md
│   └── session-start/
│       ├── HOOK.md
│       └── handler.ts
├── hooks/managed/       # 托管钩子 (moltbot-managed)
│   └── ...
└── workspace/
    └── .clawdbot/hooks/  # 工作空间钩子 (moltbot-workspace)
        └── ...

钩子文件格式

HOOK.md (Frontmatter)

---
name: "My Hook"
description: "A custom hook for agent events"
events: ["agent_start", "tool_call"]
export: "default"
os: ["darwin", "linux"]
requires:
  bins: ["git"]
  env: ["MY_ENV_VAR"]
  config: ["agents.defaults.model"]
always: false
emoji: "🔌"
---

Hook description and documentation here...

## Example

This hook demonstrates how to respond to agent events.

handler.ts

// 默认导出
export default async function handler(context: HookContext) {
  console.log('Hook triggered:', context.event);
  return { success: true, message: 'Hook executed' };
}

// 命名导出
export async function myHandler(context: HookContext) {
  // ...
}

钩子加载流程

文件: src/hooks/loader.ts

loadWorkspaceHookEntries

export function loadWorkspaceHookEntries(
  workspaceDir: string,
  opts: { config: MoltbotConfig }
): HookEntry[] {
  const paths = [
    `${workspaceDir}/hooks/bundled`,
    `${workspaceDir}/hooks/managed`,
    `${workspaceDir}/.clawdbot/hooks`,
  ];

  const entries: HookEntry[] = [];
  for (const basePath of paths) {
    const found = scanHooksDirectory(basePath);
    entries.push(...found);
  }

  return entries;
}

钩子事件类型

事件类型完整列表

文件: src/hooks/internal-hooks.ts

// Command 事件
"command"
"command:new"
"command:reset"
"command:status"
"command:help"

// Session 事件
"session"
"session:start"
"session:end"
"session:reset"

// Agent 事件
"agent"
"agent:bootstrap"
"agent:start"
"agent:end"
"agent:tool_call"
"agent:tool_result"

// Gateway 事件
"gateway"
"gateway:start"
"gateway:stop"
"gateway:reload"

Agent Bootstrap 事件

export type AgentBootstrapHookContext = {
  workspaceDir: string;
  bootstrapFiles: WorkspaceBootstrapFile[];
  cfg?: MoltbotConfig;
  sessionKey?: string;
  sessionId?: string;
  agentId?: string;
};

export type AgentBootstrapHookEvent = InternalHookEvent & {
  type: "agent";
  action: "bootstrap";
  context: AgentBootstrapHookContext;
};

钩子配置

配置结构

export type NormalizedHooksConfig = {
  internal?: {
    enabled: boolean;              // 全局开关
    handlers?: Array<{
      module: string;               // 模块路径
      event: string;                // 监听的事件
      export?: string;              // 导出名称 (默认: "default")
    }>;
  };
};

配置示例

{
  hooks: {
    internal: {
      enabled: true,
      handlers: [
        {
          module: "/path/to/my-hook.js",
          event: "agent:start",
          export: "onAgentStart"
        }
      ]
    }
  }
}

钩子 eligibility 上下文

平台检测

文件: src/hooks/types.ts

export type HookEligibilityContext = {
  remote?: {
    platforms: string[];           // 支持的平台列表
    hasBin: (bin: string) => boolean;   // 检查二进制文件
    hasAnyBin: (bins: string[]) => boolean; // 检查任一文件
    note?: string;
  };
};

检查示例

// 检查平台兼容性
if (metadata.os) {
  const platform = process.platform;
  if (!metadata.os.includes(platform)) {
    return { eligible: false, reason: `Platform ${platform} not supported` };
  }
}

// 检查二进制依赖
if (metadata.requires?.bins) {
  for (const bin of metadata.requires.bins) {
    if (!ctx.remote.hasBin(bin)) {
      return { eligible: false, reason: `Missing binary: ${bin}` };
    }
  }
}

// 检查环境变量
if (metadata.requires?.env) {
  for (const envVar of metadata.requires.env) {
    if (!process.env[envVar]) {
      return { eligible: false, reason: `Missing env var: ${envVar}` };
    }
  }
}

钩子生命周期

Internal Hooks

Directory-Based Hooks

钩子安装

HookInstallSpec

export type HookInstallSpec = {
  id?: string;
  kind: "bundled" | "npm" | "git";
  label?: string;
  package?: string;
  repository?: string;
  bins?: string[];
};

安装示例

---
name: "Git Hook"
description: "Git integration hook"
events: ["before_tool_call"]
requires:
  bins: ["git"]
install:
  - kind: "npm"
    package: "simple-git"
---

This hook requires git to be installed and installs the simple-git package.

内置钩子

Gmail Hooks

文件: src/hooks/gmail.ts

// Gmail Pub/Sub 集成
export function setupGmailHooks(config: MoltbotConfig) {
  registerInternalHook('hooks.gmail', async (event) => {
    // 处理 Gmail 事件
  });
}

Legacy Config Hooks

文件: src/hooks/soul-evil.ts

// 遗留配置迁移
export function registerLegacyHooks() {
  registerInternalHook('legacy:config', async (event) => {
    // 迁移遗留配置
  });
}

错误处理

优雅降级

// 内置钩子错误处理
export async function triggerInternalHook(event: InternalHookEvent): Promise<void> {
  const allHandlers = [...typeHandlers, ...specificHandlers];

  if (allHandlers.length === 0) return;

  for (const handler of allHandlers) {
    try {
      await handler(event);
    } catch (err) {
      console.error(
        `Hook error [${event.type}:${event.action}]:`,
        err instanceof Error ? err.message : String(err),
      );
      // 继续执行其他处理器
    }
  }
}

代码路径引用

功能文件路径
Internal Hookssrc/hooks/internal-hooks.ts
钩子类型定义src/hooks/types.ts
钩子加载器src/hooks/loader.ts
钩子配置src/hooks/config.ts
钩子验证src/hooks/frontmatter.ts
Gmail Hookssrc/hooks/gmail.ts
Gmail Setup Utilssrc/hooks/gmail-setup-utils.ts
Legacy Hookssrc/hooks/soul-evil.ts
Workspace Hookssrc/hooks/workspace.ts
Hooks Statussrc/hooks/hooks-status.ts
Hooks Installsrc/hooks/install.ts