thymus-ai 1.0.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- thymus_ai-1.0.0/.github/workflows/ci.yml +42 -0
- thymus_ai-1.0.0/.github/workflows/release.yml +60 -0
- thymus_ai-1.0.0/.gitignore +228 -0
- thymus_ai-1.0.0/.pre-commit-config.yaml +23 -0
- thymus_ai-1.0.0/CLAUDE.md +170 -0
- thymus_ai-1.0.0/LICENSE +201 -0
- thymus_ai-1.0.0/NOTICE +4 -0
- thymus_ai-1.0.0/PKG-INFO +257 -0
- thymus_ai-1.0.0/README.md +222 -0
- thymus_ai-1.0.0/Thymus_Phase0_Schemas.md +679 -0
- thymus_ai-1.0.0/Thymus_Product_Roadmap.md +246 -0
- thymus_ai-1.0.0/Thymus_features.md +100 -0
- thymus_ai-1.0.0/demo/README.md +101 -0
- thymus_ai-1.0.0/demo/poison_demo.py +187 -0
- thymus_ai-1.0.0/pyproject.toml +65 -0
- thymus_ai-1.0.0/requirements.txt +6 -0
- thymus_ai-1.0.0/run_baseline.py +80 -0
- thymus_ai-1.0.0/tests/test_conflict.py +62 -0
- thymus_ai-1.0.0/tests/test_conflict_precision.py +58 -0
- thymus_ai-1.0.0/tests/test_console_roundtrip.py +108 -0
- thymus_ai-1.0.0/tests/test_feedback.py +54 -0
- thymus_ai-1.0.0/tests/test_guard_swap.py +68 -0
- thymus_ai-1.0.0/tests/test_nonblocking_audit.py +97 -0
- thymus_ai-1.0.0/tests/test_read_path.py +69 -0
- thymus_ai-1.0.0/tests/test_remote_policy.py +164 -0
- thymus_ai-1.0.0/tests/test_review_fixes.py +126 -0
- thymus_ai-1.0.0/tests/test_rules.py +76 -0
- thymus_ai-1.0.0/tests/test_thymus_engine.py +89 -0
- thymus_ai-1.0.0/tests/test_zep.py +66 -0
- thymus_ai-1.0.0/thymus/README.md +166 -0
- thymus_ai-1.0.0/thymus/__init__.py +80 -0
- thymus_ai-1.0.0/thymus/adapters/__init__.py +24 -0
- thymus_ai-1.0.0/thymus/adapters/base.py +276 -0
- thymus_ai-1.0.0/thymus/adapters/generic.py +61 -0
- thymus_ai-1.0.0/thymus/adapters/mem0.py +37 -0
- thymus_ai-1.0.0/thymus/adapters/zep.py +85 -0
- thymus_ai-1.0.0/thymus/detectors/__init__.py +16 -0
- thymus_ai-1.0.0/thymus/detectors/base.py +67 -0
- thymus_ai-1.0.0/thymus/detectors/conflict.py +116 -0
- thymus_ai-1.0.0/thymus/detectors/egress.py +37 -0
- thymus_ai-1.0.0/thymus/detectors/injection.py +171 -0
- thymus_ai-1.0.0/thymus/detectors/origin.py +36 -0
- thymus_ai-1.0.0/thymus/engine.py +68 -0
- thymus_ai-1.0.0/thymus/feedback.py +81 -0
- thymus_ai-1.0.0/thymus/policy.py +103 -0
- thymus_ai-1.0.0/thymus/remote.py +288 -0
- thymus_ai-1.0.0/thymus/rules/__init__.py +26 -0
- thymus_ai-1.0.0/thymus/rules/loader.py +135 -0
- thymus_ai-1.0.0/thymus/rules/packs/auth_bypass.json +33 -0
- thymus_ai-1.0.0/thymus/rules/packs/exfiltration.json +33 -0
- thymus_ai-1.0.0/thymus/rules/packs/financial.json +46 -0
- thymus_ai-1.0.0/thymus/rules/packs/impersonation.json +33 -0
- thymus_ai-1.0.0/thymus/rules/packs/manipulation.json +20 -0
- thymus_ai-1.0.0/thymus/rules/packs/obfuscation.json +44 -0
- thymus_ai-1.0.0/thymus/rules/packs/ops_redirect.json +35 -0
- thymus_ai-1.0.0/thymus/rules/packs/persistence.json +33 -0
- thymus_ai-1.0.0/thymus/rules/packs/policy_override.json +33 -0
- thymus_ai-1.0.0/thymus/rules/packs/privilege_escalation.json +33 -0
- thymus_ai-1.0.0/thymus/rules/packs/prompt_extraction.json +20 -0
- thymus_ai-1.0.0/thymus/rules/packs/safety_suppression.json +20 -0
- thymus_ai-1.0.0/thymus/rules/packs/secrets_pii.json +80 -0
- thymus_ai-1.0.0/thymus/rules/packs/tool_abuse.json +46 -0
- thymus_ai-1.0.0/thymus/types.py +196 -0
- thymus_ai-1.0.0/thymus_bench/__init__.py +10 -0
- thymus_ai-1.0.0/thymus_bench/backends/__init__.py +12 -0
- thymus_ai-1.0.0/thymus_bench/backends/base.py +47 -0
- thymus_ai-1.0.0/thymus_bench/backends/inmemory.py +88 -0
- thymus_ai-1.0.0/thymus_bench/backends/mem0_backend.py +135 -0
- thymus_ai-1.0.0/thymus_bench/corpus/benign_facts.json +18 -0
- thymus_ai-1.0.0/thymus_bench/corpus/conflict_cases.json +19 -0
- thymus_ai-1.0.0/thymus_bench/corpus/hard_negatives.json +86 -0
- thymus_ai-1.0.0/thymus_bench/corpus/minja_attacks.json +256 -0
- thymus_ai-1.0.0/thymus_bench/guards/__init__.py +15 -0
- thymus_ai-1.0.0/thymus_bench/guards/base.py +47 -0
- thymus_ai-1.0.0/thymus_bench/guards/null_guard.py +18 -0
- thymus_ai-1.0.0/thymus_bench/guards/thymus_guard.py +46 -0
- thymus_ai-1.0.0/thymus_bench/metrics.py +70 -0
- thymus_ai-1.0.0/thymus_bench/report.py +107 -0
- thymus_ai-1.0.0/thymus_bench/rule_report.py +108 -0
- thymus_ai-1.0.0/thymus_bench/runner.py +163 -0
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
# Server-side enforcement of what pre-commit does locally: lint, format, tests.
|
|
4
|
+
# Runs on every push to master and on every pull request.
|
|
5
|
+
on:
|
|
6
|
+
push:
|
|
7
|
+
branches: [master]
|
|
8
|
+
pull_request:
|
|
9
|
+
|
|
10
|
+
jobs:
|
|
11
|
+
lint-and-test:
|
|
12
|
+
runs-on: ubuntu-latest
|
|
13
|
+
strategy:
|
|
14
|
+
fail-fast: false
|
|
15
|
+
matrix:
|
|
16
|
+
# The package declares requires-python >=3.10 — test the whole range so
|
|
17
|
+
# a version-specific break can't slip through.
|
|
18
|
+
python-version: ["3.10", "3.11", "3.12", "3.13"]
|
|
19
|
+
steps:
|
|
20
|
+
- uses: actions/checkout@v5
|
|
21
|
+
|
|
22
|
+
- name: Set up uv + Python ${{ matrix.python-version }}
|
|
23
|
+
uses: astral-sh/setup-uv@v6
|
|
24
|
+
with:
|
|
25
|
+
python-version: ${{ matrix.python-version }}
|
|
26
|
+
|
|
27
|
+
# Create the venv explicitly so this doesn't depend on setup-uv's default
|
|
28
|
+
# (v5 auto-created one, v6 does not) — then install into it and run tools
|
|
29
|
+
# via `uv run`.
|
|
30
|
+
- name: Install (dev extra)
|
|
31
|
+
run: |
|
|
32
|
+
uv venv
|
|
33
|
+
uv pip install -e ".[dev]"
|
|
34
|
+
|
|
35
|
+
- name: Ruff lint
|
|
36
|
+
run: uv run ruff check .
|
|
37
|
+
|
|
38
|
+
- name: Ruff format check
|
|
39
|
+
run: uv run ruff format --check .
|
|
40
|
+
|
|
41
|
+
- name: Tests
|
|
42
|
+
run: uv run pytest -q
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
# Cut a release by pushing a version tag:
|
|
4
|
+
# git tag v0.2.0 && git push origin v0.2.0
|
|
5
|
+
# This builds the package, publishes it to PyPI via Trusted Publishing (OIDC —
|
|
6
|
+
# no API token stored anywhere), and creates a GitHub Release with auto notes.
|
|
7
|
+
#
|
|
8
|
+
# One-time setup on PyPI (https://pypi.org/manage/account/publishing/): add a
|
|
9
|
+
# "pending publisher" for project `thymus-ai`, owner `UltronLabs`, repo `Thymus`,
|
|
10
|
+
# workflow `release.yml`. After the first successful publish it becomes a normal
|
|
11
|
+
# trusted publisher.
|
|
12
|
+
on:
|
|
13
|
+
push:
|
|
14
|
+
tags: ["v*"]
|
|
15
|
+
|
|
16
|
+
# Default to read (needed to check out a private repo); jobs opt into more.
|
|
17
|
+
permissions:
|
|
18
|
+
contents: read
|
|
19
|
+
|
|
20
|
+
jobs:
|
|
21
|
+
build:
|
|
22
|
+
runs-on: ubuntu-latest
|
|
23
|
+
steps:
|
|
24
|
+
- uses: actions/checkout@v5
|
|
25
|
+
- uses: astral-sh/setup-uv@v6
|
|
26
|
+
with:
|
|
27
|
+
python-version: "3.12"
|
|
28
|
+
- run: uv build
|
|
29
|
+
- uses: actions/upload-artifact@v4
|
|
30
|
+
with:
|
|
31
|
+
name: dist
|
|
32
|
+
path: dist/
|
|
33
|
+
|
|
34
|
+
publish-pypi:
|
|
35
|
+
needs: build
|
|
36
|
+
runs-on: ubuntu-latest
|
|
37
|
+
permissions:
|
|
38
|
+
id-token: write # OIDC token for PyPI Trusted Publishing
|
|
39
|
+
steps:
|
|
40
|
+
- uses: actions/download-artifact@v4
|
|
41
|
+
with:
|
|
42
|
+
name: dist
|
|
43
|
+
path: dist/
|
|
44
|
+
- uses: pypa/gh-action-pypi-publish@release/v1
|
|
45
|
+
|
|
46
|
+
github-release:
|
|
47
|
+
needs: publish-pypi
|
|
48
|
+
runs-on: ubuntu-latest
|
|
49
|
+
permissions:
|
|
50
|
+
contents: write # to create the GitHub Release
|
|
51
|
+
steps:
|
|
52
|
+
- uses: actions/checkout@v5
|
|
53
|
+
- uses: actions/download-artifact@v4
|
|
54
|
+
with:
|
|
55
|
+
name: dist
|
|
56
|
+
path: dist/
|
|
57
|
+
- name: Create GitHub Release
|
|
58
|
+
env:
|
|
59
|
+
GH_TOKEN: ${{ github.token }}
|
|
60
|
+
run: gh release create "${GITHUB_REF_NAME}" dist/* --generate-notes --verify-tag
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
# Byte-compiled / optimized / DLL files
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[codz]
|
|
4
|
+
*$py.class
|
|
5
|
+
|
|
6
|
+
# C extensions
|
|
7
|
+
*.so
|
|
8
|
+
|
|
9
|
+
# Distribution / packaging
|
|
10
|
+
.Python
|
|
11
|
+
build/
|
|
12
|
+
develop-eggs/
|
|
13
|
+
dist/
|
|
14
|
+
downloads/
|
|
15
|
+
eggs/
|
|
16
|
+
.eggs/
|
|
17
|
+
lib/
|
|
18
|
+
lib64/
|
|
19
|
+
parts/
|
|
20
|
+
sdist/
|
|
21
|
+
var/
|
|
22
|
+
wheels/
|
|
23
|
+
share/python-wheels/
|
|
24
|
+
*.egg-info/
|
|
25
|
+
.installed.cfg
|
|
26
|
+
*.egg
|
|
27
|
+
MANIFEST
|
|
28
|
+
|
|
29
|
+
# PyInstaller
|
|
30
|
+
# Usually these files are written by a python script from a template
|
|
31
|
+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
|
32
|
+
*.manifest
|
|
33
|
+
*.spec
|
|
34
|
+
|
|
35
|
+
# Installer logs
|
|
36
|
+
pip-log.txt
|
|
37
|
+
pip-delete-this-directory.txt
|
|
38
|
+
|
|
39
|
+
# Unit test / coverage reports
|
|
40
|
+
htmlcov/
|
|
41
|
+
.tox/
|
|
42
|
+
.nox/
|
|
43
|
+
.coverage
|
|
44
|
+
.coverage.*
|
|
45
|
+
.cache
|
|
46
|
+
nosetests.xml
|
|
47
|
+
coverage.xml
|
|
48
|
+
*.cover
|
|
49
|
+
*.py.cover
|
|
50
|
+
.hypothesis/
|
|
51
|
+
.pytest_cache/
|
|
52
|
+
cover/
|
|
53
|
+
|
|
54
|
+
# Translations
|
|
55
|
+
*.mo
|
|
56
|
+
*.pot
|
|
57
|
+
|
|
58
|
+
# Django stuff:
|
|
59
|
+
*.log
|
|
60
|
+
local_settings.py
|
|
61
|
+
db.sqlite3
|
|
62
|
+
db.sqlite3-journal
|
|
63
|
+
|
|
64
|
+
# Flask stuff:
|
|
65
|
+
instance/
|
|
66
|
+
.webassets-cache
|
|
67
|
+
|
|
68
|
+
# Scrapy stuff:
|
|
69
|
+
.scrapy
|
|
70
|
+
|
|
71
|
+
# Sphinx documentation
|
|
72
|
+
docs/_build/
|
|
73
|
+
|
|
74
|
+
# PyBuilder
|
|
75
|
+
.pybuilder/
|
|
76
|
+
target/
|
|
77
|
+
|
|
78
|
+
# Jupyter Notebook
|
|
79
|
+
.ipynb_checkpoints
|
|
80
|
+
|
|
81
|
+
# IPython
|
|
82
|
+
profile_default/
|
|
83
|
+
ipython_config.py
|
|
84
|
+
|
|
85
|
+
# pyenv
|
|
86
|
+
# For a library or package, you might want to ignore these files since the code is
|
|
87
|
+
# intended to run in multiple environments; otherwise, check them in:
|
|
88
|
+
# .python-version
|
|
89
|
+
|
|
90
|
+
# pipenv
|
|
91
|
+
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
|
92
|
+
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
|
93
|
+
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
|
94
|
+
# install all needed dependencies.
|
|
95
|
+
# Pipfile.lock
|
|
96
|
+
|
|
97
|
+
# UV
|
|
98
|
+
# Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
|
|
99
|
+
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
|
100
|
+
# commonly ignored for libraries.
|
|
101
|
+
# uv.lock
|
|
102
|
+
|
|
103
|
+
# poetry
|
|
104
|
+
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
|
105
|
+
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
|
106
|
+
# commonly ignored for libraries.
|
|
107
|
+
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
|
108
|
+
# poetry.lock
|
|
109
|
+
# poetry.toml
|
|
110
|
+
|
|
111
|
+
# pdm
|
|
112
|
+
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
|
113
|
+
# pdm recommends including project-wide configuration in pdm.toml, but excluding .pdm-python.
|
|
114
|
+
# https://pdm-project.org/en/latest/usage/project/#working-with-version-control
|
|
115
|
+
# pdm.lock
|
|
116
|
+
# pdm.toml
|
|
117
|
+
.pdm-python
|
|
118
|
+
.pdm-build/
|
|
119
|
+
|
|
120
|
+
# pixi
|
|
121
|
+
# Similar to Pipfile.lock, it is generally recommended to include pixi.lock in version control.
|
|
122
|
+
# pixi.lock
|
|
123
|
+
# Pixi creates a virtual environment in the .pixi directory, just like venv module creates one
|
|
124
|
+
# in the .venv directory. It is recommended not to include this directory in version control.
|
|
125
|
+
.pixi
|
|
126
|
+
|
|
127
|
+
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
|
128
|
+
__pypackages__/
|
|
129
|
+
|
|
130
|
+
# Celery stuff
|
|
131
|
+
celerybeat-schedule
|
|
132
|
+
celerybeat.pid
|
|
133
|
+
|
|
134
|
+
# Redis
|
|
135
|
+
*.rdb
|
|
136
|
+
*.aof
|
|
137
|
+
*.pid
|
|
138
|
+
|
|
139
|
+
# RabbitMQ
|
|
140
|
+
mnesia/
|
|
141
|
+
rabbitmq/
|
|
142
|
+
rabbitmq-data/
|
|
143
|
+
|
|
144
|
+
# ActiveMQ
|
|
145
|
+
activemq-data/
|
|
146
|
+
|
|
147
|
+
# SageMath parsed files
|
|
148
|
+
*.sage.py
|
|
149
|
+
|
|
150
|
+
# Environments
|
|
151
|
+
.env
|
|
152
|
+
.envrc
|
|
153
|
+
.venv
|
|
154
|
+
env/
|
|
155
|
+
venv/
|
|
156
|
+
ENV/
|
|
157
|
+
env.bak/
|
|
158
|
+
venv.bak/
|
|
159
|
+
|
|
160
|
+
# Spyder project settings
|
|
161
|
+
.spyderproject
|
|
162
|
+
.spyproject
|
|
163
|
+
|
|
164
|
+
# Rope project settings
|
|
165
|
+
.ropeproject
|
|
166
|
+
|
|
167
|
+
# mkdocs documentation
|
|
168
|
+
/site
|
|
169
|
+
|
|
170
|
+
# mypy
|
|
171
|
+
.mypy_cache/
|
|
172
|
+
.dmypy.json
|
|
173
|
+
dmypy.json
|
|
174
|
+
|
|
175
|
+
# Pyre type checker
|
|
176
|
+
.pyre/
|
|
177
|
+
|
|
178
|
+
# pytype static type analyzer
|
|
179
|
+
.pytype/
|
|
180
|
+
|
|
181
|
+
# Cython debug symbols
|
|
182
|
+
cython_debug/
|
|
183
|
+
|
|
184
|
+
# PyCharm
|
|
185
|
+
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
|
186
|
+
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
|
187
|
+
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
|
188
|
+
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
|
189
|
+
# .idea/
|
|
190
|
+
|
|
191
|
+
# Abstra
|
|
192
|
+
# Abstra is an AI-powered process automation framework.
|
|
193
|
+
# Ignore directories containing user credentials, local state, and settings.
|
|
194
|
+
# Learn more at https://abstra.io/docs
|
|
195
|
+
.abstra/
|
|
196
|
+
|
|
197
|
+
# Visual Studio Code
|
|
198
|
+
# Visual Studio Code specific template is maintained in a separate VisualStudioCode.gitignore
|
|
199
|
+
# that can be found at https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore
|
|
200
|
+
# and can be added to the global gitignore or merged into this file. However, if you prefer,
|
|
201
|
+
# you could uncomment the following to ignore the entire vscode folder
|
|
202
|
+
# .vscode/
|
|
203
|
+
# Temporary file for partial code execution
|
|
204
|
+
tempCodeRunnerFile.py
|
|
205
|
+
|
|
206
|
+
# Ruff stuff:
|
|
207
|
+
.ruff_cache/
|
|
208
|
+
|
|
209
|
+
# PyPI configuration file
|
|
210
|
+
.pypirc
|
|
211
|
+
|
|
212
|
+
# Marimo
|
|
213
|
+
marimo/_static/
|
|
214
|
+
marimo/_lsp/
|
|
215
|
+
__marimo__/
|
|
216
|
+
|
|
217
|
+
# Streamlit
|
|
218
|
+
.streamlit/secrets.toml
|
|
219
|
+
|
|
220
|
+
# thymus-bench generated output
|
|
221
|
+
results/
|
|
222
|
+
results_mem0/
|
|
223
|
+
|
|
224
|
+
# all benchmark output dirs
|
|
225
|
+
results*/
|
|
226
|
+
|
|
227
|
+
# library: do not pin the lockfile
|
|
228
|
+
uv.lock
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# Pre-commit hooks — run `uv run pre-commit install` once after cloning.
|
|
2
|
+
# On every commit: ruff lints (auto-fixing what it safely can) and formats the
|
|
3
|
+
# staged Python files. A commit is blocked if anything can't be auto-fixed.
|
|
4
|
+
#
|
|
5
|
+
# ruff pinned to match the version in pyproject's dev extra (keep them in sync).
|
|
6
|
+
repos:
|
|
7
|
+
- repo: https://github.com/astral-sh/ruff-pre-commit
|
|
8
|
+
rev: v0.15.20
|
|
9
|
+
hooks:
|
|
10
|
+
- id: ruff-check
|
|
11
|
+
args: [--fix]
|
|
12
|
+
- id: ruff-format
|
|
13
|
+
|
|
14
|
+
# Small, universal hygiene checks (no Python-code reformatting).
|
|
15
|
+
- repo: https://github.com/pre-commit/pre-commit-hooks
|
|
16
|
+
rev: v6.0.0
|
|
17
|
+
hooks:
|
|
18
|
+
- id: trailing-whitespace
|
|
19
|
+
- id: end-of-file-fixer
|
|
20
|
+
- id: check-yaml
|
|
21
|
+
- id: check-toml
|
|
22
|
+
- id: check-merge-conflict
|
|
23
|
+
- id: check-added-large-files
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
# CLAUDE.md — repo map & conventions
|
|
2
|
+
|
|
3
|
+
Orientation for anyone (human or AI) working in this repo. Read this first so you
|
|
4
|
+
don't have to re-derive the structure every session.
|
|
5
|
+
|
|
6
|
+
## What this is
|
|
7
|
+
|
|
8
|
+
**Thymus** — a trust & hygiene layer ("immune system") for AI agent memory. It
|
|
9
|
+
screens every memory on the **write path** (provenance + injection + exfil
|
|
10
|
+
detection → trust score → **admit / tag / quarantine**) so poisoned memories never
|
|
11
|
+
reach the store (mem0, Zep, Letta, …). Targets OWASP Agentic Top-10 **ASI06**
|
|
12
|
+
(Memory & Context Poisoning).
|
|
13
|
+
|
|
14
|
+
Two things live here: **`thymus/`** = the product (the firewall library), and
|
|
15
|
+
**`thymus_bench/`** = the poisoning benchmark that proves it. On the benchmark,
|
|
16
|
+
Thymus takes attack-success from **100% → 0%** on live mem0 with **0%** false
|
|
17
|
+
positives.
|
|
18
|
+
|
|
19
|
+
## Conventions (important)
|
|
20
|
+
|
|
21
|
+
- **Python env: use `uv`** — `uv venv`, `uv pip install`, `uv run python ...`. Not
|
|
22
|
+
pip/venv (system Python is Homebrew/PEP-668). Deps: `uv pip install mem0ai pillow`.
|
|
23
|
+
- **Lint + format: `ruff`** (dev dep; config in `pyproject.toml`). `uv run ruff check .`
|
|
24
|
+
and `uv run ruff format .` before committing. Note: detectors auto-register on
|
|
25
|
+
import, so before deleting any "unused" `import thymus` verify the file keeps a
|
|
26
|
+
`from thymus import …` (that runs `thymus/__init__.py` and does the registration).
|
|
27
|
+
- **Git commits: no `Co-Authored-By` / AI-attribution trailer.** Plain messages.
|
|
28
|
+
- **mem0 key** lives in `.env` (gitignored) as `MEM0_API_KEY=m0-...`. It's a dummy
|
|
29
|
+
account — safe to experiment. Load it into the shell before mem0 runs.
|
|
30
|
+
- **Generated output** (`results*/`, `.venv/`, `__pycache__/`) is gitignored.
|
|
31
|
+
|
|
32
|
+
## How to run
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
uv run python run_baseline.py # benchmark, in-memory, no keys
|
|
36
|
+
uv run python run_baseline.py --guard thymus # with the firewall
|
|
37
|
+
export MEM0_API_KEY=m0-... # (or source .env)
|
|
38
|
+
uv run python run_baseline.py --backend mem0 --guard thymus # vs live mem0
|
|
39
|
+
uv run python tests/test_thymus_engine.py # Phase 1 acceptance
|
|
40
|
+
uv run python demo/poison_demo.py --user acme-support-agent # live attack demo
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Directory hierarchy
|
|
44
|
+
|
|
45
|
+
```
|
|
46
|
+
Thymus/
|
|
47
|
+
├── CLAUDE.md # this file
|
|
48
|
+
├── README.md # public overview + benchmark results + demo gif
|
|
49
|
+
├── Thymus_Product_Roadmap.md # full phased plan (Phases 0–5)
|
|
50
|
+
├── Thymus_Phase0_Schemas.md # MemoryEnvelope + TrustPolicy JSON schemas + bench spec
|
|
51
|
+
├── requirements.txt # notes on optional mem0 install (stdlib-only core)
|
|
52
|
+
├── run_baseline.py # benchmark CLI entry point (--backend/--guard/--out/--verbose)
|
|
53
|
+
├── .env # local mem0 key (gitignored)
|
|
54
|
+
│
|
|
55
|
+
├── thymus/ # ★ THE PRODUCT — the write-path firewall library
|
|
56
|
+
│ ├── README.md # package docs + how to extend (detectors/adapters)
|
|
57
|
+
│ ├── __init__.py # public API: screen(), wrap(), types, engine
|
|
58
|
+
│ ├── types.py # Candidate, Signal, Assessment, Verdict, enums (the vocabulary)
|
|
59
|
+
│ ├── engine.py # ScreeningEngine.screen / screen_read (runs detectors + policy + ledger)
|
|
60
|
+
│ ├── policy.py # TrustPolicy: base trust per tier − detector deltas → verdict + severity floor
|
|
61
|
+
│ ├── feedback.py # [P2] TrustLedger: review outcomes (mark_safe/release/delete) adjust origin trust
|
|
62
|
+
│ ├── detectors/ # ── EXTENSION POINT 1: pluggable detection ──
|
|
63
|
+
│ │ ├── __init__.py # registry + auto-registers built-ins; DEFAULT_DETECTORS
|
|
64
|
+
│ │ ├── base.py # Detector protocol (inspect(candidate, context)) + registry
|
|
65
|
+
│ │ ├── origin.py # provenance detector → 'untrusted_origin' flag
|
|
66
|
+
│ │ ├── injection.py # ★ precision-critical: override + sensitive-action + third-party
|
|
67
|
+
│ │ │ # scope, minus self-reference (catches MINJA, passes "call me Sam")
|
|
68
|
+
│ │ ├── egress.py # exfil detector: send-verb + external email/URL
|
|
69
|
+
│ │ └── conflict.py # [P2] fact-conflict: contradicts an existing high-trust memory
|
|
70
|
+
│ ├── rules/ # ── PACKAGE B: the declarative rule library (the moat) ──
|
|
71
|
+
│ │ ├── __init__.py # registers the `rules` detector into DEFAULT_DETECTORS
|
|
72
|
+
│ │ ├── loader.py # Rule + RuleDetector; co-occurrence matching (all_of/any_of, re:)
|
|
73
|
+
│ │ └── packs/*.json # 14 curated packs / 32 rules: financial, exfiltration, auth_bypass,
|
|
74
|
+
│ │ │ # safety_suppression, manipulation, secrets_pii, privilege_escalation,
|
|
75
|
+
│ │ │ # tool_abuse, persistence, impersonation, obfuscation,
|
|
76
|
+
│ │ │ # prompt_extraction, policy_override, ops_redirect
|
|
77
|
+
│ └── adapters/ # ── EXTENSION POINT 2: framework bindings ──
|
|
78
|
+
│ ├── __init__.py # re-exports MemoryAdapter, ProtectedMemory, wrap, GenericAdapter, ...
|
|
79
|
+
│ ├── base.py # MemoryAdapter ABC + ProtectedMemory (write screen + [P2] read screen) + wrap()
|
|
80
|
+
│ ├── mem0.py # Mem0Adapter (hosted MemoryClient + OSS Memory). New framework = copy this.
|
|
81
|
+
│ ├── generic.py # [P2] DictStore + GenericAdapter — dependency-free, fully testable
|
|
82
|
+
│ └── zep.py # [P2] Zep Cloud adapter (LIVE): thread.add_messages write, graph.search read
|
|
83
|
+
│ └── remote.py # [P3] HttpAuditSink (→ console; non-blocking bg worker, sync block_on_quarantine) +
|
|
84
|
+
│ # remote_engine (console-tuned thresholds/trust/detectors + custom rules)
|
|
85
|
+
│
|
|
86
|
+
├── thymus_bench/ # ★ THE BENCHMARK — measures poisoning resistance
|
|
87
|
+
│ ├── __init__.py
|
|
88
|
+
│ ├── runner.py # plant corpus → wait for indexing → screen → fire triggers → metrics
|
|
89
|
+
│ │ # (content-based detection; guard swap = baseline vs protected)
|
|
90
|
+
│ ├── metrics.py # ASR / IAR / RCR / FPR / HN-FPR definitions
|
|
91
|
+
│ ├── report.py # console table + JSON + shareable Markdown ("mem0 raw vs +Thymus")
|
|
92
|
+
│ ├── rule_report.py # [Pkg B] per-rule precision report: attack coverage + per-rule FP rate
|
|
93
|
+
│ ├── backends/ # memory stores under test
|
|
94
|
+
│ │ ├── base.py # MemoryBackend protocol (add/search/get_all/reset) + Memory dataclass
|
|
95
|
+
│ │ ├── inmemory.py # zero-dep store mimicking mem0 semantics (token-overlap retrieval)
|
|
96
|
+
│ │ └── mem0_backend.py # real mem0 (hosted MemoryClient/OSS); async wait_ready, v2 search, namespacing
|
|
97
|
+
│ ├── guards/ # write-path guards the benchmark swaps between
|
|
98
|
+
│ │ ├── base.py # Guard protocol + benchmark-local Candidate/Assessment/Verdict
|
|
99
|
+
│ │ ├── null_guard.py # BASELINE: admits everything (the stock store)
|
|
100
|
+
│ │ └── thymus_guard.py # bridges the benchmark to the real thymus.ScreeningEngine
|
|
101
|
+
│ └── corpus/ # labeled test data
|
|
102
|
+
│ ├── minja_attacks.json # 8 query-only injection attacks (poison + trigger query + keywords)
|
|
103
|
+
│ ├── benign_facts.json # 12 safe memories (the false-positive denominator)
|
|
104
|
+
│ ├── hard_negatives.json # 6 legit imperatives ("always call me Sam") — must NOT be blocked
|
|
105
|
+
│ └── conflict_cases.json # [P2] labeled contradiction/compatible pairs for conflict precision/recall
|
|
106
|
+
│
|
|
107
|
+
├── tests/
|
|
108
|
+
│ ├── test_thymus_engine.py # Phase 1 acceptance: attacks blocked, benign+hard-neg admitted, wrap() blocks
|
|
109
|
+
│ ├── test_conflict.py # [P2] fact-conflict: contradiction flagged, compatible fact admitted
|
|
110
|
+
│ ├── test_conflict_precision.py# [P2] conflict precision/recall on conflict_cases.json (100%/100%)
|
|
111
|
+
│ ├── test_read_path.py # [P2] read-path: 8/8 pre-seeded poisons contained at retrieval (retroactive)
|
|
112
|
+
│ ├── test_feedback.py # [P2] trust ledger: mark_safe cuts repeat FPs; delete lowers origin trust
|
|
113
|
+
│ ├── test_rules.py # [Pkg B] packs load; every attack hits a rule; 0 rule false positives
|
|
114
|
+
│ ├── test_zep.py # [P2] LIVE Zep integration (skips w/o ZEP_API_KEY): poison blocked before Zep
|
|
115
|
+
│ ├── test_console_roundtrip.py # [P3] LIVE blocking round-trip vs a running thymus-console (skips if unreachable)
|
|
116
|
+
│ ├── test_remote_policy.py # [P3] console-edited policy (thresholds/trust/custom rules) changes the SDK engine — unit + LIVE
|
|
117
|
+
│ └── test_guard_swap.py # harness sanity: a demo guard drops ASR without false positives
|
|
118
|
+
│
|
|
119
|
+
└── demo/ # launch/marketing assets
|
|
120
|
+
├── README.md # how to run live, record, and regenerate
|
|
121
|
+
├── poison_demo.py # 3-beat live attack vs real mem0 (--user, --auto, --keep); prints dashboard cue
|
|
122
|
+
├── poison_demo.tape # VHS script to render the terminal video headlessly
|
|
123
|
+
├── poison_demo.mp4/.gif # terminal-only recording
|
|
124
|
+
└── poison_demo_with_dashboard.mp4/.gif # terminal + real mem0 dashboard proof (embedded in README)
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
## The core flow (how a decision is made)
|
|
128
|
+
|
|
129
|
+
```
|
|
130
|
+
Candidate ─▶ [ detectors ] ─▶ signals ─▶ [ policy ] ─▶ Assessment
|
|
131
|
+
origin/injection/egress base trust(tier) verdict:
|
|
132
|
+
(each emits Signal: − Σ trust_delta admit / tag /
|
|
133
|
+
flag + trust_delta) → thresholds + floor quarantine
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
- **admit** = store clean · **tag** = store but flagged/down-weighted · **quarantine** = never stored, held for review.
|
|
137
|
+
- Verdict combination is most-restrictive-wins. Policy is tier-aware (untrusted origin lowers base trust) with a severity floor (a critical finding from an attacker-reachable origin is never silently admitted).
|
|
138
|
+
|
|
139
|
+
## Extending it
|
|
140
|
+
|
|
141
|
+
- **New detector**: `@thymus.detectors.register` a class with `name` + `inspect(candidate)->list[Signal]`. No core change.
|
|
142
|
+
- **New framework** (Zep/Letta/LangMem): subclass `thymus.adapters.base.MemoryAdapter`, implement `store_add` / `store_search`. Nothing else changes.
|
|
143
|
+
|
|
144
|
+
## Status
|
|
145
|
+
|
|
146
|
+
- **Phase 0** (schemas + benchmark) — done. See `Thymus_Phase0_Schemas.md`.
|
|
147
|
+
- **Phase 1** (write-path firewall) — done. `thymus/` package; 100%→0% ASR on live mem0.
|
|
148
|
+
- **Phase 2** (read-path + depth) — done (except access-blocked adapters). Read-path re-screening
|
|
149
|
+
(retroactive protection, 8/8 containment), fact-conflict detector (100%/100% precision/recall on
|
|
150
|
+
conflict_cases), feedback loop (TrustLedger), generic adapter. Detector contract carries `ScreenContext`.
|
|
151
|
+
Zep adapter is LIVE + integration-tested (`test_zep.py`). DEFERRED: Letta adapter (no account).
|
|
152
|
+
- **Package B** (rule library / the moat) — Declarative JSON rule packs + `RuleDetector`
|
|
153
|
+
(`thymus/rules/`), **32 rules across 14 packs** (financial, exfiltration, auth_bypass, safety_suppression,
|
|
154
|
+
manipulation, secrets_pii, privilege_escalation, tool_abuse, persistence, impersonation, obfuscation,
|
|
155
|
+
prompt_extraction, policy_override, ops_redirect), per-rule precision report (`thymus_bench/rule_report.py`):
|
|
156
|
+
18-attack corpus, 100% attack coverage, 100% precision per rule (only the fuzzy `injection` code detector
|
|
157
|
+
carries FPs, by design). Ongoing track: keep growing packs + corpus.
|
|
158
|
+
- **Phase 3** (hosted console) — essentially done, in the separate `UltronLabs/thymus-console` repo
|
|
159
|
+
(Next.js 16 + Prisma 7 + Clerk auth/orgs). Done: dashboard/quarantine/audit/evidence pages, real
|
|
160
|
+
per-tenant data isolation (Clerk org or personal user id), per-tenant API keys, realtime SSE
|
|
161
|
+
streaming, the blocking approval round-trip (`thymus.remote.HttpAuditSink(block_on_quarantine=True)`
|
|
162
|
+
+ console `GET /api/decisions/:id/status`, tested live via `test_console_roundtrip.py`), and policy
|
|
163
|
+
tuning — the FULL policy editor: quarantine/tag thresholds, per-tier base trust, floor severity,
|
|
164
|
+
detector on/off, per-built-in-rule on/off, and **custom rule authoring** (same `all_of` co-occurrence
|
|
165
|
+
format as the packs), with a draft→publish→numbered-version model and one-click rollback. Edited in
|
|
166
|
+
Settings, served to the SDK via `thymus.remote.remote_engine()`, which builds the `TrustPolicy` +
|
|
167
|
+
a `RuleDetector` (built-ins minus disabled + custom rules); tested via `test_remote_policy.py`
|
|
168
|
+
(unit + live). Remaining: Clerk production instance, webhook registration (needs a public URL),
|
|
169
|
+
the actual Railway deploy.
|
|
170
|
+
- **Next**: deploy `thymus-console` (Railway) for real launch, or start the launch/validation track.
|