/** * Hooks — Scene + Purpose injection into agent system prompt * * Registers a `before_prompt_build` hook that appends channel-specific * rules (skill) and agent purpose to the system context for clawlink sessions. * * Only fires for sessions with ID starting with `clawlink:`. */ import { registry } from '../runtime/registry.js'; import { logger } from '../util/logger.js'; interface HookEvent { [key: string]: unknown; } interface HookContext { sessionId?: string; [key: string]: unknown; } // eslint-disable-next-line @typescript-eslint/no-explicit-any type AnyApi = any; /** * Register all ClawLink hooks with OpenClaw API. */ export function registerHooks(api: AnyApi): void { api.registerHook('before_prompt_build', async (_event: HookEvent, ctx: HookContext) => { // Only fire for clawlink channel sessions if (!ctx.sessionId?.startsWith('clawlink:')) return {}; const channelId = ctx.sessionId.replace('clawlink:', ''); const rt = registry.getDefault(); if (!rt) return {}; const parts: string[] = []; // Inject channel rules (skill/scene) try { const rules = await rt.getChannelSkill(channelId); if (rules) parts.push(`[SCENE: ${channelId}]\n${rules}\n[/SCENE]`); } catch (err) { logger.warn(`[hooks] rules fetch failed: ${(err as Error).message}`); } // Inject agent purpose for this channel try { const purpose = rt.store?.getPurpose(channelId); if (purpose) parts.push(`[PURPOSE]\n${purpose}\n[/PURPOSE]`); } catch (err) { logger.warn(`[hooks] purpose fetch failed: ${(err as Error).message}`); } if (parts.length === 0) return {}; logger.debug(`[hooks] Injected Scene+Purpose for ${channelId}`); return { appendSystemContext: parts.join('\n\n') }; }, { name: 'clawlink-scene-purpose' }); }