Background
We implemented a POC for CaMeL style prompt injection defense (DeepMind, 2024) as a native TypeScript port for OpenClaw. The full implementation touches too many core systems to land as a single PR: taint tracking, policy enforcement, approval routing, session isolation, and provenance propagation all need to coordinate across the tool call pipeline, outbound message routing, exec approvals, session binding, and persistence. We wrote up the full analysis in a separate article.
Along the way, we identified several hook enrichments that have standalone value for any security policy plugin, independent of whether CaMeL ever lands. This is the first of three focused proposals. Each one is independently useful for rate limiting, DLP, compliance, and audit logging. Together, they would also provide enough hook context for CaMeL style enforcement to eventually live in a plugin rather than requiring core changes.
Problem
The before_tool_call hook lets plugins block tool calls with block/blockReason, and the fix in #19381 ensures sessionKey and agentId are now available. But the event payload still only includes toolName, params, and session identifiers.
For plugins that need to make security policy decisions, this is not enough context. A plugin implementing rate limiting by action type, compliance blocking of outbound operations, or audit logging with provenance needs to know what kind of action is happening, not just which tool is being called. Today that means maintaining a brittle mapping of tool names to action categories that breaks when tools are renamed or added.
This is a gap that several security-focused plugins in the ecosystem have encountered. Richer hook context would let policy plugins reason about action categories using stable framework metadata instead of reimplementing tool classification logic.
Proposal
Add optional fields to PluginHookBeforeToolCallEvent that classify the action and provide input provenance:
// Proposed additions to PluginHookBeforeToolCallEvent
export type PluginHookBeforeToolCallEvent = {
toolName: string;
params: Record<string, unknown>;
runId?: string;
toolCallId?: string;
sessionKey?: string;
agentId?: string;
// NEW: action classification
actionCategory?: 'outbound_communication' | 'persistence_write' | 'external_retrieval'
| 'privileged_execution' | 'autonomous_scheduling' | 'internal';
// NEW: input provenance (from existing input-provenance.ts)
inputProvenance?: {
kind: 'external_user' | 'inter_session' | 'internal_system';
sourceChannel?: string;
sourceSessionKey?: string;
};
};
Use cases
- Rate limiting by category: throttle outbound messages separately from file reads
- Compliance blocking: deny all outbound_communication actions in certain session types
- Audit logging: log persistence writes with full provenance for regulatory compliance
- Cost controls: track and limit external_retrieval calls that consume API credits
- Sandbox enforcement: restrict privileged_execution in sub-agent sessions
What exists today
The framework already has the data needed to populate these fields. input-provenance.ts tracks external_user, inter_session, and internal_system provenance on user messages. outbound-policy.ts classifies cross-context targets. Session binding tracks agent identity and chat type. The gap is surfacing this existing context to the plugin hook.
This would also complement the direction in #33958 (immutable operator defaults policy). An immutable policy layer needs enforcement hooks with enough context to evaluate policy rules. Action classification in before_tool_call provides that enforcement surface.
Backward compatibility
All new fields are optional. Existing plugins that destructure only toolName and params continue to work unchanged. No performance cost when no plugin subscribes to the enriched fields.
Precedent
#19381 fixed the missing sessionKey and agentId — this extends the same trajectory: giving plugins enough context to make informed decisions. #19231 proved hook enforcement matters (return values were previously silently discarded). Now that enforcement works, richer context makes the enforcement more useful.
Questions for maintainers
- Is actionCategory the right abstraction, or would capability flags (isOutbound, isPersistent, readsExternal) be more composable?
- Should inputProvenance be a direct copy of the existing InputProvenance type, or a simplified surface?
- Any concerns about the provenance fields leaking information that plugins should not have access to?
Context: See Building a CaMeL Prototype for OpenClaw for full motivation and architectural analysis.
Background
We implemented a POC for CaMeL style prompt injection defense (DeepMind, 2024) as a native TypeScript port for OpenClaw. The full implementation touches too many core systems to land as a single PR: taint tracking, policy enforcement, approval routing, session isolation, and provenance propagation all need to coordinate across the tool call pipeline, outbound message routing, exec approvals, session binding, and persistence. We wrote up the full analysis in a separate article.
Along the way, we identified several hook enrichments that have standalone value for any security policy plugin, independent of whether CaMeL ever lands. This is the first of three focused proposals. Each one is independently useful for rate limiting, DLP, compliance, and audit logging. Together, they would also provide enough hook context for CaMeL style enforcement to eventually live in a plugin rather than requiring core changes.
Problem
The before_tool_call hook lets plugins block tool calls with block/blockReason, and the fix in #19381 ensures sessionKey and agentId are now available. But the event payload still only includes toolName, params, and session identifiers.
For plugins that need to make security policy decisions, this is not enough context. A plugin implementing rate limiting by action type, compliance blocking of outbound operations, or audit logging with provenance needs to know what kind of action is happening, not just which tool is being called. Today that means maintaining a brittle mapping of tool names to action categories that breaks when tools are renamed or added.
This is a gap that several security-focused plugins in the ecosystem have encountered. Richer hook context would let policy plugins reason about action categories using stable framework metadata instead of reimplementing tool classification logic.
Proposal
Add optional fields to PluginHookBeforeToolCallEvent that classify the action and provide input provenance:
Use cases
What exists today
The framework already has the data needed to populate these fields. input-provenance.ts tracks external_user, inter_session, and internal_system provenance on user messages. outbound-policy.ts classifies cross-context targets. Session binding tracks agent identity and chat type. The gap is surfacing this existing context to the plugin hook.
This would also complement the direction in #33958 (immutable operator defaults policy). An immutable policy layer needs enforcement hooks with enough context to evaluate policy rules. Action classification in before_tool_call provides that enforcement surface.
Backward compatibility
All new fields are optional. Existing plugins that destructure only toolName and params continue to work unchanged. No performance cost when no plugin subscribes to the enriched fields.
Precedent
#19381 fixed the missing sessionKey and agentId — this extends the same trajectory: giving plugins enough context to make informed decisions. #19231 proved hook enforcement matters (return values were previously silently discarded). Now that enforcement works, richer context makes the enforcement more useful.
Questions for maintainers
Context: See Building a CaMeL Prototype for OpenClaw for full motivation and architectural analysis.