decepticon-core 1.1.2__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.
- decepticon_core-1.1.2/.gitignore +117 -0
- decepticon_core-1.1.2/PKG-INFO +43 -0
- decepticon_core-1.1.2/README.md +20 -0
- decepticon_core-1.1.2/decepticon_core/__init__.py +43 -0
- decepticon_core-1.1.2/decepticon_core/contracts/__init__.py +35 -0
- decepticon_core-1.1.2/decepticon_core/contracts/contributions.py +153 -0
- decepticon_core-1.1.2/decepticon_core/contracts/slots.py +135 -0
- decepticon_core-1.1.2/decepticon_core/plugin_loader.py +671 -0
- decepticon_core-1.1.2/decepticon_core/protocols/__init__.py +41 -0
- decepticon_core-1.1.2/decepticon_core/protocols/agent.py +31 -0
- decepticon_core-1.1.2/decepticon_core/protocols/backend.py +38 -0
- decepticon_core-1.1.2/decepticon_core/protocols/callback.py +31 -0
- decepticon_core-1.1.2/decepticon_core/protocols/llm.py +28 -0
- decepticon_core-1.1.2/decepticon_core/protocols/middleware.py +43 -0
- decepticon_core-1.1.2/decepticon_core/protocols/sandbox.py +32 -0
- decepticon_core-1.1.2/decepticon_core/protocols/tool.py +32 -0
- decepticon_core-1.1.2/decepticon_core/py.typed +0 -0
- decepticon_core-1.1.2/decepticon_core/registry/__init__.py +55 -0
- decepticon_core-1.1.2/decepticon_core/registry/conflict.py +42 -0
- decepticon_core-1.1.2/decepticon_core/registry/plugins.py +238 -0
- decepticon_core-1.1.2/decepticon_core/registry/resolution.py +69 -0
- decepticon_core-1.1.2/decepticon_core/registry/roles.py +94 -0
- decepticon_core-1.1.2/decepticon_core/registry/safety.py +87 -0
- decepticon_core-1.1.2/decepticon_core/registry/skills.py +67 -0
- decepticon_core-1.1.2/decepticon_core/types/__init__.py +23 -0
- decepticon_core-1.1.2/decepticon_core/types/engagement.py +1073 -0
- decepticon_core-1.1.2/decepticon_core/types/kg.py +374 -0
- decepticon_core-1.1.2/decepticon_core/types/llm.py +776 -0
- decepticon_core-1.1.2/decepticon_core/utils/__init__.py +12 -0
- decepticon_core-1.1.2/decepticon_core/utils/config.py +69 -0
- decepticon_core-1.1.2/decepticon_core/utils/logging.py +120 -0
- decepticon_core-1.1.2/pyproject.toml +51 -0
- decepticon_core-1.1.2/tests/test_no_runtime_deps.py +74 -0
- decepticon_core-1.1.2/tests/test_plugin_registry.py +153 -0
- decepticon_core-1.1.2/tests/test_public_api_stability.py +137 -0
- decepticon_core-1.1.2/tests/test_safety_registry.py +106 -0
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
# Environments
|
|
2
|
+
.env
|
|
3
|
+
.venv
|
|
4
|
+
env/
|
|
5
|
+
venv/
|
|
6
|
+
ENV/
|
|
7
|
+
env.bak/
|
|
8
|
+
venv.bak/
|
|
9
|
+
|
|
10
|
+
# Byte-compiled / optimized / DLL files
|
|
11
|
+
__pycache__/
|
|
12
|
+
*.py[cod]
|
|
13
|
+
*$py.class
|
|
14
|
+
|
|
15
|
+
# IDE and Editor
|
|
16
|
+
.vscode/
|
|
17
|
+
.idea/
|
|
18
|
+
*.swp
|
|
19
|
+
*.swo
|
|
20
|
+
|
|
21
|
+
# Python specific
|
|
22
|
+
build/
|
|
23
|
+
develop-eggs/
|
|
24
|
+
dist/
|
|
25
|
+
downloads/
|
|
26
|
+
eggs/
|
|
27
|
+
.eggs/
|
|
28
|
+
parts/
|
|
29
|
+
sdist/
|
|
30
|
+
var/
|
|
31
|
+
wheels/
|
|
32
|
+
share/python-wheels/
|
|
33
|
+
*.egg-info/
|
|
34
|
+
.installed.cfg
|
|
35
|
+
*.egg
|
|
36
|
+
|
|
37
|
+
# Coverage reports
|
|
38
|
+
htmlcov/
|
|
39
|
+
.tox/
|
|
40
|
+
.nox/
|
|
41
|
+
.coverage
|
|
42
|
+
.coverage.*
|
|
43
|
+
.cache
|
|
44
|
+
nosetests.xml
|
|
45
|
+
coverage.xml
|
|
46
|
+
*.cover
|
|
47
|
+
*.py,cover
|
|
48
|
+
.hypothesis/
|
|
49
|
+
.pytest_cache/
|
|
50
|
+
|
|
51
|
+
# Jupyter Notebook
|
|
52
|
+
.ipynb_checkpoints
|
|
53
|
+
|
|
54
|
+
# PEP 582; used by e.g. github.com/David-OConnor/pyflow
|
|
55
|
+
__pypackages__/
|
|
56
|
+
|
|
57
|
+
# OS generated files
|
|
58
|
+
.DS_Store
|
|
59
|
+
.DS_Store?
|
|
60
|
+
._*
|
|
61
|
+
.Spotlight-V100
|
|
62
|
+
.Trashes
|
|
63
|
+
ehthumbs.db
|
|
64
|
+
Thumbs.db
|
|
65
|
+
|
|
66
|
+
# Node
|
|
67
|
+
node_modules/
|
|
68
|
+
|
|
69
|
+
# etc
|
|
70
|
+
sliver-py/
|
|
71
|
+
/src/
|
|
72
|
+
config/*.local.*
|
|
73
|
+
reference/
|
|
74
|
+
demo.tape
|
|
75
|
+
|
|
76
|
+
# Decepticon runtime data
|
|
77
|
+
.decepticon/
|
|
78
|
+
.dogfood/
|
|
79
|
+
workspace/
|
|
80
|
+
.bg-shell/
|
|
81
|
+
|
|
82
|
+
.langgraph_api/
|
|
83
|
+
.omc/
|
|
84
|
+
.ouroboros/
|
|
85
|
+
.gstack/
|
|
86
|
+
.claude/
|
|
87
|
+
clients/ee/
|
|
88
|
+
|
|
89
|
+
# Project-specific Claude Code instructions (private)
|
|
90
|
+
CLAUDE.md
|
|
91
|
+
references/octogent
|
|
92
|
+
clients/launcher/decepticon
|
|
93
|
+
|
|
94
|
+
# Benchmark
|
|
95
|
+
# Per-run artefacts (timestamped batch + evidence/ + reports/) are local-only.
|
|
96
|
+
# Curated per-challenge results (XBEN-*-24/) and the LEVEL3 index are committed.
|
|
97
|
+
# LangSmith trace dumps live as public share URLs in READMEs — never committed.
|
|
98
|
+
benchmark/results/*
|
|
99
|
+
!benchmark/results/.gitkeep
|
|
100
|
+
!benchmark/results/XBEN-*-24/
|
|
101
|
+
!benchmark/results/README.md
|
|
102
|
+
benchmark/results/XBEN-*-24/evidence/langsmith_trace.json
|
|
103
|
+
benchmark/results/XBEN-*-24/evidence/iter14_pass.json
|
|
104
|
+
benchmark/results/XBEN-*-24/evidence/*_iter*.json
|
|
105
|
+
# Per-run timestamped subdirs inside XBEN-*-24/ are local dev artefacts.
|
|
106
|
+
# The flat root (report.json / report.md / evidence/summary.{json,md}) is the
|
|
107
|
+
# committed PASS record — see benchmark/results/README.md for the policy.
|
|
108
|
+
benchmark/results/XBEN-*-24/2[0-9][0-9][0-9][0-9][0-9][0-9][0-9]_*/
|
|
109
|
+
benchmark/workspaces/
|
|
110
|
+
|
|
111
|
+
# Codex / OMX runtime artefacts (never committed)
|
|
112
|
+
.omx/
|
|
113
|
+
.env.bak.*
|
|
114
|
+
benchmark/results/XBEN-*-24/run-loop*.log
|
|
115
|
+
|
|
116
|
+
# Git worktrees
|
|
117
|
+
.worktrees/
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: decepticon-core
|
|
3
|
+
Version: 1.1.2
|
|
4
|
+
Summary: Decepticon contract layer: pure types, protocols, plugin contracts, registry primitives
|
|
5
|
+
Project-URL: Homepage, https://github.com/PurpleAILAB/Decepticon
|
|
6
|
+
Project-URL: Repository, https://github.com/PurpleAILAB/Decepticon
|
|
7
|
+
Author: Decepticon Team
|
|
8
|
+
License: Apache-2.0
|
|
9
|
+
Keywords: agents,ai,contracts,plugins,red-team,security
|
|
10
|
+
Classifier: Development Status :: 4 - Beta
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: License :: OSI Approved :: Apache Software License
|
|
13
|
+
Classifier: Operating System :: OS Independent
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
16
|
+
Classifier: Topic :: Security
|
|
17
|
+
Classifier: Typing :: Typed
|
|
18
|
+
Requires-Python: >=3.13
|
|
19
|
+
Requires-Dist: pydantic-settings>=2.0.0
|
|
20
|
+
Requires-Dist: pydantic>=2.0.0
|
|
21
|
+
Requires-Dist: typing-extensions>=4.0.0
|
|
22
|
+
Description-Content-Type: text/markdown
|
|
23
|
+
|
|
24
|
+
# decepticon-core
|
|
25
|
+
|
|
26
|
+
The Decepticon contract layer. Pure types, protocols, plugin contracts,
|
|
27
|
+
and registry primitives — zero `langchain` / `langgraph` / `deepagents`
|
|
28
|
+
runtime dependency. Suitable to import from any context: CLI tooling,
|
|
29
|
+
serverless workers, type-checking-only environments.
|
|
30
|
+
|
|
31
|
+
Stable surface for plugin authors and downstream commercial layers
|
|
32
|
+
(e.g. SaaS dashboards, B2B API services). See the umbrella
|
|
33
|
+
[`README.md`](../../README.md) and the design spec at
|
|
34
|
+
[`docs/superpowers/specs/2026-05-23-core-framework-sdk-split-design.md`](../../docs/superpowers/specs/2026-05-23-core-framework-sdk-split-design.md).
|
|
35
|
+
|
|
36
|
+
## Install
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
pip install decepticon-core
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Most consumers should install `decepticon` (which depends on this) or
|
|
43
|
+
`decepticon-sdk` (the plugin-author entrypoint).
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# decepticon-core
|
|
2
|
+
|
|
3
|
+
The Decepticon contract layer. Pure types, protocols, plugin contracts,
|
|
4
|
+
and registry primitives — zero `langchain` / `langgraph` / `deepagents`
|
|
5
|
+
runtime dependency. Suitable to import from any context: CLI tooling,
|
|
6
|
+
serverless workers, type-checking-only environments.
|
|
7
|
+
|
|
8
|
+
Stable surface for plugin authors and downstream commercial layers
|
|
9
|
+
(e.g. SaaS dashboards, B2B API services). See the umbrella
|
|
10
|
+
[`README.md`](../../README.md) and the design spec at
|
|
11
|
+
[`docs/superpowers/specs/2026-05-23-core-framework-sdk-split-design.md`](../../docs/superpowers/specs/2026-05-23-core-framework-sdk-split-design.md).
|
|
12
|
+
|
|
13
|
+
## Install
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
pip install decepticon-core
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Most consumers should install `decepticon` (which depends on this) or
|
|
20
|
+
`decepticon-sdk` (the plugin-author entrypoint).
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"""decepticon-core — contract layer for the Decepticon agent framework.
|
|
2
|
+
|
|
3
|
+
Pure types, protocols, plugin contracts, and registry primitives. This
|
|
4
|
+
package never imports ``langchain``, ``langgraph``, ``deepagents``,
|
|
5
|
+
``httpx``, or ``fastapi`` — see the design spec at
|
|
6
|
+
``docs/superpowers/specs/2026-05-23-core-framework-sdk-split-design.md``.
|
|
7
|
+
|
|
8
|
+
Phase 1.A status: ``types`` submodule extracted from the framework
|
|
9
|
+
(engagement / llm / kg). Subsequent commits add ``protocols``,
|
|
10
|
+
``contracts``, ``registry``, and ``utils`` per spec §6.1.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from __future__ import annotations
|
|
14
|
+
|
|
15
|
+
from importlib.metadata import PackageNotFoundError
|
|
16
|
+
from importlib.metadata import version as _version
|
|
17
|
+
|
|
18
|
+
from decepticon_core import (
|
|
19
|
+
contracts,
|
|
20
|
+
plugin_loader,
|
|
21
|
+
protocols,
|
|
22
|
+
registry,
|
|
23
|
+
types,
|
|
24
|
+
utils,
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
try:
|
|
28
|
+
# pyproject carries a "0.0.0" sentinel; release.yml stamps the real
|
|
29
|
+
# tag into the wheel metadata at build time, and importlib.metadata
|
|
30
|
+
# reads it back here. Local checkouts read 0.0.0.
|
|
31
|
+
__version__ = _version("decepticon-core")
|
|
32
|
+
except PackageNotFoundError:
|
|
33
|
+
__version__ = "0.0.0"
|
|
34
|
+
|
|
35
|
+
__all__ = [
|
|
36
|
+
"__version__",
|
|
37
|
+
"contracts",
|
|
38
|
+
"plugin_loader",
|
|
39
|
+
"protocols",
|
|
40
|
+
"registry",
|
|
41
|
+
"types",
|
|
42
|
+
"utils",
|
|
43
|
+
]
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"""Plugin contracts — the surface plugin authors implement against.
|
|
2
|
+
|
|
3
|
+
Submodules:
|
|
4
|
+
|
|
5
|
+
* ``slots`` — middleware slot enum and per-role applicability mapping.
|
|
6
|
+
Was ``decepticon.agents.middleware_slots``; framework keeps the
|
|
7
|
+
default factory helpers (which need langchain runtime) and imports
|
|
8
|
+
the enum/constants from here.
|
|
9
|
+
|
|
10
|
+
Future Phase 1 commits will add ``contributions`` (ToolContribution,
|
|
11
|
+
MiddlewareContribution, PromptContribution, SubAgentContribution,
|
|
12
|
+
SafetyDeclaration) and ``PluginBundle`` proper (currently lives at
|
|
13
|
+
``decepticon_core.plugin_loader`` after Phase 1.B).
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
from __future__ import annotations
|
|
17
|
+
|
|
18
|
+
from decepticon_core.contracts import contributions, slots
|
|
19
|
+
from decepticon_core.contracts.contributions import (
|
|
20
|
+
MiddlewareContribution,
|
|
21
|
+
PromptContribution,
|
|
22
|
+
SafetyDeclaration,
|
|
23
|
+
SubAgentContribution,
|
|
24
|
+
ToolContribution,
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
__all__ = [
|
|
28
|
+
"MiddlewareContribution",
|
|
29
|
+
"PromptContribution",
|
|
30
|
+
"SafetyDeclaration",
|
|
31
|
+
"SubAgentContribution",
|
|
32
|
+
"ToolContribution",
|
|
33
|
+
"contributions",
|
|
34
|
+
"slots",
|
|
35
|
+
]
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
"""Focused contribution dataclasses for plugin authors.
|
|
2
|
+
|
|
3
|
+
Per spec §7.2 Principle 3, the kitchen-sink ``PluginBundle`` of the
|
|
4
|
+
pre-redesign era splits into five focused contribution types — each
|
|
5
|
+
covers one extension surface so plugin authors construct just what
|
|
6
|
+
they need and the framework's introspection (``PluginRegistry``)
|
|
7
|
+
attributes overrides to specific contributions.
|
|
8
|
+
|
|
9
|
+
The aggregate ``PluginBundle`` (currently at
|
|
10
|
+
``decepticon_core.plugin_loader``) keeps a back-compat shape during
|
|
11
|
+
the transition; Phase 2 introduces the rebuilt version that aggregates
|
|
12
|
+
these contributions.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
|
|
17
|
+
from collections.abc import Mapping
|
|
18
|
+
from dataclasses import dataclass, field
|
|
19
|
+
from typing import Any, Literal
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@dataclass(frozen=True)
|
|
23
|
+
class ToolContribution:
|
|
24
|
+
"""Tools a plugin contributes to one or more roles.
|
|
25
|
+
|
|
26
|
+
Mirrors the old ``PluginBundle.replaced_tools`` / ``disabled_tools``
|
|
27
|
+
fields, but typed precisely and with the ``roles`` field
|
|
28
|
+
intentionally required (no implicit "all roles" — closes gap §8
|
|
29
|
+
#6). An empty ``roles`` tuple raises ``ValueError`` at construction.
|
|
30
|
+
|
|
31
|
+
Fields:
|
|
32
|
+
items: tools added to the role's tool list.
|
|
33
|
+
disabled_names: tool names to remove from the OSS baseline.
|
|
34
|
+
replaced: name -> replacement tool (combines disable + add).
|
|
35
|
+
roles: roles this contribution applies to. ``()`` is rejected
|
|
36
|
+
at construction time.
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
items: tuple[Any, ...] = ()
|
|
40
|
+
disabled_names: tuple[str, ...] = ()
|
|
41
|
+
replaced: Mapping[str, Any] = field(default_factory=dict)
|
|
42
|
+
roles: tuple[str, ...] = ()
|
|
43
|
+
|
|
44
|
+
def __post_init__(self) -> None:
|
|
45
|
+
if not self.roles:
|
|
46
|
+
raise ValueError(
|
|
47
|
+
"ToolContribution requires an explicit non-empty ``roles=`` tuple "
|
|
48
|
+
"(closes spec §8 gap #6 — implicit all-roles is forbidden). "
|
|
49
|
+
"Pass roles=('recon',) etc."
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
@dataclass(frozen=True)
|
|
54
|
+
class MiddlewareContribution:
|
|
55
|
+
"""Middleware a plugin contributes to one or more roles.
|
|
56
|
+
|
|
57
|
+
Same shape as ``ToolContribution`` but keyed by slot name (the
|
|
58
|
+
``MiddlewareSlot`` value). ``roles`` is required at construction.
|
|
59
|
+
"""
|
|
60
|
+
|
|
61
|
+
items: tuple[Any, ...] = ()
|
|
62
|
+
disabled_slots: tuple[str, ...] = ()
|
|
63
|
+
replaced_slots: Mapping[str, Any] = field(default_factory=dict)
|
|
64
|
+
roles: tuple[str, ...] = ()
|
|
65
|
+
|
|
66
|
+
def __post_init__(self) -> None:
|
|
67
|
+
if not self.roles:
|
|
68
|
+
raise ValueError(
|
|
69
|
+
"MiddlewareContribution requires an explicit non-empty "
|
|
70
|
+
"``roles=`` tuple (closes spec §8 gap #6). Pass roles=('recon',) etc."
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
@dataclass(frozen=True)
|
|
75
|
+
class PromptContribution:
|
|
76
|
+
"""Prompt fragments a plugin contributes to one or more roles.
|
|
77
|
+
|
|
78
|
+
Closes gap §8 #8 (prompt-only plugin no longer has to wrap in
|
|
79
|
+
``PluginBundle``) — packages can ship a ``PromptContribution``
|
|
80
|
+
directly under the new ``decepticon.prompts`` entry-point group.
|
|
81
|
+
|
|
82
|
+
Fields:
|
|
83
|
+
fragments: role name -> text. The framework applies the
|
|
84
|
+
fragment per the ``mode`` setting.
|
|
85
|
+
mode: ``prepend`` / ``append`` / ``replace`` (replace wholly
|
|
86
|
+
substitutes the loaded prompt; prepend/append wrap it).
|
|
87
|
+
roles: roles this contribution applies to.
|
|
88
|
+
"""
|
|
89
|
+
|
|
90
|
+
fragments: Mapping[str, str] = field(default_factory=dict)
|
|
91
|
+
mode: Literal["prepend", "append", "replace"] = "append"
|
|
92
|
+
roles: tuple[str, ...] = ()
|
|
93
|
+
|
|
94
|
+
def __post_init__(self) -> None:
|
|
95
|
+
if not self.roles:
|
|
96
|
+
raise ValueError(
|
|
97
|
+
"PromptContribution requires an explicit non-empty ``roles=`` "
|
|
98
|
+
"tuple (closes spec §8 gap #6). Pass roles=('recon',) etc."
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
@dataclass(frozen=True)
|
|
103
|
+
class SubAgentContribution:
|
|
104
|
+
"""Sub-agents a plugin contributes to one or more parent agents.
|
|
105
|
+
|
|
106
|
+
Mirrors the parent-agent scoping used by ``load_subagents_for_parent``
|
|
107
|
+
in ``decepticon_core.plugin_loader``. The ``items`` carry
|
|
108
|
+
``SubAgentSpec`` objects (defined in plugin_loader, eventually to
|
|
109
|
+
move under ``contracts``). ``parent_agents`` required.
|
|
110
|
+
"""
|
|
111
|
+
|
|
112
|
+
items: tuple[Any, ...] = ()
|
|
113
|
+
disabled_names: tuple[str, ...] = ()
|
|
114
|
+
replaced: Mapping[str, Any] = field(default_factory=dict)
|
|
115
|
+
parent_agents: tuple[str, ...] = ()
|
|
116
|
+
|
|
117
|
+
def __post_init__(self) -> None:
|
|
118
|
+
if not self.parent_agents:
|
|
119
|
+
raise ValueError(
|
|
120
|
+
"SubAgentContribution requires an explicit non-empty "
|
|
121
|
+
"``parent_agents=`` tuple (closes spec §8 gap #6). "
|
|
122
|
+
"Pass parent_agents=('decepticon',) etc."
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
@dataclass(frozen=True)
|
|
127
|
+
class SafetyDeclaration:
|
|
128
|
+
"""Plugin-declared additions to the safety-critical set.
|
|
129
|
+
|
|
130
|
+
Per spec §16.4 #4 this contract is **additive only**: plugins can
|
|
131
|
+
declare *their own* tool / middleware names safety-critical, but
|
|
132
|
+
cannot remove safety on OSS-declared names. The framework merges
|
|
133
|
+
plugin SafetyDeclarations with ``SAFETY_CRITICAL_TOOLS`` /
|
|
134
|
+
``SAFETY_CRITICAL_SLOTS`` from ``decepticon-core``.
|
|
135
|
+
|
|
136
|
+
Fields:
|
|
137
|
+
tools: tool names the plugin marks safety-critical.
|
|
138
|
+
middleware: middleware slot names the plugin marks
|
|
139
|
+
safety-critical. Custom plugin slot names are accepted
|
|
140
|
+
(any string).
|
|
141
|
+
"""
|
|
142
|
+
|
|
143
|
+
tools: tuple[str, ...] = ()
|
|
144
|
+
middleware: tuple[str, ...] = ()
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
__all__ = [
|
|
148
|
+
"MiddlewareContribution",
|
|
149
|
+
"PromptContribution",
|
|
150
|
+
"SafetyDeclaration",
|
|
151
|
+
"SubAgentContribution",
|
|
152
|
+
"ToolContribution",
|
|
153
|
+
]
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
"""Middleware slot enum and per-role applicability mapping.
|
|
2
|
+
|
|
3
|
+
The Decepticon agent factories assemble their middleware stack by
|
|
4
|
+
walking ``MiddlewareSlot`` in declaration order; each role declares
|
|
5
|
+
which slots apply via ``SLOTS_PER_ROLE``.
|
|
6
|
+
|
|
7
|
+
This module is the pure-data half of the original
|
|
8
|
+
``decepticon.agents.middleware_slots``. The langchain/deepagents-bound
|
|
9
|
+
default factories (``DEFAULT_SLOT_FACTORIES`` and the ``_make_*``
|
|
10
|
+
helpers) stay in the framework — keeping core langchain-free.
|
|
11
|
+
|
|
12
|
+
Plugin authors implementing custom middleware reach for
|
|
13
|
+
``MiddlewareSlot`` and the ``SAFETY_CRITICAL_SLOTS`` set when wiring
|
|
14
|
+
``PluginBundle.replaced_middleware`` / ``disabled_middleware``.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
from __future__ import annotations
|
|
18
|
+
|
|
19
|
+
from enum import StrEnum
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class MiddlewareSlot(StrEnum):
|
|
23
|
+
"""Named slots in the agent middleware stack.
|
|
24
|
+
|
|
25
|
+
Enum declaration order = assembly order. The 16 agent factories
|
|
26
|
+
walk this enum top-to-bottom; only slots in ``SLOTS_PER_ROLE[role]``
|
|
27
|
+
are instantiated.
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
ENGAGEMENT_CONTEXT = "engagement-context"
|
|
31
|
+
SKILLS = "skills"
|
|
32
|
+
FILESYSTEM = "filesystem"
|
|
33
|
+
SUBAGENT = "subagent"
|
|
34
|
+
OPPLAN = "opplan"
|
|
35
|
+
SANDBOX_NOTIFICATION = "sandbox-notification"
|
|
36
|
+
MODEL_OVERRIDE = "model-override"
|
|
37
|
+
MODEL_FALLBACK = "model-fallback"
|
|
38
|
+
SUMMARIZATION = "summarization"
|
|
39
|
+
PROMPT_CACHING = "prompt-caching"
|
|
40
|
+
PATCH_TOOL_CALLS = "patch-tool-calls"
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
SAFETY_CRITICAL_SLOTS: frozenset[MiddlewareSlot] = frozenset(
|
|
44
|
+
{
|
|
45
|
+
# EngagementContextMiddleware carries RoE constraints into every
|
|
46
|
+
# tool call — disabling it lets an agent target out-of-scope
|
|
47
|
+
# hosts without any guard rail. Replacement is fine if the new
|
|
48
|
+
# middleware honours the same contract; full disable is the
|
|
49
|
+
# actual hazard.
|
|
50
|
+
MiddlewareSlot.ENGAGEMENT_CONTEXT,
|
|
51
|
+
# SandboxNotification tracks background-job completion + emits
|
|
52
|
+
# the CLI's ``● Background command`` event. Disabling it leaves
|
|
53
|
+
# operator visibility broken on every background tool call.
|
|
54
|
+
MiddlewareSlot.SANDBOX_NOTIFICATION,
|
|
55
|
+
}
|
|
56
|
+
)
|
|
57
|
+
"""Slots a plugin can only replace/disable when
|
|
58
|
+
``DECEPTICON_ALLOW_SAFETY_OVERRIDES=1`` is set in the environment.
|
|
59
|
+
|
|
60
|
+
The gate is enforced by ``build_middleware`` in
|
|
61
|
+
``decepticon.agents.build``. Plugins are expected to honour the
|
|
62
|
+
overall contract (e.g. a replacement EngagementContextMiddleware still
|
|
63
|
+
needs to inject scope) — the gate exists so an accidentally-installed
|
|
64
|
+
plugin can't silently subvert the safety story.
|
|
65
|
+
"""
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
# Common slots every agent uses (the "tail" of the middleware stack).
|
|
69
|
+
_TAIL_SLOTS: frozenset[MiddlewareSlot] = frozenset(
|
|
70
|
+
{
|
|
71
|
+
MiddlewareSlot.MODEL_FALLBACK,
|
|
72
|
+
MiddlewareSlot.SUMMARIZATION,
|
|
73
|
+
MiddlewareSlot.PROMPT_CACHING,
|
|
74
|
+
MiddlewareSlot.PATCH_TOOL_CALLS,
|
|
75
|
+
}
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
# Base slots — knowledge + filesystem + tail. Every agent gets these.
|
|
79
|
+
_BASE_SLOTS: frozenset[MiddlewareSlot] = _TAIL_SLOTS | {
|
|
80
|
+
MiddlewareSlot.SKILLS,
|
|
81
|
+
MiddlewareSlot.FILESYSTEM,
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
# Standard bash-executing agents (recon/exploit/postexploit/analyst/
|
|
85
|
+
# reverser/contract_auditor/cloud_hunter/ad_operator + plugin
|
|
86
|
+
# specialists verifier/patcher/scanner/exploiter): base + engagement
|
|
87
|
+
# context + sandbox notification.
|
|
88
|
+
_BASH_AGENT_SLOTS: frozenset[MiddlewareSlot] = _BASE_SLOTS | {
|
|
89
|
+
MiddlewareSlot.ENGAGEMENT_CONTEXT,
|
|
90
|
+
MiddlewareSlot.SANDBOX_NOTIFICATION,
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
SLOTS_PER_ROLE: dict[str, frozenset[MiddlewareSlot]] = {
|
|
95
|
+
# ── Standard orchestrator ──
|
|
96
|
+
"decepticon": _BASE_SLOTS
|
|
97
|
+
| {
|
|
98
|
+
MiddlewareSlot.ENGAGEMENT_CONTEXT,
|
|
99
|
+
MiddlewareSlot.SUBAGENT,
|
|
100
|
+
MiddlewareSlot.OPPLAN,
|
|
101
|
+
MiddlewareSlot.MODEL_OVERRIDE,
|
|
102
|
+
},
|
|
103
|
+
# ── Standard non-bash agent (planning + interview) ──
|
|
104
|
+
"soundwave": _BASE_SLOTS | {MiddlewareSlot.ENGAGEMENT_CONTEXT},
|
|
105
|
+
# ── Standard bash-executing specialists ──
|
|
106
|
+
"recon": _BASH_AGENT_SLOTS,
|
|
107
|
+
"exploit": _BASH_AGENT_SLOTS,
|
|
108
|
+
"postexploit": _BASH_AGENT_SLOTS,
|
|
109
|
+
"analyst": _BASH_AGENT_SLOTS,
|
|
110
|
+
"reverser": _BASH_AGENT_SLOTS,
|
|
111
|
+
"contract_auditor": _BASH_AGENT_SLOTS,
|
|
112
|
+
"cloud_hunter": _BASH_AGENT_SLOTS,
|
|
113
|
+
"ad_operator": _BASH_AGENT_SLOTS,
|
|
114
|
+
# ── Plugin orchestrator (no EngagementContext per the existing
|
|
115
|
+
# vulnresearch factory — it consumes its parent's context) ──
|
|
116
|
+
"vulnresearch": _BASE_SLOTS | {MiddlewareSlot.SUBAGENT, MiddlewareSlot.OPPLAN},
|
|
117
|
+
# ── Plugin read-only specialist (no bash, no SandboxNotification) ──
|
|
118
|
+
"detector": _BASE_SLOTS,
|
|
119
|
+
# ── Plugin bash-executing specialists ──
|
|
120
|
+
"verifier": _BASH_AGENT_SLOTS,
|
|
121
|
+
"patcher": _BASH_AGENT_SLOTS,
|
|
122
|
+
"scanner": _BASH_AGENT_SLOTS,
|
|
123
|
+
"exploiter": _BASH_AGENT_SLOTS,
|
|
124
|
+
}
|
|
125
|
+
"""Role → slot-set mapping. The assembler only walks slots present in
|
|
126
|
+
the role's set; anything else is skipped silently. Plugin agents
|
|
127
|
+
register their own role here via the ``decepticon.agents`` entry-point
|
|
128
|
+
group (handled by ``plugin_loader``)."""
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
__all__ = [
|
|
132
|
+
"MiddlewareSlot",
|
|
133
|
+
"SAFETY_CRITICAL_SLOTS",
|
|
134
|
+
"SLOTS_PER_ROLE",
|
|
135
|
+
]
|