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.
Files changed (36) hide show
  1. datalayer_agent_shield-0.1.1/.gitignore +141 -0
  2. datalayer_agent_shield-0.1.1/CHANGELOG.md +43 -0
  3. datalayer_agent_shield-0.1.1/LICENSE +29 -0
  4. datalayer_agent_shield-0.1.1/PKG-INFO +225 -0
  5. datalayer_agent_shield-0.1.1/README.md +152 -0
  6. datalayer_agent_shield-0.1.1/agent_shield/__init__.py +15 -0
  7. datalayer_agent_shield-0.1.1/agent_shield/__version__.py +7 -0
  8. datalayer_agent_shield-0.1.1/agent_shield/acp/__init__.py +36 -0
  9. datalayer_agent_shield-0.1.1/agent_shield/acp/channel.py +444 -0
  10. datalayer_agent_shield-0.1.1/agent_shield/acp/events.py +219 -0
  11. datalayer_agent_shield-0.1.1/agent_shield/acp/normalize.py +133 -0
  12. datalayer_agent_shield-0.1.1/agent_shield/acp/proxy.py +944 -0
  13. datalayer_agent_shield-0.1.1/agent_shield/acp/workspace.py +230 -0
  14. datalayer_agent_shield-0.1.1/agent_shield/audit.py +141 -0
  15. datalayer_agent_shield-0.1.1/agent_shield/cli.py +536 -0
  16. datalayer_agent_shield-0.1.1/agent_shield/config.py +89 -0
  17. datalayer_agent_shield-0.1.1/agent_shield/policy/__init__.py +87 -0
  18. datalayer_agent_shield-0.1.1/agent_shield/policy/classify.py +83 -0
  19. datalayer_agent_shield-0.1.1/agent_shield/policy/decide.py +264 -0
  20. datalayer_agent_shield-0.1.1/agent_shield/policy/default.py +57 -0
  21. datalayer_agent_shield-0.1.1/agent_shield/policy/files.py +91 -0
  22. datalayer_agent_shield-0.1.1/agent_shield/policy/model.py +207 -0
  23. datalayer_agent_shield-0.1.1/agent_shield/reactor_extension.py +65 -0
  24. datalayer_agent_shield-0.1.1/agent_shield/register/__init__.py +214 -0
  25. datalayer_agent_shield-0.1.1/docs/LICENSE +21 -0
  26. datalayer_agent_shield-0.1.1/docs/README.md +38 -0
  27. datalayer_agent_shield-0.1.1/pyproject.toml +134 -0
  28. datalayer_agent_shield-0.1.1/schema/policy.schema.json +259 -0
  29. datalayer_agent_shield-0.1.1/tests/__init__.py +5 -0
  30. datalayer_agent_shield-0.1.1/tests/fixtures/fake_agent.py +233 -0
  31. datalayer_agent_shield-0.1.1/tests/test_channel.py +205 -0
  32. datalayer_agent_shield-0.1.1/tests/test_cli.py +218 -0
  33. datalayer_agent_shield-0.1.1/tests/test_normalize.py +131 -0
  34. datalayer_agent_shield-0.1.1/tests/test_policy.py +454 -0
  35. datalayer_agent_shield-0.1.1/tests/test_register.py +88 -0
  36. 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
+ [![Datalayer](https://images.datalayer.io/legacy/datalayer-25.svg)](https://datalayer.ai)
81
+
82
+ [![Become a Sponsor](https://img.shields.io/static/v1?label=Become%20a%20Sponsor&message=%E2%9D%A4&logo=GitHub&style=flat&color=1ABC9C)](https://github.com/sponsors/datalayer)
83
+
84
+ # 🤖 🛡️ Agent Shield
85
+
86
+ [![PyPI - Version](https://img.shields.io/pypi/v/datalayer-agent-shield)](https://pypi.org/project/datalayer-agent-shield)
87
+ [![npm](https://img.shields.io/npm/v/%40datalayer%2Fagent-shield)](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
+ [![Datalayer](https://images.datalayer.io/legacy/datalayer-25.svg)](https://datalayer.ai)
8
+
9
+ [![Become a Sponsor](https://img.shields.io/static/v1?label=Become%20a%20Sponsor&message=%E2%9D%A4&logo=GitHub&style=flat&color=1ABC9C)](https://github.com/sponsors/datalayer)
10
+
11
+ # 🤖 🛡️ Agent Shield
12
+
13
+ [![PyPI - Version](https://img.shields.io/pypi/v/datalayer-agent-shield)](https://pypi.org/project/datalayer-agent-shield)
14
+ [![npm](https://img.shields.io/npm/v/%40datalayer%2Fagent-shield)](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,7 @@
1
+ # Copyright (c) 2025-2026 Datalayer, Inc.
2
+ #
3
+ # BSD 3-Clause License
4
+
5
+ """Agent Shield."""
6
+
7
+ __version__ = "0.1.1"
@@ -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
+ ]