dreadnode 2.0.44__py3-none-any.whl → 2.0.45__py3-none-any.whl
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.
- dreadnode/airt/_base.py +74 -13
- dreadnode/app/cli/evaluation.py +3 -1
- dreadnode/app/cli/main.py +150 -35
- dreadnode/app/server/app.py +44 -21
- dreadnode/app/server/prompt.py +7 -1
- dreadnode/app/tui/app.py +36 -2
- dreadnode/app/tui/dreadnode.tcss +37 -34
- dreadnode/app/tui/screens/raw_spans.py +2 -2
- dreadnode/app/tui/theme.py +54 -31
- dreadnode/app/tui/widgets/tool.py +2 -1
- dreadnode/builtin_capabilities/dreadnode/skills/dreadnode-cli/references/cli/evaluation.md +3 -3
- dreadnode/builtin_capabilities/dreadnode/skills/dreadnode-concepts/references/runtimes/configuration.md +1 -3
- dreadnode/builtin_capabilities/dreadnode/skills/dreadnode-concepts/references/runtimes/manifest-reference.md +1 -2
- dreadnode/builtin_capabilities/dreadnode/skills/dreadnode-concepts/references/sandboxes/isolation.md +59 -26
- dreadnode/builtin_capabilities/dreadnode/skills/dreadnode-concepts/references/tui/local-storage.md +4 -4
- dreadnode/core/tls.py +1 -1
- dreadnode/storage/storage.py +4 -3
- {dreadnode-2.0.44.dist-info → dreadnode-2.0.45.dist-info}/METADATA +1 -1
- {dreadnode-2.0.44.dist-info → dreadnode-2.0.45.dist-info}/RECORD +22 -22
- {dreadnode-2.0.44.dist-info → dreadnode-2.0.45.dist-info}/WHEEL +0 -0
- {dreadnode-2.0.44.dist-info → dreadnode-2.0.45.dist-info}/entry_points.txt +0 -0
- {dreadnode-2.0.44.dist-info → dreadnode-2.0.45.dist-info}/licenses/LICENSE +0 -0
dreadnode/airt/_base.py
CHANGED
|
@@ -89,12 +89,27 @@ class BlackBoxAttack:
|
|
|
89
89
|
self.rng = np.random.default_rng(seed)
|
|
90
90
|
self.airt_assessment_id = airt_assessment_id
|
|
91
91
|
self.airt_target_model = airt_target_model or target.name
|
|
92
|
-
|
|
92
|
+
# Make the default goal target-aware so the same objective against
|
|
93
|
+
# different models counts as distinct goals on the dashboard (e.g. evasion
|
|
94
|
+
# of the fraud, digits, and sentiment classifiers = 3 goals, not 1). An
|
|
95
|
+
# explicit airt_goal is respected verbatim; only the default is suffixed.
|
|
96
|
+
self.airt_goal = airt_goal or (
|
|
97
|
+
f"{self.default_goal} on {self.airt_target_model}"
|
|
98
|
+
if self.airt_target_model
|
|
99
|
+
else self.default_goal
|
|
100
|
+
)
|
|
93
101
|
self.airt_goal_category = airt_goal_category or self.default_goal_category
|
|
94
102
|
self._query_count = 0
|
|
95
103
|
#: A sample of real (input -> prediction) pairs so the finding can show
|
|
96
104
|
#: the actual queries the attacker sent, not just the total count.
|
|
97
105
|
self._query_samples: list[dict[str, t.Any]] = []
|
|
106
|
+
#: Count of trial spans emitted this run. Each algorithm iteration is one
|
|
107
|
+
#: trial (mirroring how a generative Study counts a trial per step), so the
|
|
108
|
+
#: platform can count trials / incurred budget for traditional-ML attacks.
|
|
109
|
+
self._trials_emitted = 0
|
|
110
|
+
#: Hard safety cap on trial spans so a pathologically long loop cannot
|
|
111
|
+
#: explode the trace tree.
|
|
112
|
+
self._max_trial_spans = 1000
|
|
98
113
|
|
|
99
114
|
# -- target I/O -------------------------------------------------------- #
|
|
100
115
|
async def _query(self, xs: t.Sequence[QueryInput]) -> list[Prediction]:
|
|
@@ -146,28 +161,74 @@ class BlackBoxAttack:
|
|
|
146
161
|
metrics: dict[str, float] | None = None,
|
|
147
162
|
**attrs: t.Any,
|
|
148
163
|
) -> None:
|
|
149
|
-
"""Emit a
|
|
164
|
+
"""Emit a **trial** span for one algorithm iteration, recording the
|
|
150
165
|
intermediate adversarial state so the full attack trajectory is
|
|
151
|
-
inspectable in the Traces tab.
|
|
166
|
+
inspectable in the Traces tab and every iteration is counted as a trial.
|
|
167
|
+
|
|
168
|
+
One iteration == one trial: this mirrors how a generative ``Study`` emits
|
|
169
|
+
a trial span per refinement step, so the platform counts trials (and the
|
|
170
|
+
incurred query budget) for traditional-ML attacks the same way. The span
|
|
171
|
+
carries the AIRT assessment/attack attributes and is a child of the run's
|
|
172
|
+
``study`` span (opened in :meth:`run`), which is how ClickHouse links a
|
|
173
|
+
trial to its assessment.
|
|
152
174
|
|
|
153
175
|
``input`` is the current candidate (perturbed text / feature vector /
|
|
154
176
|
image preview) the attacker sent; ``output`` is the target's response at
|
|
155
|
-
this step
|
|
156
|
-
|
|
157
|
-
|
|
177
|
+
this step; ``metrics`` are the scalar signals (distance, confidence,
|
|
178
|
+
fidelity) plotted over the run. The lightweight trial span is emitted
|
|
179
|
+
every iteration; the heavier input/output previews are attached only for
|
|
180
|
+
the first 20 iterations then every 10th, so a long run does not bloat the
|
|
158
181
|
trace tree."""
|
|
159
|
-
if
|
|
182
|
+
if self._trials_emitted >= self._max_trial_spans:
|
|
160
183
|
return
|
|
161
|
-
|
|
162
|
-
|
|
184
|
+
self._trials_emitted += 1
|
|
185
|
+
heavy = iteration < 20 or iteration % 10 == 0
|
|
186
|
+
with self._trial(iteration, **attrs) as span:
|
|
187
|
+
if span is None or not hasattr(span, "log_metric"):
|
|
163
188
|
return
|
|
164
189
|
with suppress(Exception):
|
|
165
|
-
if input is not None:
|
|
166
|
-
span.log_input("candidate", input)
|
|
167
|
-
if output is not None:
|
|
168
|
-
span.log_output("prediction", output)
|
|
169
190
|
for key, value in (metrics or {}).items():
|
|
170
191
|
span.log_metric(key, float(value), step=iteration)
|
|
192
|
+
if heavy and input is not None and hasattr(span, "log_input"):
|
|
193
|
+
span.log_input("candidate", input)
|
|
194
|
+
if heavy and output is not None and hasattr(span, "log_output"):
|
|
195
|
+
span.log_output("prediction", output)
|
|
196
|
+
|
|
197
|
+
def _trial(self, iteration: int, **attrs: t.Any) -> t.Any:
|
|
198
|
+
"""Open a ``trial`` span for one iteration, tagged with the AIRT
|
|
199
|
+
assessment/attack attributes so it is counted against this assessment."""
|
|
200
|
+
try:
|
|
201
|
+
from dreadnode.tracing.spans import trial_span
|
|
202
|
+
|
|
203
|
+
ctx = trial_span(
|
|
204
|
+
trial_id=f"{self._attack_name}-{iteration}-{self._trials_emitted}",
|
|
205
|
+
step=int(iteration),
|
|
206
|
+
airt_assessment_id=self.airt_assessment_id,
|
|
207
|
+
airt_trial_index=int(iteration),
|
|
208
|
+
airt_attack_name=self._attack_name,
|
|
209
|
+
airt_attack_domain=self.attack_domain,
|
|
210
|
+
airt_target_model=self.airt_target_model,
|
|
211
|
+
airt_goal=self.airt_goal,
|
|
212
|
+
airt_goal_category=self.airt_goal_category,
|
|
213
|
+
airt_input_modality=self.modality,
|
|
214
|
+
)
|
|
215
|
+
except Exception:
|
|
216
|
+
return nullcontext()
|
|
217
|
+
if not attrs:
|
|
218
|
+
return ctx
|
|
219
|
+
# Fold any per-iteration attrs (e.g. distance norm) onto the span.
|
|
220
|
+
from contextlib import contextmanager
|
|
221
|
+
|
|
222
|
+
@contextmanager
|
|
223
|
+
def _wrapped() -> t.Iterator[t.Any]:
|
|
224
|
+
with ctx as span:
|
|
225
|
+
if span is not None and hasattr(span, "set_attribute"):
|
|
226
|
+
with suppress(Exception):
|
|
227
|
+
for key, value in attrs.items():
|
|
228
|
+
span.set_attribute(str(key), value)
|
|
229
|
+
yield span
|
|
230
|
+
|
|
231
|
+
return _wrapped()
|
|
171
232
|
|
|
172
233
|
# -- run scaffold ------------------------------------------------------ #
|
|
173
234
|
@property
|
dreadnode/app/cli/evaluation.py
CHANGED
|
@@ -792,7 +792,9 @@ def create(
|
|
|
792
792
|
max_steps: Maximum agent tool/generation steps per sample.
|
|
793
793
|
Enforced via a headless session policy; the task timeout still
|
|
794
794
|
applies as a wall-clock bound.
|
|
795
|
-
cleanup_policy: Sandbox cleanup policy.
|
|
795
|
+
cleanup_policy: Sandbox cleanup policy. Hosted E2B evaluations support
|
|
796
|
+
only ``always``; ``on_success`` is available on Docker and
|
|
797
|
+
OpenSandbox deployments.
|
|
796
798
|
judge_model: Override the judge model for all tasks in this evaluation.
|
|
797
799
|
egress: Additional outbound destination for this run's sandboxes, as an
|
|
798
800
|
FQDN, a '*.suffix' wildcard, an IP, or a CIDR (repeatable). Unions
|
dreadnode/app/cli/main.py
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
"""Root cyclopts App with top-level commands (tui, login, serve)."""
|
|
2
2
|
|
|
3
|
+
import importlib
|
|
3
4
|
import importlib.metadata
|
|
4
5
|
import os
|
|
5
6
|
import secrets
|
|
6
7
|
import sys
|
|
8
|
+
import threading
|
|
7
9
|
import time
|
|
8
10
|
import typing as t
|
|
9
11
|
|
|
@@ -14,25 +16,8 @@ import cyclopts
|
|
|
14
16
|
from loguru import logger
|
|
15
17
|
from rich.panel import Panel
|
|
16
18
|
|
|
17
|
-
from dreadnode.app.cli.airt import cli as airt_cli
|
|
18
19
|
from dreadnode.app.cli.args import RESUME_PICK_SENTINEL, PlatformArgs, TuiArgs
|
|
19
|
-
from dreadnode.app.cli.capability import cli as capability_cli
|
|
20
|
-
from dreadnode.app.cli.dataset import cli as dataset_cli
|
|
21
|
-
from dreadnode.app.cli.environment import cli as environment_cli
|
|
22
|
-
from dreadnode.app.cli.evaluation import cli as evaluation_cli
|
|
23
|
-
from dreadnode.app.cli.inference_model import cli as inference_model_cli
|
|
24
|
-
from dreadnode.app.cli.judge import cli as judge_cli
|
|
25
|
-
from dreadnode.app.cli.model import cli as model_cli
|
|
26
|
-
from dreadnode.app.cli.optimize import cli as optimize_cli
|
|
27
|
-
from dreadnode.app.cli.runtime import cli as runtime_cli
|
|
28
|
-
from dreadnode.app.cli.sandbox import cli as sandbox_cli
|
|
29
|
-
from dreadnode.app.cli.secret import cli as secret_cli
|
|
30
|
-
from dreadnode.app.cli.session import cli as session_cli
|
|
31
20
|
from dreadnode.app.cli.shared import console, print_error, print_success
|
|
32
|
-
from dreadnode.app.cli.task import cli as task_cli
|
|
33
|
-
from dreadnode.app.cli.task_set import cli as task_set_cli
|
|
34
|
-
from dreadnode.app.cli.train import cli as train_cli
|
|
35
|
-
from dreadnode.app.cli.worlds import cli as worlds_cli
|
|
36
21
|
from dreadnode.app.model_catalog import resolve_model
|
|
37
22
|
from dreadnode.core.tls import format_tls_error
|
|
38
23
|
|
|
@@ -68,7 +53,55 @@ def _render_profile_context(profile: t.Any) -> str:
|
|
|
68
53
|
# Root app
|
|
69
54
|
# ---------------------------------------------------------------------------
|
|
70
55
|
|
|
71
|
-
|
|
56
|
+
|
|
57
|
+
class _LazyCommandApp(cyclopts.App):
|
|
58
|
+
"""Root app that resolves subcommands from the tokens it is handed.
|
|
59
|
+
|
|
60
|
+
Subcommands import on demand (see ``_LAZY_COMMANDS`` below), so anything
|
|
61
|
+
reaching this object directly rather than through ``run()`` would otherwise
|
|
62
|
+
see only the commands defined in this module. That failure is silent:
|
|
63
|
+
cyclopts answers an unregistered command by printing the *root* help and
|
|
64
|
+
exiting 0, so the caller gets a plausible wrong answer instead of an error.
|
|
65
|
+
|
|
66
|
+
Preparing inside the entry points covers every caller rather than the two
|
|
67
|
+
that happen to be known today — the agent's ``dreadnode_cli`` tool calls
|
|
68
|
+
``cli(tokens)``, tests call ``parse_args``, and the next one has not been
|
|
69
|
+
written yet. Registration is idempotent, so the repeat calls cyclopts makes
|
|
70
|
+
internally cost a set lookup.
|
|
71
|
+
"""
|
|
72
|
+
|
|
73
|
+
@staticmethod
|
|
74
|
+
def _as_tokens(tokens: "None | str | t.Iterable[str]") -> list[str]:
|
|
75
|
+
if tokens is None:
|
|
76
|
+
return sys.argv[1:]
|
|
77
|
+
if isinstance(tokens, str):
|
|
78
|
+
return tokens.split()
|
|
79
|
+
return list(tokens)
|
|
80
|
+
|
|
81
|
+
def __call__(self, tokens: "None | str | t.Iterable[str]" = None, **kwargs: t.Any) -> t.Any:
|
|
82
|
+
_prepare_commands(self._as_tokens(tokens))
|
|
83
|
+
return super().__call__(tokens, **kwargs)
|
|
84
|
+
|
|
85
|
+
def parse_args( # type: ignore[override]
|
|
86
|
+
self, tokens: "None | str | t.Iterable[str]" = None, **kwargs: t.Any
|
|
87
|
+
) -> t.Any:
|
|
88
|
+
_prepare_commands(self._as_tokens(tokens))
|
|
89
|
+
return super().parse_args(tokens, **kwargs)
|
|
90
|
+
|
|
91
|
+
def parse_known_args( # type: ignore[override]
|
|
92
|
+
self, tokens: "None | str | t.Iterable[str]" = None, **kwargs: t.Any
|
|
93
|
+
) -> t.Any:
|
|
94
|
+
_prepare_commands(self._as_tokens(tokens))
|
|
95
|
+
return super().parse_known_args(tokens, **kwargs)
|
|
96
|
+
|
|
97
|
+
def parse_commands( # type: ignore[override]
|
|
98
|
+
self, tokens: "None | str | t.Iterable[str]" = None, **kwargs: t.Any
|
|
99
|
+
) -> t.Any:
|
|
100
|
+
_prepare_commands(self._as_tokens(tokens))
|
|
101
|
+
return super().parse_commands(tokens, **kwargs)
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
cli = _LazyCommandApp(
|
|
72
105
|
name="dreadnode",
|
|
73
106
|
help="Dreadnode CLI — agent development and platform tooling.",
|
|
74
107
|
version=_get_version_safe(),
|
|
@@ -85,23 +118,99 @@ cli["--install-completion"].group = "Meta"
|
|
|
85
118
|
# Subcommands
|
|
86
119
|
# ---------------------------------------------------------------------------
|
|
87
120
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
cli.
|
|
104
|
-
cli.
|
|
121
|
+
# Subcommands are imported when one is invoked, not when the CLI loads.
|
|
122
|
+
#
|
|
123
|
+
# Registering them eagerly meant every invocation paid for every command:
|
|
124
|
+
# `dreadnode serve` imported the AIRT tree and, under it, pandas, pyarrow,
|
|
125
|
+
# numpy and optuna, none of which a running runtime uses. That was the
|
|
126
|
+
# majority of a runtime's cold start (ENG-8259).
|
|
127
|
+
#
|
|
128
|
+
# This is the same lazy-table shape the SDK already uses for its own
|
|
129
|
+
# subpackages (`__lazy_submodules__` and friends in `dreadnode/__init__.py`);
|
|
130
|
+
# cyclopts has no native support for deferred subcommands, so registration is
|
|
131
|
+
# driven from the invoked token instead.
|
|
132
|
+
#
|
|
133
|
+
# Keys are the command name as it is typed. The value is the module holding
|
|
134
|
+
# the sub-app and its alias, if it has one.
|
|
135
|
+
_LAZY_COMMANDS: dict[str, tuple[str, str | None]] = {
|
|
136
|
+
"airt": ("dreadnode.app.cli.airt", None),
|
|
137
|
+
"capability": ("dreadnode.app.cli.capability", None),
|
|
138
|
+
"dataset": ("dreadnode.app.cli.dataset", None),
|
|
139
|
+
"environment": ("dreadnode.app.cli.environment", "env"),
|
|
140
|
+
"evaluation": ("dreadnode.app.cli.evaluation", None),
|
|
141
|
+
"inference-model": ("dreadnode.app.cli.inference_model", "llm"),
|
|
142
|
+
"judge": ("dreadnode.app.cli.judge", None),
|
|
143
|
+
"model": ("dreadnode.app.cli.model", None),
|
|
144
|
+
"optimize": ("dreadnode.app.cli.optimize", None),
|
|
145
|
+
"runtime": ("dreadnode.app.cli.runtime", None),
|
|
146
|
+
"sandbox": ("dreadnode.app.cli.sandbox", None),
|
|
147
|
+
"secret": ("dreadnode.app.cli.secret", None),
|
|
148
|
+
"session": ("dreadnode.app.cli.session", None),
|
|
149
|
+
"task": ("dreadnode.app.cli.task", None),
|
|
150
|
+
"task-set": ("dreadnode.app.cli.task_set", None),
|
|
151
|
+
"train": ("dreadnode.app.cli.train", None),
|
|
152
|
+
"worlds": ("dreadnode.app.cli.worlds", None),
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
_COMMAND_ALIASES: dict[str, str] = {
|
|
156
|
+
alias: name for name, (_, alias) in _LAZY_COMMANDS.items() if alias
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
_registered_commands: set[str] = set()
|
|
160
|
+
# `dreadnode serve` hosts concurrent sessions and the agent's CLI tool is
|
|
161
|
+
# dispatched with asyncio.to_thread, so two threads can reach the same
|
|
162
|
+
# unregistered command at once. cyclopts raises CommandCollisionError on a
|
|
163
|
+
# duplicate name, which would fail the second caller's invocation.
|
|
164
|
+
_registration_lock = threading.Lock()
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
def register_command(name: str) -> bool:
|
|
168
|
+
"""Register one lazy subcommand, importing its module. True if it exists.
|
|
169
|
+
|
|
170
|
+
Idempotent, so a caller may register a command that is already present.
|
|
171
|
+
"""
|
|
172
|
+
primary = _COMMAND_ALIASES.get(name, name)
|
|
173
|
+
entry = _LAZY_COMMANDS.get(primary)
|
|
174
|
+
if entry is None:
|
|
175
|
+
return False
|
|
176
|
+
if primary in _registered_commands:
|
|
177
|
+
return True
|
|
178
|
+
with _registration_lock:
|
|
179
|
+
# Re-check under the lock: another thread may have registered it while
|
|
180
|
+
# this one waited, and registering twice raises.
|
|
181
|
+
if primary not in _registered_commands:
|
|
182
|
+
module_path, alias = entry
|
|
183
|
+
subapp = importlib.import_module(module_path).cli
|
|
184
|
+
if alias:
|
|
185
|
+
cli.command(subapp, alias=alias)
|
|
186
|
+
else:
|
|
187
|
+
cli.command(subapp)
|
|
188
|
+
_registered_commands.add(primary)
|
|
189
|
+
return True
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
def register_all_commands() -> None:
|
|
193
|
+
"""Register every subcommand, importing all of them.
|
|
194
|
+
|
|
195
|
+
Needed whenever the whole command tree has to be visible at once: help,
|
|
196
|
+
shell completion, an unrecognised command that should be reported against
|
|
197
|
+
the full list, and tests that assert on the CLI surface.
|
|
198
|
+
"""
|
|
199
|
+
for name in _LAZY_COMMANDS:
|
|
200
|
+
register_command(name)
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
def _prepare_commands(tokens: t.Sequence[str]) -> None:
|
|
204
|
+
"""Import whatever the invocation needs, and nothing else."""
|
|
205
|
+
for token in tokens:
|
|
206
|
+
if token.startswith("-"):
|
|
207
|
+
continue # a flag before the command, e.g. `dreadnode --version`
|
|
208
|
+
if register_command(token):
|
|
209
|
+
return
|
|
210
|
+
if token in cli:
|
|
211
|
+
return # defined here rather than in a subcommand module
|
|
212
|
+
break # unknown: fall through so the error lists every command
|
|
213
|
+
register_all_commands()
|
|
105
214
|
|
|
106
215
|
|
|
107
216
|
# ---------------------------------------------------------------------------
|
|
@@ -115,6 +224,7 @@ def _meta(
|
|
|
115
224
|
) -> None:
|
|
116
225
|
try:
|
|
117
226
|
console.print()
|
|
227
|
+
_prepare_commands(tokens)
|
|
118
228
|
cli(tokens)
|
|
119
229
|
except Exception as exc:
|
|
120
230
|
if DEBUG:
|
|
@@ -645,6 +755,11 @@ def run(argv: list[str] | None = None) -> None:
|
|
|
645
755
|
try:
|
|
646
756
|
tokens = argv if argv is not None else sys.argv[1:]
|
|
647
757
|
tokens = _rewrite_bare_resume(list(tokens))
|
|
758
|
+
# Before dispatch, not inside the meta handler: cyclopts answers
|
|
759
|
+
# `--help` and `--version` on the meta app itself and never runs the
|
|
760
|
+
# handler body, so registering there would leave the help listing
|
|
761
|
+
# showing only the commands defined in this module.
|
|
762
|
+
_prepare_commands(tokens)
|
|
648
763
|
cli.meta(tokens)
|
|
649
764
|
except SystemExit as exc:
|
|
650
765
|
if exc.code not in (None, 0):
|
dreadnode/app/server/app.py
CHANGED
|
@@ -611,6 +611,31 @@ def create_agent(
|
|
|
611
611
|
# =============================================================================
|
|
612
612
|
|
|
613
613
|
|
|
614
|
+
def _configure_litellm_env() -> None:
|
|
615
|
+
"""Configure litellm through the environment, before anything imports it.
|
|
616
|
+
|
|
617
|
+
litellm reads these at import time:
|
|
618
|
+
|
|
619
|
+
drop_params = bool(os.getenv("LITELLM_DROP_PARAMS", False))
|
|
620
|
+
modify_params = bool(os.getenv("LITELLM_MODIFY_PARAMS", False))
|
|
621
|
+
|
|
622
|
+
so setting them configures whoever imports litellm first — the warm thread,
|
|
623
|
+
or a request that beats it. Going through the environment is what lets the
|
|
624
|
+
import itself come off the readiness path: this needs no import of its own.
|
|
625
|
+
|
|
626
|
+
``suppress_debug_info`` has no environment hook and is set as an attribute
|
|
627
|
+
by the warm thread. It only silences debug prints, so the warm window costs
|
|
628
|
+
noise rather than correctness.
|
|
629
|
+
|
|
630
|
+
``LITELLM_LOCAL_MODEL_COST_MAP`` stops litellm fetching its model-cost map
|
|
631
|
+
over the network on first import. The platform sets it in the sandbox
|
|
632
|
+
environment; this covers a local ``dreadnode serve``.
|
|
633
|
+
"""
|
|
634
|
+
os.environ.setdefault("LITELLM_DROP_PARAMS", "True")
|
|
635
|
+
os.environ.setdefault("LITELLM_MODIFY_PARAMS", "True")
|
|
636
|
+
os.environ.setdefault("LITELLM_LOCAL_MODEL_COST_MAP", "True")
|
|
637
|
+
|
|
638
|
+
|
|
614
639
|
def _warm_litellm() -> None:
|
|
615
640
|
"""Import litellm so the first chat turn doesn't pay the cold-import cost.
|
|
616
641
|
|
|
@@ -622,10 +647,13 @@ def _warm_litellm() -> None:
|
|
|
622
647
|
"""
|
|
623
648
|
_t0 = time.perf_counter()
|
|
624
649
|
try:
|
|
625
|
-
#
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
650
|
+
# Belt and braces: the lifecycle sets these, but this runs on other
|
|
651
|
+
# paths too and must never be the one that imports litellm unconfigured.
|
|
652
|
+
_configure_litellm_env()
|
|
653
|
+
with startup_clock.timed("litellm.import"):
|
|
654
|
+
import litellm
|
|
655
|
+
import litellm.exceptions
|
|
656
|
+
startup_clock.mark("litellm_imported")
|
|
629
657
|
|
|
630
658
|
litellm.drop_params = True
|
|
631
659
|
litellm.suppress_debug_info = True # ty: ignore[invalid-assignment]
|
|
@@ -695,23 +723,18 @@ async def server_lifecycle() -> t.AsyncIterator[None]:
|
|
|
695
723
|
await registry.worker_manager.start(registry)
|
|
696
724
|
state.startup.mark("workers_started")
|
|
697
725
|
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
#
|
|
701
|
-
#
|
|
702
|
-
#
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
except Exception: # noqa: S110 - _warm_litellm will retry the full import
|
|
711
|
-
pass
|
|
712
|
-
|
|
713
|
-
# Warm litellm in a background thread so the health endpoint goes live
|
|
714
|
-
# immediately while the ~2s import finishes in the background.
|
|
726
|
+
_configure_litellm_env()
|
|
727
|
+
|
|
728
|
+
# litellm is deliberately NOT imported here. It was the largest single item
|
|
729
|
+
# on the readiness path (~1.8s on a laptop, ~4.3s on a sandbox) and it ran
|
|
730
|
+
# last, so readiness waited on it (ENG-8259). The warm thread below still
|
|
731
|
+
# shifts the import cost off the first chat turn; it just no longer holds
|
|
732
|
+
# readiness open while it does.
|
|
733
|
+
#
|
|
734
|
+
# The flags this block used to import litellm to set are covered by the
|
|
735
|
+
# environment above. Note that LiteLLMGenerator.__post_model_init__ looks
|
|
736
|
+
# like it sets them too, but that is not a pydantic hook and never runs —
|
|
737
|
+
# see ENG-8259. Do not rely on it.
|
|
715
738
|
warm_future = asyncio.get_running_loop().run_in_executor(None, _warm_litellm)
|
|
716
739
|
warm_future.add_done_callback(lambda _future: state.startup.mark("litellm_warmed"))
|
|
717
740
|
|
dreadnode/app/server/prompt.py
CHANGED
|
@@ -38,7 +38,13 @@ def _get_cli_commands() -> str:
|
|
|
38
38
|
its internals, the CLI section will be omitted from the system prompt.
|
|
39
39
|
"""
|
|
40
40
|
try:
|
|
41
|
-
from dreadnode.app.cli.main import cli
|
|
41
|
+
from dreadnode.app.cli.main import cli, register_all_commands
|
|
42
|
+
|
|
43
|
+
# Subcommands import on demand (ENG-8259), and this listing is built
|
|
44
|
+
# once per process behind a cache. Under `dreadnode serve` nothing else
|
|
45
|
+
# would have registered them, so the agent's system prompt would name
|
|
46
|
+
# only the handful of commands defined in main.py, permanently.
|
|
47
|
+
register_all_commands()
|
|
42
48
|
|
|
43
49
|
# Access internal command registry — guarded by try/except
|
|
44
50
|
command_registry = getattr(cli, "_commands", None)
|
dreadnode/app/tui/app.py
CHANGED
|
@@ -75,14 +75,24 @@ from dreadnode.app.tui.status_messages import STATUS_READY, STATUS_STARTING
|
|
|
75
75
|
from dreadnode.app.tui.theme import (
|
|
76
76
|
ACCENT,
|
|
77
77
|
BG,
|
|
78
|
+
BG_LIGHT,
|
|
79
|
+
BG_LIGHTER,
|
|
80
|
+
BORDER,
|
|
81
|
+
BORDER_LIGHT,
|
|
78
82
|
BRAND,
|
|
83
|
+
CODE,
|
|
84
|
+
CODE_BG,
|
|
79
85
|
ERROR,
|
|
80
86
|
FG,
|
|
81
87
|
FG_FAINTEST,
|
|
82
88
|
FG_MUTED,
|
|
83
89
|
FG_SUBTLE,
|
|
84
90
|
INFO,
|
|
91
|
+
IS_LIGHT,
|
|
92
|
+
LINK,
|
|
93
|
+
PURPLE,
|
|
85
94
|
SUCCESS,
|
|
95
|
+
TEAL,
|
|
86
96
|
WARNING,
|
|
87
97
|
)
|
|
88
98
|
from dreadnode.app.tui.turn_lifecycle import TurnLifecycle
|
|
@@ -1731,11 +1741,35 @@ class DreadnodeTextualApp(App[None]):
|
|
|
1731
1741
|
warning=WARNING,
|
|
1732
1742
|
error=ERROR,
|
|
1733
1743
|
success=SUCCESS,
|
|
1734
|
-
dark=
|
|
1744
|
+
dark=not IS_LIGHT,
|
|
1735
1745
|
background=BG,
|
|
1736
1746
|
surface=BG,
|
|
1737
|
-
panel
|
|
1747
|
+
# $panel is the raised tier Textual paints its own widgets on
|
|
1748
|
+
# (DataTable headers, tooltips, collapsibles). Dark flattens it
|
|
1749
|
+
# onto BG; light needs the real step up, because BG there is
|
|
1750
|
+
# pure white and the surface would otherwise disappear.
|
|
1751
|
+
panel=BG_LIGHT if IS_LIGHT else BG,
|
|
1738
1752
|
variables={
|
|
1753
|
+
"dn-bg": BG,
|
|
1754
|
+
"dn-bg-light": BG_LIGHT,
|
|
1755
|
+
"dn-bg-lighter": BG_LIGHTER,
|
|
1756
|
+
"dn-fg": FG,
|
|
1757
|
+
"dn-fg-subtle": FG_SUBTLE,
|
|
1758
|
+
"dn-fg-muted": FG_MUTED,
|
|
1759
|
+
"dn-fg-faintest": FG_FAINTEST,
|
|
1760
|
+
"dn-border": BORDER,
|
|
1761
|
+
"dn-border-light": BORDER_LIGHT,
|
|
1762
|
+
"dn-code": CODE,
|
|
1763
|
+
"dn-code-bg": CODE_BG,
|
|
1764
|
+
"dn-link": LINK,
|
|
1765
|
+
"dn-info": INFO,
|
|
1766
|
+
"dn-success": SUCCESS,
|
|
1767
|
+
"dn-warning": WARNING,
|
|
1768
|
+
"dn-error": ERROR,
|
|
1769
|
+
"dn-brand": BRAND,
|
|
1770
|
+
"dn-accent": ACCENT,
|
|
1771
|
+
"dn-purple": PURPLE,
|
|
1772
|
+
"dn-teal": TEAL,
|
|
1739
1773
|
# Calm markdown headers — avoid primary orange bleeding in.
|
|
1740
1774
|
# H1-H4 share $fg so headings read against $fg-subtle body
|
|
1741
1775
|
# copy; H5/H6 step down to $fg-muted.
|
dreadnode/app/tui/dreadnode.tcss
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/* Dreadnode TUI — minimal dark
|
|
1
|
+
/* Dreadnode TUI — minimal theme, dark by default
|
|
2
2
|
*
|
|
3
3
|
* Design principles:
|
|
4
4
|
* 1. Content is king — full width for conversation
|
|
@@ -7,41 +7,44 @@
|
|
|
7
7
|
* 4. Minimal chrome — one status line, no sidebar, no hotkey bar
|
|
8
8
|
* 5. Clear hierarchy — bullets + whitespace over bordered panels
|
|
9
9
|
* 6. Quiet confidence — subtle indicators, not flashy gradients
|
|
10
|
+
*
|
|
11
|
+
* Every variable below aliases a `$dn-*` theme variable that app.py feeds from
|
|
12
|
+
* theme.py, which resolves the dark or light value. Edit the palette there;
|
|
13
|
+
* a literal hex here would be correct for only one theme.
|
|
10
14
|
*/
|
|
11
15
|
|
|
12
|
-
$bg:
|
|
13
|
-
$bg-light:
|
|
14
|
-
$bg-lighter:
|
|
15
|
-
|
|
16
|
-
$fg:
|
|
17
|
-
$fg-subtle:
|
|
18
|
-
$fg-muted:
|
|
19
|
-
$fg-faintest:
|
|
20
|
-
|
|
21
|
-
/* Inline code + fenced blocks:
|
|
22
|
-
* The pill carries the "code" affordance; tune
|
|
23
|
-
*
|
|
24
|
-
|
|
25
|
-
$code: $
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
$border:
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
$
|
|
36
|
-
$
|
|
37
|
-
$
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
$
|
|
42
|
-
$
|
|
43
|
-
$
|
|
44
|
-
$teal: #20dfc8;
|
|
16
|
+
$bg: $dn-bg;
|
|
17
|
+
$bg-light: $dn-bg-light;
|
|
18
|
+
$bg-lighter: $dn-bg-lighter;
|
|
19
|
+
|
|
20
|
+
$fg: $dn-fg;
|
|
21
|
+
$fg-subtle: $dn-fg-subtle;
|
|
22
|
+
$fg-muted: $dn-fg-muted;
|
|
23
|
+
$fg-faintest: $dn-fg-faintest;
|
|
24
|
+
|
|
25
|
+
/* Inline code + fenced blocks: prose-colored text on a raised background
|
|
26
|
+
* "pill". The pill carries the "code" affordance; tune CODE / CODE_BG in
|
|
27
|
+
* theme.py to restyle all code. */
|
|
28
|
+
$code: $dn-code;
|
|
29
|
+
$code-bg: $dn-code-bg;
|
|
30
|
+
|
|
31
|
+
/* Links: always underlined, colored by LINK in theme.py. Warm accent is
|
|
32
|
+
* reserved for tool calls. */
|
|
33
|
+
$link: $dn-link;
|
|
34
|
+
|
|
35
|
+
$border: $dn-border;
|
|
36
|
+
$border-light: $dn-border-light;
|
|
37
|
+
|
|
38
|
+
$info: $dn-info;
|
|
39
|
+
$success: $dn-success;
|
|
40
|
+
$warning: $dn-warning;
|
|
41
|
+
$error: $dn-error;
|
|
42
|
+
|
|
43
|
+
/* Extended palette — platform screens, data visualization */
|
|
44
|
+
$brand: $dn-brand;
|
|
45
|
+
$accent: $dn-accent;
|
|
46
|
+
$purple: $dn-purple;
|
|
47
|
+
$teal: $dn-teal;
|
|
45
48
|
|
|
46
49
|
Screen {
|
|
47
50
|
background: $bg;
|
|
@@ -17,7 +17,7 @@ from textual.widgets import DataTable, Static
|
|
|
17
17
|
|
|
18
18
|
from dreadnode.app.tui.screens.base import DreadnodeScreen
|
|
19
19
|
from dreadnode.app.tui.spans_reader import RawSpansReader, SpanLineSummary
|
|
20
|
-
from dreadnode.app.tui.theme import FG, FG_FAINTEST, FG_MUTED, FG_SUBTLE, WARNING
|
|
20
|
+
from dreadnode.app.tui.theme import FG, FG_FAINTEST, FG_MUTED, FG_SUBTLE, SYNTAX_THEME, WARNING
|
|
21
21
|
|
|
22
22
|
if t.TYPE_CHECKING:
|
|
23
23
|
from pathlib import Path
|
|
@@ -185,6 +185,6 @@ class RawSpansScreen(DreadnodeScreen):
|
|
|
185
185
|
renderable = Group(
|
|
186
186
|
self._detail_header(summary),
|
|
187
187
|
Text(""),
|
|
188
|
-
Syntax(payload, "json", theme=
|
|
188
|
+
Syntax(payload, "json", theme=SYNTAX_THEME, word_wrap=True),
|
|
189
189
|
)
|
|
190
190
|
self.query_one("#raw-spans-detail", Static).update(renderable)
|
dreadnode/app/tui/theme.py
CHANGED
|
@@ -8,48 +8,71 @@ The palette is aligned with the variables in `dreadnode.tcss`.
|
|
|
8
8
|
|
|
9
9
|
from __future__ import annotations
|
|
10
10
|
|
|
11
|
+
import os
|
|
12
|
+
|
|
11
13
|
# --- Color Palette -----------------------------------------------------------
|
|
12
|
-
# Single source of truth
|
|
13
|
-
#
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
#
|
|
14
|
+
# Single source of truth for both render paths. `dreadnode.tcss` declares its
|
|
15
|
+
# `$` variables as aliases of the `$dn-*` theme variables that `app.py` feeds
|
|
16
|
+
# from the constants below, so a value changed here reaches the stylesheet on
|
|
17
|
+
# its own. Never write a literal hex into the stylesheet — it would be correct
|
|
18
|
+
# for only one theme.
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def _is_light() -> bool:
|
|
22
|
+
"""Whether ``DREADNODE_TUI_THEME`` selects the light palette.
|
|
23
|
+
|
|
24
|
+
Any other value — unset, misspelled, or explicitly ``dark`` — selects dark.
|
|
25
|
+
"""
|
|
26
|
+
return os.environ.get("DREADNODE_TUI_THEME", "dark").strip().lower() == "light"
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
# Read once at import. The palette is baked into the module constants below and
|
|
30
|
+
# into the Textual theme registered at app start, so it cannot change without
|
|
31
|
+
# restarting the process.
|
|
32
|
+
IS_LIGHT = _is_light()
|
|
33
|
+
|
|
34
|
+
BG = "#ffffff" if IS_LIGHT else "#17191c"
|
|
35
|
+
BG_LIGHT = "#f4f5f7" if IS_LIGHT else "#1f2226"
|
|
36
|
+
BG_LIGHTER = "#e8eaed" if IS_LIGHT else "#292c31"
|
|
37
|
+
|
|
38
|
+
# Foreground ramp — four tiers walking away from the background in even ~17 L*
|
|
39
|
+
# (perceptual lightness) steps, so each tier is equally distinguishable. Dark
|
|
40
|
+
# starts at a crisp, faintly cool near-white (L*97) and steps down; light
|
|
41
|
+
# mirrors that spacing upward from near-black. The top three tiers clear WCAG
|
|
42
|
+
# AA against BG in both themes; FG_FAINTEST is the quiet tail at roughly 3:1.
|
|
43
|
+
FG = "#1f2328" if IS_LIGHT else "#f2f7fc"
|
|
44
|
+
FG_SUBTLE = "#57606a" if IS_LIGHT else "#c2c7cc"
|
|
45
|
+
FG_MUTED = "#6e7781" if IS_LIGHT else "#95999e"
|
|
46
|
+
FG_FAINTEST = "#8c959f" if IS_LIGHT else "#696d72"
|
|
47
|
+
|
|
48
|
+
# Inline `code` and fenced blocks — prose-colored text on a raised background
|
|
28
49
|
# "pill". The pill carries the "this is code" affordance, so the text stays in
|
|
29
50
|
# the prose family instead of a saturated accent (which is reserved for tool
|
|
30
51
|
# calls). Aliased to the palette by design; set to a literal hex to diverge.
|
|
31
|
-
# Keep in sync with $code / $code-bg in dreadnode.tcss.
|
|
32
52
|
CODE = FG
|
|
33
53
|
CODE_BG = BG_LIGHT
|
|
34
54
|
|
|
35
|
-
# Links —
|
|
36
|
-
#
|
|
37
|
-
#
|
|
38
|
-
|
|
55
|
+
# Links — always underlined. Dark keeps them in the prose family so the
|
|
56
|
+
# underline alone carries the affordance; light needs a conventional blue,
|
|
57
|
+
# because near-black on white is indistinguishable from body copy. Warm accent
|
|
58
|
+
# is reserved for tool calls, so links stay quiet and hover to $accent.
|
|
59
|
+
LINK = "#0969da" if IS_LIGHT else FG
|
|
39
60
|
|
|
40
|
-
BORDER = "#2b343f"
|
|
41
|
-
BORDER_LIGHT = "#434a55"
|
|
61
|
+
BORDER = "#d0d7de" if IS_LIGHT else "#2b343f"
|
|
62
|
+
BORDER_LIGHT = "#afb8c1" if IS_LIGHT else "#434a55"
|
|
42
63
|
|
|
43
|
-
INFO = "#4689bf"
|
|
44
|
-
SUCCESS = "#68c147"
|
|
45
|
-
WARNING = "#c8ac4a"
|
|
46
|
-
ERROR = "#e44f4f"
|
|
64
|
+
INFO = "#0969da" if IS_LIGHT else "#4689bf"
|
|
65
|
+
SUCCESS = "#1a7f37" if IS_LIGHT else "#68c147"
|
|
66
|
+
WARNING = "#9a6700" if IS_LIGHT else "#c8ac4a"
|
|
67
|
+
ERROR = "#cf222e" if IS_LIGHT else "#e44f4f"
|
|
47
68
|
|
|
48
69
|
# Extended palette — platform screens, data visualization
|
|
49
|
-
BRAND = "#ca5e44"
|
|
50
|
-
ACCENT = "#ef562f"
|
|
51
|
-
PURPLE = "#a650fb"
|
|
52
|
-
TEAL = "#20dfc8"
|
|
70
|
+
BRAND = "#bc4c34" if IS_LIGHT else "#ca5e44"
|
|
71
|
+
ACCENT = "#cf4b2f" if IS_LIGHT else "#ef562f"
|
|
72
|
+
PURPLE = "#8250df" if IS_LIGHT else "#a650fb"
|
|
73
|
+
TEAL = "#0b7d75" if IS_LIGHT else "#20dfc8"
|
|
74
|
+
|
|
75
|
+
SYNTAX_THEME = "default" if IS_LIGHT else "monokai"
|
|
53
76
|
|
|
54
77
|
|
|
55
78
|
# --- Graphic (paste new art here; wordmark is auto-composed to the right) ---
|
|
@@ -31,6 +31,7 @@ from dreadnode.app.tui.theme import (
|
|
|
31
31
|
FG_MUTED,
|
|
32
32
|
FG_SUBTLE,
|
|
33
33
|
LINK,
|
|
34
|
+
SYNTAX_THEME,
|
|
34
35
|
WARNING,
|
|
35
36
|
)
|
|
36
37
|
|
|
@@ -208,7 +209,7 @@ class ThemedMarkdown(Markdown):
|
|
|
208
209
|
def __init__(
|
|
209
210
|
self,
|
|
210
211
|
markup: str,
|
|
211
|
-
code_theme: str =
|
|
212
|
+
code_theme: str = SYNTAX_THEME,
|
|
212
213
|
justify: JustifyMethod | None = None,
|
|
213
214
|
style: str | Style = "none",
|
|
214
215
|
hyperlinks: bool = True,
|
|
@@ -48,7 +48,7 @@ Builds the evaluation request from CLI flags, an evaluation.yaml manifest (`--fi
|
|
|
48
48
|
* `--concurrency` — Maximum concurrent evaluation samples.
|
|
49
49
|
* `--task-timeout-sec` — Timeout per task in seconds.
|
|
50
50
|
* `--max-steps` — Maximum agent tool/generation steps per sample. Enforced via a headless session policy; the task timeout still applies as a wall-clock bound.
|
|
51
|
-
* `--cleanup-policy` — Sandbox cleanup policy. *\[choices: always, on\_success]*
|
|
51
|
+
* `--cleanup-policy` — Sandbox cleanup policy. Hosted E2B evaluations support only `always`; `on_success` is available on Docker and OpenSandbox deployments. *\[choices: always, on\_success]*
|
|
52
52
|
* `--judge-model` — Override the judge model for all tasks in this evaluation.
|
|
53
53
|
* `--env-model` — Override a task ENVIRONMENT (defender) model by role: ROLE=MODEL\_ID (e.g. —env-model blue=dn/claude-sonnet-4-6, repeatable). Distinct from —model (solver) and —judge-model. The role must be declared overridable in the task’s models map.
|
|
54
54
|
* `--egress` — Additional outbound destination for this run’s sandboxes (e.g. —egress staging.example.com, —egress ‘\*.internal.corp’, —egress 10.20.0.0/16; repeatable). Targets union with the task’s own egress.allow and are checked against the deployment’s egress floor identically, so they grant no reach a task declaration could not. Supplying any target makes this run a declaration, restricting it to what is named plus the platform destinations the platform derives.
|
|
@@ -200,9 +200,9 @@ Terminal window
|
|
|
200
200
|
$ dn evaluation retry <evaluation-id>
|
|
201
201
|
```
|
|
202
202
|
|
|
203
|
-
Retry failed
|
|
203
|
+
Retry failed samples in an evaluation.
|
|
204
204
|
|
|
205
|
-
Resets samples that ended in failed, timed\_out, or infra\_error back to queued so they are picked up by workers again.
|
|
205
|
+
Resets samples that ended in failed, timed\_out, cancelled, or infra\_error back to queued so they are picked up by workers again. Samples whose judge errored are not requeued: rerunning the agent would produce a different trajectory.
|
|
206
206
|
|
|
207
207
|
**Options**
|
|
208
208
|
|
|
@@ -122,13 +122,12 @@ resources:
|
|
|
122
122
|
|
|
123
123
|
sandbox:
|
|
124
124
|
timeout_seconds: 1800
|
|
125
|
-
workspace_mount: true
|
|
126
125
|
exposed_ports:
|
|
127
126
|
- 8080
|
|
128
127
|
- 9229
|
|
129
128
|
```
|
|
130
129
|
|
|
131
|
-
`cpu_cores` and `memory_mb` size the provider instance. `
|
|
130
|
+
`cpu_cores` and `memory_mb` size the provider instance. `exposed_ports` lists ports the platform should surface for host-side access. The deprecated `workspace_mount` field is accepted for existing manifests but ignored; the sandbox workspace is always a local directory. Defaults and valid ranges are in the [manifest reference](./manifest-reference.md).
|
|
132
131
|
|
|
133
132
|
## Outbound network scope
|
|
134
133
|
|
|
@@ -207,7 +206,6 @@ resources:
|
|
|
207
206
|
|
|
208
207
|
sandbox:
|
|
209
208
|
timeout_seconds: 1800
|
|
210
|
-
workspace_mount: true
|
|
211
209
|
exposed_ports:
|
|
212
210
|
- 8080
|
|
213
211
|
|
|
@@ -89,7 +89,7 @@ The CLI resolves `selectors` into `secret_ids` before submitting the manifest. S
|
|
|
89
89
|
| Field | Type | Default | Notes |
|
|
90
90
|
| ----------------- | ------------ | ------- | -------------------------------------------------------------------------------------------------------------------------- |
|
|
91
91
|
| `timeout_seconds` | integer | none | Sandbox expiry in seconds. Minimum 60, capped by your organization’s sandbox runtime limit. Omit for the platform default. |
|
|
92
|
-
| `workspace_mount` | boolean | `true` |
|
|
92
|
+
| `workspace_mount` | boolean | `true` | **Deprecated.** Accepted for existing manifests but ignored; the sandbox workspace is local. |
|
|
93
93
|
| `exposed_ports` | list of ints | `[]` | Ports to expose for host-side access. Must be 1–65535. Deduplicated and sorted. |
|
|
94
94
|
| `egress` | object | none | Outbound scope for this runtime’s sandbox. Omitted and empty differ. See below. |
|
|
95
95
|
|
|
@@ -155,7 +155,6 @@ resources:
|
|
|
155
155
|
|
|
156
156
|
sandbox:
|
|
157
157
|
timeout_seconds: 1800
|
|
158
|
-
workspace_mount: true
|
|
159
158
|
exposed_ports:
|
|
160
159
|
- 8080
|
|
161
160
|
- 9229
|
dreadnode/builtin_capabilities/dreadnode/skills/dreadnode-concepts/references/sandboxes/isolation.md
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
> How sandboxes isolate workloads, how to scope what an agent can reach on the network, and which controls remain yours.
|
|
9
9
|
|
|
10
|
-
An agent in one hosted sandbox cannot read the memory, processes, or local filesystem of another sandbox.
|
|
10
|
+
An agent in one hosted sandbox cannot read the memory, processes, or local filesystem of another sandbox. Standalone and interactive sandboxes can still make network requests to public services. Evaluation sandboxes use a narrower network boundary described below.
|
|
11
11
|
|
|
12
12
|
This distinction matters when you run untrusted agent code: **compute isolation does not imply network isolation.**
|
|
13
13
|
|
|
@@ -17,34 +17,67 @@ Dreadnode runs each hosted sandbox in a separate E2B Firecracker microVM. Each m
|
|
|
17
17
|
|
|
18
18
|
Agents may have broad privileges inside their own sandbox. They can run arbitrary commands, install packages, and modify local files. Those privileges stop at the microVM boundary.
|
|
19
19
|
|
|
20
|
-
| Resource
|
|
21
|
-
|
|
|
22
|
-
| Memory and processes
|
|
23
|
-
| Local filesystem
|
|
24
|
-
| E2B project API
|
|
25
|
-
| Dreadnode runtime API
|
|
26
|
-
|
|
|
27
|
-
|
|
|
20
|
+
| Resource | Isolated between sandboxes? | Enforcement |
|
|
21
|
+
| ------------------------ | --------------------------- | ------------------------------------------------------------ |
|
|
22
|
+
| Memory and processes | Yes | Firecracker microVM |
|
|
23
|
+
| Local filesystem | Yes | Separate microVM storage |
|
|
24
|
+
| E2B project API | Yes | The E2B project API key stays in the Dreadnode control plane |
|
|
25
|
+
| Dreadnode runtime API | Yes | A bearer credential scoped to one sandbox |
|
|
26
|
+
| Evaluation task services | Yes | Restricted E2B ingress and paired-runtime egress rules |
|
|
27
|
+
| Public network services | No | Each service must authenticate its callers |
|
|
28
|
+
| Outbound network | Governed by policy | An egress policy composed per sandbox — see below |
|
|
28
29
|
|
|
29
|
-
Evaluation agents and task environments also run in separate sandboxes.
|
|
30
|
+
Evaluation agents and task environments also run in separate sandboxes. Dreadnode restricts each task environment’s public ingress and configures its paired runtime to reach only declared task service hosts plus the platform and model gateway. E2B adds the task sandbox’s traffic credential only when the paired runtime calls those exact hosts, including HTTP and WebSocket services.
|
|
31
|
+
|
|
32
|
+
```
|
|
33
|
+
flowchart LR
|
|
34
|
+
Anonymous["Anonymous caller"]
|
|
35
|
+
API["Dreadnode API"]
|
|
36
|
+
Gateway["Model gateway"]
|
|
37
|
+
Internet["Other public hosts"]
|
|
38
|
+
|
|
39
|
+
subgraph Pair["E2B evaluation pair"]
|
|
40
|
+
direction LR
|
|
41
|
+
Runtime["Agent runtime sandbox<br/>deny-all egress"]
|
|
42
|
+
Task["Task sandbox<br/>traffic-token ingress"]
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
Anonymous -. "blocked" .-> Task
|
|
46
|
+
Runtime -->|"declared host + token header"| Task
|
|
47
|
+
Runtime -->|"allowlisted"| API
|
|
48
|
+
Runtime -->|"allowlisted"| Gateway
|
|
49
|
+
Runtime -. "blocked" .-> Internet
|
|
50
|
+
|
|
51
|
+
classDef sandbox fill:#171124,stroke:#a78bfa,color:#ececf0,stroke-width:2px
|
|
52
|
+
classDef allowed fill:#102018,stroke:#34d399,color:#d5ffe2,stroke-width:2px
|
|
53
|
+
classDef blocked fill:#2a0b10,stroke:#d40924,color:#ffd6d6,stroke-width:2px
|
|
54
|
+
class Runtime,Task sandbox
|
|
55
|
+
class API,Gateway allowed
|
|
56
|
+
class Anonymous,Internet blocked
|
|
57
|
+
style Pair fill:#08080b,stroke:#26262c,color:#ececf0
|
|
58
|
+
linkStyle 0,4 stroke:#d40924,stroke-width:2px
|
|
59
|
+
linkStyle 1,2,3 stroke:#34d399,stroke-width:2px
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Only the paired agent runtime gets deny-all egress by default. The task sandbox’s outbound traffic follows its egress declaration when present; otherwise it follows E2B defaults.
|
|
30
63
|
|
|
31
64
|
## Access to another sandbox
|
|
32
65
|
|
|
33
66
|
A sandbox cannot enumerate or manage other sandboxes through the E2B project API because Dreadnode does not inject its E2B project API key into agent workloads. Dreadnode also checks the requesting user’s organization, workspace access, and sandbox ownership before allowing sandbox operations through the platform API.
|
|
34
67
|
|
|
35
|
-
Network services are a separate boundary. E2B assigns public URLs to services running inside a sandbox. By default, anyone who knows one of these URLs can send requests to it.
|
|
68
|
+
Network services are a separate boundary. E2B assigns public URLs to services running inside a sandbox. By default, anyone who knows one of these URLs can send requests to it. Evaluation task sandboxes disable that default: anonymous requests fail, and E2B adds the provider traffic credential only when the paired runtime calls ports declared by the task.
|
|
36
69
|
|
|
37
|
-
Dreadnode’s agent runtime API requires a bearer credential scoped to one sandbox. Only the sandbox owner can obtain it, and every client they attach presents the same value. Services supplied by a task, such as a web application or API under test, define their own authentication
|
|
70
|
+
Dreadnode’s agent runtime API requires a bearer credential scoped to one sandbox. Only the sandbox owner can obtain it, and every client they attach presents the same value. Services supplied by a task, such as a web application or API under test, define their own authentication when used outside an evaluation pair.
|
|
38
71
|
|
|
39
72
|
Sandbox isolation is not service authentication
|
|
40
73
|
|
|
41
|
-
Treat
|
|
74
|
+
Treat services exposed from standalone or interactive sandboxes as internet-reachable. Evaluation pairing does not change those workloads.
|
|
42
75
|
|
|
43
|
-
E2B
|
|
76
|
+
Dreadnode applies E2B traffic access tokens internally to evaluation task sandboxes. This control is not customer-configurable. See E2B’s [public access controls](https://docs.e2b.dev/network/restrict-public-access.md) for the provider capability.
|
|
44
77
|
|
|
45
78
|
## Internet and private networks
|
|
46
79
|
|
|
47
|
-
By default an agent reaches the public internet. It can download packages, clone repositories, call public APIs, and send data to external hosts.
|
|
80
|
+
By default an agent reaches the public internet. It can download packages, clone repositories, call public APIs, and send data to external hosts. Evaluation runtimes instead use deny-all egress with exact allowances for their task services, the Dreadnode API, and the model gateway.
|
|
48
81
|
|
|
49
82
|
Hosted sandboxes additionally have private and link-local ranges blocked at E2B’s network edge — `10.0.0.0/8`, `100.64.0.0/10`, `127.0.0.0/8`, `169.254.0.0/16`, `172.16.0.0/12`, and `192.168.0.0/16` — which keeps agents off common private-network and cloud metadata addresses. It restricts no public destination.
|
|
50
83
|
|
|
@@ -157,17 +190,17 @@ On hosted sandboxes, enforcement rides the sandbox create call and is always in
|
|
|
157
190
|
|
|
158
191
|
Dreadnode limits the credentials available inside each sandbox:
|
|
159
192
|
|
|
160
|
-
| Credential or data | Control
|
|
161
|
-
| ---------------------------- |
|
|
162
|
-
| E2B project API key | Held by the Dreadnode API and never injected into agent sandboxes
|
|
163
|
-
| Evaluation and job API keys | Restricted to the platform scopes required by that workload
|
|
164
|
-
| Interactive runtime API keys | Follow the initiating user’s platform permissions
|
|
165
|
-
| Runtime bearer credential | Unique to one sandbox; required by the Dreadnode runtime API
|
|
166
|
-
|
|
|
167
|
-
| Model gateway key | Limited to permitted models, expires, and cleanup requests revocation
|
|
168
|
-
| User secrets | Injected only when selected for the workload
|
|
169
|
-
|
|
170
|
-
Processes inside a sandbox can read credentials and secrets injected into that sandbox. Wherever public egress remains available, compromised or malicious code can send those values to an external host. Inject only the secrets the workload needs, use the narrowest permissions available, and declare an egress scope when the workload’s destinations are knowable — it bounds where a leaked secret can go.
|
|
193
|
+
| Credential or data | Control |
|
|
194
|
+
| ---------------------------- | ------------------------------------------------------------------------------------------- |
|
|
195
|
+
| E2B project API key | Held by the Dreadnode API and never injected into agent sandboxes |
|
|
196
|
+
| Evaluation and job API keys | Restricted to the platform scopes required by that workload |
|
|
197
|
+
| Interactive runtime API keys | Follow the initiating user’s platform permissions |
|
|
198
|
+
| Runtime bearer credential | Unique to one sandbox; required by the Dreadnode runtime API |
|
|
199
|
+
| SDK remote storage | Fetches temporary workspace-scoped credentials on demand; provisioning does not inject them |
|
|
200
|
+
| Model gateway key | Limited to permitted models, expires, and cleanup requests revocation |
|
|
201
|
+
| User secrets | Injected only when selected for the workload |
|
|
202
|
+
|
|
203
|
+
Processes inside a sandbox can read credentials and secrets injected into that sandbox. A paired evaluation runtime can send them only to its allowlisted hosts. Wherever public egress remains available, compromised or malicious code can send those values to an external host. Inject only the secrets the workload needs, use the narrowest permissions available, and declare an egress scope when the workload’s destinations are knowable — it bounds where a leaked secret can go.
|
|
171
204
|
|
|
172
205
|
## Lifetime and audit trail
|
|
173
206
|
|
dreadnode/builtin_capabilities/dreadnode/skills/dreadnode-concepts/references/tui/local-storage.md
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
> Every file Dreadnode writes under ~/.dreadnode/ — config, profiles, transcripts, spans, caches, and auth tokens.
|
|
9
9
|
|
|
10
|
-
Dreadnode keeps
|
|
10
|
+
Dreadnode keeps its local state under `~/.dreadnode/`. Managed sessions also sync the state needed by the platform, while credentials and local caches remain in this directory. Back it up and you back up every local session, profile, and cached artifact.
|
|
11
11
|
|
|
12
12
|
```
|
|
13
13
|
~/.dreadnode/
|
|
@@ -69,8 +69,8 @@ Caution
|
|
|
69
69
|
|
|
70
70
|
Don’t check `~/.dreadnode/` into version control. `config.yaml` and `mcp-auth.json` contain credentials.
|
|
71
71
|
|
|
72
|
-
##
|
|
72
|
+
## Storage in managed sandboxes
|
|
73
73
|
|
|
74
|
-
|
|
74
|
+
Inside a Dreadnode-managed sandbox, `~/.dreadnode/` and `/home/user/workspace` are local to that sandbox. They are not object-storage mounts. Pausing and resuming an E2B sandbox preserves its local filesystem because it resumes the same sandbox.
|
|
75
75
|
|
|
76
|
-
|
|
76
|
+
Resetting, replacing, or deleting a sandbox discards files that were not synced elsewhere. Managed session state is synchronized through the platform API, and SDK remote-storage operations access object storage explicitly.
|
dreadnode/core/tls.py
CHANGED
|
@@ -41,7 +41,7 @@ _GENERATED_MARKER = "# dreadnode-generated trust bundle (system + operator CA)"
|
|
|
41
41
|
# CURL_CA_BUNDLE ............... curl
|
|
42
42
|
# GIT_SSL_CAINFO ............... git
|
|
43
43
|
# NODE_EXTRA_CA_CERTS .......... Node, Bun, Claude Code CLI, agent-browser
|
|
44
|
-
# AWS_CA_BUNDLE ................ botocore, s3fs
|
|
44
|
+
# AWS_CA_BUNDLE ................ botocore, Python s3fs
|
|
45
45
|
# GRPC_DEFAULT_SSL_ROOTS_FILE_PATH ... the OTLP span exporter
|
|
46
46
|
TRUST_FILE_ENV_VARS: tuple[str, ...] = (
|
|
47
47
|
"SSL_CERT_FILE",
|
dreadnode/storage/storage.py
CHANGED
|
@@ -68,8 +68,9 @@ class Storage:
|
|
|
68
68
|
job.json ← terminal-only frontier hashes
|
|
69
69
|
```
|
|
70
70
|
|
|
71
|
-
|
|
72
|
-
|
|
71
|
+
Remote operations use an fsspec-compatible object-storage client. In a
|
|
72
|
+
managed sandbox, the local cache remains on the sandbox filesystem; it is
|
|
73
|
+
not an object-storage mount.
|
|
73
74
|
"""
|
|
74
75
|
|
|
75
76
|
def __init__(
|
|
@@ -419,7 +420,7 @@ class Storage:
|
|
|
419
420
|
return self.cas_path / algo / hash_val[:2] / hash_val[2:4] / hash_val
|
|
420
421
|
|
|
421
422
|
def remote_blob_path(self, oid: str) -> str:
|
|
422
|
-
"""Remote path for blob
|
|
423
|
+
"""Remote path for blob, including the provider bucket."""
|
|
423
424
|
algo, hash_val = oid.split(":", 1)
|
|
424
425
|
bucket = self._credentials.bucket if self._credentials else "user-data"
|
|
425
426
|
return f"{bucket}/{self.remote_prefix}/cas/{algo}/{hash_val[:2]}/{hash_val[2:4]}/{hash_val}"
|
|
@@ -29,7 +29,7 @@ dreadnode/agents/mcp/client.py,sha256=13sEJhLDfk1lPeTXfOsoc2j_akX1WX4rc4PSp_Cw36
|
|
|
29
29
|
dreadnode/agents/mcp/config.py,sha256=hBROQZqUF4Y9wI6aLpYc5wHjneIA593PjfHDO1voAN0,2433
|
|
30
30
|
dreadnode/agents/mcp/server.py,sha256=LyEgY7xvAwMZG3ioMUwfPsP0zSAlKeXtgU2XearDx_0,3358
|
|
31
31
|
dreadnode/airt/__init__.py,sha256=VMUzuBuzQdsKYkPs5OLyINCgfLX9Rrr0XQpObEYofp8,10589
|
|
32
|
-
dreadnode/airt/_base.py,sha256=
|
|
32
|
+
dreadnode/airt/_base.py,sha256=qb2jsTOsDtwZg7u5DndyjGSYZLsE8pAlQ7cLuj9QJYg,11567
|
|
33
33
|
dreadnode/airt/adversarial_reasoning.py,sha256=pDTIDIHRF_DL0YjEGmSeEtPGKCltjuFlAEIFlKOqqmU,13630
|
|
34
34
|
dreadnode/airt/advpromptier.py,sha256=TJPPUdUTJu2nnY5VJKNapzDcClBHI49yU9HSSV4R2Ik,10062
|
|
35
35
|
dreadnode/airt/agent_suite.py,sha256=alR7iuUi9Pa2HH9p8OdbMj-bkGFHrnqcr-slVx-_Rao,8180
|
|
@@ -203,10 +203,10 @@ dreadnode/app/cli/args.py,sha256=MSHhdpCHfhvlSYfP6tEyPnNBH9i_TkEbvpV8RZ8vrs4,874
|
|
|
203
203
|
dreadnode/app/cli/capability.py,sha256=H4sowggKHKw1HRkFP3OzHOYPy6n1yKraiAGO8rSfvos,56034
|
|
204
204
|
dreadnode/app/cli/dataset.py,sha256=cv5xNxBO0SihNmQauLJxIpZzNFl1sKxP8GIRKmCulwI,14023
|
|
205
205
|
dreadnode/app/cli/environment.py,sha256=Slm_4d9VuzbhuzC3Pino2n5C6orKG7riT4E58Lx-5v8,17054
|
|
206
|
-
dreadnode/app/cli/evaluation.py,sha256=
|
|
206
|
+
dreadnode/app/cli/evaluation.py,sha256=rUTGwBTBZG-Prh5PzK2-hbceCV4Tk3dj_5mRUxstA98,69050
|
|
207
207
|
dreadnode/app/cli/inference_model.py,sha256=W9zxwlspvSr2r1MoaTCuY8K2wEoedugPdhZHKq1_QfM,4707
|
|
208
208
|
dreadnode/app/cli/judge.py,sha256=aqI5jL4xhCti9ZJmZ14ihhDgKj_RJsfiG5WjhllVJnU,15864
|
|
209
|
-
dreadnode/app/cli/main.py,sha256=
|
|
209
|
+
dreadnode/app/cli/main.py,sha256=ObfPdqZcL58KK_aOSdVnNLKQIGrG5A9ONzACHTTRS8Y,26964
|
|
210
210
|
dreadnode/app/cli/model.py,sha256=A4kWIRLWm26B296McNWM1WEpDqCFGoxgOirCoNy66Tw,17372
|
|
211
211
|
dreadnode/app/cli/optimize.py,sha256=qQ8YnNFG3CCgrqZw4MiS9d_8BOsTwIjw7WtqDapcqPU,22788
|
|
212
212
|
dreadnode/app/cli/runtime.py,sha256=MOtCGndZ5QivE3LKgkhUriVMk2ma-wiVqOszPGUZl_8,37348
|
|
@@ -235,11 +235,11 @@ dreadnode/app/client/models.py,sha256=TbzXwrpoog3oGWpgbpZ2-JoTaxBuSQx1DDh4GSpsfb
|
|
|
235
235
|
dreadnode/app/client/runtime_client.py,sha256=KXX-z1PjNm-2GCapoVPpUz--aLAA2Piu03Ykj_lHRlY,55714
|
|
236
236
|
dreadnode/app/client/transports.py,sha256=SOUX_tMNhoMPPNnk_6HRoyPNGeeDMn1KhnrdlS2d304,15921
|
|
237
237
|
dreadnode/app/server/__init__.py,sha256=fub75X9MOi9mK_-vFPD03rNMC04gYsNkecVSPbsI4Ic,187
|
|
238
|
-
dreadnode/app/server/app.py,sha256=
|
|
238
|
+
dreadnode/app/server/app.py,sha256=u1nG7X8VOrohd6uI0u4YhVoyDiMVsH-5j-gXU4irQaI,218039
|
|
239
239
|
dreadnode/app/server/auth.py,sha256=LVQ6lgSc74Y3sWRGM2pf6Xgp2n7c6oWQZZPJ6k6PjnY,4440
|
|
240
240
|
dreadnode/app/server/capability_manager.py,sha256=91FJYQ8RIQAkRRFCoCnUbQc89Y0ghSOAJ3-PGVaJUv4,52428
|
|
241
241
|
dreadnode/app/server/model_resolution.py,sha256=LwahZgnaToC6kOePylLJCzCN1ajTj47YxIceo2syAa4,4735
|
|
242
|
-
dreadnode/app/server/prompt.py,sha256=
|
|
242
|
+
dreadnode/app/server/prompt.py,sha256=_1Q9G8i-KlfNnApK4mK_2_nqrMDOZb0-49vS0kaS1GY,12618
|
|
243
243
|
dreadnode/app/server/prompt_registry.py,sha256=tap9N9vAzQk0BHhECCHr3JKrLX3oMhzF14X4rH-Rur4,6805
|
|
244
244
|
dreadnode/app/server/runtime_credentials.py,sha256=Zo0EfulHxTSycOhoYE4O0gKXwMdCKCMSyn1RkfEB_FY,11744
|
|
245
245
|
dreadnode/app/server/runtime_events.py,sha256=a4b2iuKjJaly2pcDxVs7wHXZ2uHe1snSW3H0lS4k9pg,17792
|
|
@@ -251,14 +251,14 @@ dreadnode/app/server/websocket.py,sha256=PEUI5gYofWW-t5sQd-DvKps6ezD9tSFSWDG_6MR
|
|
|
251
251
|
dreadnode/app/server/worker_manager.py,sha256=krmTM_asIp3SB91iuD3DlrQm0S62k9LMh-igDG1kyAI,28219
|
|
252
252
|
dreadnode/app/server/ws_auth.py,sha256=n1Bo91yM6IFQHNZy3j-r6RiCjzA44205q9cQxf9_iLU,3991
|
|
253
253
|
dreadnode/app/tui/__init__.py,sha256=DwG6Q1FhmmFcxQ75ziJFjj940dtPeR3J6rilSCOe5WE,64
|
|
254
|
-
dreadnode/app/tui/app.py,sha256=
|
|
254
|
+
dreadnode/app/tui/app.py,sha256=4K9UCIP7Mp9wIj5CklbGmyeCC5aOYeQKPGCAIUr10aI,199999
|
|
255
255
|
dreadnode/app/tui/auth_flow.py,sha256=ufU7c9lmGUc80ewzw9CJo8riahQjqAt2pKjccH39-74,6872
|
|
256
256
|
dreadnode/app/tui/bug_report.py,sha256=AtfFhdeddnvA8OtCFBd90U2TVAwH2nlKYm3X6cbwMcY,17696
|
|
257
257
|
dreadnode/app/tui/capabilities_manager.py,sha256=bft9Q2tvDkFQQBuqAfVYfrC-VZ2_tbi2vUBYZRXZLzg,22231
|
|
258
258
|
dreadnode/app/tui/command_dispatcher.py,sha256=zO8Rwa4-96ySO5h1s3hwLiGf68CYl22jjOQgBt_H9ag,32942
|
|
259
259
|
dreadnode/app/tui/commands.py,sha256=PwLg5CdNoaEJwI27HIw5nlfqsdfpSyjf9HSya5aChfA,4085
|
|
260
260
|
dreadnode/app/tui/connection.py,sha256=Ca4tXPOjjYxM5eIjTgXQTHAqLlOj8vCCFoRYn7KwkL8,12676
|
|
261
|
-
dreadnode/app/tui/dreadnode.tcss,sha256=
|
|
261
|
+
dreadnode/app/tui/dreadnode.tcss,sha256=g-ZAKkACc3ns7IA6yDOZqGozkDToEvAghlxXLQ-bACs,19923
|
|
262
262
|
dreadnode/app/tui/error_handler.py,sha256=qcZLaLgBNbyf540Yl5BYFxcZXBnaeDKI8cB81waOnFU,7074
|
|
263
263
|
dreadnode/app/tui/model_manager.py,sha256=t0I52eq9ks-mk2ui7Oldn0Cl7jZauJMSqe6EIbfxlZw,36548
|
|
264
264
|
dreadnode/app/tui/model_variants.py,sha256=e8_zTTZLUCtAM_BR8QDDHeV3SswqoFHLBPvThrKSIaE,8835
|
|
@@ -267,7 +267,7 @@ dreadnode/app/tui/screen_router.py,sha256=Xn7UKPAt0J2SoXqpFcTiSjwXRd8VM1uzgoG4T2
|
|
|
267
267
|
dreadnode/app/tui/sessions_manager.py,sha256=nSX7hmzC5Ga9qNYkuho5BV39x4X6_1tUyNAwYoho4sc,91973
|
|
268
268
|
dreadnode/app/tui/spans_reader.py,sha256=0jsdGBsTTt2uektvsyQH1Wl-_uW2blRsamOy9HnXmRg,4201
|
|
269
269
|
dreadnode/app/tui/status_messages.py,sha256=fr4wcNl0VbgsNAQkLGCewMaFpoP1S8IS33w599fYog8,426
|
|
270
|
-
dreadnode/app/tui/theme.py,sha256=
|
|
270
|
+
dreadnode/app/tui/theme.py,sha256=UQ9diWF-6RhPvrTQJF-bQJBkyXZVWk17xbX8jPRenSQ,7040
|
|
271
271
|
dreadnode/app/tui/tool_format.py,sha256=qdJ0jz0aIL2c6_aRVKXBrfVqWExAhr3kL-BrgJNZfU8,22081
|
|
272
272
|
dreadnode/app/tui/turn_coordinator.py,sha256=JUHs-za7zGLCgWEgt_nf080t-rJ5qK0CH_jPy0fJRaw,26812
|
|
273
273
|
dreadnode/app/tui/turn_lifecycle.py,sha256=K_QnfRccQC9nFEhsMwbS_cq3wz0QMEKJ0O8d7tuvDAc,7716
|
|
@@ -285,7 +285,7 @@ dreadnode/app/tui/screens/console.py,sha256=UfSnixGXOkP4w0gPfYMOTu0dYsSy7Ftli9-M
|
|
|
285
285
|
dreadnode/app/tui/screens/environments.py,sha256=thEKsMXCvD9gSpQS0ScrTAE_wgNsvr6DDYOBnW-Bd5A,18646
|
|
286
286
|
dreadnode/app/tui/screens/evaluations.py,sha256=nlmllhQA2NRPjIie4oCy3wswuUytEoTtRXd-I0Gg2KA,20441
|
|
287
287
|
dreadnode/app/tui/screens/models.py,sha256=u_Dv4W5GhKtLM6YPc8HiYtwNNGlJBAAELRkDac75SgE,38395
|
|
288
|
-
dreadnode/app/tui/screens/raw_spans.py,sha256=
|
|
288
|
+
dreadnode/app/tui/screens/raw_spans.py,sha256=62fq45xUNO1Fsfw6VTS5RVbwOs-Ug9phsek41K0LOMg,7163
|
|
289
289
|
dreadnode/app/tui/screens/readme.py,sha256=wEaBZhTE7FmA6T4MMEeGvxYDIOFfV-SV0K7mPnHK6wU,3684
|
|
290
290
|
dreadnode/app/tui/screens/report_bug.py,sha256=lPs7xsXPwREv8jYY00KQ4wL85C55j1cNEBb8MT9VQ80,16784
|
|
291
291
|
dreadnode/app/tui/screens/runtimes.py,sha256=zBZQ2vFnLmaH_8c9QChQmPJSi7NJ95bnwSDI7WSK_AI,51540
|
|
@@ -321,7 +321,7 @@ dreadnode/app/tui/widgets/skills_dialog.py,sha256=ykIPJfquX6eVTScnhyrQiZ2obe2eZB
|
|
|
321
321
|
dreadnode/app/tui/widgets/slash_overlay.py,sha256=KFNrosL8lmzc3BdJ0kWAKO8ByUTJTmWCFA2jAicCnDQ,3522
|
|
322
322
|
dreadnode/app/tui/widgets/status_bar.py,sha256=eEH4isKvMaVtbIVorPnAS14zXhXkMkiqmfbl5tWoS0I,4799
|
|
323
323
|
dreadnode/app/tui/widgets/throbber.py,sha256=NbgzuL86GgPnNS5P-X07ZanACkqwFksH1ELg-UbVzAs,2428
|
|
324
|
-
dreadnode/app/tui/widgets/tool.py,sha256=
|
|
324
|
+
dreadnode/app/tui/widgets/tool.py,sha256=kw0x9ibh9kkQQAIM8sO-F-Y-fgw1dRlnaCeP0SpNQLI,25116
|
|
325
325
|
dreadnode/app/tui/widgets/tool_progress.py,sha256=VYr-vpC5214DgIAxrG8elONbTO1KHbIKYBzpmiCh_TM,3682
|
|
326
326
|
dreadnode/app/tui/widgets/tools_dialog.py,sha256=KvwObbYvT2yRpYXcIpYHR_Jx9Vo3rCAYy_4Solra_y4,2096
|
|
327
327
|
dreadnode/app/tui/widgets/welcome.py,sha256=-XI4BcV1uQBGAGootUGunmU7fb1Krg065zzph23tDM4,15731
|
|
@@ -370,7 +370,7 @@ dreadnode/builtin_capabilities/dreadnode/skills/dreadnode-cli/references/cli/air
|
|
|
370
370
|
dreadnode/builtin_capabilities/dreadnode/skills/dreadnode-cli/references/cli/capability.md,sha256=SKOsUhcLv_Ou3YKp3oJvN9zp4hsHRJOMJ6aMpwqV03I,10131
|
|
371
371
|
dreadnode/builtin_capabilities/dreadnode/skills/dreadnode-cli/references/cli/dataset.md,sha256=dUDXFotOLmYjouQu7tKI1GJ0h_bNyHSkyo4mVmvKiTY,4871
|
|
372
372
|
dreadnode/builtin_capabilities/dreadnode/skills/dreadnode-cli/references/cli/env.md,sha256=rSvZJYykjxREeumyA3656yGMqxI7O6SjcJZGbsPdQXY,4542
|
|
373
|
-
dreadnode/builtin_capabilities/dreadnode/skills/dreadnode-cli/references/cli/evaluation.md,sha256=
|
|
373
|
+
dreadnode/builtin_capabilities/dreadnode/skills/dreadnode-cli/references/cli/evaluation.md,sha256=IQ3-GxjOXgMx79MJd5wLzfkDCyz-Ex5olCxQF9yuvro,11115
|
|
374
374
|
dreadnode/builtin_capabilities/dreadnode/skills/dreadnode-cli/references/cli/inference-model.md,sha256=tyZifFBzlYFXCNkAk2HIFu828mc62LOBRUEuAr_-FBs,1657
|
|
375
375
|
dreadnode/builtin_capabilities/dreadnode/skills/dreadnode-cli/references/cli/judge.md,sha256=-pX8CtXpAytZWaBekbKR8GUi9qlnCfdl5FyXrwhVrgo,2023
|
|
376
376
|
dreadnode/builtin_capabilities/dreadnode/skills/dreadnode-cli/references/cli/main.md,sha256=2AG506lKzD7N9Zh5BqqE9U0xO3_A0s9XM2XCihq9ylE,2338
|
|
@@ -401,18 +401,18 @@ dreadnode/builtin_capabilities/dreadnode/skills/dreadnode-concepts/references/pl
|
|
|
401
401
|
dreadnode/builtin_capabilities/dreadnode/skills/dreadnode-concepts/references/platform/secrets.md,sha256=r2pSuwamAHmg7oasdlad32I2LmBFXCYYxWOeEipsquo,7094
|
|
402
402
|
dreadnode/builtin_capabilities/dreadnode/skills/dreadnode-concepts/references/platform/settings.md,sha256=lpxTXDWP0_Yq_JS9a3j_CTE7Qn5G00E8-ZaFj6eYshg,11346
|
|
403
403
|
dreadnode/builtin_capabilities/dreadnode/skills/dreadnode-concepts/references/platform/workspaces.md,sha256=srQeAmPoSnzW0lTAHdioBwbYseW04sKnLULkNePuqCQ,4722
|
|
404
|
-
dreadnode/builtin_capabilities/dreadnode/skills/dreadnode-concepts/references/runtimes/configuration.md,sha256=
|
|
404
|
+
dreadnode/builtin_capabilities/dreadnode/skills/dreadnode-concepts/references/runtimes/configuration.md,sha256=E-Onx_TN7CfoIIjMsyffQufrXFjgvPXPc-xKAiCEhEQ,8471
|
|
405
405
|
dreadnode/builtin_capabilities/dreadnode/skills/dreadnode-concepts/references/runtimes/managing.md,sha256=geUNFZ2xNgjVOXqlSyKS0vZxIAQDmcTOmwMZBhE_AdU,8446
|
|
406
|
-
dreadnode/builtin_capabilities/dreadnode/skills/dreadnode-concepts/references/runtimes/manifest-reference.md,sha256=
|
|
406
|
+
dreadnode/builtin_capabilities/dreadnode/skills/dreadnode-concepts/references/runtimes/manifest-reference.md,sha256=imaGIjpTXAcO7Yrz8eqpLoTz9tOlg0e1g7O8QJz9GO0,10309
|
|
407
407
|
dreadnode/builtin_capabilities/dreadnode/skills/dreadnode-concepts/references/runtimes/overview.md,sha256=g_zWSUBrLcnzQyq8SyEKPgGBT3GSKigK_sKJtAVEuTA,4447
|
|
408
408
|
dreadnode/builtin_capabilities/dreadnode/skills/dreadnode-concepts/references/runtimes/quickstart.md,sha256=57MDx0gEI_dtXHCLcCigiQJgwtC4eoEDEPW1cECBgFM,3364
|
|
409
409
|
dreadnode/builtin_capabilities/dreadnode/skills/dreadnode-concepts/references/runtimes/serve.md,sha256=INt3vyDxpIwRdjKQEH4oaLtKK32VnWMa0D66oqZ56oM,4628
|
|
410
410
|
dreadnode/builtin_capabilities/dreadnode/skills/dreadnode-concepts/references/sandboxes/environment-lifecycle.md,sha256=SQ4B822MCdBib6L6mrvyCvPWO_3RE1uTiCZAyT8aYmA,5491
|
|
411
411
|
dreadnode/builtin_capabilities/dreadnode/skills/dreadnode-concepts/references/sandboxes/inspecting.md,sha256=HWyXLphy3JgLR90khcxEvGOc4pGeQ6OGjbwo2bSzMvY,4478
|
|
412
|
-
dreadnode/builtin_capabilities/dreadnode/skills/dreadnode-concepts/references/sandboxes/isolation.md,sha256=
|
|
412
|
+
dreadnode/builtin_capabilities/dreadnode/skills/dreadnode-concepts/references/sandboxes/isolation.md,sha256=0g1tlPxmSIdSGP_10K4mPinBuEjY_GafShD9fOETkjQ,17503
|
|
413
413
|
dreadnode/builtin_capabilities/dreadnode/skills/dreadnode-concepts/references/sandboxes/overview.md,sha256=Mwu6cArpkSvDDnMaDVO7XdNSPuifcu_fhLYLAe9mjhk,5847
|
|
414
414
|
dreadnode/builtin_capabilities/dreadnode/skills/dreadnode-concepts/references/sandboxes/runtime-limits.md,sha256=YWjoUzk2ntRObTVuuT8jC3Cm_N05dv9zydcoO6Xxf2E,2206
|
|
415
|
-
dreadnode/builtin_capabilities/dreadnode/skills/dreadnode-concepts/references/tui/local-storage.md,sha256=
|
|
415
|
+
dreadnode/builtin_capabilities/dreadnode/skills/dreadnode-concepts/references/tui/local-storage.md,sha256=IZk9FSVzqKNEIQqtZtajIdRReJUc79DW0fLvn07Lo8k,6340
|
|
416
416
|
dreadnode/builtin_capabilities/dreadnode/skills/dreadnode-concepts/references/tui/overview.md,sha256=GL9i-vPrWWKFlPZsH2x5jHqq4YB1Rt83QDQ_NBi-SGM,3048
|
|
417
417
|
dreadnode/builtin_capabilities/dreadnode/skills/getting-started/SKILL.md,sha256=P8Bl8n0-NQZwzfzNy79uUX5ycxZW-XR6gij3GdP-NGE,13030
|
|
418
418
|
dreadnode/builtin_capabilities/dreadnode/skills/research-capabilities/SKILL.md,sha256=Dc-kHc_BDUAaCbFy3UyRbvoM-GF8CX8umIblj2AsApg,4871
|
|
@@ -458,7 +458,7 @@ dreadnode/core/startup_clock.py,sha256=aJXDPJYx1WZrl6PFS9JIcoZT1PNDxvxLNEtkFeIAo
|
|
|
458
458
|
dreadnode/core/stopping.py,sha256=cm4b1hbOT731gZLptLAMzwlO13LmjHc1c-r9lAls8sw,11889
|
|
459
459
|
dreadnode/core/task.py,sha256=FnXjYUBWhxcL5qcLkg79tZv4CtPspvr_aLfXltce92g,32758
|
|
460
460
|
dreadnode/core/templating.py,sha256=djDbkOEQoQNf3FFE7vSEihjVviaPkl6HdAk0ZkoTbY0,4653
|
|
461
|
-
dreadnode/core/tls.py,sha256=
|
|
461
|
+
dreadnode/core/tls.py,sha256=fyqqjfvyuQyfRO1Nhh7PYGv-MiyyZIzyAf1sG0FQggE,15089
|
|
462
462
|
dreadnode/core/transforms.py,sha256=wuJLZzUTr1PzT3tTlOdf4LQvDgk18t20sUYo2TVkaTs,13248
|
|
463
463
|
dreadnode/core/util.py,sha256=z9zxwVcmzOgXzOEVsuEJQKaOnuKukua2MtaJGYz_4oY,22683
|
|
464
464
|
dreadnode/core/meta/__init__.py,sha256=P4kVnPufWQcetysMULq37Ghpwi1FcjwQ-BOE9gI3mHU,1325
|
|
@@ -601,7 +601,7 @@ dreadnode/scorers/supply_chain_detection.py,sha256=QyS53yYSPXTiV-lGRJy7G7eJBYbPM
|
|
|
601
601
|
dreadnode/storage/__init__.py,sha256=4iiLn_vhBqpNlfuTHs1fWY-It-hqJIF7mPUqg_7Dlk4,1201
|
|
602
602
|
dreadnode/storage/providers.py,sha256=BmtHZcfxZRf741k0DFiNmv0zryf5rYs7nGK9jHiAg2Q,7422
|
|
603
603
|
dreadnode/storage/session_store.py,sha256=HZC8l0TJ6I42PwvLKGWLJNBJqvj8UGTYBqhSEpx-0Yc,18498
|
|
604
|
-
dreadnode/storage/storage.py,sha256=
|
|
604
|
+
dreadnode/storage/storage.py,sha256=Wac7h0n-chy14zSKR_8M8xD0Ni3hhPHkSvb7T4JccvI,34120
|
|
605
605
|
dreadnode/tools/__init__.py,sha256=a3TLVpCFiS2VwvRMN9U_z46SESMxbLOBjpl31Kqev30,4594
|
|
606
606
|
dreadnode/tools/_ripgrep.py,sha256=GVIzsfl6DkFR9GwohIJS1ybi5UiZwD-jjiD0M_tnfmU,1465
|
|
607
607
|
dreadnode/tools/apply_patch.py,sha256=RjzzB2CJ2-9EcBi2iD7qYunzN8hk4amACw7nsBOiFIQ,14880
|
|
@@ -736,8 +736,8 @@ dreadnode/transforms/system_prompt_extraction.py,sha256=rtWKibIipr1tmFH3P95UE4W4
|
|
|
736
736
|
dreadnode/transforms/text.py,sha256=8qEldTVeXblxxCCU31Q2MGx3dccMxESOoSga-Y87Mnk,19923
|
|
737
737
|
dreadnode/transforms/video.py,sha256=pWPSAN3YokvWpYsAOvOVH1d2Zd5JPldl17ICw1532a8,33136
|
|
738
738
|
dreadnode/transforms/xml_tools.py,sha256=Zj2sdvmyCoVLua4XkWnKYFRmS8ZFSWrQ5nunspCqpM4,15057
|
|
739
|
-
dreadnode-2.0.
|
|
740
|
-
dreadnode-2.0.
|
|
741
|
-
dreadnode-2.0.
|
|
742
|
-
dreadnode-2.0.
|
|
743
|
-
dreadnode-2.0.
|
|
739
|
+
dreadnode-2.0.45.dist-info/METADATA,sha256=B2zm-2VUBbZw-RTfYROIRIalGtULTzC-SqQG3gGisgE,13895
|
|
740
|
+
dreadnode-2.0.45.dist-info/WHEEL,sha256=zOwg4jB6zX2kU910N-cMawjivD6tO8NEWvE12je1bVk,87
|
|
741
|
+
dreadnode-2.0.45.dist-info/entry_points.txt,sha256=LvV-eGt7GlPEHy1YDiClWhr4qbsX_D9l0_QNo0j_XPI,81
|
|
742
|
+
dreadnode-2.0.45.dist-info/licenses/LICENSE,sha256=UMJAkT3OxyPBvOmQ74h_1v8DYFRYIfMfFiaP2lFZCmc,11375
|
|
743
|
+
dreadnode-2.0.45.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|