agentic-dev-loop 0.10.2__py3-none-any.whl → 0.10.3__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.
@@ -1,4 +1,4 @@
1
- """Banned-command deny-list (036 D1).
1
+ """Banned-command deny-list (036 D1, revised by 056).
2
2
 
3
3
  The sandbox terminal enforces a deny-list before any shell spawn: a command
4
4
  whose argv matches a step's ``forbidden_commands:`` regex (merged with the
@@ -6,11 +6,15 @@ engine defaults) returns a structured observation instead of reaching the
6
6
  sandbox. Command steps are unaffected - they are declared infrastructure and
7
7
  already fail fast on their exit code.
8
8
 
9
+ The defaults are run invariants only: git mutations and docker.
10
+ Tool-specific rules are not engine defaults - a step declares them with
11
+ ``forbidden_commands`` (the bundled onboarding recipes carry the engine's
12
+ own toolchain guidance). The deny-list is network-independent by design;
13
+ network is a sandbox egress concern.
14
+
9
15
  The default list carries human-readable labels; custom entries use their
10
- regex text as the label. ``dotnet restore`` and ``dotnet add`` are network
11
- gated: denied only while the step's ``network:`` flag is off.
16
+ regex text as the label.
12
17
  """
13
-
14
18
  from __future__ import annotations
15
19
 
16
20
  import re
@@ -30,22 +34,9 @@ _DEFAULT_PATTERNS: tuple[tuple[str, str], ...] = (
30
34
  ("git checkout", r"\bgit\s+checkout\b"),
31
35
  ("git reset", r"\bgit\s+reset\b"),
32
36
  ("git tag", r"\bgit\s+tag\b"),
33
- ("npm install", r"\bnpm\s+install\b"),
34
- ("npm ci", r"\bnpm\s+ci\b"),
35
- ("pip install", r"\bpip\s+install\b"),
36
- ("uv add", r"\buv\s+add\b"),
37
- ("pip-compile", r"\bpip-compile\b"),
38
- ("dotnet add package", r"\bdotnet\s+add\s+package\b"),
39
- ("npx playwright", r"\bnpx\s+playwright\b"),
40
- ("playwright", r"\bplaywright\b"),
41
37
  ("docker", r"\bdocker\b"),
42
38
  )
43
39
 
44
- _NETWORK_GATED_PATTERNS: tuple[tuple[str, str], ...] = (
45
- ("dotnet restore", r"\bdotnet\s+restore\b"),
46
- ("dotnet add", r"\bdotnet\s+add\b"),
47
- )
48
-
49
40
 
50
41
  def _compile(label: str, pattern: str) -> ForbiddenRule:
51
42
  return ForbiddenRule(label=label, regex=re.compile(pattern, re.IGNORECASE))
@@ -54,21 +45,16 @@ def _compile(label: str, pattern: str) -> ForbiddenRule:
54
45
  DEFAULT_FORBIDDEN_RULES: tuple[ForbiddenRule, ...] = tuple(
55
46
  _compile(label, pattern) for label, pattern in _DEFAULT_PATTERNS)
56
47
 
57
- NETWORK_GATED_FORBIDDEN_RULES: tuple[ForbiddenRule, ...] = tuple(
58
- _compile(label, pattern) for label, pattern in _NETWORK_GATED_PATTERNS)
59
-
60
48
 
61
- def resolve_forbidden_rules(custom_patterns: list[str] | tuple[str, ...] | None,
62
- network: bool) -> tuple[ForbiddenRule, ...]:
49
+ def resolve_forbidden_rules(
50
+ custom_patterns: list[str] | tuple[str, ...] | None,
51
+ ) -> tuple[ForbiddenRule, ...]:
63
52
  """Merge the engine defaults with a step's custom regex list.
64
53
 
65
54
  Custom entries are appended after the defaults, so the first match wins
66
- on overlap. Network-gated defaults are included only while ``network`` is
67
- off; there is no disable escape hatch for the engine defaults.
55
+ on overlap; there is no disable escape hatch for the engine defaults.
68
56
  """
69
57
  rules = list(DEFAULT_FORBIDDEN_RULES)
70
- if not network:
71
- rules.extend(NETWORK_GATED_FORBIDDEN_RULES)
72
58
  for pattern in custom_patterns or ():
73
59
  rules.append(_compile(pattern, pattern))
74
60
  return tuple(rules)
@@ -14,7 +14,18 @@ dev_impossible_set:
14
14
  - "database-category tests"
15
15
  - "commands that boot servers or need external services"
16
16
  steps:
17
- - {name: brief, kind: brief, brief_inputs: {include: [AGENTS.md]}, on_pass: dev}
17
+ - name: brief
18
+ kind: brief
19
+ brief_inputs: {include: [AGENTS.md]}
20
+ forbidden_commands:
21
+ - npm install
22
+ - npm ci
23
+ - dotnet add package
24
+ - dotnet restore
25
+ - dotnet add
26
+ - npx playwright
27
+ - playwright
28
+ on_pass: dev
18
29
  - name: dev
19
30
  kind: agent
20
31
  agent: dev
@@ -22,6 +33,14 @@ steps:
22
33
  max_iterations: 100
23
34
  finalize_nudge_turns: 10
24
35
  produces: implementation-summary.md
36
+ forbidden_commands:
37
+ - npm install
38
+ - npm ci
39
+ - dotnet add package
40
+ - dotnet restore
41
+ - dotnet add
42
+ - npx playwright
43
+ - playwright
25
44
  setup: 'cd __FRONTEND_DIR__ && npm ci && cd .. && dotnet restore __SOLUTION__'
26
45
  mounts: [{path: ".", mode: rw}]
27
46
  on_pass: restore
@@ -62,9 +81,23 @@ steps:
62
81
  optional: {detect: tools/run-conformance.sh}
63
82
  on_pass: review
64
83
  on_fail: {goto: dev, retry: true}
65
- - {name: review, kind: agent, agent: review, reasoning_effort: xhigh,
66
- artifact: review-findings.md, verdicts: {pass: APPROVED, fail: NEEDS CHANGES},
67
- mounts: [{path: ".", mode: ro}], on_pass: qa, on_fail: {goto: dev, retry: true}}
84
+ - name: review
85
+ kind: agent
86
+ agent: review
87
+ reasoning_effort: xhigh
88
+ artifact: review-findings.md
89
+ verdicts: {pass: APPROVED, fail: NEEDS CHANGES}
90
+ mounts: [{path: ".", mode: ro}]
91
+ forbidden_commands:
92
+ - npm install
93
+ - npm ci
94
+ - dotnet add package
95
+ - dotnet restore
96
+ - dotnet add
97
+ - npx playwright
98
+ - playwright
99
+ on_pass: qa
100
+ on_fail: {goto: dev, retry: true}
68
101
  - name: qa
69
102
  kind: agent
70
103
  agent: qa
@@ -11,7 +11,14 @@ brief_env_facts:
11
11
  dev_impossible_set:
12
12
  - "package-manager fetches after a new pin run only in the network-on restore step"
13
13
  steps:
14
- - {name: brief, kind: brief, brief_inputs: {include: [AGENTS.md]}, on_pass: dev}
14
+ - name: brief
15
+ kind: brief
16
+ brief_inputs: {include: [AGENTS.md]}
17
+ forbidden_commands:
18
+ - pip install
19
+ - uv add
20
+ - pip-compile
21
+ on_pass: dev
15
22
  - name: dev
16
23
  kind: agent
17
24
  agent: dev
@@ -19,6 +26,10 @@ steps:
19
26
  max_iterations: 150
20
27
  finalize_nudge_turns: 10
21
28
  produces: implementation-summary.md
29
+ forbidden_commands:
30
+ - pip install
31
+ - uv add
32
+ - pip-compile
22
33
  setup: 'python -m venv __VENV__ && __VENV__/bin/pip install -e "__DEV_EXTRAS__"'
23
34
  mounts: [{path: ".", mode: rw}]
24
35
  on_pass: restore
@@ -38,9 +49,19 @@ steps:
38
49
  mounts: [{path: ".", mode: rw}]
39
50
  on_pass: review
40
51
  on_fail: {goto: dev, retry: true}
41
- - {name: review, kind: agent, agent: review, reasoning_effort: xhigh,
42
- artifact: review-findings.md, verdicts: {pass: APPROVED, fail: NEEDS CHANGES},
43
- mounts: [{path: ".", mode: ro}], on_pass: qa, on_fail: {goto: dev, retry: true}}
52
+ - name: review
53
+ kind: agent
54
+ agent: review
55
+ reasoning_effort: xhigh
56
+ artifact: review-findings.md
57
+ verdicts: {pass: APPROVED, fail: NEEDS CHANGES}
58
+ mounts: [{path: ".", mode: ro}]
59
+ forbidden_commands:
60
+ - pip install
61
+ - uv add
62
+ - pip-compile
63
+ on_pass: qa
64
+ on_fail: {goto: dev, retry: true}
44
65
  - name: qa
45
66
  kind: agent
46
67
  agent: qa
@@ -158,8 +158,7 @@ class Runtime:
158
158
  else:
159
159
  cwd, mount_set, egress_policy = (self.workspace, None, None)
160
160
  forbidden_commands = (
161
- resolve_forbidden_rules(step.forbidden_commands,
162
- step.network)
161
+ resolve_forbidden_rules(step.forbidden_commands)
163
162
  if step is not None else None)
164
163
  allowlist = (
165
164
  tuple(self.engine_allowlist)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: agentic-dev-loop
3
- Version: 0.10.2
3
+ Version: 0.10.3
4
4
  Summary: Turn an LLM into a gated, sandboxed, resumable construction loop over your codebase.
5
5
  Author: Luke Wilson
6
6
  License-Expression: Apache-2.0
@@ -229,6 +229,7 @@ steps:
229
229
  agent: dev
230
230
  produces: implementation-summary.md
231
231
  terminal_timeout: 900
232
+ # forbidden_commands: ["npm install", "npm ci"] # per-step tool deny-list; engine defaults are git mutations + docker
232
233
  on_pass: test
233
234
 
234
235
  - name: test
@@ -268,6 +269,7 @@ steps:
268
269
  | `brief_inputs:` | The discriminator for a native brief step: `{ include: [<paths/globs>], env_facts: [<strings>], sections: [<headings>] }`. There is no `agent:`, `produces:`, or `max_iterations:` - the prompt is engine-embedded, the capture file is `attempt-brief.md`, and the iteration cap is 12, all engine-owned. The engine resolves the include list against the workspace, passes the resolved docs + include contents + env facts as the step's task message, and regenerates the step only when those inputs change. `sections` is the ordered list of digest headings the brief must emit (default `[Repo rules digest, Unit scope digest, Environment facts, Open risks]`; non-empty and unique). `name` stays free-form, so multiple brief steps are allowed. |
269
270
  | `terminal_timeout:` | Optional agent-step per-action timeout in seconds (gt 0). `SandboxTerminalExecutor` uses it when the agent leaves the terminal action timeout unset, overriding the SDK tool's default. `None` = current behavior. |
270
271
  | `finalize_nudge_turns:` | Optional agent-step final-response nudge (gt 0). Requires `max_iterations:` and must be less than it. At `max_iterations - finalize_nudge_turns` completed turns the engine injects one advisory turn ("`N turns remain: produce your final response now`"), exactly once; the agent may still exhaust its turns and existing stuck/unclear handling applies unchanged. Absent = current behavior. Provisional pending one consumer run (revert if noisy or the raised cap inflates cost). |
272
+ | `forbidden_commands:` | Optional per-step list of regex patterns. The sandbox terminal matches them against each agent command before any shell spawn; a match returns a structured `[forbidden: <label>]` observation instead of running. A single string is coerced to a one-item list; an invalid regex fails at load. Defaults are run invariants only (git mutations, docker); tool-specific rules belong to the toolchain profile/recipe or the pipeline author. Command steps are unaffected. See [Banned commands](#banned-commands-forbidden_commands). |
271
273
  | `custom_prompt:` | Planned (planning-docs/040). Optional agent-step value: literal text, or a path whose file contents are read at load time, appended verbatim to the agent prompt at context assembly (after the prompt, before the task/artifacts). A value that looks like a path but names no file is a validation error. Agent steps only. |
272
274
  | `llms:` (top-level) / step `llm:` | Named LLM profiles, each a full `LlmConfig` (`model`, `base_url`, `api_key_env`, `timeout`, `reasoning_effort`). A step names one with `llm: <profile-name>`; the whole profile moves together (no partial merges). Allowed only on agent and brief steps. The top-level `llm:` block remains the default. Profile keys are host-side - sandbox access still goes through `secrets:`. |
273
275
  | `reasoning_effort:` | Optional `low` / `medium` / `high` / `xhigh` / `none`. Set on the `llm` block, a `llms:` profile, or per-step (a step value overrides the effective profile's). Unset anywhere = reasoning off: nothing is forwarded and reasoning is stripped from requests. A thinking model without an effort level is invalid config and fails at the first request (crap in, crap out). See [Reasoning effort](#reasoning-effort-opt-in). |
@@ -284,6 +286,22 @@ steps:
284
286
  | `caches:` / `sync_excludes:` | Top-level run-wide lists. `caches:` names repo-relative toolchain cache dirs bind-mounted rw from host-side stores under `{state_dir}/caches/<name>` (auto-created); a `setup:` hook can warm them (the provision container carries the same cache mounts); `sync_excludes:` names non-gitignored scratch paths skipped from both the export copy and sync-back (wildcard `*` spans separators, so `**/bin/**` matches a `/bin/` segment at any depth). Both default to `[]`. See [docs/setup.md](docs/setup.md). |
285
287
  | `merge_on_conflict:` | Top-level run-wide list of append-style logs (same path vocabulary as `sync_excludes:`, default `[]`). A concurrent host edit to a listed path is union-merged instead of raising: the host file wins and the sandbox's export-only lines are appended in order; both sides converge and the run continues. See [docs/setup.md](docs/setup.md). |
286
288
 
289
+ ### Banned commands (`forbidden_commands:`)
290
+
291
+ `forbidden_commands:` is a per-step list of regex patterns. The sandbox
292
+ terminal checks them against every command the agent runs, before any shell
293
+ spawn: a match returns a structured `[forbidden: <label>]` observation instead
294
+ of executing. The engine defaults are run invariants only - git mutations and
295
+ docker - so the deny-list is network-independent by design: `network:` stays a
296
+ docker/egress concern. A single string is coerced to a one-item list; an
297
+ invalid regex is a load error. Command steps bypass the filter by design.
298
+
299
+ Tool-specific rules are not engine defaults. A toolchain profile or bundled
300
+ recipe declares them per step (for example the `python` recipe denies
301
+ `pip install`, `uv add`, and `pip-compile` on its agent steps), and a
302
+ consumer pipeline authors its own for its toolchain. There is no disable
303
+ escape hatch for the engine defaults.
304
+
287
305
  ### Reasoning effort (opt-in)
288
306
 
289
307
  `reasoning_effort` is an opt-in latency dial on the pipeline `llm` block, a
@@ -23,7 +23,7 @@ agentic_dev_loop/engine/deps.py,sha256=bxwEtn04koZhP5B6SmC7hzlpQqoCKoPg5UAUJf2sE
23
23
  agentic_dev_loop/engine/dryrun.py,sha256=KaEAtIiYF9AvVCGawesD8vP-XQkIF671ux_Q9DAZRc0,6876
24
24
  agentic_dev_loop/engine/egress_policy.py,sha256=Gon_S6arEMjRJOEcjr-iqZGKxxu5YQxMo5eYHgz0_zs,4676
25
25
  agentic_dev_loop/engine/egress_proxy.py,sha256=hZxyUzagYu8pkzk-IbzefRrRawfIMTMh_gRKNymrIbQ,13087
26
- agentic_dev_loop/engine/forbidden.py,sha256=Iz8q1LUil9bT8g3jC_JFmICIqsmDSbN2XcIYvxCw3Ew,3226
26
+ agentic_dev_loop/engine/forbidden.py,sha256=_SoxtnJXsw7dDBxvms2Yv_3R2311jxNLzF72AFR-zfw,2642
27
27
  agentic_dev_loop/engine/graph.py,sha256=LzM8vkgZyMyibQLA0tA_0UkUQNsz3uW4H3RQ_MuyHDE,17181
28
28
  agentic_dev_loop/engine/hostboundary.py,sha256=kEDtCWQXSlMwmdPRL7C41rVV55dRLHWMfpcUOPoWb6c,5893
29
29
  agentic_dev_loop/engine/isolation.py,sha256=bRPch_xWrz3KSBZgZzpbB3WMh8qUu98LdHqq8rtpJEM,81887
@@ -35,7 +35,7 @@ agentic_dev_loop/engine/presets.py,sha256=Avo9Mb4YNyv4e5y7Ib0HptJ_S5srvTssdN-keT
35
35
  agentic_dev_loop/engine/proc.py,sha256=MU-tV1_MXm28NHKuNpSW8zm3U_pFCgQhd-yAVCXoUB0,2739
36
36
  agentic_dev_loop/engine/registry.py,sha256=qepez9iU-dBE-yG67qI7Q7Ol89NsERHy34eoQc98Ek0,14296
37
37
  agentic_dev_loop/engine/secrets.py,sha256=WCVyDk97AGvRkwxXw6tbgztHVZbtxJvJC23b4I1EeNY,4678
38
- agentic_dev_loop/engine/stages.py,sha256=TJEl-yQYIPEfDyWJ0c_QxWz4uVsZ0rc0xVJaPCzaz0I,56500
38
+ agentic_dev_loop/engine/stages.py,sha256=ftImuf1WYpy0qJ22vsBB1bhy1qNIfw6lU9rBdDAhj_s,56442
39
39
  agentic_dev_loop/engine/step_workspace.py,sha256=YyaZWk8ozc_v7AFBt63QAn4VFL-a1wvhzNvnbascYYw,7937
40
40
  agentic_dev_loop/engine/sync_back.py,sha256=Mc6YAAhc_Iydz-7Ht3OkyGekaa6s1HitCsOlIepDfOc,13942
41
41
  agentic_dev_loop/engine/extensions/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -46,8 +46,8 @@ agentic_dev_loop/engine/onboarding/doctor.py,sha256=gSZxXU7AaS7DajlMppeEWReZm-2-
46
46
  agentic_dev_loop/engine/onboarding/init.py,sha256=aBIcFHbI_nLu_EjVksopdaZQja1aE6dtClW0xqko32g,15811
47
47
  agentic_dev_loop/engine/onboarding/recipes.py,sha256=s_SF87VjXyS7uOuPUg4i5UBFRL9Ad2plnw3UHfDeTFQ,2326
48
48
  agentic_dev_loop/engine/onboarding/templates.py,sha256=uE5hMjRKmyBxggr72MsS1ZjDMSBr61GoPdEaQNPERkE,2115
49
- agentic_dev_loop/engine/onboarding/recipes/dotnet-node.yaml,sha256=irwsKtVMT8c04TTEP5O0o1frYVBdGOQ5uSmhUkp2mhA,3214
50
- agentic_dev_loop/engine/onboarding/recipes/python.yaml,sha256=fXpzysCdZpjN3xGQ2wTLH9IIvxEOHyD3TKqPRlA-15k,2235
49
+ agentic_dev_loop/engine/onboarding/recipes/dotnet-node.yaml,sha256=Uqq__RbPbZ3NuNtW04-CAdY-mARP3oQHzt2byTZMz_g,3743
50
+ agentic_dev_loop/engine/onboarding/recipes/python.yaml,sha256=42nvggXaQ4rrPAlNjrYnLeD6czuwchYVRnrFziefRKA,2491
51
51
  agentic_dev_loop/engine/onboarding/templates/dev.agent.md,sha256=GdbENdOrWSJ8FKWqf97Z9JsjnEZuC9p0RHTNR6Cmuk8,6460
52
52
  agentic_dev_loop/engine/onboarding/templates/env.example,sha256=sM3ykvPfGNg_VAgaS4NwLdGjeX3Ow4UA_ZC_dRXV_1w,463
53
53
  agentic_dev_loop/engine/onboarding/templates/qa.agent.md,sha256=QE_z8ssefRRKXHI-ssvPW8Njv4OUycRDCfRaAUHIS5k,10045
@@ -58,9 +58,9 @@ agentic_dev_loop/engine/seccomp/chromium-userns.json,sha256=ahmqvWprLe_oG_oLXFMU
58
58
  agentic_dev_loop/viz/__init__.py,sha256=eoRfcUCC3-j7JhShft7Ruk8ruT5oFUoMKEKClO45B4I,520
59
59
  agentic_dev_loop/viz/__main__.py,sha256=baycB1QwQtM-B2bXo4Du5lwvItcMgJwJlAzsHACxM-4,1409
60
60
  agentic_dev_loop/viz/server.py,sha256=-7lHiBCBQx3h0g-TfQyIaWG1tIfrWrqfLJco1-4GJOI,47637
61
- agentic_dev_loop-0.10.2.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
62
- agentic_dev_loop-0.10.2.dist-info/METADATA,sha256=ffXQiRJ-GUQl6KOBrNvxyfinphE-e_k8yo6fLooZXBU,44760
63
- agentic_dev_loop-0.10.2.dist-info/WHEEL,sha256=mOdXR9C3wMVov7cSY8PY-XCzo3SmmWCHvGQr5csJoao,91
64
- agentic_dev_loop-0.10.2.dist-info/entry_points.txt,sha256=Us59oHX6NBy-7ZN3LW5w3HAjnvOPJBfgNiv6ftnmHWw,70
65
- agentic_dev_loop-0.10.2.dist-info/top_level.txt,sha256=n1ktP1gXxGN6DJAnACR0mt276GRJB9PVDezf9DVPwaw,17
66
- agentic_dev_loop-0.10.2.dist-info/RECORD,,
61
+ agentic_dev_loop-0.10.3.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
62
+ agentic_dev_loop-0.10.3.dist-info/METADATA,sha256=LTXvRTeuM44NCgguicRpew9dCO_8__FoxbBeYooCmsw,46341
63
+ agentic_dev_loop-0.10.3.dist-info/WHEEL,sha256=mOdXR9C3wMVov7cSY8PY-XCzo3SmmWCHvGQr5csJoao,91
64
+ agentic_dev_loop-0.10.3.dist-info/entry_points.txt,sha256=Us59oHX6NBy-7ZN3LW5w3HAjnvOPJBfgNiv6ftnmHWw,70
65
+ agentic_dev_loop-0.10.3.dist-info/top_level.txt,sha256=n1ktP1gXxGN6DJAnACR0mt276GRJB9PVDezf9DVPwaw,17
66
+ agentic_dev_loop-0.10.3.dist-info/RECORD,,