agentic-dev-loop 0.9.2__py3-none-any.whl → 0.10.0__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.
@@ -396,9 +396,13 @@ class StepConfig(BaseModel):
396
396
  terminal_timeout: float | None = Field(default=None, gt=0) # seconds, agent steps only (032)
397
397
  finalize_nudge_turns: int | None = Field(default=None, gt=0) # agent steps only (035)
398
398
  brief_inputs: BriefInputsSpec | None = None # agent steps only (033)
399
- # 047 (D7): per-step opt-in override; unset falls back to the llm block,
400
- # then the SDK default. Full SDK literal, passthrough semantics.
399
+ # 047 (D7) / 054 (D6): per-step opt-in override; unset falls back to the
400
+ # effective profile's value (the named `llms:` profile, else the `llm:`
401
+ # block), else reasoning is off. Full SDK literal, passthrough semantics.
401
402
  reasoning_effort: ReasoningEffort | None = None
403
+ # 054 (D3): names a top-level `llms:` profile; the whole profile moves
404
+ # together (model, endpoint, key, effort default). None = the `llm:` block.
405
+ llm: str | None = None
402
406
  custom_prompt: str | None = None # agent steps only (040); file path resolved at load
403
407
  env: dict[str, str | None] | None = None # NFR-1: null = inherit host value
404
408
  setup: str | None = None # command or agent steps; runs fresh at each step entry (052)
@@ -486,6 +490,16 @@ class StepConfig(BaseModel):
486
490
  f"step {self.name!r}: exactly one of 'agent', 'action', "
487
491
  "'command', or 'brief_inputs' is required"
488
492
  )
493
+ if self.llm is not None:
494
+ if not self.llm.strip():
495
+ raise ValueError(
496
+ f"step {self.name!r}: 'llm' must be a non-empty profile name"
497
+ )
498
+ if self.kind not in ("agent", "brief"):
499
+ raise ValueError(
500
+ f"step {self.name!r}: 'llm' names an LLM profile and is "
501
+ "only allowed on 'agent' or 'brief_inputs' steps"
502
+ )
489
503
  if self.brief_inputs is not None:
490
504
  if self.produces:
491
505
  raise ValueError(
@@ -587,9 +601,9 @@ class LlmConfig(BaseModel):
587
601
  base_url: str | None = None
588
602
  api_key_env: str
589
603
  timeout: float | None = None # omitted → opinionated no-timeout default
590
- # 047 (D1): opt-in passthrough; unset means the field is not passed at
591
- # all, so the SDK default ("high") applies and today's behavior is
592
- # byte-identical. No default may move without an observed run (D6).
604
+ # 047 (D1) / 054 (D6): opt-in passthrough; unset means reasoning is off
605
+ # (the engine forwards none and strips reasoning passback). The SDK
606
+ # "high" default never engages because build_llm always passes the value.
593
607
  reasoning_effort: ReasoningEffort | None = None
594
608
 
595
609
 
@@ -606,6 +620,9 @@ class PipelineConfig(BaseModel):
606
620
  extensions_dir: str = "~/.agentic-dev-loop/extensions"
607
621
  env_allowlist: list[str] = []
608
622
  llm: LlmConfig
623
+ # 054 (D1): named LLM profiles, each a strict LlmConfig. Optional and
624
+ # purely additive - the `llm:` block above remains the default.
625
+ llms: dict[str, LlmConfig] = {}
609
626
  isolation: IsolationConfig | None = None # None = always-on defaults
610
627
  state_dir: str = ".pr" # default relocated to ~/.agentic-dev-loop/runs/<hash>/ at load
611
628
  secrets: dict[str, SecretSpec] = {} # ref name -> locator; values never appear here
@@ -690,6 +707,23 @@ class PipelineConfig(BaseModel):
690
707
  "enforces, or set 'deps_policy: open'")
691
708
  return self
692
709
 
710
+ @model_validator(mode="after")
711
+ def _check_llm_profiles(self) -> "PipelineConfig":
712
+ """054 (D5): the registry is additive and referential. A profile
713
+ literally named 'default' is rejected (the `llm:` block already is the
714
+ default), and any step naming an undeclared profile fails at load,
715
+ never mid-run."""
716
+ if "default" in self.llms:
717
+ raise ValueError(
718
+ "'llms' cannot declare a profile named 'default' - the "
719
+ "top-level 'llm:' block is already the default")
720
+ for step in self.steps:
721
+ if step.llm is not None and step.llm not in self.llms:
722
+ raise ValueError(
723
+ f"step {step.name!r}: unknown LLM profile {step.llm!r} - "
724
+ f"declared profiles: {sorted(self.llms)}")
725
+ return self
726
+
693
727
  @model_validator(mode="after")
694
728
  def _mounts_avoid_state_dir(self) -> "PipelineConfig":
695
729
  """BR-1.3: the host-only run-state dir is never a mount path.
@@ -177,11 +177,25 @@ def run_pipeline(cfg_path, args, *, viz_bus=None,
177
177
  sandbox.close()
178
178
 
179
179
 
180
+ def _build_profile_llms(cfg) -> dict:
181
+ """054 (D7): build the LLM for each profile referenced by any step,
182
+ eagerly, so a missing api_key_env fails at run start, not mid-run.
183
+ Declared-but-unreferenced profiles are shape-validated at load; their
184
+ keys are not checked here."""
185
+ referenced = {step.llm for step in cfg.steps if step.llm is not None}
186
+ return {name: build_llm(cfg.llms[name]) for name in sorted(referenced)}
187
+
188
+
180
189
  def _run_pipeline(cfg, base, registry, state_dir, max_attempts, args, viz_bus,
181
190
  sandbox, resolved_secrets, tooling_paths) -> tuple[bool, int]:
182
191
  export_root = getattr(sandbox, "export_root", None)
183
192
  engine_allowlist = resolve_engine_allowlist(registry, args)
193
+ # 054 (D7): build the default LLM plus one per profile referenced by any
194
+ # step, eagerly, so a missing api_key_env for a referenced profile fails
195
+ # at run start, not mid-run.
196
+ profile_llms = _build_profile_llms(cfg)
184
197
  runtime = Runtime(cfg=cfg, registry=registry, llm=build_llm(cfg.llm),
198
+ llms=profile_llms,
185
199
  agents_dir=base / cfg.agents_dir, state_dir=state_dir,
186
200
  workspace=Path.cwd(), sandbox=sandbox,
187
201
  sandbox_cwd=Path(export_root) if export_root else None,
@@ -4,64 +4,98 @@ never fall back to another env var (no silent fallbacks)."""
4
4
  import os
5
5
 
6
6
  from openhands.sdk import LLM
7
- from openhands.sdk.llm.utils import model_features as _model_features
8
7
 
9
8
  from agentic_dev_loop.core.config import LlmConfig
10
9
 
11
- # Compat shim: OpenHands SDK 1.17 gates reasoning_content passback on a closed
12
- # model-name list (SEND_REASONING_CONTENT_MODELS) that does not know
13
- # deepseek-v4-pro. DeepSeek's thinking mode 400s on any multi-turn request
14
- # whose history omits prior reasoning_content. Unlike force_string_serializer,
15
- # the SDK exposes no instance-level override, so register the model here.
16
- # Remove once the SDK lists the model or adds an override.
17
- _DEEPSEEK_MODEL = "deepseek-v4-pro"
18
- if _DEEPSEEK_MODEL not in _model_features.SEND_REASONING_CONTENT_MODELS:
19
- _model_features.SEND_REASONING_CONTENT_MODELS.append(_DEEPSEEK_MODEL)
20
-
21
- # Compat shim 2: the SDK splits a multi-tool-call turn into one assistant
22
- # message per call (Agent.step) and carries reasoning_content only on the
23
- # first; Message.to_chat_dict also drops empty-string reasoning. DeepSeek's
24
- # thinking mode 400s any request whose history contains reasoning_content
25
- # but omits it from an assistant tool-call message ("The reasoning_content
26
- # in the thinking mode must be passed back to the API"). Repair the
27
- # serialized history: an assistant tool-call message without its own
28
- # reasoning borrows the turn's last reasoning (reset on user turns), and a
29
- # present-but-empty reasoning is still written (field presence matters).
30
- # Remove once the SDK distributes reasoning_content to every message.
10
+ # 054 (D8): reasoning passback is config-gated, never name-list-gated. The
11
+ # SDK gates reasoning_content passback on a closed model-name list
12
+ # (SEND_REASONING_CONTENT_MODELS) the engine never consults; instead each
13
+ # LLM instance carries the engine-resolved effort on
14
+ # ``_agentic_dev_loop_reasoning_effort`` and the serializer below gates
15
+ # passback on it. Gate on = the effort is set and not "none"; gate off =
16
+ # reasoning is stripped and, when history still carries reasoning_content,
17
+ # a clear engine error fires before the doomed request is sent (D12).
31
18
  _ORIGINAL_FORMAT_MESSAGES = LLM.format_messages_for_llm
19
+
20
+
21
+ def _reasoning_gate_on(llm: LLM) -> bool:
22
+ """The engine-configured effort for this instance, if any. The SDK's own
23
+ ``reasoning_effort`` field defaults to "high", so a direct (non-engine)
24
+ instance would read as gate-on; the dedicated attribute is only set by
25
+ ``build_llm``, keeping absent effort = reasoning off."""
26
+ effort = getattr(llm, "_agentic_dev_loop_reasoning_effort", None)
27
+ return effort is not None and effort != "none"
28
+
29
+
30
+ def _reasoning_stripped_error(llm: LLM) -> RuntimeError:
31
+ """054 (D12): gate off while the conversation still carries
32
+ reasoning_content - a request that strips it would 400 at the provider."""
33
+ effort = getattr(llm, "_agentic_dev_loop_reasoning_effort", None)
34
+ return RuntimeError(
35
+ f"model {llm.model!r} is configured with reasoning off "
36
+ f"(reasoning_effort={effort!r}) but the conversation history carries "
37
+ "reasoning_content that would be stripped from the request. Set "
38
+ "'reasoning_effort' (low/medium/high/xhigh) on the step or its llm "
39
+ "profile, use 'none' only where the provider honors it, or use a "
40
+ "non-thinking model."
41
+ )
42
+
43
+
32
44
  if not getattr(_ORIGINAL_FORMAT_MESSAGES, "_agentic_dev_loop_reasoning_repair", False):
33
- def _repair_reasoning_passback(self, messages: list) -> list[dict]:
45
+ def _config_gated_reasoning_serializer(self, messages: list) -> list[dict]:
46
+ """The engine-owned reasoning passback serializer (054 D8).
47
+
48
+ Gate on: every assistant message that carries reasoning_content
49
+ writes it back (the SDK only does so for listed models and only on
50
+ the first message of a split tool-call turn), and a tool-call
51
+ message without its own reasoning borrows the turn's last reasoning
52
+ (reset on user turns). Gate off: reasoning is stripped from the
53
+ outgoing request and, if history still carries reasoning_content,
54
+ the D12 error fires instead of sending a doomed request.
55
+ """
34
56
  out = _ORIGINAL_FORMAT_MESSAGES(self, messages)
57
+ if not _reasoning_gate_on(self):
58
+ for d in out:
59
+ d.pop("reasoning_content", None)
60
+ if any(getattr(m, "reasoning_content", None) for m in messages):
61
+ raise _reasoning_stripped_error(self)
62
+ return out
63
+
35
64
  last = None
36
65
  for msg, d in zip(messages, out):
37
66
  if msg.role == "user":
38
67
  last = None
39
- if msg.role == "assistant" and msg.tool_calls:
40
- rc = getattr(msg, "reasoning_content", None)
68
+ continue
69
+ if msg.role != "assistant":
70
+ continue
71
+ rc = getattr(msg, "reasoning_content", None)
72
+ if msg.tool_calls:
41
73
  if rc is not None:
42
74
  last = rc
43
75
  if last is not None:
44
76
  d["reasoning_content"] = last
77
+ elif rc:
78
+ d["reasoning_content"] = rc
45
79
  return out
46
80
 
47
- _repair_reasoning_passback._agentic_dev_loop_reasoning_repair = True
48
- LLM.format_messages_for_llm = _repair_reasoning_passback
81
+ _config_gated_reasoning_serializer._agentic_dev_loop_reasoning_repair = True
82
+ LLM.format_messages_for_llm = _config_gated_reasoning_serializer
49
83
 
50
84
 
51
85
  def build_llm(cfg: LlmConfig, reasoning_effort: str | None = None) -> LLM:
52
86
  api_key = os.environ.get(cfg.api_key_env)
53
87
  if not api_key:
54
88
  raise RuntimeError(f"{cfg.api_key_env} environment variable is required")
55
- # 047 (D2/D7): an explicit per-step override wins, else the llm block's
56
- # value applies, else the field is omitted entirely and the SDK default
57
- # ("high") is in force. The reasoning_content shims above stay untouched.
89
+ # 054 (D6): a step's value wins, else the effective profile's value, else
90
+ # off. reasoning_effort is always passed (None when unset) so the SDK
91
+ # "high" default cannot engage; absent effort = reasoning off.
58
92
  effort = reasoning_effort if reasoning_effort is not None else cfg.reasoning_effort
59
- kwargs = {
60
- "model": cfg.model,
61
- "api_key": api_key,
62
- "base_url": cfg.base_url,
63
- "timeout": cfg.timeout, # policy: no LLM timeout unless explicitly set
64
- }
65
- if effort is not None:
66
- kwargs["reasoning_effort"] = effort
67
- return LLM(**kwargs)
93
+ llm = LLM(
94
+ model=cfg.model,
95
+ api_key=api_key,
96
+ base_url=cfg.base_url,
97
+ timeout=cfg.timeout, # policy: no LLM timeout unless explicitly set
98
+ reasoning_effort=effort,
99
+ )
100
+ llm._agentic_dev_loop_reasoning_effort = effort
101
+ return llm
@@ -120,6 +120,9 @@ class Runtime:
120
120
  agents_dir: Path
121
121
  state_dir: Path
122
122
  workspace: Path
123
+ # 054 (D7): named-profile LLMs built eagerly at run start, keyed by the
124
+ # `llms:` profile name. `llm` above remains the default (`llm:` block).
125
+ llms: dict[str, "LLM"] = field(default_factory=dict)
123
126
  sandbox: Sandbox | None = None
124
127
  # unit-5: the .git-free export root sandboxed steps run in (BR-2); None =
125
128
  # the real workspace (no export materialised - fake/legacy paths).
@@ -176,13 +179,18 @@ class Runtime:
176
179
  if step is not None else None),
177
180
  forbidden_commands=forbidden_commands,
178
181
  allowlist=allowlist)
179
- # 047 (D7): a step's reasoning_effort overrides the llm block's;
180
- # self.llm already carries the block value (or the SDK default),
181
- # so only build a fresh LLM when the step overrides it.
182
+ # 054 (D6/D7): a named profile selects the whole config (model,
183
+ # endpoint, key, effort default move together); the step's own
184
+ # reasoning_effort then overrides the effective profile's value.
182
185
  llm = self.llm
183
- if step is not None and step.reasoning_effort is not None:
184
- llm = build_llm(self.cfg.llm,
185
- reasoning_effort=step.reasoning_effort)
186
+ if step is not None:
187
+ effective_cfg = (self.cfg.llms[step.llm]
188
+ if step.llm is not None else self.cfg.llm)
189
+ if step.llm is not None:
190
+ llm = self.llms.get(step.llm, self.llm)
191
+ if step.reasoning_effort is not None:
192
+ llm = build_llm(effective_cfg,
193
+ reasoning_effort=step.reasoning_effort)
186
194
  if step is not None and step.kind == "brief":
187
195
  agent = load_brief_agent(
188
196
  agent_name, step.brief_inputs.sections, llm)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: agentic-dev-loop
3
- Version: 0.9.2
3
+ Version: 0.10.0
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
@@ -202,6 +202,17 @@ llm:
202
202
  api_key_env: OPENAI_API_KEY
203
203
  # reasoning_effort: medium # optional: low | medium | high | xhigh | none
204
204
 
205
+ # Named LLM profiles (054): a step names one with `llm: <name>` to move the
206
+ # whole profile (model, endpoint, key, effort default) together - no partial
207
+ # merges. The `llm:` block above remains the default. Profile keys are
208
+ # host-side: sandbox access still goes through `secrets:`, never a profile key.
209
+ # llms:
210
+ # review:
211
+ # model: openai/deepseek-v4-pro
212
+ # base_url: https://api.deepseek.com
213
+ # api_key_env: REVIEW_API_KEY
214
+ # reasoning_effort: xhigh
215
+
205
216
  max_attempts: 10 # CLI --max-attempts overrides
206
217
  agents_dir: sdk_agents # your <name>.agent.md files
207
218
 
@@ -231,7 +242,8 @@ steps:
231
242
  agent: review
232
243
  artifact: review-findings.md
233
244
  verdicts: { pass: APPROVED, fail: NEEDS CHANGES }
234
- # reasoning_effort: xhigh # per-step override; falls back to the llm block, then SDK default
245
+ # reasoning_effort: xhigh # per-step override of the effective profile's effort
246
+ # llm: review # (054) select a named `llms:` profile for this step
235
247
  on_pass: qa
236
248
  on_fail: { goto: dev, retry: true }
237
249
 
@@ -257,7 +269,8 @@ steps:
257
269
  | `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. |
258
270
  | `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). |
259
271
  | `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. |
260
- | `reasoning_effort:` | Optional `low` / `medium` / `high` / `xhigh` / `none`. Set on the `llm` block (pipeline-global) or per-step (overrides the block). Unset anywhere = the field is not passed and the SDK default (`high`) applies, so today's behavior is unchanged. Forwarded only for models the SDK/LiteLLM mark as supporting it; an unsupported value fails at the first LLM call. See [Reasoning effort](#reasoning-effort-opt-in). |
272
+ | `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
+ | `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). |
261
274
  | `on_pass:` / `on_fail:` | A step name, terminal `success`/`failure`, or `{goto: X, retry: true}`. No loops happen unless you write them. |
262
275
  | `env:` / `setup:` / `network:` | `env:` declares step vars (fixed safe base + declared vars only); `setup:` runs fresh at every step boundary in a one-shot provision container targeting the export (command or agent steps; network ON; no stamping); `network: true` lets a command/agent step reach the hosts its `egress:` allowlist names (default off) - see [Per-stage networking](#per-stage-networking). |
263
276
  | `mounts:` / `egress:` | Per-step workspace isolation. `mounts:` lists `{path, mode}` entries (`rw`/`ro`, default `ro`) and is **strict-by-default** - empty/missing mounts nothing; only declared-rw paths sync back to the repo. `egress:` is `{class, presets, targets}` (`class: browser` = broad-but-logged; otherwise a strict allowlist). See [Per-stage networking](#per-stage-networking) and [docs/setup.md](docs/setup.md). |
@@ -272,17 +285,25 @@ steps:
272
285
 
273
286
  ### Reasoning effort (opt-in)
274
287
 
275
- `reasoning_effort` is an opt-in latency dial on the pipeline `llm` block and
276
- on individual agent steps. It takes one of `low`, `medium`, `high`, `xhigh`,
277
- or `none`; unset means the field is never passed and the SDK default
278
- (`high`) applies, so a pipeline that omits it behaves exactly as before. A
279
- step's value overrides the `llm` block's value, so the review lane can hold
280
- a higher effort than the rest.
281
-
282
- The value is forwarded only for models the SDK/LiteLLM mark as supporting
283
- it; an unsupported value fails at the first LLM call (the schema rejects
284
- unknown names at load time, but cannot know provider capability). No
285
- pipeline ships a value: adopt one only after an observed run proves lower
288
+ `reasoning_effort` is an opt-in latency dial on the pipeline `llm` block, a
289
+ named `llms:` profile, and individual agent steps. It takes one of `low`,
290
+ `medium`, `high`, `xhigh`, or `none`; unset means reasoning is off - the
291
+ field is forwarded as none, reasoning is stripped from requests, and a
292
+ thinking model whose history still carries reasoning fails fast with an
293
+ actionable engine error rather than a provider 400. A step's value overrides
294
+ the effective profile's value, so the review lane can hold a higher effort
295
+ than the rest, or a named profile can move an entire lane to a different
296
+ provider.
297
+
298
+ Reasoning is opt-in and provider-generic: any openai-compatible endpoint
299
+ expressible as an `LlmConfig` works, and reasoning passback is gated on the
300
+ configured effort level, never on a model-name list. A thinking model without
301
+ an effort level (or with `none` where the provider ignores it) is invalid
302
+ config - it still reasons but its API requires the passback the engine strips,
303
+ so the engine raises before the doomed request is sent. Set
304
+ `reasoning_effort` on the step or its profile, or use a non-thinking model.
305
+ The schema rejects unknown effort names at load time, but cannot know provider
306
+ capability; adopt an effort value only after an observed run proves lower
286
307
  per-turn latency with a green suite and no provider 400s.
287
308
 
288
309
  Recommended lane defaults (guidance only, not enforced):
@@ -515,22 +536,23 @@ adapt per repo.
515
536
  Adaptation checklist for a new repo:
516
537
 
517
538
  1. `llm:` - model, `base_url`, `api_key_env`.
518
- 2. `isolation:` - toolchain image override (the consumer repo pins a
539
+ 2. `llms:` (optional) - named profiles for multi-provider lanes (keep review on the strongest model); a step opts in with `llm: <name>`.
540
+ 3. `isolation:` - toolchain image override (the consumer repo pins a
519
541
  toolchain image by digest) and `seccomp_profile: bundled:chromium-userns`
520
542
  only if a stage runs Chromium.
521
- 3. `caches:` - toolchain cache dirs as rw bind-mounts shared across runs
543
+ 4. `caches:` - toolchain cache dirs as rw bind-mounts shared across runs
522
544
  (`.nuget-packages`, `qa/node_modules`, `qa/.browsers`,
523
545
  `frontend-editor/node_modules`); caches are never synced back, and a
524
546
  step's `setup:` hook runs with them mounted so it can warm them.
525
- 4. `sync_excludes:` - build output trees (`**/bin/**`, `**/obj/**`).
526
- 5. `secrets:` - refs only, resolved from host env; each step opts in to the
547
+ 5. `sync_excludes:` - build output trees (`**/bin/**`, `**/obj/**`).
548
+ 6. `secrets:` - refs only, resolved from host env; each step opts in to the
527
549
  refs it needs.
528
- 6. `egress:` - `presets: [package-registries]` for fetch steps;
550
+ 7. `egress:` - `presets: [package-registries]` for fetch steps;
529
551
  `class: browser` (broad-but-logged) for browser-tier qa only.
530
- 7. `deps_policy:` and `lockfiles:` - see
552
+ 8. `deps_policy:` and `lockfiles:` - see
531
553
  [Dependency policy](#dependency-policy-locked-by-default).
532
- 8. The dev prompt's impossible set - write it for your toolchain.
533
- 9. `additional_files:` - the repo's `AGENTS.md`, coding standards, design
554
+ 9. The dev prompt's impossible set - write it for your toolchain.
555
+ 10. `additional_files:` - the repo's `AGENTS.md`, coding standards, design
534
556
  docs: shared context for every agent step.
535
557
 
536
558
  Validate before the first run - dry-run loads and prints the resolved plan
@@ -1,7 +1,7 @@
1
1
  agentic_dev_loop/__init__.py,sha256=Z9TG7878W6z4jIU-ka6EBrogM6jniJd35KB7T23a-yU,91
2
2
  agentic_dev_loop/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
3
  agentic_dev_loop/core/__init__.py,sha256=iFaDNqZ8klDD6WF9hSFgJVGhz2PPMaVixezMUHVs8ts,775
4
- agentic_dev_loop/core/config.py,sha256=_tmEXhgMk4Lyy3Q9BMMf3-zANK7VH1RPxL-AfT9uynM,37105
4
+ agentic_dev_loop/core/config.py,sha256=2XFCta8EWLyjOBPHY7ZUbxMKwHDEraC6qn-Dj2eMAHE,38856
5
5
  agentic_dev_loop/core/paths.py,sha256=yqM_OjVQqRk5TQFzSjFZdM6liOpEQC-5DMKoDHwTca0,3448
6
6
  agentic_dev_loop/core/results.py,sha256=7coSkXoP8xbLvhIS4bUSwZYL0gn6tbjmp62jvc4w-ZM,1078
7
7
  agentic_dev_loop/core/verdict.py,sha256=GA4alcxYC_pQ6RY4bwdumeimilw0Jri7LYHU5hzGsD8,4337
@@ -21,10 +21,10 @@ agentic_dev_loop/engine/dryrun.py,sha256=KaEAtIiYF9AvVCGawesD8vP-XQkIF671ux_Q9DA
21
21
  agentic_dev_loop/engine/egress_policy.py,sha256=Gon_S6arEMjRJOEcjr-iqZGKxxu5YQxMo5eYHgz0_zs,4676
22
22
  agentic_dev_loop/engine/egress_proxy.py,sha256=hZxyUzagYu8pkzk-IbzefRrRawfIMTMh_gRKNymrIbQ,13087
23
23
  agentic_dev_loop/engine/forbidden.py,sha256=Iz8q1LUil9bT8g3jC_JFmICIqsmDSbN2XcIYvxCw3Ew,3226
24
- agentic_dev_loop/engine/graph.py,sha256=CDtkcnKXmalgUXq9_V5Ltzp6PdabhvZOUj27jbJsEQE,16468
24
+ agentic_dev_loop/engine/graph.py,sha256=LzM8vkgZyMyibQLA0tA_0UkUQNsz3uW4H3RQ_MuyHDE,17181
25
25
  agentic_dev_loop/engine/hostboundary.py,sha256=-dL1GLhenIA40U6-QkJgiX2O_0sRz_1lFPyExvPp8Uk,5729
26
26
  agentic_dev_loop/engine/isolation.py,sha256=bRPch_xWrz3KSBZgZzpbB3WMh8qUu98LdHqq8rtpJEM,81887
27
- agentic_dev_loop/engine/llm.py,sha256=R7jQY3bKRhu1U48_e4xrlOFuEVGmYgjzqkbvKMZrsFY,3297
27
+ agentic_dev_loop/engine/llm.py,sha256=A3xuuynNPFkOeQLVBv2h3MQ4kO22vE3SYetwIysmDJ8,4582
28
28
  agentic_dev_loop/engine/loaders.py,sha256=mWvjJ_k9iRIMvVI323W7GlMdieOFB9q28WPaSwoqO0w,6260
29
29
  agentic_dev_loop/engine/mounts.py,sha256=z3UFcloQKG6cFqT4TLm_xLxH4-1kqELQhlELXnKIJvM,2058
30
30
  agentic_dev_loop/engine/outputs.py,sha256=NSsorBw6iKFISXuexHiJqSkWJQm3pxxexBNDNqWSvNA,2218
@@ -32,7 +32,7 @@ agentic_dev_loop/engine/presets.py,sha256=Avo9Mb4YNyv4e5y7Ib0HptJ_S5srvTssdN-keT
32
32
  agentic_dev_loop/engine/proc.py,sha256=MU-tV1_MXm28NHKuNpSW8zm3U_pFCgQhd-yAVCXoUB0,2739
33
33
  agentic_dev_loop/engine/registry.py,sha256=qepez9iU-dBE-yG67qI7Q7Ol89NsERHy34eoQc98Ek0,14296
34
34
  agentic_dev_loop/engine/secrets.py,sha256=WCVyDk97AGvRkwxXw6tbgztHVZbtxJvJC23b4I1EeNY,4678
35
- agentic_dev_loop/engine/stages.py,sha256=6tXZP4hDsGd9ktVocRDOI10B2xn1YfmtHzHI2jEt6BE,56026
35
+ agentic_dev_loop/engine/stages.py,sha256=TJEl-yQYIPEfDyWJ0c_QxWz4uVsZ0rc0xVJaPCzaz0I,56500
36
36
  agentic_dev_loop/engine/step_workspace.py,sha256=YyaZWk8ozc_v7AFBt63QAn4VFL-a1wvhzNvnbascYYw,7937
37
37
  agentic_dev_loop/engine/sync_back.py,sha256=Mc6YAAhc_Iydz-7Ht3OkyGekaa6s1HitCsOlIepDfOc,13942
38
38
  agentic_dev_loop/engine/extensions/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -42,9 +42,9 @@ agentic_dev_loop/engine/seccomp/chromium-userns.json,sha256=ahmqvWprLe_oG_oLXFMU
42
42
  agentic_dev_loop/viz/__init__.py,sha256=eoRfcUCC3-j7JhShft7Ruk8ruT5oFUoMKEKClO45B4I,520
43
43
  agentic_dev_loop/viz/__main__.py,sha256=baycB1QwQtM-B2bXo4Du5lwvItcMgJwJlAzsHACxM-4,1409
44
44
  agentic_dev_loop/viz/server.py,sha256=-7lHiBCBQx3h0g-TfQyIaWG1tIfrWrqfLJco1-4GJOI,47637
45
- agentic_dev_loop-0.9.2.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
46
- agentic_dev_loop-0.9.2.dist-info/METADATA,sha256=s1Tql2vpok3i6dMdByJcLMkhgTH-yEBRgLrYe3NDwYI,41543
47
- agentic_dev_loop-0.9.2.dist-info/WHEEL,sha256=mOdXR9C3wMVov7cSY8PY-XCzo3SmmWCHvGQr5csJoao,91
48
- agentic_dev_loop-0.9.2.dist-info/entry_points.txt,sha256=Us59oHX6NBy-7ZN3LW5w3HAjnvOPJBfgNiv6ftnmHWw,70
49
- agentic_dev_loop-0.9.2.dist-info/top_level.txt,sha256=n1ktP1gXxGN6DJAnACR0mt276GRJB9PVDezf9DVPwaw,17
50
- agentic_dev_loop-0.9.2.dist-info/RECORD,,
45
+ agentic_dev_loop-0.10.0.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
46
+ agentic_dev_loop-0.10.0.dist-info/METADATA,sha256=7iu1lAbrrnGOWSm3Usz4a3Ow_gr07cFhJwv6sku9ugA,43236
47
+ agentic_dev_loop-0.10.0.dist-info/WHEEL,sha256=mOdXR9C3wMVov7cSY8PY-XCzo3SmmWCHvGQr5csJoao,91
48
+ agentic_dev_loop-0.10.0.dist-info/entry_points.txt,sha256=Us59oHX6NBy-7ZN3LW5w3HAjnvOPJBfgNiv6ftnmHWw,70
49
+ agentic_dev_loop-0.10.0.dist-info/top_level.txt,sha256=n1ktP1gXxGN6DJAnACR0mt276GRJB9PVDezf9DVPwaw,17
50
+ agentic_dev_loop-0.10.0.dist-info/RECORD,,