How DeskcommCRM Enforces AI Guardrails to Prevent Hallucinated Prices and Unsafe Tool Calls
DeskcommCRM implements a deterministic dual-layer guardrail system inside its agent-engine package that validates monetary promises against organizational tables and blocks unauthorized tool calls before execution, forcing the LLM to regenerate compliant outputs.
DeskcommCRM embeds a rigorous validation layer within the agent-engine package to stop large language models from generating fabricated pricing or executing harmful operations. The system combines regex-based promise extraction in lib/agent-engine/guardrails/promise/engine.ts with strict tool-call interception via lib/agent-engine/agent/tool-breaker.ts, ensuring every AI response adheres to business rules and security policies. These guardrails operate deterministically between the LLM and external systems, creating an auditable checkpoint for all AI-generated content and actions.
How the Promise Guard Validates Monetary Outputs
The Promise Guard acts as a deterministic filter that scans every LLM-generated message for monetary expressions—prices, discount percentages, and installment counts—before the text reaches the end user. Located in lib/agent-engine/guardrails/promise/engine.ts, this guard extracts numerical commitments using locale-aware regular expressions and validates them against organization-specific thresholds stored in Supabase.
Regex-Based Extraction of Prices and Discounts
The extractPromises() function employs three targeted regular expressions to identify Brazilian Real (R$) monetary values and related terms within the candidate message:
const RE_PRICE_RS = /R\$\s*(\d{1,3}(?:\.\d{3})+(?:,\d{2})?|\d+(?:,\d{2})?)/gi;
const RE_DISCOUNT_PREFIX = /desconto\s+(?:de\s+)?(\d{1,3})\s*(?:%|por\s*cento)/gi;
const RE_INSTALLMENTS_PREFIX = /\b(?:parcel\w*|dividid\w*\s+em|em(?:\s+at[ée])?)\s+(\d{1,3})\s*(?:x\b|vezes\b)/gi;
The engine normalizes extracted strings into cent integers via moneyToCents() and compiles them into a DetectedPromise[] array. Each entry carries a kind discriminator ('price', 'discount', or 'installments') and a numeric value, creating a structured representation of every financial commitment implied in the text.
Table-Driven Compliance Checks
The decidePromise() function loads the organization’s PromiseTable—containing minPriceCents, maxDiscountPercent, and maxInstallments—and performs a short-circuiting validation loop. When a value violates configured limits, the guard immediately returns a structured rejection:
if (p.kind === 'price' && table.minPriceCents !== undefined && p.value < table.minPriceCents) {
return { allow: false, code: 'promise_out_of_table', reason: 'o preço está fora da tabela do playbook...' };
}
This rejection prevents hallucinated discounts (e.g., "20% off" when the maximum is 10%) or impossible installment plans from reaching customers.
How the Tool-Breaker Guard Sanitizes Tool Calls
The Tool-Breaker Guard intercepts every outbound toolCall request emitted by the LLM, ensuring that only vetted integrations execute with safe arguments. Implemented in lib/agent-engine/agent/tool-breaker.ts, this guard functions as a mandatory middleware between the agent and external APIs, database RPCs, or webhook dispatchers like WAHA.
Whitelist Enforcement and Argument Validation
When the LLM requests a tool invocation, the breaker performs two sequential checks. First, it validates the tool name against a static allow-list defined in lib/agent-engine/guardrails/tool-allowlist.ts (e.g., ["sendMessage", "createLead", "fetchCatalog"]). Second, it inspects the JSON payload for disallowed patterns, including raw secrets, file-system path traversals, or malformed URLs.
If either check fails, the guard returns a ToolCallError object containing a deterministic code and human-readable reason. The LLM receives this error as a system instruction to retry without the prohibited operation, effectively neutralizing attempts at prompt injection or unauthorized data exfiltration.
Feedback Loop Implementation
Both guardrails integrate with DeskcommCRM’s API layer through standardized response wrappers defined in lib/api/wrappers.ts. The fail() helper serializes guardrail rejections into a consistent payload that the LLM consumes as a correction signal.
Structured Rejection Responses
When the Promise Guard detects a violation, it returns a PromiseDecision object that routes through the API wrapper:
import { decidePromise } from '@/lib/agent-engine/guardrails/promise/engine';
import { getPromiseTable } from '@/lib/agent-engine/db/repository';
async function handleAgentMessage(msg: string, orgId: string) {
const table = await getPromiseTable(orgId);
const decision = decidePromise({ candidate: msg, table });
if (!decision.allow) {
return fail(decision.code, decision.reason, 422);
}
// Proceed with safe message
}
Similarly, the Tool-Breaker uses breakToolCall() to gate execution:
import { breakToolCall } from '@/lib/agent-engine/agent/tool-breaker';
async function processToolCall(toolName: string, args: any) {
const verdict = breakToolCall(toolName, args);
if (!verdict.allow) {
return fail(verdict.code, verdict.reason, 403);
}
return await actualToolDispatcher(toolName, args);
}
This architecture ensures that violations never reach downstream systems; instead, the LLM receives immediate, actionable feedback to correct its output.
Summary
- Promise Guard in
lib/agent-engine/guardrails/promise/engine.tsuses regex extraction (extractPromises()) and table lookups (decidePromise()) to block hallucinated prices and unauthorized discounts. - Tool-Breaker in
lib/agent-engine/agent/tool-breaker.tsenforces an allow-list and argument sanitization to prevent unsafe external calls. - Both systems return structured rejections via
lib/api/wrappers.ts, creating a feedback loop that forces the LLM to retry with compliant values. - The guardrails reference organization-specific configurations loaded from Supabase via
lib/agent-engine/db/repository.ts, enabling per-tenant policy enforcement.
Frequently Asked Questions
How does the promise guard handle different currency formats?
The current implementation focuses on Brazilian Real (R$) using locale-specific regex patterns that recognize dot-thousand separators and comma-decimal notation (e.g., R$ 1.234,56). The moneyToCents() helper normalizes these into integer cent values for consistent comparison against the PromiseTable thresholds, though the architecture supports extension for additional currency formats.
Can organizations customize the validation thresholds?
Yes. The getPromiseTable() function in lib/agent-engine/db/repository.ts loads organization-specific limits from Supabase, including minPriceCents, maxDiscountPercent, and maxInstallments. Each tenant maintains independent thresholds, allowing different sales playbooks to enforce distinct pricing floors or promotional caps without code changes.
What happens when the LLM attempts an unauthorized tool call?
The Tool-Breaker intercepts the request before execution and returns a ToolCallError with an HTTP 403 status via the fail() wrapper. The LLM receives this error as a system message instructing it to retry without the prohibited tool, ensuring that only whitelisted operations (such as sendMessage or createLead) ever reach external APIs.
Is the guardrail layer deterministic?
Absolutely. Unlike probabilistic LLM outputs, the guardrails in promise/engine.ts and tool-breaker.ts execute deterministic regex matches and strict equality checks against static allow-lists. This determinism guarantees that identical inputs always produce identical validation results, creating an auditable security boundary between the AI and business-critical operations.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →