Skip to content
cccskills
FreePublished on GitHubEnglish overview

agent-runtime-hooks

Provides guidance for registering hooks around an agent’s execution steps, tool calls, human approvals, context compression, and sub-agent calls. Hooks can observe events, supply local mock tool results, or use webhooks to allow or deny tool results under configured policies.

OriginalShow the original description

Use for agent lifecycle hooks, tool mocks, intervention, sub-agent calls and context compression.

lobehub/lobehub83,504 stars(GitHub 83,504 + cccskills 0)

Run score Not tested

See how to install

Good for

  • Observing agent steps and tool errors
  • Mocking tool results during local execution
  • Requiring webhook decisions before or after a tool call
  • Tracking human approvals and sub-agent calls

Overview

What it does

This guide explains how to register hooks for an agent’s execution lifecycle and handle events at different stages. Hooks can observe step starts and completions, tool calls and errors, human approvals, context compression, sub-agent calls, and final operation states. Local handlers can provide mock tool results. Webhooks can deliver event notifications or, when configured for decisions, allow or deny tool calls. The guide describes event context such as operation IDs, tool details, and results, as well as webhook payloads and delivery error behavior.

When it helps

Use it when building ways to monitor agent runs or intervene in their operation. It can also help when replacing tool results for testing or tracing workflows that include approvals and sub-agents.

Example requests

  • “Set up a webhook hook to check permission before a tool runs.”
  • “Add hooks that record this agent’s steps and errors.”
  • “Mock a tool result locally during tests.”

Notes

Webhook configuration requires a destination URL. If headers use environment variables, specify an allowlist; secret values are resolved at send time rather than when configuration is saved. Hooks registered only as webhooks cannot restore their function handlers after a process restart.

This overview was written by AI (OpenAI Codex) from the public SKILL.md. Check the original below for the exact behavior.

Document check

AI checked the SKILL.md for operations that need care and for setup requirements. The score comes only from actually running the skill (hands-on test).

SafetyAre sending data, deleting files, and running commands disclosed?
BPartly lackingWebhooks can allow or deny execution, though failure behavior spans multiple settings.
Easy setupDoes it need few tools or API keys, and are they documented?
BPartly lackingIt states that webhooks need a destination URL and an environment-variable allowlist.

Not counted in the score. Rated by AI (OpenAI Codex) as of Oct 26, 2026. Treat it as a guide and read the original before installing.

Disagree? Request a re-evaluation

Tell us which point is wrong and why. No login needed. We will check it against the SKILL.md, redo the evaluation, and show the result here.

This form is protected by reCAPTCHA, and the Google Privacy Policy and Terms of Service apply.

Files included (1)

  • SKILL.md15.9 KB

SKILL.md (original)

Review the instructions the agent will receive before you install.

Agent Runtime Hooks

Register lifecycle hooks through execAgent({ hooks }). HookDispatcher stores them per operation and dispatches them in registration order.

Hook Types

16 hook types across 5 categories:

execAgent({ hooks })
  │
  ├─ beforeStep ──────────── Before each step executes
  │     │
  │     ├─ [call_llm]        LLM inference
  │     │
  │     ├─ [call_tool]
  │     │     ├─ beforeToolCall ── Before tool executes (supports mocking)
  │     │     ├─ (tool execution)
  │     │     ├─ afterToolCall ─── After tool completes (optional result control)
  │     │     └─ onToolCallError ─ Tool threw an exception
  │     │
  │     ├─ [request_human_approve]
  │     │     ├─ beforeHumanIntervention ── Before agent pauses
  │     │     ├─ afterHumanIntervention ─── After approve/reject + resume
  │     │     └─ onStopByHumanIntervention ── User rejected, agent halted
  │     │
  │     ├─ [compress_context]
  │     │     ├─ beforeCompact ──── Before compression starts
  │     │     ├─ afterCompact ───── After compression completes
  │     │     └─ onCompactError ─── Compression failed
  │     │
  │     ├─ [callAgent] (via execSubAgentTask)
  │     │     ├─ beforeCallAgent ── Before sub-agent starts
  │     │     ├─ afterCallAgent ─── After creation/start returns
  │     │     └─ onCallAgentError ── Sub-agent failed
  │     │
  │     └─ afterStep ──────────── After step completes
  │
  ├─ (next step...)
  │
  ├─ onComplete ───────────── Operation reaches terminal state
  └─ onError ──────────────── Error during execution

Key Files

FileRole
packages/agent-runtime/src/types/hooks.tsEvent types and required/optional fields
apps/server/src/services/agentRuntime/hooks/types.tsRegistration and webhook types
apps/server/src/services/agentRuntime/hooks/HookDispatcher.tsRegistration, dispatch, local mocks, HTTP/QStash delivery
apps/server/src/services/agentRuntime/hooks/webhookPayload.tsEvent projection and email enrichment
apps/server/src/modules/AgentRuntime/adapters/toolCallHookContext.tsShared tool-event context builder
apps/server/src/modules/AgentRuntime/adapters/ServerToolTransport.tsTool notifications and execution results
apps/server/src/modules/AgentRuntime/RuntimeExecutors.tsTool, compression and human-intervention execution
apps/server/src/services/agentRuntime/AgentRuntimeService/ (executeStep/, intervention.ts)Step events and human-intervention continuation
apps/server/src/services/agentRuntime/CompletionLifecycle/index.tsTerminal events
apps/server/src/services/aiAgent/subAgentRuns.tsSub-agent events

Registration

const hooks: AgentHook[] = [
  {
    id: 'observe-step',
    type: 'afterStep',
    handler: async (event) => {
      console.log(event.operationId, event.stepIndex);
    },
  },
];
await aiAgentService.execAgent({ agentId, prompt, hooks });

Completion cleans up registrations through hookDispatcher.unregister(operationId).

Webhook Hooks

Webhook-only hooks deliver in both local and queue modes. For hooks with both handler and webhook, local dispatch calls the handler; queue dispatch uses the serialized webhook. Only webhook configuration is persisted, so functions are unavailable after a process restart.

const hook: AgentHook = {
  id: 'tool-notification',
  type: 'afterToolCall',
  matcher: '^fs/readFile$',
  webhook: {
    url: 'https://example.com/hooks',
    delivery: 'fetch',
    timeout: 5, // seconds; default 30
    headers: { Authorization: 'Bearer ${HOOK_TOKEN}' },
    allowedEnvVars: ['HOOK_TOKEN'],
  },
};
  • matcher is a regex against ${identifier}/${apiName}, supported only on tool events. Omitted, empty, or * matches all tools.
  • Header environment templates resolve only at send time from allowedEnvVars; persist templates, never resolved secrets.
  • Notifications ignore response content and may return HTTP 204.
  • beforeToolCall with responseHandling: 'toolCall' awaits an optional HTTP decision before tool execution. Return HTTP 2xx JSON with { "decision": "allow" } or { "decision": "deny", "reason": "禁止执行该操作" } to intervene. The type is { decision: 'allow' } | { decision: 'deny'; reason?: string }; a deny with an omitted or empty reason uses Blocked by beforeToolCall hook.. Denials preserve the reason in the tool result/card with classification hook_denied.
  • afterToolCall also accepts responseHandling: 'toolCall'. It synchronously checks the full request/result before archival and model consumption; deny replaces the original result with the receiver's optional reason in both content and blocked state. A deny with an omitted or empty reason uses Blocked by afterToolCall hook., following the same Blocked by ${type} hook. rule as beforeToolCall. The reason enters model context and the tool card, as with beforeToolCall. It does not undo tool side effects, retries, or charges. See the result-control contract for deferred completions and runtime boundaries.
  • Environment response modes are ignore (default notifications) and toolCall (optional decisions for both before and after). Only subscribed events are enabled. Replace removed toolResult/toolCallAndResult values with toolCall; use the events list to select after-only checking. Existing after responses with a valid decision now control results.
  • HTTP 204, empty successful bodies, invalid JSON, and JSON without a top-level decision (including arrays, scalars, null, or only the old nested hookSpecificOutput) are notification responses, even with onError: 'block'. Continue checking subsequent hooks; do not treat a notification as permission to skip later policies. Explicit invalid decisions, non-2xx status, oversized bodies, invalid UTF-8, network failures, and timeouts follow onError: 'continue' | 'block' (default continue); blocking uses hook_control_error as the tool-result content and reason for both before and after hooks. Cancellation never grants permission.
  • Extra response fields are allowed and discarded. Only decision and a deny's optional string reason are consumed; updatedInput/additionalContext do not change tool arguments or conversation context.
  • Configuration: packages/types/src/agentHook.ts; response parsing: packages/types/src/agentHookResponse.ts; HTTP delivery: apps/server/src/services/agentRuntime/hooks/httpWebhook.ts.

Hook configuration lifetime

  • Environment hooks (AGENT_HOOK_WEBHOOK_*) always come from the executing worker's current environment, including after human approval and deferred completion. Never persist their configuration or restore a previous environment's hooks.
  • Code-supplied webhook hooks (execAgent({ hooks })) retain the existing per-operation serialization and persistence. Recover these caller hooks from the runtime snapshot or durable operation record; preserve environment-variable templates without expanding secrets into storage.
  • Evaluate each operation's recovered caller hooks in order, then evaluate current environment hooks once in the final round. Do not deduplicate caller hook IDs across operations. Environment-only hooks do not require durable operation storage. Removing an environment hook stops it from controlling subsequent results; when no matching result control remains, allow the result. Do not freeze a previous environment policy across an approval or restart.
  • Deferred completion publishes into its empty placeholder once; duplicate callbacks retain that first final result, including a denial. Propagate caller-policy storage errors rather than treating failed reads as an empty hook list. Do not extend default allow to configured-hook delivery failures: those follow onError.
  • Cover environment removal/addition across approval and snapshot expiry, publication without review markers, and continued enforcement of persisted caller hooks in regression tests.

Publish after the result hook

  • Await result control before archival, message content/state writes, streaming, and model consumption. Persist the allowed result or the sanitized denial directly; do not add toolResultControl pending/allowed/blocked metadata or hide raw output through history projections.
  • Human approval sources claim the decision and retain the answer in their private resolution/outbox. MessageModel.resolveHumanApproval defaults to claim-only; only the runtime calls it with publishResult: true after result control. Stop/rejection receipts can publish directly because they contain no executed tool output.
  • Recover original caller hooks through the existing intervention operation identity or deferred callback's parent operation. Keep the in-memory hook verdict separate from tool-owned state; a tool returning type: 'blocked' is not a hook verdict.
  • Every deferred backfill, including a member that fails to start, follows the same hook-before-write order. Use the existing empty placeholder to arbitrate duplicate final writes; do not introduce a replacement review state machine.
  • When a thread has a sub-agent or group-member completion bridge, that bridge exclusively publishes its tool result. Thread lifecycle hooks update thread metadata only; never prefill the tool message with an ungated summary, including after approval recovery.
  • Cover an answer held while a hook runs, atomic publication and rollback, duplicate callbacks after policy removal, and partial member startup failure. History readers should need no result-review logic.

Events

EventTiming and payload
beforeStepBefore a step; AgentHookEvent
afterStepAfter a step; content, tool calls/results and usage totals
onCompleteTerminal state; reason such as done, error, interrupted, max_steps or cost_limit
onErrorOperation error; errorMessage, errorDetail and available error metadata
beforeToolCallBefore tool execution; shared tool context and local mock() callback
afterToolCallAfter tool execution; structured result and mocked
onToolCallErrorTool execution throws; shared tool context and error
beforeHumanInterventionBefore approval; pendingTools, operationId, stepIndex
afterHumanInterventionApproval decision and continuation; action, optional toolCallId and rejectionReason
onStopByHumanInterventionHuman rejection stops the run; optional toolCallId and rejectionReason
beforeCompactBefore compression; messageCount, tokenCount, stepIndex
afterCompactAfter compression; groupId, messagesBefore, messagesAfter, summary
onCompactErrorCompression error; error, tokenCount, stepIndex
beforeCallAgentBefore sub-agent creation; agentId, instruction
afterCallAgentCreation/start returns; agentId, subOperationId, success, optional threadId
onCallAgentErrorSub-agent call fails; agentId, error

CallAgent events dispatch on the parent operation identified by parentOperationId. An isolated child supplies threadId; shared group members use the shared conversation. Child completion is reported by the child's onComplete event.

Tool Context

Use buildToolCallHookContext() for the three tool events.

  • Required fields: native toolCallId, assistantMessageId, identifier, apiName, effective args, callIndex, stepIndex, operationId and executor.
  • Optional associations come from the run context: agent, topic, session, thread, group, task, workspace, document, source/tool message and parent operation IDs.
  • userId uses runtime.userId ?? origin.userId.
  • toolSource identifies the tool's origin; executor identifies the server/client dispatch route. executionTarget and activeDeviceId describe the run's execution plan and device selection.
  • afterToolCall.result carries content, success, optional executionTime and error/state data such as state.type: 'blocked'. mocked marks results supplied by a local hook.

Sandbox and MCP tools use the same event path. Filter with identifier, apiName or toolSource.

Local Mocking

dispatchBeforeToolCall() exposes event.mock(result). The first accepted mock wins and short-circuits the remaining mock handlers. The method returns { isMocked: true, result } for a mock, or null to continue execution.

event.mock({ content: '{"items":[]}', success: true });

Tool observation payloads use BeforeToolCallObservationEvent; the callback belongs to the in-memory handler event. dispatchBeforeToolCall() does not deliver webhooks.

Webhook Payloads

createWebhookPayloadBuilder() selects eventFields, adds hookId/hookType, then merges webhook.body. The body determines the final value of overlapping fields. Tool-result notification payloads use redactResultForEvents() to trim raw skill Work data; controls receive the full result. Work registration retains the full in-process result. finalState is available to local handlers; serialized payloads carry the event's data fields.

When email is selected, the builder resolves userEmail from the final effective userId, replacing supplied email values. Email-only projections use the event ID. An explicit invalid ID, missing email or lookup error leaves email omitted.

Email queries share a per-dispatcher cache of up to 1,000 users for five minutes. Query timeouts belong to the database layer. The builder's optional fourth { signal } argument lets a waiter cancel independently; cancellation returns undefined so the caller can stop delivery. Fetch and QStash use the enriched payload.

Delivery Errors

Dispatch awaits each handler or webhook. Ordinary errors are logged and dispatch continues. Webhooks with fallback: 'none' accumulate a CriticalHookDeliveryError, which is thrown after sibling hooks have run. QStash delivery defaults to a fetch fallback; use fallback: 'none' for QStash-signed endpoints.

Reviews

No reviews yet. Tell others what you thought.

Comments

No login needed. Share how it worked for you, ask a question, or note where it did not run.

No comments yet. Be the first to write one.

Comments are published right away. Do not include personal information such as email addresses or phone numbers. Up to 2 URLs.

0 / 1,000 characters

This form is protected by reCAPTCHA, and the Google Privacy Policy and Terms of Service apply.

More skills from this repository

acceptance

FreeSummary

Use this skill to verify a delivery by exercising the real product through a suitable surface such as a CLI, web app, desktop app, or iOS Simulator. It helps create or find a verification plan, capture evidence, and publish an acceptance report.

  • Verifying a completed product change
  • Collecting screenshots or other evidence
  • Creating a verification plan and report
lobehub/lobehub83.5KUpdated Oct 26, 2026

Use this skill to add an AI model provider to LobeHub across runtime integration, model cards, settings, branding, documentation, and acceptance checks. It also covers provider documentation updates and distinguishes new providers from models added to an existing provider.

  • Integrating a new AI model provider into LobeHub
  • Updating setup or usage documentation for a provider
  • Checking whether a model needs a new provider integration
lobehub/lobehub83.5KUpdated Oct 26, 2026

add-setting-env

FreeSummary

Use this skill to add a server-side environment variable that sets a default for a user setting. It walks through validation, server configuration and store merging, plus updating the example environment file and self-hosting documentation.

  • Adding an environment variable for a setting default
  • Wiring a new setting domain into server config
  • Documenting a new variable for self-hosting
lobehub/lobehub83.5KUpdated Oct 26, 2026

agent-signal

FreeSummary

Build event-driven background workflows for agents by defining sources, interpreting them as signals, and running actions through policies. It also guides asynchronous handoffs, deduplication, scoped execution, completion receipts, and observability.

  • Adding a new event source to an agent workflow
  • Routing interpreted signals to concrete background actions
  • Enqueuing work for asynchronous processing
lobehub/lobehub83.5KUpdated Oct 26, 2026

Use this skill to run end-to-end acceptance checks for bot channels in real desktop chat apps. It drives apps such as Discord, Slack, Telegram, WeChat, Lark, and QQ to send a message, wait for a response, and capture the result.

  • Checking a bot response in a real Discord channel
  • Testing chat behavior through Slack or Telegram
  • Capturing a bot interaction in a native macOS app
lobehub/lobehub83.5KUpdated Oct 26, 2026

agent-tracing

FreeSummary

Inspect recorded agent runs by operation ID to see failed tool calls, their arguments and results, available tools, execution context, and LLM inputs. The CLI also supports context maps and replaying recorded calls with different models for comparison.

  • Finding why an agent tool call failed
  • Checking which tools and context an operation received
  • Viewing the messages sent to an LLM
lobehub/lobehub83.5KUpdated Oct 26, 2026

See all skills by lobehub

Report a problem with this skill