datalayer-agent-shield 0.1.1__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.
- datalayer_agent_shield-0.1.1/.gitignore +141 -0
- datalayer_agent_shield-0.1.1/CHANGELOG.md +43 -0
- datalayer_agent_shield-0.1.1/LICENSE +29 -0
- datalayer_agent_shield-0.1.1/PKG-INFO +225 -0
- datalayer_agent_shield-0.1.1/README.md +152 -0
- datalayer_agent_shield-0.1.1/agent_shield/__init__.py +15 -0
- datalayer_agent_shield-0.1.1/agent_shield/__version__.py +7 -0
- datalayer_agent_shield-0.1.1/agent_shield/acp/__init__.py +36 -0
- datalayer_agent_shield-0.1.1/agent_shield/acp/channel.py +444 -0
- datalayer_agent_shield-0.1.1/agent_shield/acp/events.py +219 -0
- datalayer_agent_shield-0.1.1/agent_shield/acp/normalize.py +133 -0
- datalayer_agent_shield-0.1.1/agent_shield/acp/proxy.py +944 -0
- datalayer_agent_shield-0.1.1/agent_shield/acp/workspace.py +230 -0
- datalayer_agent_shield-0.1.1/agent_shield/audit.py +141 -0
- datalayer_agent_shield-0.1.1/agent_shield/cli.py +536 -0
- datalayer_agent_shield-0.1.1/agent_shield/config.py +89 -0
- datalayer_agent_shield-0.1.1/agent_shield/policy/__init__.py +87 -0
- datalayer_agent_shield-0.1.1/agent_shield/policy/classify.py +83 -0
- datalayer_agent_shield-0.1.1/agent_shield/policy/decide.py +264 -0
- datalayer_agent_shield-0.1.1/agent_shield/policy/default.py +57 -0
- datalayer_agent_shield-0.1.1/agent_shield/policy/files.py +91 -0
- datalayer_agent_shield-0.1.1/agent_shield/policy/model.py +207 -0
- datalayer_agent_shield-0.1.1/agent_shield/reactor_extension.py +65 -0
- datalayer_agent_shield-0.1.1/agent_shield/register/__init__.py +214 -0
- datalayer_agent_shield-0.1.1/docs/LICENSE +21 -0
- datalayer_agent_shield-0.1.1/docs/README.md +38 -0
- datalayer_agent_shield-0.1.1/pyproject.toml +134 -0
- datalayer_agent_shield-0.1.1/schema/policy.schema.json +259 -0
- datalayer_agent_shield-0.1.1/tests/__init__.py +5 -0
- datalayer_agent_shield-0.1.1/tests/fixtures/fake_agent.py +233 -0
- datalayer_agent_shield-0.1.1/tests/test_channel.py +205 -0
- datalayer_agent_shield-0.1.1/tests/test_cli.py +218 -0
- datalayer_agent_shield-0.1.1/tests/test_normalize.py +131 -0
- datalayer_agent_shield-0.1.1/tests/test_policy.py +454 -0
- datalayer_agent_shield-0.1.1/tests/test_register.py +88 -0
- datalayer_agent_shield-0.1.1/tests/test_workspace_and_audit.py +161 -0
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
*.egg-info/
|
|
2
|
+
.ipynb_checkpoints
|
|
3
|
+
|
|
4
|
+
# Node / TypeScript (the @datalayer/agent-shield package)
|
|
5
|
+
node_modules/
|
|
6
|
+
*.tsbuildinfo
|
|
7
|
+
package-lock.json
|
|
8
|
+
|
|
9
|
+
# Created by https://www.gitignore.io/api/python
|
|
10
|
+
# Edit at https://www.gitignore.io/?templates=python
|
|
11
|
+
|
|
12
|
+
### Python ###
|
|
13
|
+
# Byte-compiled / optimized / DLL files
|
|
14
|
+
__pycache__/
|
|
15
|
+
*.py[cod]
|
|
16
|
+
*$py.class
|
|
17
|
+
|
|
18
|
+
# C extensions
|
|
19
|
+
*.so
|
|
20
|
+
|
|
21
|
+
# Distribution / packaging
|
|
22
|
+
.Python
|
|
23
|
+
build/
|
|
24
|
+
dist/
|
|
25
|
+
downloads/
|
|
26
|
+
lib/
|
|
27
|
+
lib64/
|
|
28
|
+
parts/
|
|
29
|
+
sdist/
|
|
30
|
+
var/
|
|
31
|
+
wheels/
|
|
32
|
+
pip-wheel-metadata/
|
|
33
|
+
share/python-wheels/
|
|
34
|
+
.installed.cfg
|
|
35
|
+
|
|
36
|
+
# PyInstaller
|
|
37
|
+
# Usually these files are written by a python script from a template
|
|
38
|
+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
|
39
|
+
*.manifest
|
|
40
|
+
*.spec
|
|
41
|
+
|
|
42
|
+
# Installer logs
|
|
43
|
+
pip-log.txt
|
|
44
|
+
pip-delete-this-directory.txt
|
|
45
|
+
|
|
46
|
+
# Unit test / coverage reports
|
|
47
|
+
htmlcov/
|
|
48
|
+
.tox/
|
|
49
|
+
.nox/
|
|
50
|
+
.coverage
|
|
51
|
+
.coverage.*
|
|
52
|
+
.cache
|
|
53
|
+
nosetests.xml
|
|
54
|
+
coverage.xml
|
|
55
|
+
*.cover
|
|
56
|
+
.hypothesis/
|
|
57
|
+
.pytest_cache/
|
|
58
|
+
|
|
59
|
+
# Translations
|
|
60
|
+
*.mo
|
|
61
|
+
*.pot
|
|
62
|
+
|
|
63
|
+
# Environment variables:
|
|
64
|
+
.env
|
|
65
|
+
|
|
66
|
+
# Scrapy stuff:
|
|
67
|
+
.scrapy
|
|
68
|
+
|
|
69
|
+
# Sphinx documentation
|
|
70
|
+
docs/_build/
|
|
71
|
+
|
|
72
|
+
# PyBuilder
|
|
73
|
+
target/
|
|
74
|
+
|
|
75
|
+
# pyenv
|
|
76
|
+
.python-version
|
|
77
|
+
|
|
78
|
+
# celery beat schedule file
|
|
79
|
+
celerybeat-schedule
|
|
80
|
+
|
|
81
|
+
# SageMath parsed files
|
|
82
|
+
*.sage.py
|
|
83
|
+
|
|
84
|
+
# Spyder project settings
|
|
85
|
+
.spyderproject
|
|
86
|
+
.spyproject
|
|
87
|
+
|
|
88
|
+
# Rope project settings
|
|
89
|
+
.ropeproject
|
|
90
|
+
|
|
91
|
+
# Mr Developer
|
|
92
|
+
.mr.developer.cfg
|
|
93
|
+
.project
|
|
94
|
+
.pydevproject
|
|
95
|
+
|
|
96
|
+
# mkdocs documentation
|
|
97
|
+
/site
|
|
98
|
+
|
|
99
|
+
# mypy
|
|
100
|
+
.mypy_cache/
|
|
101
|
+
.dmypy.json
|
|
102
|
+
dmypy.json
|
|
103
|
+
|
|
104
|
+
# ruff
|
|
105
|
+
.ruff_cache
|
|
106
|
+
|
|
107
|
+
# Pyre type checker
|
|
108
|
+
.pyre/
|
|
109
|
+
|
|
110
|
+
# End of https://www.gitignore.io/api/python
|
|
111
|
+
|
|
112
|
+
# OSX files
|
|
113
|
+
.DS_Store
|
|
114
|
+
|
|
115
|
+
# Include
|
|
116
|
+
!**/.*ignore
|
|
117
|
+
!**/.*rc
|
|
118
|
+
!**/.*rc.js
|
|
119
|
+
!**/.*rc.json
|
|
120
|
+
!**/.*rc.yml
|
|
121
|
+
!**/.*config
|
|
122
|
+
!*.*rc.json
|
|
123
|
+
!.github
|
|
124
|
+
!.devcontainer
|
|
125
|
+
|
|
126
|
+
untracked_notebooks/*
|
|
127
|
+
.jupyter_ystore
|
|
128
|
+
.jupyter_ystore.db
|
|
129
|
+
docs/.yarn/*
|
|
130
|
+
|
|
131
|
+
uv.lock
|
|
132
|
+
|
|
133
|
+
generated
|
|
134
|
+
skills
|
|
135
|
+
examples/agent/*.txt
|
|
136
|
+
|
|
137
|
+
launcher.py
|
|
138
|
+
|
|
139
|
+
!docs/docs/skills
|
|
140
|
+
!examples/**/skills
|
|
141
|
+
!skills
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
~ Copyright (c) 2025-2026 Datalayer, Inc.
|
|
3
|
+
~
|
|
4
|
+
~ BSD 3-Clause License
|
|
5
|
+
-->
|
|
6
|
+
|
|
7
|
+
# Changelog
|
|
8
|
+
|
|
9
|
+
## 0.1.1
|
|
10
|
+
|
|
11
|
+
The package made real (SHIELD.md Phase 0: SH-07, SH-02, SH-08, SH-09, SH-04).
|
|
12
|
+
|
|
13
|
+
- `agent_shield.policy`: the policy document (one schema, published as
|
|
14
|
+
`schema/policy.schema.json`), the six classes of STUDIO F-10, three scopes
|
|
15
|
+
strictest first, *first decision wins*, stateful limits with soft and hard
|
|
16
|
+
thresholds, `decide(action, context, policies) -> Decision` pure and tested;
|
|
17
|
+
the default policy of decision 5.
|
|
18
|
+
- `agent_shield.register`: the harness register read from spec-shaped
|
|
19
|
+
documents, with `claude-code`, `codex`, `gemini-cli`, `goose`, `opencode`
|
|
20
|
+
and `openhands`, each with its terms, credential kind, pinned version and
|
|
21
|
+
where it was checked; a harness without terms is not in the catalogue.
|
|
22
|
+
- `agent_shield.acp`: the stdio and WebSocket `ACPChannel` on the official
|
|
23
|
+
SDK (protocol version 1), the normaliser of `session/update` into the
|
|
24
|
+
package's neutral events (a malformed update ends the turn in a sentence),
|
|
25
|
+
the workspace that answers `fs/*` and `terminal/*`, and the **proxy**:
|
|
26
|
+
`agent-shield run -- <command>`, an ACP agent in front of the agent it
|
|
27
|
+
launched, permissions decided by the policy, tool calls classed and
|
|
28
|
+
limited, files and terminals confined to the workspace, `mcpServers`
|
|
29
|
+
rewritten to the allow-list, every action in `~/.agent-shield/audit.jsonl`.
|
|
30
|
+
- `agent-shield setup | run | log | policy show | policy schema | policy init |
|
|
31
|
+
harnesses | upgrade [--check] | uninstall`; `serve` says it is not built.
|
|
32
|
+
- `@datalayer/agent-shield`: the policy types and the decisions' sentences,
|
|
33
|
+
checked against the same schema in `npm test`.
|
|
34
|
+
- The agent-teams scaffold's leftovers removed (the demo extra, the
|
|
35
|
+
orchestration extension, the team CI step); the `datalayer.cli` and
|
|
36
|
+
`datalayer.reactor.cli` entry points real and import-safe.
|
|
37
|
+
|
|
38
|
+
## 0.1.0
|
|
39
|
+
|
|
40
|
+
The name reservation: the Python distribution is **`datalayer-agent-shield`**
|
|
41
|
+
(PyPI refused `agent-shield` as too close to an existing project); the import
|
|
42
|
+
stays `agent_shield`, the npm package `@datalayer/agent-shield`, the repository
|
|
43
|
+
`datalayer/agent-shield`. Nothing but the version was in it.
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
BSD 3-Clause License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025, Datalayer
|
|
4
|
+
All rights reserved.
|
|
5
|
+
|
|
6
|
+
Redistribution and use in source and binary forms, with or without
|
|
7
|
+
modification, are permitted provided that the following conditions are met:
|
|
8
|
+
|
|
9
|
+
1. Redistributions of source code must retain the above copyright notice, this
|
|
10
|
+
list of conditions and the following disclaimer.
|
|
11
|
+
|
|
12
|
+
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
13
|
+
this list of conditions and the following disclaimer in the documentation
|
|
14
|
+
and/or other materials provided with the distribution.
|
|
15
|
+
|
|
16
|
+
3. Neither the name of the copyright holder nor the names of its
|
|
17
|
+
contributors may be used to endorse or promote products derived from
|
|
18
|
+
this software without specific prior written permission.
|
|
19
|
+
|
|
20
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
21
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
22
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
23
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
24
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
25
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
26
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
27
|
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
28
|
+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
29
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: datalayer-agent-shield
|
|
3
|
+
Version: 0.1.1
|
|
4
|
+
Summary: Datalayer Agent Shield: a runtime firewall for every agent
|
|
5
|
+
Project-URL: Home, https://github.com/datalayer/agent-shield
|
|
6
|
+
Project-URL: Documentation, https://agent-shield.datalayer.tech
|
|
7
|
+
Author-email: Datalayer <info@datalayer.io>
|
|
8
|
+
License: BSD 3-Clause License
|
|
9
|
+
|
|
10
|
+
Copyright (c) 2025, Datalayer
|
|
11
|
+
All rights reserved.
|
|
12
|
+
|
|
13
|
+
Redistribution and use in source and binary forms, with or without
|
|
14
|
+
modification, are permitted provided that the following conditions are met:
|
|
15
|
+
|
|
16
|
+
1. Redistributions of source code must retain the above copyright notice, this
|
|
17
|
+
list of conditions and the following disclaimer.
|
|
18
|
+
|
|
19
|
+
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
20
|
+
this list of conditions and the following disclaimer in the documentation
|
|
21
|
+
and/or other materials provided with the distribution.
|
|
22
|
+
|
|
23
|
+
3. Neither the name of the copyright holder nor the names of its
|
|
24
|
+
contributors may be used to endorse or promote products derived from
|
|
25
|
+
this software without specific prior written permission.
|
|
26
|
+
|
|
27
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
28
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
29
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
30
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
31
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
32
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
33
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
34
|
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
35
|
+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
36
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
37
|
+
License-File: LICENSE
|
|
38
|
+
Keywords: ACP,AI,Agents,Datalayer,Firewall,Policy,Shield
|
|
39
|
+
Classifier: Intended Audience :: Developers
|
|
40
|
+
Classifier: Intended Audience :: System Administrators
|
|
41
|
+
Classifier: License :: OSI Approved :: BSD License
|
|
42
|
+
Classifier: Programming Language :: Python
|
|
43
|
+
Classifier: Programming Language :: Python :: 3
|
|
44
|
+
Requires-Python: >=3.10
|
|
45
|
+
Requires-Dist: agent-client-protocol<0.8,>=0.7.1
|
|
46
|
+
Requires-Dist: agentspecs>=0.0.100
|
|
47
|
+
Requires-Dist: pydantic>=2.0
|
|
48
|
+
Requires-Dist: pyyaml>=6.0
|
|
49
|
+
Requires-Dist: typer>=0.9
|
|
50
|
+
Provides-Extra: all
|
|
51
|
+
Requires-Dist: websockets>=13; extra == 'all'
|
|
52
|
+
Provides-Extra: claude-code
|
|
53
|
+
Provides-Extra: codex
|
|
54
|
+
Provides-Extra: gemini-cli
|
|
55
|
+
Provides-Extra: goose
|
|
56
|
+
Provides-Extra: lint
|
|
57
|
+
Requires-Dist: mdformat-gfm>=0.3.5; extra == 'lint'
|
|
58
|
+
Requires-Dist: mdformat>0.7; extra == 'lint'
|
|
59
|
+
Requires-Dist: ruff; extra == 'lint'
|
|
60
|
+
Provides-Extra: opencode
|
|
61
|
+
Provides-Extra: openhands
|
|
62
|
+
Provides-Extra: test
|
|
63
|
+
Requires-Dist: pytest-asyncio>=0.21; extra == 'test'
|
|
64
|
+
Requires-Dist: pytest-timeout>=2.0; extra == 'test'
|
|
65
|
+
Requires-Dist: pytest>=7.0; extra == 'test'
|
|
66
|
+
Requires-Dist: tomli>=2; (python_version < '3.11') and extra == 'test'
|
|
67
|
+
Requires-Dist: websockets>=13; extra == 'test'
|
|
68
|
+
Provides-Extra: typing
|
|
69
|
+
Requires-Dist: mypy>=0.990; extra == 'typing'
|
|
70
|
+
Provides-Extra: websocket
|
|
71
|
+
Requires-Dist: websockets>=13; extra == 'websocket'
|
|
72
|
+
Description-Content-Type: text/markdown
|
|
73
|
+
|
|
74
|
+
<!--
|
|
75
|
+
~ Copyright (c) 2025-2026 Datalayer, Inc.
|
|
76
|
+
~
|
|
77
|
+
~ BSD 3-Clause License
|
|
78
|
+
-->
|
|
79
|
+
|
|
80
|
+
[](https://datalayer.ai)
|
|
81
|
+
|
|
82
|
+
[](https://github.com/sponsors/datalayer)
|
|
83
|
+
|
|
84
|
+
# 🤖 🛡️ Agent Shield
|
|
85
|
+
|
|
86
|
+
[](https://pypi.org/project/datalayer-agent-shield)
|
|
87
|
+
[](https://www.npmjs.com/package/@datalayer/agent-shield)
|
|
88
|
+
|
|
89
|
+
**A runtime firewall for every agent.** Deploy a shield with each agent. Enforce
|
|
90
|
+
permissions before actions execute, manage policies centrally, and retain an
|
|
91
|
+
audit trail.
|
|
92
|
+
|
|
93
|
+
The Shield stands between an agent and the world. Its first shippable is the
|
|
94
|
+
**proxy**: `agent-shield run -- <an ACP agent's command>` starts the agent as
|
|
95
|
+
its subprocess and stands as an [Agent Client Protocol](https://agentclientprotocol.com)
|
|
96
|
+
agent on its own stdio, so any ACP client — Zed, `vscode-acp`, Marimo,
|
|
97
|
+
Datalayer's runtimes — talks to the Shield and the Shield talks to the agent.
|
|
98
|
+
In between it decides:
|
|
99
|
+
|
|
100
|
+
- `session/request_permission` by the policy: *do it* is answered without the
|
|
101
|
+
client, *ask me first* is forwarded with the same options and answered as
|
|
102
|
+
the person chose, *leave it to me* is refused in a sentence the person sees;
|
|
103
|
+
- the agent's `tool_call` notifications are classed (ACP `kind` → `read`,
|
|
104
|
+
`write`, `delete`, `execute`; `other` fails closed to `write`) and held to
|
|
105
|
+
the session's limits; a refusal cancels the turn and says why;
|
|
106
|
+
- `fs/*` and `terminal/*` are answered by the Shield against the **workspace**
|
|
107
|
+
— a path outside it is refused, a command runs with a trimmed environment
|
|
108
|
+
— and every one is a tool row the client sees;
|
|
109
|
+
- `mcpServers` are rewritten so the agent gets only what the policy lends;
|
|
110
|
+
- every action and decision is a line of `~/.agent-shield/audit.jsonl`,
|
|
111
|
+
credentials redacted, a file's content never in it.
|
|
112
|
+
|
|
113
|
+
## The first five minutes
|
|
114
|
+
|
|
115
|
+
```bash
|
|
116
|
+
pip install "datalayer-agent-shield[claude-code]" # one extra per harness
|
|
117
|
+
agent-shield setup # finds node, the vendor CLIs and the credentials present; installs the pinned adapter into ~/.agent-shield/harnesses/<id>@<version>; writes agent-shield.yaml beside the project
|
|
118
|
+
agent-shield run -- claude-agent-acp # Claude Code behind the Shield, on this directory, with the default policy
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
Measured on 15 October 2026 from a clean prefix: `setup` took **27 s**
|
|
122
|
+
(the adapter `@agentclientprotocol/claude-agent-acp@0.91.0`, 325 MB, through
|
|
123
|
+
`npm install --prefix`, never a global `npx`); `run` initialized the adapter
|
|
124
|
+
and opened a session in **1.5 s**; the first decision followed the first tool
|
|
125
|
+
call. The whole, from `pip install` to a decision seen in the terminal, is
|
|
126
|
+
under a minute, five times under the five minutes of SHIELD §5.3.
|
|
127
|
+
|
|
128
|
+
`setup` asks for nothing it finds: it says which credentials are present by
|
|
129
|
+
name (never a value), which are missing and how to export them, and stores
|
|
130
|
+
no secret. It refuses, in a sentence, a credential the register forbids — a
|
|
131
|
+
Claude Code or ChatGPT subscription login where only an API key serves to
|
|
132
|
+
third parties, a Google login on Gemini's individuals tier.
|
|
133
|
+
|
|
134
|
+
Then:
|
|
135
|
+
|
|
136
|
+
```bash
|
|
137
|
+
agent-shield log # the audit, one line per action and decision
|
|
138
|
+
agent-shield policy show # every line of the policy in force, and where it came from
|
|
139
|
+
agent-shield policy schema # the document's JSON Schema
|
|
140
|
+
agent-shield harnesses # the register: each harness, its terms, where they were read
|
|
141
|
+
agent-shield upgrade --check # a newer Shield or adapter?
|
|
142
|
+
agent-shield upgrade # move the adapters to the register's pinned versions
|
|
143
|
+
agent-shield uninstall # previews; --yes removes the adapters and the config, keeps the audit and the policy
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
Zed, `settings.json`:
|
|
147
|
+
|
|
148
|
+
```json
|
|
149
|
+
"agent_servers": {
|
|
150
|
+
"Claude Code behind the Shield": { "command": "agent-shield", "args": ["run", "--", "claude-agent-acp"] }
|
|
151
|
+
}
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
## The policy
|
|
155
|
+
|
|
156
|
+
One document per scope — `organization`, `team`, `session` — in
|
|
157
|
+
`agent-shield.yaml` beside the project (else `~/.agent-shield/agent-shield.yaml`),
|
|
158
|
+
with the schema in [`schema/policy.schema.json`](schema/policy.schema.json).
|
|
159
|
+
The strictest scope decides first and **the first decision wins**; a scope
|
|
160
|
+
that says nothing passes the action down; nothing a rule names falls to
|
|
161
|
+
*ask me first*, never to *do it*.
|
|
162
|
+
|
|
163
|
+
The default `setup` writes: read and search inside the workspace — do it;
|
|
164
|
+
edit and move inside it — do it, shown as diffs; delete, run a command, and
|
|
165
|
+
any path outside the workspace — ask me first; a command naming a credential,
|
|
166
|
+
`sudo`, a package install from the network, or `git push` — ask me first with
|
|
167
|
+
the command shown; a fetch — through `network.fetch`'s allow-list, which is
|
|
168
|
+
empty, so refused; send or buy — ask me first. Limits: 100 then 200 tool
|
|
169
|
+
calls, 4 then 8 terminals, 5 then 20 MiB written — a **soft** threshold asks
|
|
170
|
+
once, a **hard** one refuses each call that would cross it and says what would
|
|
171
|
+
let the session go on.
|
|
172
|
+
|
|
173
|
+
```yaml
|
|
174
|
+
scope: session
|
|
175
|
+
rules:
|
|
176
|
+
- name: read inside the workspace
|
|
177
|
+
decision: allow
|
|
178
|
+
classes: [read]
|
|
179
|
+
where: inside
|
|
180
|
+
- name: git push
|
|
181
|
+
decision: ask
|
|
182
|
+
classes: [execute]
|
|
183
|
+
commands: ['\bgit\b[^;&|]*\bpush\b']
|
|
184
|
+
limits:
|
|
185
|
+
tool_calls: {soft: 100, hard: 200}
|
|
186
|
+
network:
|
|
187
|
+
fetch: []
|
|
188
|
+
mcp:
|
|
189
|
+
allow: []
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
`agent_shield.policy.decide(action, context, policies)` is pure: it answers
|
|
193
|
+
`allow | ask | deny`, the sentence, and the scope and rule that decided.
|
|
194
|
+
|
|
195
|
+
## What is built, and what is not
|
|
196
|
+
|
|
197
|
+
Built (0.1.1, SHIELD.md Phase 0): the policy engine and its schema; the
|
|
198
|
+
register read from [agentspecs](https://github.com/datalayer/agentspecs)
|
|
199
|
+
(`claude-code`, `codex`, `gemini-cli`, `goose`, `opencode`, `openhands`,
|
|
200
|
+
`fast-agent`); the stdio and WebSocket client channels on the official
|
|
201
|
+
`agent-client-protocol` SDK (protocol version 1); the proxy, which passes the
|
|
202
|
+
[ACP Test Compatibility Kit](https://github.com/agentclientprotocol/acp-tck)
|
|
203
|
+
in front of the test agent (`VERDICT: CONFORMANT`); the audit; the
|
|
204
|
+
`agent-shield` command; `@datalayer/agent-shield`'s policy types.
|
|
205
|
+
|
|
206
|
+
Not built: `agent-shield serve` (the proxy over WebSocket, SH-52);
|
|
207
|
+
registration with Datalayer and the policy pulled from IAM (SH-47, SH-50);
|
|
208
|
+
the OTEL export (SH-51); the Shield report and conformance (SH-36, SH-60);
|
|
209
|
+
the TypeScript renderers of diffs and terminals (SH-53).
|
|
210
|
+
|
|
211
|
+
Claude Code has been run behind the proxy (`claude-agent-acp` 0.91.0:
|
|
212
|
+
initialize, session, modes); a turn needs a funded Anthropic API key.
|
|
213
|
+
|
|
214
|
+
## Development
|
|
215
|
+
|
|
216
|
+
```bash
|
|
217
|
+
pip install -e ".[test]"
|
|
218
|
+
pytest tests/ -q
|
|
219
|
+
npm install && npm test # the TypeScript types against schema/policy.schema.json
|
|
220
|
+
make tck # the ACP TCK against the proxy (uv tool install acp-tck first)
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
## Documentation
|
|
224
|
+
|
|
225
|
+
[agent-shield.datalayer.tech](https://agent-shield.datalayer.tech)
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
~ Copyright (c) 2025-2026 Datalayer, Inc.
|
|
3
|
+
~
|
|
4
|
+
~ BSD 3-Clause License
|
|
5
|
+
-->
|
|
6
|
+
|
|
7
|
+
[](https://datalayer.ai)
|
|
8
|
+
|
|
9
|
+
[](https://github.com/sponsors/datalayer)
|
|
10
|
+
|
|
11
|
+
# 🤖 🛡️ Agent Shield
|
|
12
|
+
|
|
13
|
+
[](https://pypi.org/project/datalayer-agent-shield)
|
|
14
|
+
[](https://www.npmjs.com/package/@datalayer/agent-shield)
|
|
15
|
+
|
|
16
|
+
**A runtime firewall for every agent.** Deploy a shield with each agent. Enforce
|
|
17
|
+
permissions before actions execute, manage policies centrally, and retain an
|
|
18
|
+
audit trail.
|
|
19
|
+
|
|
20
|
+
The Shield stands between an agent and the world. Its first shippable is the
|
|
21
|
+
**proxy**: `agent-shield run -- <an ACP agent's command>` starts the agent as
|
|
22
|
+
its subprocess and stands as an [Agent Client Protocol](https://agentclientprotocol.com)
|
|
23
|
+
agent on its own stdio, so any ACP client — Zed, `vscode-acp`, Marimo,
|
|
24
|
+
Datalayer's runtimes — talks to the Shield and the Shield talks to the agent.
|
|
25
|
+
In between it decides:
|
|
26
|
+
|
|
27
|
+
- `session/request_permission` by the policy: *do it* is answered without the
|
|
28
|
+
client, *ask me first* is forwarded with the same options and answered as
|
|
29
|
+
the person chose, *leave it to me* is refused in a sentence the person sees;
|
|
30
|
+
- the agent's `tool_call` notifications are classed (ACP `kind` → `read`,
|
|
31
|
+
`write`, `delete`, `execute`; `other` fails closed to `write`) and held to
|
|
32
|
+
the session's limits; a refusal cancels the turn and says why;
|
|
33
|
+
- `fs/*` and `terminal/*` are answered by the Shield against the **workspace**
|
|
34
|
+
— a path outside it is refused, a command runs with a trimmed environment
|
|
35
|
+
— and every one is a tool row the client sees;
|
|
36
|
+
- `mcpServers` are rewritten so the agent gets only what the policy lends;
|
|
37
|
+
- every action and decision is a line of `~/.agent-shield/audit.jsonl`,
|
|
38
|
+
credentials redacted, a file's content never in it.
|
|
39
|
+
|
|
40
|
+
## The first five minutes
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
pip install "datalayer-agent-shield[claude-code]" # one extra per harness
|
|
44
|
+
agent-shield setup # finds node, the vendor CLIs and the credentials present; installs the pinned adapter into ~/.agent-shield/harnesses/<id>@<version>; writes agent-shield.yaml beside the project
|
|
45
|
+
agent-shield run -- claude-agent-acp # Claude Code behind the Shield, on this directory, with the default policy
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Measured on 15 October 2026 from a clean prefix: `setup` took **27 s**
|
|
49
|
+
(the adapter `@agentclientprotocol/claude-agent-acp@0.91.0`, 325 MB, through
|
|
50
|
+
`npm install --prefix`, never a global `npx`); `run` initialized the adapter
|
|
51
|
+
and opened a session in **1.5 s**; the first decision followed the first tool
|
|
52
|
+
call. The whole, from `pip install` to a decision seen in the terminal, is
|
|
53
|
+
under a minute, five times under the five minutes of SHIELD §5.3.
|
|
54
|
+
|
|
55
|
+
`setup` asks for nothing it finds: it says which credentials are present by
|
|
56
|
+
name (never a value), which are missing and how to export them, and stores
|
|
57
|
+
no secret. It refuses, in a sentence, a credential the register forbids — a
|
|
58
|
+
Claude Code or ChatGPT subscription login where only an API key serves to
|
|
59
|
+
third parties, a Google login on Gemini's individuals tier.
|
|
60
|
+
|
|
61
|
+
Then:
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
agent-shield log # the audit, one line per action and decision
|
|
65
|
+
agent-shield policy show # every line of the policy in force, and where it came from
|
|
66
|
+
agent-shield policy schema # the document's JSON Schema
|
|
67
|
+
agent-shield harnesses # the register: each harness, its terms, where they were read
|
|
68
|
+
agent-shield upgrade --check # a newer Shield or adapter?
|
|
69
|
+
agent-shield upgrade # move the adapters to the register's pinned versions
|
|
70
|
+
agent-shield uninstall # previews; --yes removes the adapters and the config, keeps the audit and the policy
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Zed, `settings.json`:
|
|
74
|
+
|
|
75
|
+
```json
|
|
76
|
+
"agent_servers": {
|
|
77
|
+
"Claude Code behind the Shield": { "command": "agent-shield", "args": ["run", "--", "claude-agent-acp"] }
|
|
78
|
+
}
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## The policy
|
|
82
|
+
|
|
83
|
+
One document per scope — `organization`, `team`, `session` — in
|
|
84
|
+
`agent-shield.yaml` beside the project (else `~/.agent-shield/agent-shield.yaml`),
|
|
85
|
+
with the schema in [`schema/policy.schema.json`](schema/policy.schema.json).
|
|
86
|
+
The strictest scope decides first and **the first decision wins**; a scope
|
|
87
|
+
that says nothing passes the action down; nothing a rule names falls to
|
|
88
|
+
*ask me first*, never to *do it*.
|
|
89
|
+
|
|
90
|
+
The default `setup` writes: read and search inside the workspace — do it;
|
|
91
|
+
edit and move inside it — do it, shown as diffs; delete, run a command, and
|
|
92
|
+
any path outside the workspace — ask me first; a command naming a credential,
|
|
93
|
+
`sudo`, a package install from the network, or `git push` — ask me first with
|
|
94
|
+
the command shown; a fetch — through `network.fetch`'s allow-list, which is
|
|
95
|
+
empty, so refused; send or buy — ask me first. Limits: 100 then 200 tool
|
|
96
|
+
calls, 4 then 8 terminals, 5 then 20 MiB written — a **soft** threshold asks
|
|
97
|
+
once, a **hard** one refuses each call that would cross it and says what would
|
|
98
|
+
let the session go on.
|
|
99
|
+
|
|
100
|
+
```yaml
|
|
101
|
+
scope: session
|
|
102
|
+
rules:
|
|
103
|
+
- name: read inside the workspace
|
|
104
|
+
decision: allow
|
|
105
|
+
classes: [read]
|
|
106
|
+
where: inside
|
|
107
|
+
- name: git push
|
|
108
|
+
decision: ask
|
|
109
|
+
classes: [execute]
|
|
110
|
+
commands: ['\bgit\b[^;&|]*\bpush\b']
|
|
111
|
+
limits:
|
|
112
|
+
tool_calls: {soft: 100, hard: 200}
|
|
113
|
+
network:
|
|
114
|
+
fetch: []
|
|
115
|
+
mcp:
|
|
116
|
+
allow: []
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
`agent_shield.policy.decide(action, context, policies)` is pure: it answers
|
|
120
|
+
`allow | ask | deny`, the sentence, and the scope and rule that decided.
|
|
121
|
+
|
|
122
|
+
## What is built, and what is not
|
|
123
|
+
|
|
124
|
+
Built (0.1.1, SHIELD.md Phase 0): the policy engine and its schema; the
|
|
125
|
+
register read from [agentspecs](https://github.com/datalayer/agentspecs)
|
|
126
|
+
(`claude-code`, `codex`, `gemini-cli`, `goose`, `opencode`, `openhands`,
|
|
127
|
+
`fast-agent`); the stdio and WebSocket client channels on the official
|
|
128
|
+
`agent-client-protocol` SDK (protocol version 1); the proxy, which passes the
|
|
129
|
+
[ACP Test Compatibility Kit](https://github.com/agentclientprotocol/acp-tck)
|
|
130
|
+
in front of the test agent (`VERDICT: CONFORMANT`); the audit; the
|
|
131
|
+
`agent-shield` command; `@datalayer/agent-shield`'s policy types.
|
|
132
|
+
|
|
133
|
+
Not built: `agent-shield serve` (the proxy over WebSocket, SH-52);
|
|
134
|
+
registration with Datalayer and the policy pulled from IAM (SH-47, SH-50);
|
|
135
|
+
the OTEL export (SH-51); the Shield report and conformance (SH-36, SH-60);
|
|
136
|
+
the TypeScript renderers of diffs and terminals (SH-53).
|
|
137
|
+
|
|
138
|
+
Claude Code has been run behind the proxy (`claude-agent-acp` 0.91.0:
|
|
139
|
+
initialize, session, modes); a turn needs a funded Anthropic API key.
|
|
140
|
+
|
|
141
|
+
## Development
|
|
142
|
+
|
|
143
|
+
```bash
|
|
144
|
+
pip install -e ".[test]"
|
|
145
|
+
pytest tests/ -q
|
|
146
|
+
npm install && npm test # the TypeScript types against schema/policy.schema.json
|
|
147
|
+
make tck # the ACP TCK against the proxy (uv tool install acp-tck first)
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
## Documentation
|
|
151
|
+
|
|
152
|
+
[agent-shield.datalayer.tech](https://agent-shield.datalayer.tech)
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# Copyright (c) 2025-2026 Datalayer, Inc.
|
|
2
|
+
#
|
|
3
|
+
# BSD 3-Clause License
|
|
4
|
+
|
|
5
|
+
"""Datalayer Agent Shield: a runtime firewall for every agent.
|
|
6
|
+
|
|
7
|
+
The engine of the Shield (SHIELD.md §5.1): ``policy`` decides, ``acp`` runs
|
|
8
|
+
an ACP agent behind the Shield and answers its files and terminals against
|
|
9
|
+
the workspace, ``audit`` writes every action, ``register`` says which
|
|
10
|
+
harnesses are served and on what terms, ``cli`` is ``agent-shield``.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from .__version__ import __version__
|
|
14
|
+
|
|
15
|
+
__all__ = ["__version__"]
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# Copyright (c) 2025-2026 Datalayer, Inc.
|
|
2
|
+
#
|
|
3
|
+
# BSD 3-Clause License
|
|
4
|
+
|
|
5
|
+
"""ACP: the client-side channels, the normaliser, the workspace and the proxy (SHIELD §5.1)."""
|
|
6
|
+
|
|
7
|
+
from .channel import (
|
|
8
|
+
SHIELD_CLIENT_CAPABILITIES,
|
|
9
|
+
SHIELD_CLIENT_INFO,
|
|
10
|
+
ACPChannel,
|
|
11
|
+
ChannelRefused,
|
|
12
|
+
ConnectionChannel,
|
|
13
|
+
StdioACPChannel,
|
|
14
|
+
WebSocketACPChannel,
|
|
15
|
+
build_handler,
|
|
16
|
+
)
|
|
17
|
+
from .events import Event
|
|
18
|
+
from .normalize import MalformedUpdate, normalize
|
|
19
|
+
from .workspace import OutsideWorkspace, Terminals, Workspace
|
|
20
|
+
|
|
21
|
+
__all__ = [
|
|
22
|
+
"SHIELD_CLIENT_CAPABILITIES",
|
|
23
|
+
"SHIELD_CLIENT_INFO",
|
|
24
|
+
"ACPChannel",
|
|
25
|
+
"ChannelRefused",
|
|
26
|
+
"ConnectionChannel",
|
|
27
|
+
"Event",
|
|
28
|
+
"MalformedUpdate",
|
|
29
|
+
"OutsideWorkspace",
|
|
30
|
+
"StdioACPChannel",
|
|
31
|
+
"Terminals",
|
|
32
|
+
"WebSocketACPChannel",
|
|
33
|
+
"Workspace",
|
|
34
|
+
"build_handler",
|
|
35
|
+
"normalize",
|
|
36
|
+
]
|