import type { SpendGuard } from '../spend-guard'; import { createFrameworkGuard, recordFrameworkReceipt, redactDataPlane, safeRequestShape, stringValue, type FrameworkAdapterOptions } from './common'; export interface HermesToolEvent { toolName?: string; name?: string; args?: Record; toolArgs?: Record; workflowId?: string; sessionId?: string; metadata?: Record; result?: Record; } export interface HermesHookResult { allow: boolean; approved?: boolean; reason?: string; receiptId?: string; editedArgs?: Record; } export interface HermesPlugin { name: 'agentguard-spend'; guard: SpendGuard; hooks: { 'pre-tool-call': (event: HermesToolEvent) => Promise; approval: (event: HermesToolEvent) => Promise; 'post-tool-call': (event: HermesToolEvent) => Promise; }; preToolCall(event: HermesToolEvent): Promise; approval(event: HermesToolEvent): Promise; postToolCall(event: HermesToolEvent): Promise; } export function createHermesPlugin(opts: FrameworkAdapterOptions): HermesPlugin { const guard = createFrameworkGuard({ ...opts, framework: 'hermes' }); const plugin: HermesPlugin = { name: 'agentguard-spend', guard, hooks: { 'pre-tool-call': (event) => preToolCall(guard, opts, event), approval: (event) => approval(guard, opts, event), 'post-tool-call': (event) => postToolCall(guard, event), }, preToolCall: (event) => preToolCall(guard, opts, event), approval: (event) => approval(guard, opts, event), postToolCall: (event) => postToolCall(guard, event), }; return plugin; } export const agentguardHermesPlugin = createHermesPlugin; async function preToolCall(guard: SpendGuard, opts: FrameworkAdapterOptions, event: HermesToolEvent): Promise { const gate = await guard.guardToolCall({ toolName: toolName(event), toolArgs: safeRequestShape(redactDataPlane(event.toolArgs ?? event.args ?? {})), workflowId: event.workflowId ?? event.sessionId, scope: opts.scope, }); if (!gate.approved) return { allow: false, approved: false, reason: gate.decision.reasons.join('; ') }; return { allow: true, approved: true, editedArgs: gate.editedArgs }; } async function approval(guard: SpendGuard, opts: FrameworkAdapterOptions, event: HermesToolEvent): Promise { return preToolCall(guard, opts, event); } async function postToolCall(guard: SpendGuard, event: HermesToolEvent): Promise { const { decision } = await recordFrameworkReceipt(guard, 'hermes', { event: 'post-tool-call', toolName: toolName(event), workflowId: event.workflowId ?? event.sessionId ?? null, metadata: redactDataPlane(event.metadata ?? {}), result: redactDataPlane(event.result ?? {}), }); return { allow: true, approved: true, receiptId: decision.decisionId }; } function toolName(event: HermesToolEvent): string { return stringValue(event.toolName) ?? stringValue(event.name) ?? 'unknown_tool'; }