Skip to main content
PIIRedactor is an Intercept middleware for Laravel AI SDK agents. It detects and handles sensitive personal or secret-like data before an agent prompt reaches the AI provider. It can redact, mask, log, block, or delegate handling to a custom callback.
This middleware is deterministic and regex-based. It is designed to catch common structured sensitive data. It is not intended to guarantee complete detection of every possible personal identifier.

Installation

Install the package with Composer:
If you prefer to install the main intercept package to get the full middleware collection, run:

Basic usage

Return the PIIRedactor middleware on an agent’s middleware method.
To add middleware to an agent, implement the HasMiddleware interface and define a middleware method that returns an array of middleware classes.
By default, the middleware will:
  • use the redact action
  • detect emails, phone numbers, credit cards, IP addresses, MAC addresses, URLs, API keys, and bearer tokens
  • block credit cards, API keys, and bearer tokens
  • redact lower-risk values such as emails, phone numbers, MAC addresses, URLs, and IP addresses
  • not log raw detected values
  • not log prompt previews

Supported actions

The recommended default action is redact. Some entities can still be blocked even when the action is redact. By default, credit cards, API keys, and bearer tokens are blocked.

Supported entities

Names, addresses, passports, national insurance numbers, and medical identifiers are not included in the current version. These values are harder to detect safely with regex alone and can create false positives.

Configuration

No configuration is required. The middleware works out of the box using safe internal defaults. The defaults may be overridden via the constructor or via the shared config/intercept.php file, published with:
Explore the configuration guide for more details on how to configure this middleware globally.

Configuration priority

Configuration is resolved in this order: That means constructor values always win over published config values. For example, if your config says:
You can still override it for a specific agent:
In this case, the middleware will use log for that agent, even though the global config says redact.

Partial configuration

You do not need to define every option in config/intercept.php. This is valid:
All missing options fall back to the middleware’s internal defaults.

Usage examples

Redacting PII

Use redact when you want to remove detected values while preserving the rest of the user request.
Example input:

Email victor@example.com about this ticket.

Example output:

Email [EMAIL_1] about this ticket.

Masking PII

Use mask when the model needs a rough hint of the value format but should not see the full value.
Example input:

Email victor@example.com about this ticket.

Example output:

Email v*****@example.com about this ticket.

Logging detections

Use log when you want to observe real traffic before redacting or blocking.
The prompt continues unchanged, but detections are logged safely. Prompt previews are disabled by default because prompts may contain sensitive user data. Enable previews only if you are comfortable storing a short prompt sample in your logs.
Avoid enabling prompt previews in production unless you have reviewed your logging and retention policies.

Blocking PII

Use block when the prompt should stop as soon as supported PII is detected.
If PII is detected, the middleware throws a PIIRedactorException.
You may also catch the parent exception class InterceptException throwable by all Intercept middleware.

Blocked entities

Some entities are treated as high-risk and are blocked by default, even when the action is redact. Default blocked entities:
This means a prompt containing a supported API key or bearer token will be blocked by default. You can override blocked entities:
Only do this if you are sure the values can safely be sent to the provider after redaction or masking.

Detecting selected entities

You can limit detection to specific entity types.
This ignores other supported entity types.

Allowing specific email addresses

Use allowedEmails when known safe email addresses should not be redacted or blocked.
Example input:

Email support@example.com about this ticket.

This passes through unchanged.

Allowing email domains

Use allowedDomains when all email addresses from a trusted domain should be ignored.

Custom replacement format

The default redaction format is:
You can customise it:
Supported placeholders:

Custom mask character

The default mask character is *.

Custom callback handling

Use a callback when you want full control over the response. The callback receives:
Example:
When a callback is provided, it takes priority over the configured action.

Tool approval resumes

When an agent pauses for tool approval, the run is resumed by passing Decisions back to the agent instead of a new prompt. A resumed prompt carries no prompt text. The only new content is what a human supplied while resolving the pending tool calls:
  • edited tool arguments, from Decision::edit()
  • rejection results, from Decision::reject()
Both reach the AI provider, so this middleware scans them using the same detectors, Luhn validation, and email allowlists it applies to a prompt.
The email address above is detected exactly as it would be inside a prompt.

Actions on a resumed run

A paused turn must replay verbatim against the provider that recorded it, so the Laravel AI SDK makes resumed prompts immutable. redact and mask have nowhere to write their output, so they degrade to logging. Blocked entities still stop the run regardless of the configured action. A credit card, API key, or bearer token in an edited tool argument is refused exactly as it would be in a prompt.

Reading the logs

Detections found in approval decisions are logged under a distinct message, PII detected in tool approval decisions., with a source of approval_decisions:
The degraded_from key appears only when the run continued despite a configured action that could not be applied. A blocked run never reports it. The field value is a dot path into the edited arguments, so nested values are reported precisely, for example arguments.filters.contact.email.

Disabling approval decision scanning

Scanning is enabled by default. To disable it globally:
Or for a single agent:
Detection offsets on this path are relative to the decision value the detection was found in, not to the prompt. Offsets exist to drive redaction, which cannot be applied to a resumed run, so custom callbacks should use the detection type and value rather than start and length.

Production rollout

A practical rollout path:
Recommended defaults:

Security notes

Use this middleware as one layer in a broader AI safety and privacy strategy. Recommended additional controls:
  • avoid sending secrets to AI providers
  • minimise prompt context
  • keep system instructions separate from user input
  • limit tool permissions
  • validate tool arguments
  • redact sensitive data before tool calls where appropriate
  • log detections safely
  • avoid prompt previews in production logs
  • review false positives before blocking aggressively
  • use provider-level safety and privacy controls where available

Detection limitations

This middleware is regex-based and intentionally focused on structured values. It can miss:
  • names
  • free-form addresses
  • uncommon phone formats
  • unusual API key formats
  • identifiers without clear patterns
  • sensitive context that does not look like structured PII
It can also produce false positives for values that look like PII but are not actually sensitive. For high-risk workflows, combine this middleware with application-level validation, data minimisation, access controls, and human review.

When to use each action

Use redact when you want to remove detected values while preserving the rest of the user request. Use mask when the model needs a rough hint of the value format but should not see the full value. Use log when you are tuning detection, observing real traffic, or rolling out gradually. Use block when sensitive data should never reach the provider. Use blockEntities when only specific high-risk entities should stop the prompt, even if the general action is redact, mask, or log. Use a callback when your application needs custom behaviour such as audit logging, custom exceptions, approval flows, or user-facing fallback responses.