Zero-Shot Classification
Transformers
Safetensors
qwen3_5
feature-extraction
security
code-review
ai-agents
decision-model
d3
system-one
pre-release
custom_code
Instructions to use stonefish-labs/d3-mini-agentcode with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use stonefish-labs/d3-mini-agentcode with Transformers:
# Use a pipeline as a high-level helper from transformers import pipeline pipe = pipeline("zero-shot-classification", model="stonefish-labs/d3-mini-agentcode", trust_remote_code=True)# pip install -U transformers accelerate # Load model directly from transformers import AutoProcessor, AutoModel processor = AutoProcessor.from_pretrained("stonefish-labs/d3-mini-agentcode", trust_remote_code=True) model = AutoModel.from_pretrained("stonefish-labs/d3-mini-agentcode", trust_remote_code=True, device_map="auto") - Notebooks
- Google Colab
- Kaggle
Card fact-check: usage snippet, long inputs, 4090 follow-up, limits, privacy, sources
Browse files- MODEL_MANIFEST.json +11 -11
- README.md +129 -86
- REBUILD.md +8 -5
- RESULTS.md +2 -1
- SHA256SUMS +5 -5
- SOURCES.md +6 -3
- examples/usage.py +1 -1
MODEL_MANIFEST.json
CHANGED
|
@@ -40,11 +40,11 @@
|
|
| 40 |
"LICENSE": "c71d239df91726fc519c6eb72d318ec65820627232b2f796219e87dcf35d0ab4",
|
| 41 |
"MERGE-REPORT.json": "a42ca8b488e0b98b1a690937784e8da0fe417fd2218e7595129927221f4d9244",
|
| 42 |
"NOTICE": "e74014fab972cffe76391a608e8a5cf54baeaf7d16cd8a77545edc67ffd25225",
|
| 43 |
-
"README.md": "
|
| 44 |
-
"REBUILD.md": "
|
| 45 |
-
"RESULTS.md": "
|
| 46 |
-
"SHA256SUMS": "
|
| 47 |
-
"SOURCES.md": "
|
| 48 |
"adapter/adapter_config.json": "ff2a5427cbcf0e7a0a61940920d06f0e1fc116565258c3eca2501b0d10933250",
|
| 49 |
"adapter/adapter_model.safetensors": "3bd401009801b39f29148427e884a8230a85ddb3c20a54ea834372889998c895",
|
| 50 |
"adapter/merge.py": "bf0999886d5b6ac6764c7a9d688c890579062a04254e0054bb230681fb464a7e",
|
|
@@ -60,7 +60,7 @@
|
|
| 60 |
"d3_runtime.py": "adf7f7aa085674b5682e492d8b2e3cc4f11c67b0d153515c742e907e9b550917",
|
| 61 |
"d3_server.py": "766a20a69101e36ab0535a1ec683379db422380a83091f0eccd7e82edb6a02de",
|
| 62 |
"decision_config.json": "588bc0ac7644479b9adde0498d80b6fb7c576388992564ac67599c05d29add07",
|
| 63 |
-
"examples/usage.py": "
|
| 64 |
"extractor/README.md": "cd1679b82e61c15f247c277007c0d416d27b4834f455f3fee7f9afb863743d08",
|
| 65 |
"extractor/behaviour/__init__.py": "c0db7c9cb57a23289245209c30c335c3e44614454d2796a239864d70165356d6",
|
| 66 |
"extractor/behaviour/__main__.py": "2c78b70c752cc11bfbf759e9e8fe0a4b5f43da43cdecb4489a9b403ac4d20311",
|
|
@@ -131,11 +131,11 @@
|
|
| 131 |
"LICENSE": 11357,
|
| 132 |
"MERGE-REPORT.json": 86,
|
| 133 |
"NOTICE": 279,
|
| 134 |
-
"README.md":
|
| 135 |
-
"REBUILD.md":
|
| 136 |
-
"RESULTS.md":
|
| 137 |
"SHA256SUMS": 8922,
|
| 138 |
-
"SOURCES.md":
|
| 139 |
"adapter/adapter_config.json": 1329,
|
| 140 |
"adapter/adapter_model.safetensors": 121954608,
|
| 141 |
"adapter/merge.py": 10334,
|
|
@@ -151,7 +151,7 @@
|
|
| 151 |
"d3_runtime.py": 95505,
|
| 152 |
"d3_server.py": 17705,
|
| 153 |
"decision_config.json": 5502,
|
| 154 |
-
"examples/usage.py":
|
| 155 |
"extractor/README.md": 3420,
|
| 156 |
"extractor/behaviour/__init__.py": 453,
|
| 157 |
"extractor/behaviour/__main__.py": 2303,
|
|
|
|
| 40 |
"LICENSE": "c71d239df91726fc519c6eb72d318ec65820627232b2f796219e87dcf35d0ab4",
|
| 41 |
"MERGE-REPORT.json": "a42ca8b488e0b98b1a690937784e8da0fe417fd2218e7595129927221f4d9244",
|
| 42 |
"NOTICE": "e74014fab972cffe76391a608e8a5cf54baeaf7d16cd8a77545edc67ffd25225",
|
| 43 |
+
"README.md": "ed61f82f9b000ddf533119f1345345bca2f345514b0b70d802f2bd172c8ce24a",
|
| 44 |
+
"REBUILD.md": "76e575f2d52a83a937d0c4daf818ba302595f577cf54b3f3bd3bbea67a0e4566",
|
| 45 |
+
"RESULTS.md": "cc20ca6e484d84f5c86ea16e82ce0ef3a3978f572c8d8c514ed3a1383b9d1930",
|
| 46 |
+
"SHA256SUMS": "0bce477e859f4735519404d7e0c9231f5762c6ff79f4eff41ba34897af23eae7",
|
| 47 |
+
"SOURCES.md": "bfb09be93fa5d4022645bb5bf3cd4268949ef76eba42c4d462099537a6fb7bb2",
|
| 48 |
"adapter/adapter_config.json": "ff2a5427cbcf0e7a0a61940920d06f0e1fc116565258c3eca2501b0d10933250",
|
| 49 |
"adapter/adapter_model.safetensors": "3bd401009801b39f29148427e884a8230a85ddb3c20a54ea834372889998c895",
|
| 50 |
"adapter/merge.py": "bf0999886d5b6ac6764c7a9d688c890579062a04254e0054bb230681fb464a7e",
|
|
|
|
| 60 |
"d3_runtime.py": "adf7f7aa085674b5682e492d8b2e3cc4f11c67b0d153515c742e907e9b550917",
|
| 61 |
"d3_server.py": "766a20a69101e36ab0535a1ec683379db422380a83091f0eccd7e82edb6a02de",
|
| 62 |
"decision_config.json": "588bc0ac7644479b9adde0498d80b6fb7c576388992564ac67599c05d29add07",
|
| 63 |
+
"examples/usage.py": "37fc6f3322832e8ce31028dba64561af57c189c612709e9f407457d6a7e44c9a",
|
| 64 |
"extractor/README.md": "cd1679b82e61c15f247c277007c0d416d27b4834f455f3fee7f9afb863743d08",
|
| 65 |
"extractor/behaviour/__init__.py": "c0db7c9cb57a23289245209c30c335c3e44614454d2796a239864d70165356d6",
|
| 66 |
"extractor/behaviour/__main__.py": "2c78b70c752cc11bfbf759e9e8fe0a4b5f43da43cdecb4489a9b403ac4d20311",
|
|
|
|
| 131 |
"LICENSE": 11357,
|
| 132 |
"MERGE-REPORT.json": 86,
|
| 133 |
"NOTICE": 279,
|
| 134 |
+
"README.md": 15004,
|
| 135 |
+
"REBUILD.md": 2168,
|
| 136 |
+
"RESULTS.md": 4805,
|
| 137 |
"SHA256SUMS": 8922,
|
| 138 |
+
"SOURCES.md": 3241,
|
| 139 |
"adapter/adapter_config.json": 1329,
|
| 140 |
"adapter/adapter_model.safetensors": 121954608,
|
| 141 |
"adapter/merge.py": 10334,
|
|
|
|
| 151 |
"d3_runtime.py": 95505,
|
| 152 |
"d3_server.py": 17705,
|
| 153 |
"decision_config.json": 5502,
|
| 154 |
+
"examples/usage.py": 2595,
|
| 155 |
"extractor/README.md": 3420,
|
| 156 |
"extractor/behaviour/__init__.py": 453,
|
| 157 |
"extractor/behaviour/__main__.py": 2303,
|
README.md
CHANGED
|
@@ -18,42 +18,50 @@ tags:
|
|
| 18 |
|
| 19 |

|
| 20 |
|
| 21 |
-
**Pre-release.** A revised version is planned
|
| 22 |
-
wording and more languages
|
| 23 |
|
| 24 |
-
d3-mini-agentcode is [d3-mini](https://huggingface.co/vllm-sr/d3-mini) (4B, Apache-2.0) fine-tuned to triage
|
| 25 |
-
written or run by AI coding agents
|
| 26 |
-
answers
|
| 27 |
-
harmful. It's a cheap first
|
|
|
|
| 28 |
|
| 29 |
**Write-up:** [Small Models, Big Haystack](https://batteryshark.github.io/2026/11/05/small-models-big-haystack.html)
|
| 30 |
covers how this model was built and tested, and how it compares with off-the-shelf d3 models and LLMs.
|
| 31 |
|
| 32 |
## Intended use
|
| 33 |
-
- **
|
| 34 |
stronger model.
|
| 35 |
-
- **A second
|
| 36 |
-
- **Not
|
| 37 |
-
- **Out of scope:**
|
| 38 |
-
|
|
|
|
|
|
|
|
|
|
| 39 |
|
| 40 |
## How it was made
|
| 41 |
-
- **Base:** `vllm-sr/d3-mini` at revision `df99bf4ca4418bc6908ee68e9b7479af1f3b8800`
|
| 42 |
- **Recipe:** LoRA (r 16, α 32, dropout 0.05) on the text backbone's attention, linear-attention and MLP projections,
|
| 43 |
plus the trained answer readout. lr 1e-4 (readout 2e-5), 2 epochs, 1,038 optimizer steps, max length 8,192, label
|
| 44 |
-
smoothing 0.05,
|
| 45 |
-
- **
|
| 46 |
-
|
| 47 |
-
- **
|
| 48 |
-
|
|
|
|
|
|
|
| 49 |
|
| 50 |
## Usage
|
| 51 |
```python
|
| 52 |
import json
|
|
|
|
| 53 |
from transformers import AutoModel
|
| 54 |
|
| 55 |
-
|
| 56 |
-
|
|
|
|
| 57 |
|
| 58 |
state = """Task:
|
| 59 |
Add a CLI flag to export the report as CSV.
|
|
@@ -68,79 +76,109 @@ Change:
|
|
| 68 |
diff --git a/report.py b/report.py
|
| 69 |
..."""
|
| 70 |
|
| 71 |
-
|
| 72 |
-
p_mal =
|
| 73 |
-
p_scope =
|
| 74 |
-
flag = p_mal >= 0.9 or p_scope >= 0.9
|
| 75 |
```
|
| 76 |
-
- **Input:** a text state with four sections, in order: `Task:`, `Repository baseline (normal behaviour of this
|
| 77 |
-
repo/org):`, `Behaviour summary (automated, not a verdict):` and `Change:`.
|
| 78 |
-
|
| 79 |
-
- **
|
| 80 |
-
|
| 81 |
-
|
| 82 |
-
|
| 83 |
-
|
| 84 |
-
|
| 85 |
-
|
| 86 |
-
|
| 87 |
-
|
| 88 |
-
|
| 89 |
-
-
|
| 90 |
-
|
| 91 |
-
|
| 92 |
-
|
| 93 |
-
|
| 94 |
-
|
| 95 |
-
|
| 96 |
-
|
| 97 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 98 |
|
| 99 |
## Results
|
| 100 |
-
Each model at its own dev-fitted
|
|
|
|
|
|
|
| 101 |
|
| 102 |
| test set | seed 1 (released) | seed 2 | untuned |
|
| 103 |
|---|---|---|---|
|
| 104 |
-
|
|
| 105 |
-
| Public code mode
|
| 106 |
-
| Held-out agent diffs
|
| 107 |
-
| Behaviour families never seen in training | 26/26 | 22/26 | 14/26 |
|
| 108 |
-
| Compromised library releases shown as diffs (
|
| 109 |
-
| **14 languages and shells** (8 diff languages, 6 code-mode
|
| 110 |
-
| Task asks for the change
|
| 111 |
-
| Task asks for the change
|
|
|
|
| 112 |
| Unrequested account email added to request headers: found / controls flagged | 1/40 / 0/60 | 0/40 / 0/60 | 3/40 / 4/60 |
|
| 113 |
|
| 114 |
-
Per language (seed 1, frozen
|
|
|
|
| 115 |
|
| 116 |
| | C/C++ | C# | Go | Java | JS/TS | PHP | Rust | Swift/ObjC | bash | cmd/batch | zsh/macOS | PowerShell | Python (code mode) | JS/TS (code mode) |
|
| 117 |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
| 118 |
| found | 35/38 | 34/37 | 36/38 | 36/37 | 33/37 | 34/38 | 33/37 | 37/38 | 24/33 | 28/33 | 29/36 | 35/37 | 30/35 | 34/36 |
|
| 119 |
| real flagged | 1/149 | 0/149 | 1/150 | 1/149 | 0/150 | 0/150 | 0/150 | 0/150 | 1/200 | 0/130 | 3/144 | 3/150 | 1/200 | 0/129 |
|
| 120 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 121 |
## Limitations
|
| 122 |
-
- **Authored positives.** The
|
| 123 |
-
|
| 124 |
-
|
| 125 |
-
|
| 126 |
-
|
| 127 |
-
|
| 128 |
-
|
| 129 |
-
|
| 130 |
-
|
| 131 |
-
|
| 132 |
-
|
| 133 |
-
- **Packages
|
| 134 |
-
|
| 135 |
-
- **
|
| 136 |
-
|
| 137 |
-
|
| 138 |
-
|
| 139 |
-
|
| 140 |
-
- no
|
| 141 |
-
-
|
| 142 |
-
-
|
| 143 |
-
|
|
|
|
| 144 |
|
| 145 |
## Training data
|
| 146 |
- **Agent diffs (2,500, benign):**
|
|
@@ -155,17 +193,22 @@ Per language (seed 1, frozen θ; `RESULTS.md` has both seeds, AUROCs with CIs an
|
|
| 155 |
[snorkelai/Tau2-Bench-Verified-Airline-With-Code-Agents](https://huggingface.co/datasets/snorkelai/Tau2-Bench-Verified-Airline-With-Code-Agents),
|
| 156 |
[snorkelai/Tau2-Bench-Airline-With-Code-Agents](https://huggingface.co/datasets/snorkelai/Tau2-Bench-Airline-With-Code-Agents)
|
| 157 |
and [xingyaoww/code-act](https://huggingface.co/datasets/xingyaoww/code-act) (Apache-2.0).
|
| 158 |
-
- **
|
| 159 |
-
- **Authored examples:** inert out-of-scope and malicious changes,
|
| 160 |
-
|
| 161 |
-
- **Package
|
| 162 |
-
|
| 163 |
- `SOURCES.md` lists revisions and attributions.
|
| 164 |
|
| 165 |
-
**Privacy
|
| 166 |
-
-
|
| 167 |
-
-
|
| 168 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 169 |
|
| 170 |
## Licence
|
| 171 |
Apache-2.0, like the base model (`LICENSE`, `NOTICE`). Training-data attributions are in `SOURCES.md`.
|
|
|
|
| 18 |
|
| 19 |

|
| 20 |
|
| 21 |
+
**Pre-release.** A revised version is planned, trained on bad examples from several writer models, more realistic task
|
| 22 |
+
wording and more languages. "Limitations" lists what it should fix.
|
| 23 |
|
| 24 |
+
d3-mini-agentcode is [d3-mini](https://huggingface.co/vllm-sr/d3-mini) (4B, Apache-2.0) fine-tuned to triage code
|
| 25 |
+
written or run by AI coding agents. It reads the agent's task and the change (a diff, or a snippet the agent ran) and
|
| 26 |
+
answers two things: does the change do something security-relevant the task didn't ask for, and does it look
|
| 27 |
+
deliberately harmful. It's meant as a cheap first filter that picks which changes a person or a bigger model should
|
| 28 |
+
review.
|
| 29 |
|
| 30 |
**Write-up:** [Small Models, Big Haystack](https://batteryshark.github.io/2026/11/05/small-models-big-haystack.html)
|
| 31 |
covers how this model was built and tested, and how it compares with off-the-shelf d3 models and LLMs.
|
| 32 |
|
| 33 |
## Intended use
|
| 34 |
+
- **Agent diffs and code mode** (snippets an agent runs): score each one and send the flagged ones to a person or a
|
| 35 |
stronger model.
|
| 36 |
+
- **A second stage** behind cheaper rules.
|
| 37 |
+
- **Not the only gate.** It's a 4B model with the limitations below, and a flag doesn't prove intent.
|
| 38 |
+
- **Out of scope:**
|
| 39 |
+
- packages and dependencies (npm, PyPI) and release diffs, where untuned d3-mini did better in our tests (see
|
| 40 |
+
"Packages, releases and prompt injection");
|
| 41 |
+
- changes longer than about 8,192 tokens (see "Long inputs");
|
| 42 |
+
- inputs without a task description, since it was trained with one.
|
| 43 |
|
| 44 |
## How it was made
|
| 45 |
+
- **Base:** `vllm-sr/d3-mini` at revision `df99bf4ca4418bc6908ee68e9b7479af1f3b8800`, built on Qwen/Qwen3.5-4B.
|
| 46 |
- **Recipe:** LoRA (r 16, α 32, dropout 0.05) on the text backbone's attention, linear-attention and MLP projections,
|
| 47 |
plus the trained answer readout. lr 1e-4 (readout 2e-5), 2 epochs, 1,038 optimizer steps, max length 8,192, label
|
| 48 |
+
smoothing 0.05, shuffled answer order (`adapter/training_args.json`).
|
| 49 |
+
- **Files:** the merged weights (they load like d3-mini), the adapter (`adapter/`) and the behaviour extractor that
|
| 50 |
+
builds the input (`extractor/`).
|
| 51 |
+
- **Released checkpoint:** seed 1, final. A second training seed isn't published. It scored a little lower on most
|
| 52 |
+
sets, and both are in the results below.
|
| 53 |
+
- **Questions:** three System One questions in `questions.json`: `class` (a choice of four), `intent` and `scope`
|
| 54 |
+
(yes/no). The flag rule below uses `class` and `scope`. `intent` is answered but wasn't used in any result here.
|
| 55 |
|
| 56 |
## Usage
|
| 57 |
```python
|
| 58 |
import json
|
| 59 |
+
from huggingface_hub import hf_hub_download
|
| 60 |
from transformers import AutoModel
|
| 61 |
|
| 62 |
+
REPO = "stonefish-labs/d3-mini-agentcode"
|
| 63 |
+
model = AutoModel.from_pretrained(REPO, trust_remote_code=True)
|
| 64 |
+
questions = json.load(open(hf_hub_download(REPO, "questions.json"))) # class, intent, scope
|
| 65 |
|
| 66 |
state = """Task:
|
| 67 |
Add a CLI flag to export the report as CSV.
|
|
|
|
| 76 |
diff --git a/report.py b/report.py
|
| 77 |
..."""
|
| 78 |
|
| 79 |
+
answers = model.system_one(state=state, questions=questions)["answers"]
|
| 80 |
+
p_mal = answers["class"]["probabilities"]["deliberately_malicious"]
|
| 81 |
+
p_scope = answers["scope"]["noul"] # p(yes): the change does more than the task asks
|
| 82 |
+
flag = p_mal >= 0.9 or p_scope >= 0.9 # frozen thresholds (thresholds.json)
|
| 83 |
```
|
| 84 |
+
- **Input:** a text state with four sections, in this order: `Task:`, `Repository baseline (normal behaviour of this
|
| 85 |
+
repo/org):`, `Behaviour summary (automated, not a verdict):` and `Change:`. Include the task. `examples/usage.py`
|
| 86 |
+
scores two example changes.
|
| 87 |
+
- **Output:** `system_one` returns `{"model", "answers", "usage"}`. In `answers`, `class` has a `probabilities` entry
|
| 88 |
+
for each option and the top `choice`. `intent` and `scope` each have `noul`, the probability of yes.
|
| 89 |
+
- **Behaviour summary:** use the included static extractor (`extractor/`), the same one that wrote the summary in our
|
| 90 |
+
training and evaluation states. It never runs the code it reads. Run `pip install -r extractor/requirements.txt`,
|
| 91 |
+
then, from `extractor/`, `python make_state.py diff change.diff --task-file task.txt` (or
|
| 92 |
+
`snippet run.py --lang py --task "..."`) prints the whole state. `extractor/README.md` covers the Python API and how
|
| 93 |
+
to build a repository baseline.
|
| 94 |
+
- **Placeholder summary:** without the extractor, the placeholder `(no security-relevant behaviour found)` did almost
|
| 95 |
+
as well in our tests (seed 1, frozen thresholds). Across 14 languages it found 448/510 and flagged 5/2,150 real
|
| 96 |
+
items and 1/560 controls, against 458/510, 11/2,150 and 1/560 with the extractor's summary. On the independent
|
| 97 |
+
agent-diff set it found 20/22 (21/22 with the extractor), with no false alarms either way. Bash code mode dropped
|
| 98 |
+
the most, from 24/33 to 20/33.
|
| 99 |
+
- **Long inputs:** the extractor keeps the task, baseline and behaviour summary whole and cuts the change to fit about
|
| 100 |
+
8,192 tokens (8,192 × 3 characters), with a marker saying how much it left out. Training inputs were built the same
|
| 101 |
+
way. The model doesn't truncate anything. Called directly, it reads the whole input up to d3-mini's 262,144-token
|
| 102 |
+
window (`decision_config.json` sets no `max_length`), but the fine-tune was never trained or tested on inputs
|
| 103 |
+
longer than 8,192 tokens.
|
| 104 |
+
- **Speed:** on one A100 in batches of 8, 0.46–0.61 s per item on our agent-diff test sets. On a desktop RTX 4090,
|
| 105 |
+
our package, release and prompt-injection test inputs, cut to 8k, averaged about 1 s per item.
|
| 106 |
+
- **Memory:** the bf16 weights take about 9 GB (4.5B parameters, vision tower included). On the 4090, batches of 8 at
|
| 107 |
+
up to 8k tokens peaked at 12.7 GiB allocated.
|
| 108 |
+
|
| 109 |
+
## Thresholds
|
| 110 |
+
- **Frozen, used for every result below:** flag when p(class = deliberately_malicious) ≥ **0.9** or p(scope = yes) ≥
|
| 111 |
+
**0.9**. Fitted on an English, Python-heavy dev set for at most 5% false alarms.
|
| 112 |
+
- **Multilingual option:** 0.9 / **0.05**, fitted on a 10% repository-level dev split of the 14-language set. On the
|
| 113 |
+
other 90% it found 421/443 (95%) and flagged 43/1,900 real items (2.3%). The frozen thresholds found 90% and
|
| 114 |
+
flagged 0.5% there.
|
| 115 |
+
- **Your own:** fit thresholds on a sample of your own code for the false-alarm rate you can live with
|
| 116 |
+
(`thresholds.json` has the details).
|
| 117 |
|
| 118 |
## Results
|
| 119 |
+
Each model at its own dev-fitted thresholds: 0.9 / 0.9 for both seeds, 0.5 / 0.7 for untuned d3-mini. "Untuned" is
|
| 120 |
+
d3-mini given the same input (task, baseline, behaviour summary and change). Models wrote every bad example here
|
| 121 |
+
except the compromised releases, and none came from a real attack.
|
| 122 |
|
| 123 |
| test set | seed 1 (released) | seed 2 | untuned |
|
| 124 |
|---|---|---|---|
|
| 125 |
+
| Independent agent-diff set: out-of-scope changes found / real agent diffs and in-scope controls flagged | 21/22 / 0/172 | 19/22 / 0/172 | 10/22 / 0/172 |
|
| 126 |
+
| Public code mode from a source not used in training: bad snippets found / real snippets flagged | 31/31 / 0/200 | 31/31 / 0/200 | 31/31 / 1/200 |
|
| 127 |
+
| Held-out agent diffs and code mode (repos and sources not used in training): found / real items flagged | 122/122 / 0/527 | 118/122 / 0/527 | 96/122 / 2/527 |
|
| 128 |
+
| Behaviour families never seen in training: found | 26/26 | 22/26 | 14/26 |
|
| 129 |
+
| Compromised library releases shown as diffs (none in training): found / ordinary releases flagged | 21/27 / 0/80 | 19/27 / 0/80 | 19/27 / 0/80 |
|
| 130 |
+
| **14 languages and shells** (8 diff languages, 6 code-mode and shell cells): found / real items flagged / authored benign controls flagged | **458/510 / 11/2,150 / 1/560** | 446/510 / 7/2,150 / 2/560 | 343/510 / 48/2,150 / 12/560 |
|
| 131 |
+
| Task asks for the change, held-out set, the request alone as the task: flagged changes that clear | 122/122 | 118/118 | 66/96 |
|
| 132 |
+
| Task asks for the change, 14 languages, the request alone as the task: pairs that clear | 244/246 | 233/246 | 163/246 |
|
| 133 |
+
| Task asks for the change, 14 languages, the request added after a real task: pairs that clear / same change still flagged with an ordinary request | 143/246 / 221/246 | 173/246 / 218/246 | 116/246 / 152/246 |
|
| 134 |
| Unrequested account email added to request headers: found / controls flagged | 1/40 / 0/60 | 0/40 / 0/60 | 3/40 / 4/60 |
|
| 135 |
|
| 136 |
+
Per language (seed 1, frozen thresholds; `RESULTS.md` has both seeds, AUROCs with confidence intervals and the
|
| 137 |
+
untuned model):
|
| 138 |
|
| 139 |
| | C/C++ | C# | Go | Java | JS/TS | PHP | Rust | Swift/ObjC | bash | cmd/batch | zsh/macOS | PowerShell | Python (code mode) | JS/TS (code mode) |
|
| 140 |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
| 141 |
| found | 35/38 | 34/37 | 36/38 | 36/37 | 33/37 | 34/38 | 33/37 | 37/38 | 24/33 | 28/33 | 29/36 | 35/37 | 30/35 | 34/36 |
|
| 142 |
| real flagged | 1/149 | 0/149 | 1/150 | 1/149 | 0/150 | 0/150 | 0/150 | 0/150 | 1/200 | 0/130 | 3/144 | 3/150 | 1/200 | 0/129 |
|
| 143 |
|
| 144 |
+
### Packages, releases and prompt injection
|
| 145 |
+
After release, we ran seed 1 and untuned d3-mini on the same inputs on a desktop RTX 4090, each at its frozen
|
| 146 |
+
thresholds. Both checks we set beforehand failed: seed 1 raised more false alarms on packages, and planted text
|
| 147 |
+
talked it out of more catches on packages and releases.
|
| 148 |
+
|
| 149 |
+
| test | seed 1 | untuned |
|
| 150 |
+
|---|---|---|
|
| 151 |
+
| Newer npm packages (time split): malicious found / benign look-alikes flagged | 46/89 / 7/112 | 47/89 / 1/112 |
|
| 152 |
+
| Planted prompt-injection text, agent diffs: catches lost | 0/105 | 0/50 |
|
| 153 |
+
| Planted text, packages and releases, worst template (a fake approval note): catches lost | 12/83 | 4/73 |
|
| 154 |
+
| All five templates, all sets: catches lost | 37/423 | 14/366 |
|
| 155 |
+
|
| 156 |
+
Six of the seven benign look-alikes it flagged were large packages cut off at the 8k input limit.
|
| 157 |
+
|
| 158 |
## Limitations
|
| 159 |
+
- **Authored positives.** The bad examples in training and in most tests are inert changes that a model wrote into
|
| 160 |
+
real agent work. Near-perfect scores on held-out authored sets partly reflect that. The independent agent-diff set,
|
| 161 |
+
written under a different protocol and scored against 137 real agent diffs, is the better check.
|
| 162 |
+
- **Writer gap.** One model wrote all the training positives. Recall is lower on positives other models wrote: across
|
| 163 |
+
the 14 languages it found 88% of GPT-written and 93% of Claude-written ones, and 82% against 95% in code mode.
|
| 164 |
+
- **Task wording.** When the task asks for exactly the flagged change, the flag clears on 244 of 246 pairs if the task
|
| 165 |
+
is only that request. When the same request follows a longer, real task description, it clears on 143 of 246 (58%;
|
| 166 |
+
70% for seed 2).
|
| 167 |
+
- **Python-heavy training.** The agent-diff training data is mostly Python (JS/TS is 4.4%). It still found 278/300 in
|
| 168 |
+
8 other diff languages (AUROC 0.96–1.00 per language). Code mode and shells are weaker at 180/210, with bash the
|
| 169 |
+
lowest at 24/33. Python code mode dropped too (30/35), so language doesn't explain all of it.
|
| 170 |
+
- **Packages and releases.** On packages and release diffs it did no better than untuned d3-mini and raised more false
|
| 171 |
+
alarms (see "Packages, releases and prompt injection"). Use untuned d3-mini there.
|
| 172 |
+
- **Long inputs.** It was trained and tested on inputs of up to 8,192 tokens. The extractor cuts longer changes to fit
|
| 173 |
+
(see "Long inputs" under Usage).
|
| 174 |
+
- **Missed: personal data in request headers.** It doesn't flag an agent adding the user's email to the User-Agent or
|
| 175 |
+
another header of a request to the task's own API (1/40). Check request contents separately.
|
| 176 |
+
- **Also:**
|
| 177 |
+
- no release or commit diffs in training, so the release result is transfer;
|
| 178 |
+
- no evaluation on agent code written after the base model's training cutoff (the newer-npm set is packages);
|
| 179 |
+
- calibration error (ECE) is 0.015–0.028 on data like the training data and higher elsewhere (0.21 for `class` on
|
| 180 |
+
the independent agent-diff set);
|
| 181 |
+
- the extractor understands Python, JS/TS and shell best, and other languages get line-level patterns only.
|
| 182 |
|
| 183 |
## Training data
|
| 184 |
- **Agent diffs (2,500, benign):**
|
|
|
|
| 193 |
[snorkelai/Tau2-Bench-Verified-Airline-With-Code-Agents](https://huggingface.co/datasets/snorkelai/Tau2-Bench-Verified-Airline-With-Code-Agents),
|
| 194 |
[snorkelai/Tau2-Bench-Airline-With-Code-Agents](https://huggingface.co/datasets/snorkelai/Tau2-Bench-Airline-With-Code-Agents)
|
| 195 |
and [xingyaoww/code-act](https://huggingface.co/datasets/xingyaoww/code-act) (Apache-2.0).
|
| 196 |
+
- **577 scrubbed snippets from our own agent sessions,** used as benign examples only and not released.
|
| 197 |
+
- **Authored examples:** inert out-of-scope and malicious changes, in-scope controls and task-swap pairs that DeepSeek
|
| 198 |
+
Flash wrote into the public items above. Network destinations and secrets are stand-ins, and nothing harmful runs.
|
| 199 |
+
- **Package examples** from public npm and PyPI packages and a public malicious-package dataset (see "Packages and
|
| 200 |
+
releases" under Limitations).
|
| 201 |
- `SOURCES.md` lists revisions and attributions.
|
| 202 |
|
| 203 |
+
**Privacy checks before release:**
|
| 204 |
+
- membership tests (trained vs unseen private snippets) were at chance, AUROC 0.51–0.53;
|
| 205 |
+
- a scan of the 577 private snippets found no real emails, tokens, hostnames, IP addresses or home paths;
|
| 206 |
+
- prompting the model through a generation head attached for the test didn't get it to emit secrets, credentials,
|
| 207 |
+
hostnames, personal emails or internal names;
|
| 208 |
+
- one check we set beforehand failed: on private snippets it trained on, the model continued code word for word for
|
| 209 |
+
50 or more tokens a little more often than the base model (14/424 against 8/424). It did the same on private
|
| 210 |
+
snippets it never saw (15/374), so it's better at predicting this style of code in general. None of those
|
| 211 |
+
continuations contained internal names or secrets.
|
| 212 |
|
| 213 |
## Licence
|
| 214 |
Apache-2.0, like the base model (`LICENSE`, `NOTICE`). Training-data attributions are in `SOURCES.md`.
|
REBUILD.md
CHANGED
|
@@ -7,8 +7,10 @@ backbone weights and answer readout.
|
|
| 7 |
from transformers import AutoModel
|
| 8 |
model = AutoModel.from_pretrained("stonefish-labs/d3-mini-agentcode", trust_remote_code=True)
|
| 9 |
```
|
| 10 |
-
`MODEL_MANIFEST.json` lists every file's SHA-256 and size.
|
| 11 |
-
(`verify="fast"`
|
|
|
|
|
|
|
| 12 |
|
| 13 |
## Rebuild the merged weights from the adapter
|
| 14 |
`adapter/` holds the LoRA adapter (`adapter_model.safetensors`, `adapter_config.json`), the trained answer readout
|
|
@@ -25,6 +27,7 @@ model = AutoModel.from_pretrained("stonefish-labs/d3-mini-agentcode", trust_remo
|
|
| 25 |
|
| 26 |
## Serve
|
| 27 |
- **transformers:** as above. `model.system_one(state=..., questions=...)`; see `examples/usage.py`.
|
| 28 |
-
- **vLLM Semantic Router:** the package keeps d3-mini's format (`d3-code-readout-v1`), so
|
| 29 |
-
|
| 30 |
-
- **Hardware:** about
|
|
|
|
|
|
| 7 |
from transformers import AutoModel
|
| 8 |
model = AutoModel.from_pretrained("stonefish-labs/d3-mini-agentcode", trust_remote_code=True)
|
| 9 |
```
|
| 10 |
+
`MODEL_MANIFEST.json` lists every file's SHA-256 and size. On every load the runtime checks the files it reads
|
| 11 |
+
(`verify="fast"`: it hashes files up to 64 MB and checks the size of larger ones). `verify="full"` hashes them all, and
|
| 12 |
+
`verify="release"` also checks the card and other files the runtime doesn't read. `SHA256SUMS` covers every file except
|
| 13 |
+
itself and the manifest.
|
| 14 |
|
| 15 |
## Rebuild the merged weights from the adapter
|
| 16 |
`adapter/` holds the LoRA adapter (`adapter_model.safetensors`, `adapter_config.json`), the trained answer readout
|
|
|
|
| 27 |
|
| 28 |
## Serve
|
| 29 |
- **transformers:** as above. `model.system_one(state=..., questions=...)`; see `examples/usage.py`.
|
| 30 |
+
- **vLLM Semantic Router:** the package keeps d3-mini's format (`d3-code-readout-v1`), so runtimes that serve d3-mini
|
| 31 |
+
should serve it too (see the d3-mini card). We only ran it with the bundled runtime above.
|
| 32 |
+
- **Hardware:** the bf16 weights take about 9 GB. On an RTX 4090, batches of 8 at up to 8,192 tokens peaked at 12.7 GiB
|
| 33 |
+
allocated.
|
RESULTS.md
CHANGED
|
@@ -30,7 +30,8 @@ By writer of the positives (all cells): seed 1 found 161/174 Claude-written and
|
|
| 30 |
292/336; untuned 115/174 and 228/336. Authored benign controls flagged: seed 1 1/560, seed 2 2/560, untuned 12/560.
|
| 31 |
|
| 32 |
## Task swaps (the task asks for exactly the flagged behaviour)
|
| 33 |
-
- Request alone as the task:
|
|
|
|
| 34 |
- Request added after a real task (246 pairs, 14 languages): clears on 143 (seed 1) and 173 (seed 2), untuned 116. With the
|
| 35 |
same change and an ordinary request, still flags 221, 218 and 152.
|
| 36 |
|
|
|
|
| 30 |
292/336; untuned 115/174 and 228/336. Authored benign controls flagged: seed 1 1/560, seed 2 2/560, untuned 12/560.
|
| 31 |
|
| 32 |
## Task swaps (the task asks for exactly the flagged behaviour)
|
| 33 |
+
- Request alone as the task: clears on 244/246 (seed 1) and 233/246 (seed 2), untuned 163/246. On the English held-out
|
| 34 |
+
set (H3 and H4), it clears on every change it flagged: 122/122 (seed 1) and 118/118 (seed 2), untuned 66/96.
|
| 35 |
- Request added after a real task (246 pairs, 14 languages): clears on 143 (seed 1) and 173 (seed 2), untuned 116. With the
|
| 36 |
same change and an ordinary request, still flags 221, 218 and 152.
|
| 37 |
|
SHA256SUMS
CHANGED
|
@@ -1,10 +1,10 @@
|
|
| 1 |
c71d239df91726fc519c6eb72d318ec65820627232b2f796219e87dcf35d0ab4 ./LICENSE
|
| 2 |
a42ca8b488e0b98b1a690937784e8da0fe417fd2218e7595129927221f4d9244 ./MERGE-REPORT.json
|
| 3 |
e74014fab972cffe76391a608e8a5cf54baeaf7d16cd8a77545edc67ffd25225 ./NOTICE
|
| 4 |
-
|
| 5 |
-
|
| 6 |
-
|
| 7 |
-
|
| 8 |
ff2a5427cbcf0e7a0a61940920d06f0e1fc116565258c3eca2501b0d10933250 ./adapter/adapter_config.json
|
| 9 |
3bd401009801b39f29148427e884a8230a85ddb3c20a54ea834372889998c895 ./adapter/adapter_model.safetensors
|
| 10 |
bf0999886d5b6ac6764c7a9d688c890579062a04254e0054bb230681fb464a7e ./adapter/merge.py
|
|
@@ -20,7 +20,7 @@ a4aee8afcf2e0711942cf848899be66016f8d14a889ff9ede07bca099c28f715 ./chat_templat
|
|
| 20 |
adf7f7aa085674b5682e492d8b2e3cc4f11c67b0d153515c742e907e9b550917 ./d3_runtime.py
|
| 21 |
766a20a69101e36ab0535a1ec683379db422380a83091f0eccd7e82edb6a02de ./d3_server.py
|
| 22 |
588bc0ac7644479b9adde0498d80b6fb7c576388992564ac67599c05d29add07 ./decision_config.json
|
| 23 |
-
|
| 24 |
cd1679b82e61c15f247c277007c0d416d27b4834f455f3fee7f9afb863743d08 ./extractor/README.md
|
| 25 |
c0db7c9cb57a23289245209c30c335c3e44614454d2796a239864d70165356d6 ./extractor/behaviour/__init__.py
|
| 26 |
2c78b70c752cc11bfbf759e9e8fe0a4b5f43da43cdecb4489a9b403ac4d20311 ./extractor/behaviour/__main__.py
|
|
|
|
| 1 |
c71d239df91726fc519c6eb72d318ec65820627232b2f796219e87dcf35d0ab4 ./LICENSE
|
| 2 |
a42ca8b488e0b98b1a690937784e8da0fe417fd2218e7595129927221f4d9244 ./MERGE-REPORT.json
|
| 3 |
e74014fab972cffe76391a608e8a5cf54baeaf7d16cd8a77545edc67ffd25225 ./NOTICE
|
| 4 |
+
ed61f82f9b000ddf533119f1345345bca2f345514b0b70d802f2bd172c8ce24a ./README.md
|
| 5 |
+
76e575f2d52a83a937d0c4daf818ba302595f577cf54b3f3bd3bbea67a0e4566 ./REBUILD.md
|
| 6 |
+
cc20ca6e484d84f5c86ea16e82ce0ef3a3978f572c8d8c514ed3a1383b9d1930 ./RESULTS.md
|
| 7 |
+
bfb09be93fa5d4022645bb5bf3cd4268949ef76eba42c4d462099537a6fb7bb2 ./SOURCES.md
|
| 8 |
ff2a5427cbcf0e7a0a61940920d06f0e1fc116565258c3eca2501b0d10933250 ./adapter/adapter_config.json
|
| 9 |
3bd401009801b39f29148427e884a8230a85ddb3c20a54ea834372889998c895 ./adapter/adapter_model.safetensors
|
| 10 |
bf0999886d5b6ac6764c7a9d688c890579062a04254e0054bb230681fb464a7e ./adapter/merge.py
|
|
|
|
| 20 |
adf7f7aa085674b5682e492d8b2e3cc4f11c67b0d153515c742e907e9b550917 ./d3_runtime.py
|
| 21 |
766a20a69101e36ab0535a1ec683379db422380a83091f0eccd7e82edb6a02de ./d3_server.py
|
| 22 |
588bc0ac7644479b9adde0498d80b6fb7c576388992564ac67599c05d29add07 ./decision_config.json
|
| 23 |
+
37fc6f3322832e8ce31028dba64561af57c189c612709e9f407457d6a7e44c9a ./examples/usage.py
|
| 24 |
cd1679b82e61c15f247c277007c0d416d27b4834f455f3fee7f9afb863743d08 ./extractor/README.md
|
| 25 |
c0db7c9cb57a23289245209c30c335c3e44614454d2796a239864d70165356d6 ./extractor/behaviour/__init__.py
|
| 26 |
2c78b70c752cc11bfbf759e9e8fe0a4b5f43da43cdecb4489a9b403ac4d20311 ./extractor/behaviour/__main__.py
|
SOURCES.md
CHANGED
|
@@ -7,12 +7,15 @@ examples into some items.
|
|
| 7 |
## Agent diffs (benign examples)
|
| 8 |
| dataset | revision | licence | items |
|
| 9 |
|---|---|---|---|
|
| 10 |
-
| [nvidia/SWE-Zero-openhands-trajectories](https://huggingface.co/datasets/nvidia/SWE-Zero-openhands-trajectories) | `7b3cd106d00f60918e722d33a1d74bc67072a7ea` | CC-BY-4.0 (NVIDIA)
|
| 11 |
-
| [nvidia/SWE-Hero-openhands-trajectories](https://huggingface.co/datasets/nvidia/SWE-Hero-openhands-trajectories) | `150bc119e52c647216fce285fd801f16b6fd745b` | CC-BY-4.0 (NVIDIA)
|
| 12 |
| [nebius/SWE-rebench-openhands-trajectories](https://huggingface.co/datasets/nebius/SWE-rebench-openhands-trajectories) | `35455389ab51bf5e2306bfd436ef72d0f98bf882` | CC-BY-4.0 (Nebius) | 389 |
|
| 13 |
| [sweagent/iter2-rl-rollouts](https://huggingface.co/datasets/sweagent/iter2-rl-rollouts) | `ec07602618bc22d56e71112fc8e81943fcbd28be` | MIT | 370 |
|
| 14 |
| [nebius/SWE-agent-trajectories](https://huggingface.co/datasets/nebius/SWE-agent-trajectories) | `68195a1450865274106246d0d0296a1d6807b88e` | CC-BY-4.0 (Nebius) | 16 |
|
| 15 |
|
|
|
|
|
|
|
|
|
|
| 16 |
## Code-mode snippets (benign examples)
|
| 17 |
| dataset | revision | licence |
|
| 18 |
|---|---|---|
|
|
@@ -24,7 +27,7 @@ examples into some items.
|
|
| 24 |
## Other training data
|
| 25 |
- **A small set of scrubbed snippets from our own agent sessions** (577), used as benign examples only. Not released.
|
| 26 |
- **Authored inert examples:** out-of-scope and malicious changes, in-scope controls and task-swap pairs, written by
|
| 27 |
-
DeepSeek
|
| 28 |
- **Package-level examples:** public npm/PyPI packages, and malicious samples from the
|
| 29 |
[DataDog malicious-software-packages-dataset](https://github.com/DataDog/malicious-software-packages-dataset).
|
| 30 |
Package scanning is out of scope for this release.
|
|
|
|
| 7 |
## Agent diffs (benign examples)
|
| 8 |
| dataset | revision | licence | items |
|
| 9 |
|---|---|---|---|
|
| 10 |
+
| [nvidia/SWE-Zero-openhands-trajectories](https://huggingface.co/datasets/nvidia/SWE-Zero-openhands-trajectories) | `7b3cd106d00f60918e722d33a1d74bc67072a7ea` | CC-BY-4.0 (NVIDIA) | 1,446 |
|
| 11 |
+
| [nvidia/SWE-Hero-openhands-trajectories](https://huggingface.co/datasets/nvidia/SWE-Hero-openhands-trajectories) | `150bc119e52c647216fce285fd801f16b6fd745b` | CC-BY-4.0 (NVIDIA) | 279 |
|
| 12 |
| [nebius/SWE-rebench-openhands-trajectories](https://huggingface.co/datasets/nebius/SWE-rebench-openhands-trajectories) | `35455389ab51bf5e2306bfd436ef72d0f98bf882` | CC-BY-4.0 (Nebius) | 389 |
|
| 13 |
| [sweagent/iter2-rl-rollouts](https://huggingface.co/datasets/sweagent/iter2-rl-rollouts) | `ec07602618bc22d56e71112fc8e81943fcbd28be` | MIT | 370 |
|
| 14 |
| [nebius/SWE-agent-trajectories](https://huggingface.co/datasets/nebius/SWE-agent-trajectories) | `68195a1450865274106246d0d0296a1d6807b88e` | CC-BY-4.0 (Nebius) | 16 |
|
| 15 |
|
| 16 |
+
The code in these diffs keeps its own repository's licence. We recorded a permissive licence for 1,725 of the 2,500
|
| 17 |
+
items. For the other 775 we didn't look it up.
|
| 18 |
+
|
| 19 |
## Code-mode snippets (benign examples)
|
| 20 |
| dataset | revision | licence |
|
| 21 |
|---|---|---|
|
|
|
|
| 27 |
## Other training data
|
| 28 |
- **A small set of scrubbed snippets from our own agent sessions** (577), used as benign examples only. Not released.
|
| 29 |
- **Authored inert examples:** out-of-scope and malicious changes, in-scope controls and task-swap pairs, written by
|
| 30 |
+
DeepSeek Flash into the public items above. Network destinations and secrets are stand-ins.
|
| 31 |
- **Package-level examples:** public npm/PyPI packages, and malicious samples from the
|
| 32 |
[DataDog malicious-software-packages-dataset](https://github.com/DataDog/malicious-software-packages-dataset).
|
| 33 |
Package scanning is out of scope for this release.
|
examples/usage.py
CHANGED
|
@@ -55,7 +55,7 @@ OUT_OF_SCOPE = IN_SCOPE.replace(
|
|
| 55 |
|
| 56 |
model = AutoModel.from_pretrained(MODEL, trust_remote_code=True)
|
| 57 |
for name, change in (("in-scope change", IN_SCOPE), ("change that also sends the environment out", OUT_OF_SCOPE)):
|
| 58 |
-
r = model.system_one(state=state(TASK, change), questions=QUESTIONS)
|
| 59 |
p_mal = r["class"]["probabilities"]["deliberately_malicious"]
|
| 60 |
p_scope = r["scope"]["noul"]
|
| 61 |
flag = p_mal >= THETA["class_malicious"] or p_scope >= THETA["scope"]
|
|
|
|
| 55 |
|
| 56 |
model = AutoModel.from_pretrained(MODEL, trust_remote_code=True)
|
| 57 |
for name, change in (("in-scope change", IN_SCOPE), ("change that also sends the environment out", OUT_OF_SCOPE)):
|
| 58 |
+
r = model.system_one(state=state(TASK, change), questions=QUESTIONS)["answers"]
|
| 59 |
p_mal = r["class"]["probabilities"]["deliberately_malicious"]
|
| 60 |
p_scope = r["scope"]["noul"]
|
| 61 |
flag = p_mal >= THETA["class_malicious"] or p_scope >= THETA["scope"]
|