> ## Documentation Index
> Fetch the complete documentation index at: https://docs.kontext.security/llms.txt
> Use this file to discover all available pages before exploring further.

# Detection

> How endpoint detectors flag tool calls and the hosted AI assessment reviews them.

export const RiskPipeline = () => <div className="k-fig-wrap">
    <svg className="k-fig" viewBox="0 0 640 420" width="100%" style={{
  maxWidth: 640
}} role="img" aria-label="The endpoint decides a tool call before Kestrel, Merlin and the optional additional shell check make advisory predictions. Any risky prediction flags the call for review. A hosted AI assessment reviews the evidence, including the policy decision and outcome. The assessment writes the Risk page finding. A risk-present finding can also trigger a Slack alert.">
      <defs>
        <marker id="risk-arrow" viewBox="0 0 10 10" refX="8" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
          <path className="arrowhead" d="M 1 1 L 8 5 L 1 9" />
        </marker>
      </defs>
      <rect className="region" x="8" y="8" width="300" height="404" rx="10" />
      <text className="t-label" x="24" y="32" textAnchor="start">On the endpoint</text>
      <rect className="box-soft" x="24" y="52" width="120" height="44" rx="6" />
      <text className="t" x="84.0" y="79.0" textAnchor="middle">Tool call</text>
      <rect className="box-accent" x="172" y="52" width="120" height="44" rx="6" />
      <text className="t-on-accent" x="232.0" y="79.0" textAnchor="middle">Policy decision</text>
      <path className="line" d="M 144 74 L 168 74" markerEnd="url(#risk-arrow)" />
      <path className="line" d="M 232 96 L 232 136" markerEnd="url(#risk-arrow)" />
      <text className="t-label" x="24" y="128" textAnchor="start">Advisory predictions</text>
      <rect className="box" x="24" y="140" width="268" height="44" rx="6" />
      <text className="t" x="158.0" y="158.0" textAnchor="middle">Kestrel</text>
      <text className="t-small t-muted" x="158.0" y="176.0" textAnchor="middle">checks shell commands</text>
      <rect className="box" x="24" y="196" width="268" height="44" rx="6" />
      <text className="t" x="158.0" y="214.0" textAnchor="middle">Merlin</text>
      <text className="t-small t-muted" x="158.0" y="232.0" textAnchor="middle">does the call fit the request?</text>
      <rect className="region" x="24" y="252" width="268" height="44" rx="6" />
      <text className="t" x="158.0" y="270.0" textAnchor="middle">Additional shell check</text>
      <text className="t-small t-muted" x="158.0" y="288.0" textAnchor="middle">optional second check of shell commands</text>
      <text className="t-small t-muted" x="24" y="330" textAnchor="start">Kontext records predictions beside the</text>
      <text className="t-small t-muted" x="24" y="346" textAnchor="start">decision. They do not change it.</text>
      <rect className="region-solid" x="340" y="8" width="292" height="404" rx="10" />
      <text className="t-label" x="356" y="32" textAnchor="start">Workspace</text>
      <rect className="box-soft" x="356" y="52" width="260" height="44" rx="6" />
      <text className="t" x="486.0" y="79.0" textAnchor="middle">Flagged for review</text>
      <path className="line" d="M 486 96 L 486 108" markerEnd="url(#risk-arrow)" />
      <rect className="box" x="356" y="112" width="260" height="62" rx="6" />
      <text className="t" x="486.0" y="131.0" textAnchor="middle">Evidence bundle</text>
      <text className="t-small t-muted" x="486.0" y="148.0" textAnchor="middle">request, command, tool history,</text>
      <text className="t-small t-muted" x="486.0" y="165.0" textAnchor="middle">policy decision and outcome</text>
      <path className="line" d="M 486 174 L 486 192" markerEnd="url(#risk-arrow)" />
      <text className="t-muted" x="494" y="187" textAnchor="start" style={{
  fontSize: 11
}}>queued</text>
      <rect className="box-accent" x="356" y="196" width="260" height="44" rx="6" />
      <text className="t-on-accent" x="486.0" y="223.0" textAnchor="middle">AI assessment</text>
      <path className="line" d="M 486 240 L 486 258" markerEnd="url(#risk-arrow)" />
      <rect className="box-ok" x="356" y="262" width="260" height="66" rx="6" />
      <text className="t" x="486.0" y="283.0" textAnchor="middle">Risk page finding</text>
      <text className="t-small t-muted" x="486.0" y="300.0" textAnchor="middle">risk present or likely benign,</text>
      <text className="t-small t-muted" x="486.0" y="317.0" textAnchor="middle">severity, categories, explanation</text>
      <path className="line-dashed" d="M 486 328 L 486 352" markerEnd="url(#risk-arrow)" />
      <text className="t-muted" x="494" y="345" textAnchor="start" style={{
  fontSize: 11
}}>if risk present</text>
      <rect className="box" x="356" y="356" width="260" height="46" rx="6" />
      <text className="t" x="486.0" y="375.5" textAnchor="middle">Slack alert</text>
      <text className="t-small t-muted" x="486.0" y="392.5" textAnchor="middle">when risk alerts are on</text>
      <path className="line-dashed" d="M 292 162 H 318" />
      <path className="line-dashed" d="M 292 218 H 318" />
      <path className="line-dashed" d="M 292 274 H 318 V 74 H 352" markerEnd="url(#risk-arrow)" />
    </svg>
  </div>;

Endpoint detectors score tool calls after the policy decision and flag calls for review. A hosted AI assessment in your workspace reviews each flagged call. The Risk page shows its severity, category, and explanation.

Detectors and the assessment read the final decision and record advisory results beside it. Only a [policy](/policies/overview) can block a call.

<Frame caption="Detectors on the endpoint flag candidates. The hosted AI assessment reviews flagged calls afterwards, with more context.">
  <RiskPipeline />
</Frame>

## Detectors on the endpoint

| Detector               | What it checks                                                                    | Default               |
| ---------------------- | --------------------------------------------------------------------------------- | --------------------- |
| Kestrel                | Shell commands, one at a time                                                     | On for every endpoint |
| Merlin                 | Whether a short tool call follows from the user's request and recent tool history | On for every endpoint |
| Additional shell check | Shell commands, with a small local language model                                 | Off                   |

### Kestrel

Kestrel is a shell command classifier built into the Kontext CLI. It scores every shell command on the endpoint, including calls a policy denied. Kestrel needs no setup or download.

Kestrel reads the command without the task context. Because agents run `rm -rf` often, Kestrel can miss destructive forms such as `rm -rf /` or `dd` onto a disk.

### Merlin

Merlin scores short tool calls that are not file operations, using the user's request and the recent tool history. It looks for calls that do not fit the user's request. Instructions hidden in a page or file can cause this behavior.

Merlin skips file tools such as `Read`, `Write`, `Edit`, and `apply_patch`, and it skips calls whose request or arguments are too long for the model. A skipped call has no Merlin result. When Merlin flags a call, it keeps a short redacted copy of the request and tool history so the AI assessment can see the same context.

### Additional shell check

The additional shell check uses Qwen3-0.6B, a small local language model served by `llama-server`. It flags destructive shell commands that Kestrel can miss. It also flags more ordinary commands.

To run the additional shell check:

1. On a self-serve endpoint, install the runtime with `brew install llama.cpp`. Then run `kontext setup --with-local-llm` and provide your API key when prompted. Setup downloads about 680 MB of model weights. MDM-managed endpoints do not include `llama-server` and run without this check.
2. Open the endpoint from [Home](/home/overview) and turn on **Guardrail LLM** in its drawer. Turn it off there to stop the check without making changes on the endpoint.

## When detectors flag a call

Kontext flags a call for review when any of these is true:

* Kestrel rates the command risky.
* Merlin rates the call unsafe.
* The additional shell check rates the command risky.
* The call's risk score, shown in **Detection details**, is at or near its threshold.

Flagged calls make up the **flagged for review** count on the Risk page. A call flagged by more than one detector counts once.

## The AI assessment

Kontext queues each flagged call for an AI assessment in your workspace. The assessment uses a hosted OpenAI model and receives:

* the tool name and the credential-redacted command and arguments
* the user's request and recent tool results, when Merlin kept them
* the policy decision, its reason, and the mode it ran in
* the reported outcome and any error
* up to 20 earlier tool calls from the same session

Review this list against your data policies before you deploy Kontext. Your [data collection](/deploy/data-collection) settings control input and output payload capture. With **Omitted**, endpoints send size-only input and output payload records. They still send redacted command and request summaries. For calls Merlin flags, they also send redacted user-request and tool-history context.

The assessment treats the detector's flag as a weak hint and evaluates the evidence independently. It uses the user request and tool results as context without following instructions in them. It returns:

| Field             | Values                                                                                        |
| ----------------- | --------------------------------------------------------------------------------------------- |
| Assessment        | Risk present, or likely benign. The dashboard shows likely benign as **No risk found**.       |
| Severity          | **Low**, **Medium**, **High**, or **Critical**. Empty when likely benign.                     |
| Categories        | One primary and up to two secondary [categories](/risk/categories). Empty when likely benign. |
| Evidence strength | Weak, moderate, or strong. This rates how directly the evidence supports the conclusion.      |
| Explanation       | One or two sentences for a security reviewer                                                  |

When important evidence is missing, the assessment reports risk present with weak evidence and says what is missing. If Merlin's evidence arrives later, Kontext assesses the call again. Risk shows the newer result.

## Where results appear

Open a tool call from Risk and select the **Risk** tab to see **AI assessment** and **Detection details**. From Explore, the tab shows **Detection details** only. These details show what Kestrel, Merlin and the **Additional shell check** returned, or why a detector has no result for the call.

Kontext pauses AI assessments when a workspace on the Free plan reaches its monthly transaction limit. Detectors keep flagging calls. Calls flagged during the pause receive no assessment and stay **Not rated yet**.
