agentic-dev-loop 0.11.1__tar.gz → 0.12.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {agentic_dev_loop-0.11.1/src/agentic_dev_loop.egg-info → agentic_dev_loop-0.12.1}/PKG-INFO +116 -2
- {agentic_dev_loop-0.11.1 → agentic_dev_loop-0.12.1}/README.md +115 -1
- {agentic_dev_loop-0.11.1 → agentic_dev_loop-0.12.1}/pyproject.toml +1 -1
- {agentic_dev_loop-0.11.1 → agentic_dev_loop-0.12.1}/src/agentic_dev_loop/core/config.py +47 -5
- {agentic_dev_loop-0.11.1 → agentic_dev_loop-0.12.1}/src/agentic_dev_loop/engine/cli.py +68 -1
- agentic_dev_loop-0.12.1/src/agentic_dev_loop/engine/copilot_auth.py +224 -0
- {agentic_dev_loop-0.11.1 → agentic_dev_loop-0.12.1}/src/agentic_dev_loop/engine/llm.py +23 -1
- agentic_dev_loop-0.12.1/src/agentic_dev_loop/engine/provider_wire.py +38 -0
- {agentic_dev_loop-0.11.1 → agentic_dev_loop-0.12.1}/src/agentic_dev_loop/engine/secrets.py +94 -7
- {agentic_dev_loop-0.11.1 → agentic_dev_loop-0.12.1}/src/agentic_dev_loop/engine/stages.py +3 -1
- {agentic_dev_loop-0.11.1 → agentic_dev_loop-0.12.1/src/agentic_dev_loop.egg-info}/PKG-INFO +116 -2
- {agentic_dev_loop-0.11.1 → agentic_dev_loop-0.12.1}/src/agentic_dev_loop.egg-info/SOURCES.txt +5 -0
- agentic_dev_loop-0.12.1/tests/test_055_cli.py +190 -0
- {agentic_dev_loop-0.11.1 → agentic_dev_loop-0.12.1}/tests/test_cli.py +6 -4
- {agentic_dev_loop-0.11.1 → agentic_dev_loop-0.12.1}/tests/test_config.py +65 -1
- agentic_dev_loop-0.12.1/tests/test_copilot_auth.py +197 -0
- agentic_dev_loop-0.12.1/tests/test_copilot_cli.py +90 -0
- {agentic_dev_loop-0.11.1 → agentic_dev_loop-0.12.1}/tests/test_graph.py +2 -2
- {agentic_dev_loop-0.11.1 → agentic_dev_loop-0.12.1}/tests/test_llm.py +56 -0
- agentic_dev_loop-0.12.1/tests/test_provider_wire.py +118 -0
- {agentic_dev_loop-0.11.1 → agentic_dev_loop-0.12.1}/tests/test_secrets.py +114 -2
- agentic_dev_loop-0.11.1/tests/test_055_cli.py +0 -23
- {agentic_dev_loop-0.11.1 → agentic_dev_loop-0.12.1}/LICENSE +0 -0
- {agentic_dev_loop-0.11.1 → agentic_dev_loop-0.12.1}/setup.cfg +0 -0
- {agentic_dev_loop-0.11.1 → agentic_dev_loop-0.12.1}/src/agentic_dev_loop/__init__.py +0 -0
- {agentic_dev_loop-0.11.1 → agentic_dev_loop-0.12.1}/src/agentic_dev_loop/core/__init__.py +0 -0
- {agentic_dev_loop-0.11.1 → agentic_dev_loop-0.12.1}/src/agentic_dev_loop/core/paths.py +0 -0
- {agentic_dev_loop-0.11.1 → agentic_dev_loop-0.12.1}/src/agentic_dev_loop/core/profiles/__init__.py +0 -0
- {agentic_dev_loop-0.11.1 → agentic_dev_loop-0.12.1}/src/agentic_dev_loop/core/profiles/data/dotnet-node.yaml +0 -0
- {agentic_dev_loop-0.11.1 → agentic_dev_loop-0.12.1}/src/agentic_dev_loop/core/profiles/data/python.yaml +0 -0
- {agentic_dev_loop-0.11.1 → agentic_dev_loop-0.12.1}/src/agentic_dev_loop/core/results.py +0 -0
- {agentic_dev_loop-0.11.1 → agentic_dev_loop-0.12.1}/src/agentic_dev_loop/core/verdict.py +0 -0
- {agentic_dev_loop-0.11.1 → agentic_dev_loop-0.12.1}/src/agentic_dev_loop/engine/__init__.py +0 -0
- {agentic_dev_loop-0.11.1 → agentic_dev_loop-0.12.1}/src/agentic_dev_loop/engine/actions.py +0 -0
- {agentic_dev_loop-0.11.1 → agentic_dev_loop-0.12.1}/src/agentic_dev_loop/engine/agent_error_guide.md +0 -0
- {agentic_dev_loop-0.11.1 → agentic_dev_loop-0.12.1}/src/agentic_dev_loop/engine/agent_error_guide.py +0 -0
- {agentic_dev_loop-0.11.1 → agentic_dev_loop-0.12.1}/src/agentic_dev_loop/engine/agents.py +0 -0
- {agentic_dev_loop-0.11.1 → agentic_dev_loop-0.12.1}/src/agentic_dev_loop/engine/allowlist.py +0 -0
- {agentic_dev_loop-0.11.1 → agentic_dev_loop-0.12.1}/src/agentic_dev_loop/engine/brief_coverage.py +0 -0
- {agentic_dev_loop-0.11.1 → agentic_dev_loop-0.12.1}/src/agentic_dev_loop/engine/brief_prompt.md +0 -0
- {agentic_dev_loop-0.11.1 → agentic_dev_loop-0.12.1}/src/agentic_dev_loop/engine/brief_prompt.py +0 -0
- {agentic_dev_loop-0.11.1 → agentic_dev_loop-0.12.1}/src/agentic_dev_loop/engine/containers.py +0 -0
- {agentic_dev_loop-0.11.1 → agentic_dev_loop-0.12.1}/src/agentic_dev_loop/engine/deps.py +0 -0
- {agentic_dev_loop-0.11.1 → agentic_dev_loop-0.12.1}/src/agentic_dev_loop/engine/dryrun.py +0 -0
- {agentic_dev_loop-0.11.1 → agentic_dev_loop-0.12.1}/src/agentic_dev_loop/engine/egress_policy.py +0 -0
- {agentic_dev_loop-0.11.1 → agentic_dev_loop-0.12.1}/src/agentic_dev_loop/engine/egress_proxy.py +0 -0
- {agentic_dev_loop-0.11.1 → agentic_dev_loop-0.12.1}/src/agentic_dev_loop/engine/extensions/__init__.py +0 -0
- {agentic_dev_loop-0.11.1 → agentic_dev_loop-0.12.1}/src/agentic_dev_loop/engine/extensions/aidlc.py +0 -0
- {agentic_dev_loop-0.11.1 → agentic_dev_loop-0.12.1}/src/agentic_dev_loop/engine/forbidden.py +0 -0
- {agentic_dev_loop-0.11.1 → agentic_dev_loop-0.12.1}/src/agentic_dev_loop/engine/graph.py +0 -0
- {agentic_dev_loop-0.11.1 → agentic_dev_loop-0.12.1}/src/agentic_dev_loop/engine/hostboundary.py +0 -0
- {agentic_dev_loop-0.11.1 → agentic_dev_loop-0.12.1}/src/agentic_dev_loop/engine/isolation.py +0 -0
- {agentic_dev_loop-0.11.1 → agentic_dev_loop-0.12.1}/src/agentic_dev_loop/engine/loaders.py +0 -0
- {agentic_dev_loop-0.11.1 → agentic_dev_loop-0.12.1}/src/agentic_dev_loop/engine/mounts.py +0 -0
- {agentic_dev_loop-0.11.1 → agentic_dev_loop-0.12.1}/src/agentic_dev_loop/engine/onboarding/__init__.py +0 -0
- {agentic_dev_loop-0.11.1 → agentic_dev_loop-0.12.1}/src/agentic_dev_loop/engine/onboarding/checks.py +0 -0
- {agentic_dev_loop-0.11.1 → agentic_dev_loop-0.12.1}/src/agentic_dev_loop/engine/onboarding/doctor.py +0 -0
- {agentic_dev_loop-0.11.1 → agentic_dev_loop-0.12.1}/src/agentic_dev_loop/engine/onboarding/init.py +0 -0
- {agentic_dev_loop-0.11.1 → agentic_dev_loop-0.12.1}/src/agentic_dev_loop/engine/onboarding/recipes/dotnet-node.yaml +0 -0
- {agentic_dev_loop-0.11.1 → agentic_dev_loop-0.12.1}/src/agentic_dev_loop/engine/onboarding/recipes/python.yaml +0 -0
- {agentic_dev_loop-0.11.1 → agentic_dev_loop-0.12.1}/src/agentic_dev_loop/engine/onboarding/recipes.py +0 -0
- {agentic_dev_loop-0.11.1 → agentic_dev_loop-0.12.1}/src/agentic_dev_loop/engine/onboarding/templates/dev.agent.md +0 -0
- {agentic_dev_loop-0.11.1 → agentic_dev_loop-0.12.1}/src/agentic_dev_loop/engine/onboarding/templates/env.example +0 -0
- {agentic_dev_loop-0.11.1 → agentic_dev_loop-0.12.1}/src/agentic_dev_loop/engine/onboarding/templates/qa.agent.md +0 -0
- {agentic_dev_loop-0.11.1 → agentic_dev_loop-0.12.1}/src/agentic_dev_loop/engine/onboarding/templates/review.agent.md +0 -0
- {agentic_dev_loop-0.11.1 → agentic_dev_loop-0.12.1}/src/agentic_dev_loop/engine/onboarding/templates/trigger-agentic-dev-loop.sh +0 -0
- {agentic_dev_loop-0.11.1 → agentic_dev_loop-0.12.1}/src/agentic_dev_loop/engine/onboarding/templates.py +0 -0
- {agentic_dev_loop-0.11.1 → agentic_dev_loop-0.12.1}/src/agentic_dev_loop/engine/onboarding/upgrade.py +0 -0
- {agentic_dev_loop-0.11.1 → agentic_dev_loop-0.12.1}/src/agentic_dev_loop/engine/outputs.py +0 -0
- {agentic_dev_loop-0.11.1 → agentic_dev_loop-0.12.1}/src/agentic_dev_loop/engine/presets.py +0 -0
- {agentic_dev_loop-0.11.1 → agentic_dev_loop-0.12.1}/src/agentic_dev_loop/engine/proc.py +0 -0
- {agentic_dev_loop-0.11.1 → agentic_dev_loop-0.12.1}/src/agentic_dev_loop/engine/registry.py +0 -0
- {agentic_dev_loop-0.11.1 → agentic_dev_loop-0.12.1}/src/agentic_dev_loop/engine/seccomp/__init__.py +0 -0
- {agentic_dev_loop-0.11.1 → agentic_dev_loop-0.12.1}/src/agentic_dev_loop/engine/seccomp/chromium-userns.json +0 -0
- {agentic_dev_loop-0.11.1 → agentic_dev_loop-0.12.1}/src/agentic_dev_loop/engine/step_workspace.py +0 -0
- {agentic_dev_loop-0.11.1 → agentic_dev_loop-0.12.1}/src/agentic_dev_loop/engine/sync_back.py +0 -0
- {agentic_dev_loop-0.11.1 → agentic_dev_loop-0.12.1}/src/agentic_dev_loop/py.typed +0 -0
- {agentic_dev_loop-0.11.1 → agentic_dev_loop-0.12.1}/src/agentic_dev_loop/viz/__init__.py +0 -0
- {agentic_dev_loop-0.11.1 → agentic_dev_loop-0.12.1}/src/agentic_dev_loop/viz/__main__.py +0 -0
- {agentic_dev_loop-0.11.1 → agentic_dev_loop-0.12.1}/src/agentic_dev_loop/viz/layout.py +0 -0
- {agentic_dev_loop-0.11.1 → agentic_dev_loop-0.12.1}/src/agentic_dev_loop/viz/page.py +0 -0
- {agentic_dev_loop-0.11.1 → agentic_dev_loop-0.12.1}/src/agentic_dev_loop/viz/server.py +0 -0
- {agentic_dev_loop-0.11.1 → agentic_dev_loop-0.12.1}/src/agentic_dev_loop.egg-info/dependency_links.txt +0 -0
- {agentic_dev_loop-0.11.1 → agentic_dev_loop-0.12.1}/src/agentic_dev_loop.egg-info/entry_points.txt +0 -0
- {agentic_dev_loop-0.11.1 → agentic_dev_loop-0.12.1}/src/agentic_dev_loop.egg-info/requires.txt +0 -0
- {agentic_dev_loop-0.11.1 → agentic_dev_loop-0.12.1}/src/agentic_dev_loop.egg-info/top_level.txt +0 -0
- {agentic_dev_loop-0.11.1 → agentic_dev_loop-0.12.1}/tests/test_029_merge_on_conflict.py +0 -0
- {agentic_dev_loop-0.11.1 → agentic_dev_loop-0.12.1}/tests/test_033_brief_step.py +0 -0
- {agentic_dev_loop-0.11.1 → agentic_dev_loop-0.12.1}/tests/test_035_finalize_nudge.py +0 -0
- {agentic_dev_loop-0.11.1 → agentic_dev_loop-0.12.1}/tests/test_036_banned_commands_and_lane_boundary.py +0 -0
- {agentic_dev_loop-0.11.1 → agentic_dev_loop-0.12.1}/tests/test_041_brief_coverage.py +0 -0
- {agentic_dev_loop-0.11.1 → agentic_dev_loop-0.12.1}/tests/test_046_brief_prompt.py +0 -0
- {agentic_dev_loop-0.11.1 → agentic_dev_loop-0.12.1}/tests/test_055_doctor.py +0 -0
- {agentic_dev_loop-0.11.1 → agentic_dev_loop-0.12.1}/tests/test_055_init.py +0 -0
- {agentic_dev_loop-0.11.1 → agentic_dev_loop-0.12.1}/tests/test_055_profile_loading.py +0 -0
- {agentic_dev_loop-0.11.1 → agentic_dev_loop-0.12.1}/tests/test_055_profiles.py +0 -0
- {agentic_dev_loop-0.11.1 → agentic_dev_loop-0.12.1}/tests/test_055_recipes.py +0 -0
- {agentic_dev_loop-0.11.1 → agentic_dev_loop-0.12.1}/tests/test_055_schema_lock.py +0 -0
- {agentic_dev_loop-0.11.1 → agentic_dev_loop-0.12.1}/tests/test_055_templates.py +0 -0
- {agentic_dev_loop-0.11.1 → agentic_dev_loop-0.12.1}/tests/test_055_upgrade.py +0 -0
- {agentic_dev_loop-0.11.1 → agentic_dev_loop-0.12.1}/tests/test_actions.py +0 -0
- {agentic_dev_loop-0.11.1 → agentic_dev_loop-0.12.1}/tests/test_agent_error_guide.py +0 -0
- {agentic_dev_loop-0.11.1 → agentic_dev_loop-0.12.1}/tests/test_agents.py +0 -0
- {agentic_dev_loop-0.11.1 → agentic_dev_loop-0.12.1}/tests/test_aidlc.py +0 -0
- {agentic_dev_loop-0.11.1 → agentic_dev_loop-0.12.1}/tests/test_build_viz_page.py +0 -0
- {agentic_dev_loop-0.11.1 → agentic_dev_loop-0.12.1}/tests/test_containers.py +0 -0
- {agentic_dev_loop-0.11.1 → agentic_dev_loop-0.12.1}/tests/test_deps.py +0 -0
- {agentic_dev_loop-0.11.1 → agentic_dev_loop-0.12.1}/tests/test_dry_run.py +0 -0
- {agentic_dev_loop-0.11.1 → agentic_dev_loop-0.12.1}/tests/test_e2e_fixtures.py +0 -0
- {agentic_dev_loop-0.11.1 → agentic_dev_loop-0.12.1}/tests/test_egress_proxy.py +0 -0
- {agentic_dev_loop-0.11.1 → agentic_dev_loop-0.12.1}/tests/test_hostboundary.py +0 -0
- {agentic_dev_loop-0.11.1 → agentic_dev_loop-0.12.1}/tests/test_hostboundary_integration.py +0 -0
- {agentic_dev_loop-0.11.1 → agentic_dev_loop-0.12.1}/tests/test_isolation.py +0 -0
- {agentic_dev_loop-0.11.1 → agentic_dev_loop-0.12.1}/tests/test_isolation_integration.py +0 -0
- {agentic_dev_loop-0.11.1 → agentic_dev_loop-0.12.1}/tests/test_isolation_pbt.py +0 -0
- {agentic_dev_loop-0.11.1 → agentic_dev_loop-0.12.1}/tests/test_loaders.py +0 -0
- {agentic_dev_loop-0.11.1 → agentic_dev_loop-0.12.1}/tests/test_module_contract.py +0 -0
- {agentic_dev_loop-0.11.1 → agentic_dev_loop-0.12.1}/tests/test_outputs.py +0 -0
- {agentic_dev_loop-0.11.1 → agentic_dev_loop-0.12.1}/tests/test_outputs_pbt.py +0 -0
- {agentic_dev_loop-0.11.1 → agentic_dev_loop-0.12.1}/tests/test_proc.py +0 -0
- {agentic_dev_loop-0.11.1 → agentic_dev_loop-0.12.1}/tests/test_publish.py +0 -0
- {agentic_dev_loop-0.11.1 → agentic_dev_loop-0.12.1}/tests/test_qa_prompt.py +0 -0
- {agentic_dev_loop-0.11.1 → agentic_dev_loop-0.12.1}/tests/test_registry.py +0 -0
- {agentic_dev_loop-0.11.1 → agentic_dev_loop-0.12.1}/tests/test_repo_ascii.py +0 -0
- {agentic_dev_loop-0.11.1 → agentic_dev_loop-0.12.1}/tests/test_results.py +0 -0
- {agentic_dev_loop-0.11.1 → agentic_dev_loop-0.12.1}/tests/test_sdk_compat.py +0 -0
- {agentic_dev_loop-0.11.1 → agentic_dev_loop-0.12.1}/tests/test_secrets_integration.py +0 -0
- {agentic_dev_loop-0.11.1 → agentic_dev_loop-0.12.1}/tests/test_secrets_pipeline.py +0 -0
- {agentic_dev_loop-0.11.1 → agentic_dev_loop-0.12.1}/tests/test_source_resolution.py +0 -0
- {agentic_dev_loop-0.11.1 → agentic_dev_loop-0.12.1}/tests/test_stages.py +0 -0
- {agentic_dev_loop-0.11.1 → agentic_dev_loop-0.12.1}/tests/test_step_workspace.py +0 -0
- {agentic_dev_loop-0.11.1 → agentic_dev_loop-0.12.1}/tests/test_sync_back.py +0 -0
- {agentic_dev_loop-0.11.1 → agentic_dev_loop-0.12.1}/tests/test_trigger_script.py +0 -0
- {agentic_dev_loop-0.11.1 → agentic_dev_loop-0.12.1}/tests/test_unit5_config.py +0 -0
- {agentic_dev_loop-0.11.1 → agentic_dev_loop-0.12.1}/tests/test_unit5_pbt.py +0 -0
- {agentic_dev_loop-0.11.1 → agentic_dev_loop-0.12.1}/tests/test_unit5_pure.py +0 -0
- {agentic_dev_loop-0.11.1 → agentic_dev_loop-0.12.1}/tests/test_verdict.py +0 -0
- {agentic_dev_loop-0.11.1 → agentic_dev_loop-0.12.1}/tests/test_viz.py +0 -0
- {agentic_dev_loop-0.11.1 → agentic_dev_loop-0.12.1}/tests/test_viz_demo.py +0 -0
- {agentic_dev_loop-0.11.1 → agentic_dev_loop-0.12.1}/tests/test_viz_layout.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: agentic-dev-loop
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.12.1
|
|
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
|
|
@@ -60,6 +60,10 @@ checkpointed, and nothing gets committed until every gate passes.
|
|
|
60
60
|
parsed from files, retries are bounded, nothing is best-effort.
|
|
61
61
|
- **Sandboxed by default.** The agent's shell and every command step run inside
|
|
62
62
|
an always-on Docker sandbox. There is no opt-out.
|
|
63
|
+
- **Any OpenAI-compatible model, or your Copilot subscription.** Point
|
|
64
|
+
`llm.base_url` at any OpenAI-compatible endpoint with an API key, or log in
|
|
65
|
+
with `agentic-dev-loop copilot-login` and run on Copilot models. See
|
|
66
|
+
[LLM providers](#llm-providers).
|
|
63
67
|
|
|
64
68
|
---
|
|
65
69
|
|
|
@@ -94,6 +98,40 @@ Docker engine does:
|
|
|
94
98
|
Native Windows (outside WSL2) is not supported: every Windows-specific code
|
|
95
99
|
path and test was removed, and CI runs on Ubuntu only.
|
|
96
100
|
|
|
101
|
+
## LLM providers
|
|
102
|
+
|
|
103
|
+
Two ways to run the same pipeline. The engine speaks OpenAI-compatible wire
|
|
104
|
+
format throughout, so every step, profile, and gate works identically on
|
|
105
|
+
either provider style:
|
|
106
|
+
|
|
107
|
+
| Provider | Setup |
|
|
108
|
+
| --- | --- |
|
|
109
|
+
| **Any OpenAI-compatible endpoint** | API key via `api_key_env` + `base_url` in the `llm:` block: DeepSeek, OpenAI, or any OpenAI-compatible API. Full reference: [pipeline.yaml reference](#pipelineyaml-reference). |
|
|
110
|
+
| **GitHub Copilot subscription** | `agentic-dev-loop copilot-login` once, then point the `llm:` block at `https://api.githubcopilot.com` with the file-backed token. Probe-validated pins: `openai/gpt-5.6-luna` (pro) and `openai/gpt-4.1` (lite). Full config and known limitations: [Copilot subscription endpoint](#copilot-subscription-endpoint). |
|
|
111
|
+
|
|
112
|
+
```yaml
|
|
113
|
+
# OpenAI-compatible endpoint (any provider)
|
|
114
|
+
llm:
|
|
115
|
+
model: openai/deepseek-v4-pro
|
|
116
|
+
base_url: https://api.deepseek.com
|
|
117
|
+
api_key_env: OPENAI_API_KEY
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
```yaml
|
|
121
|
+
# GitHub Copilot subscription
|
|
122
|
+
llm:
|
|
123
|
+
model: openai/gpt-5.6-luna # pro; probe-validated pin
|
|
124
|
+
base_url: https://api.githubcopilot.com
|
|
125
|
+
api_key_secret:
|
|
126
|
+
from: file
|
|
127
|
+
key: ~/.agentic-dev-loop/auth/copilot.json
|
|
128
|
+
json_key: access_token
|
|
129
|
+
headers:
|
|
130
|
+
Copilot-Integration-Id: vscode-chat
|
|
131
|
+
Editor-Version: vscode/1.94.0
|
|
132
|
+
Openai-Intent: conversation-panel
|
|
133
|
+
```
|
|
134
|
+
|
|
97
135
|
## Installation
|
|
98
136
|
|
|
99
137
|
**Prerequisites**
|
|
@@ -102,7 +140,7 @@ path and test was removed, and CI runs on Ubuntu only.
|
|
|
102
140
|
| --- | --- |
|
|
103
141
|
| Python | ≥ 3.12 |
|
|
104
142
|
| Docker engine | Engine API ≥ 1.43 (Engine 24). Any compliant engine works - Docker Desktop, OrbStack, Colima, Rancher Desktop, podman docker-compat. On Windows, run under WSL2: the repo must live on the WSL filesystem, not /mnt/c. |
|
|
105
|
-
| LLM API key | The env var named by your pipeline's `api_key_env` (e.g. `OPENAI_API_KEY`) must be set - a missing key fails fast, naming it. |
|
|
143
|
+
| LLM API key | The env var named by your pipeline's `api_key_env` (e.g. `OPENAI_API_KEY`) must be set - a missing key fails fast, naming it. Alternatively, `agentic-dev-loop copilot-login` authenticates a Copilot subscription (see [LLM providers](#llm-providers)). |
|
|
106
144
|
|
|
107
145
|
```bash
|
|
108
146
|
pip install "agentic-dev-loop==1.0.0a17"
|
|
@@ -391,6 +429,12 @@ steps:
|
|
|
391
429
|
engine-written output - captured step output, retry notes, artifacts, and
|
|
392
430
|
`state_dir` files. (Hygiene: the declaring step's agent has already seen
|
|
393
431
|
the value in its own env.)
|
|
432
|
+
- **File provider and LLM keys.** `from: file` reads a host file (`key` is the
|
|
433
|
+
path, optional `json_key` selects a JSON field). The `llm:` block accepts
|
|
434
|
+
exactly one of `api_key_env` or `api_key_secret`; the resolved key joins the
|
|
435
|
+
redaction set. See [Copilot as an endpoint](docs/setup.md#copilot-subscription-endpoint-058)
|
|
436
|
+
for the GitHub Copilot subscription profile (`copilot-login` device flow,
|
|
437
|
+
`copilot-models` probe, and the required endpoint headers).
|
|
394
438
|
|
|
395
439
|
### Per-stage networking
|
|
396
440
|
|
|
@@ -462,6 +506,76 @@ lines, the sandbox's new lines are appended in order, both sides converge, and
|
|
|
462
506
|
the run continues with `[isolation] merged concurrent edits: ...` on the
|
|
463
507
|
console.
|
|
464
508
|
|
|
509
|
+
### Copilot subscription endpoint
|
|
510
|
+
|
|
511
|
+
GitHub Copilot subscriptions work as an OpenAI-compatible LLM endpoint: log in
|
|
512
|
+
once, then point an `llm:` block at it. The smoke harness runs the same plan
|
|
513
|
+
against both the Copilot endpoint and a standard OpenAI-compatible endpoint,
|
|
514
|
+
so the provider choice is independent of the pipeline design.
|
|
515
|
+
|
|
516
|
+
```bash
|
|
517
|
+
agentic-dev-loop copilot-login # device flow; writes the token (mode 600)
|
|
518
|
+
agentic-dev-loop copilot-models # the models this token's tier can use
|
|
519
|
+
```
|
|
520
|
+
|
|
521
|
+
```yaml
|
|
522
|
+
llm:
|
|
523
|
+
model: openai/gpt-5.6-luna # pro default; probe-validated (see below)
|
|
524
|
+
base_url: https://api.githubcopilot.com
|
|
525
|
+
api_key_secret:
|
|
526
|
+
from: file
|
|
527
|
+
key: ~/.agentic-dev-loop/auth/copilot.json
|
|
528
|
+
json_key: access_token
|
|
529
|
+
headers:
|
|
530
|
+
Copilot-Integration-Id: vscode-chat
|
|
531
|
+
Editor-Version: vscode/1.94.0
|
|
532
|
+
Openai-Intent: conversation-panel
|
|
533
|
+
llms:
|
|
534
|
+
flash:
|
|
535
|
+
model: openai/gpt-4.1 # lite tier; no reasoning_effort (see below)
|
|
536
|
+
base_url: https://api.githubcopilot.com
|
|
537
|
+
api_key_secret:
|
|
538
|
+
from: file
|
|
539
|
+
key: ~/.agentic-dev-loop/auth/copilot.json
|
|
540
|
+
json_key: access_token
|
|
541
|
+
headers:
|
|
542
|
+
Copilot-Integration-Id: vscode-chat
|
|
543
|
+
Editor-Version: vscode/1.94.0
|
|
544
|
+
Openai-Intent: conversation-panel
|
|
545
|
+
```
|
|
546
|
+
|
|
547
|
+
- The `openai/` prefix on model names is required: litellm otherwise routes a
|
|
548
|
+
bare `claude-*` name through Anthropic's wire format, which the gateway
|
|
549
|
+
rejects. The prefix forces the OpenAI-compatible route.
|
|
550
|
+
- The model pins are probe-validated against the live endpoint (2026-11-06).
|
|
551
|
+
`gpt-5.6-luna` routes through the Responses API (the SDK switches
|
|
552
|
+
automatically), accepts `xhigh`, and returns real verdicts on the review
|
|
553
|
+
step. `gpt-4.1` tool-calls cleanly on the chat path but hard-400s on any
|
|
554
|
+
`reasoning_effort`, so lite-tier steps set none.
|
|
555
|
+
- The brief step runs on the pro model at `medium`: no-effort `gpt-4.1`
|
|
556
|
+
cannot satisfy the mechanical brief-coverage gate.
|
|
557
|
+
- The engine forces string-encoded message content for Copilot configs: the
|
|
558
|
+
gateway silently drops ARRAY-encoded tool results, which would leave an
|
|
559
|
+
agent blind to its own terminal output.
|
|
560
|
+
|
|
561
|
+
Known limitations (probe-validated, not speculation):
|
|
562
|
+
|
|
563
|
+
- Anthropic models are unusable for agent steps on this endpoint: the claude
|
|
564
|
+
pins collapse to tool-less narration on review (planning-docs/059), and
|
|
565
|
+
claude-opus-4.8 returns two choices on the non-streamed path the engine
|
|
566
|
+
uses.
|
|
567
|
+
- `gpt-4o-mini` caps chat context at 12288 tokens (it overran on the first
|
|
568
|
+
smoke run).
|
|
569
|
+
- `gpt-4o` and `gpt-4.1` reject any `reasoning_effort` on the chat path.
|
|
570
|
+
- `gpt-5.x` models are Responses-API-only.
|
|
571
|
+
- kimi-k3 accepts only `low`/`high`/`max` effort, and its tool results were
|
|
572
|
+
dropped by the gateway until the string-content policy above.
|
|
573
|
+
- `gpt-4.1` carries a gateway "utility model" rate limit that a sustained dev
|
|
574
|
+
loop can trip; it resets within minutes.
|
|
575
|
+
|
|
576
|
+
See [docs/setup.md](docs/setup.md#copilot-subscription-endpoint-058) for the
|
|
577
|
+
token lifecycle, the probe-validated headers, and the full details.
|
|
578
|
+
|
|
465
579
|
|
|
466
580
|
## Authoring a loop for your own repo
|
|
467
581
|
|
|
@@ -16,6 +16,10 @@ checkpointed, and nothing gets committed until every gate passes.
|
|
|
16
16
|
parsed from files, retries are bounded, nothing is best-effort.
|
|
17
17
|
- **Sandboxed by default.** The agent's shell and every command step run inside
|
|
18
18
|
an always-on Docker sandbox. There is no opt-out.
|
|
19
|
+
- **Any OpenAI-compatible model, or your Copilot subscription.** Point
|
|
20
|
+
`llm.base_url` at any OpenAI-compatible endpoint with an API key, or log in
|
|
21
|
+
with `agentic-dev-loop copilot-login` and run on Copilot models. See
|
|
22
|
+
[LLM providers](#llm-providers).
|
|
19
23
|
|
|
20
24
|
---
|
|
21
25
|
|
|
@@ -50,6 +54,40 @@ Docker engine does:
|
|
|
50
54
|
Native Windows (outside WSL2) is not supported: every Windows-specific code
|
|
51
55
|
path and test was removed, and CI runs on Ubuntu only.
|
|
52
56
|
|
|
57
|
+
## LLM providers
|
|
58
|
+
|
|
59
|
+
Two ways to run the same pipeline. The engine speaks OpenAI-compatible wire
|
|
60
|
+
format throughout, so every step, profile, and gate works identically on
|
|
61
|
+
either provider style:
|
|
62
|
+
|
|
63
|
+
| Provider | Setup |
|
|
64
|
+
| --- | --- |
|
|
65
|
+
| **Any OpenAI-compatible endpoint** | API key via `api_key_env` + `base_url` in the `llm:` block: DeepSeek, OpenAI, or any OpenAI-compatible API. Full reference: [pipeline.yaml reference](#pipelineyaml-reference). |
|
|
66
|
+
| **GitHub Copilot subscription** | `agentic-dev-loop copilot-login` once, then point the `llm:` block at `https://api.githubcopilot.com` with the file-backed token. Probe-validated pins: `openai/gpt-5.6-luna` (pro) and `openai/gpt-4.1` (lite). Full config and known limitations: [Copilot subscription endpoint](#copilot-subscription-endpoint). |
|
|
67
|
+
|
|
68
|
+
```yaml
|
|
69
|
+
# OpenAI-compatible endpoint (any provider)
|
|
70
|
+
llm:
|
|
71
|
+
model: openai/deepseek-v4-pro
|
|
72
|
+
base_url: https://api.deepseek.com
|
|
73
|
+
api_key_env: OPENAI_API_KEY
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
```yaml
|
|
77
|
+
# GitHub Copilot subscription
|
|
78
|
+
llm:
|
|
79
|
+
model: openai/gpt-5.6-luna # pro; probe-validated pin
|
|
80
|
+
base_url: https://api.githubcopilot.com
|
|
81
|
+
api_key_secret:
|
|
82
|
+
from: file
|
|
83
|
+
key: ~/.agentic-dev-loop/auth/copilot.json
|
|
84
|
+
json_key: access_token
|
|
85
|
+
headers:
|
|
86
|
+
Copilot-Integration-Id: vscode-chat
|
|
87
|
+
Editor-Version: vscode/1.94.0
|
|
88
|
+
Openai-Intent: conversation-panel
|
|
89
|
+
```
|
|
90
|
+
|
|
53
91
|
## Installation
|
|
54
92
|
|
|
55
93
|
**Prerequisites**
|
|
@@ -58,7 +96,7 @@ path and test was removed, and CI runs on Ubuntu only.
|
|
|
58
96
|
| --- | --- |
|
|
59
97
|
| Python | ≥ 3.12 |
|
|
60
98
|
| Docker engine | Engine API ≥ 1.43 (Engine 24). Any compliant engine works - Docker Desktop, OrbStack, Colima, Rancher Desktop, podman docker-compat. On Windows, run under WSL2: the repo must live on the WSL filesystem, not /mnt/c. |
|
|
61
|
-
| LLM API key | The env var named by your pipeline's `api_key_env` (e.g. `OPENAI_API_KEY`) must be set - a missing key fails fast, naming it. |
|
|
99
|
+
| LLM API key | The env var named by your pipeline's `api_key_env` (e.g. `OPENAI_API_KEY`) must be set - a missing key fails fast, naming it. Alternatively, `agentic-dev-loop copilot-login` authenticates a Copilot subscription (see [LLM providers](#llm-providers)). |
|
|
62
100
|
|
|
63
101
|
```bash
|
|
64
102
|
pip install "agentic-dev-loop==1.0.0a17"
|
|
@@ -347,6 +385,12 @@ steps:
|
|
|
347
385
|
engine-written output - captured step output, retry notes, artifacts, and
|
|
348
386
|
`state_dir` files. (Hygiene: the declaring step's agent has already seen
|
|
349
387
|
the value in its own env.)
|
|
388
|
+
- **File provider and LLM keys.** `from: file` reads a host file (`key` is the
|
|
389
|
+
path, optional `json_key` selects a JSON field). The `llm:` block accepts
|
|
390
|
+
exactly one of `api_key_env` or `api_key_secret`; the resolved key joins the
|
|
391
|
+
redaction set. See [Copilot as an endpoint](docs/setup.md#copilot-subscription-endpoint-058)
|
|
392
|
+
for the GitHub Copilot subscription profile (`copilot-login` device flow,
|
|
393
|
+
`copilot-models` probe, and the required endpoint headers).
|
|
350
394
|
|
|
351
395
|
### Per-stage networking
|
|
352
396
|
|
|
@@ -418,6 +462,76 @@ lines, the sandbox's new lines are appended in order, both sides converge, and
|
|
|
418
462
|
the run continues with `[isolation] merged concurrent edits: ...` on the
|
|
419
463
|
console.
|
|
420
464
|
|
|
465
|
+
### Copilot subscription endpoint
|
|
466
|
+
|
|
467
|
+
GitHub Copilot subscriptions work as an OpenAI-compatible LLM endpoint: log in
|
|
468
|
+
once, then point an `llm:` block at it. The smoke harness runs the same plan
|
|
469
|
+
against both the Copilot endpoint and a standard OpenAI-compatible endpoint,
|
|
470
|
+
so the provider choice is independent of the pipeline design.
|
|
471
|
+
|
|
472
|
+
```bash
|
|
473
|
+
agentic-dev-loop copilot-login # device flow; writes the token (mode 600)
|
|
474
|
+
agentic-dev-loop copilot-models # the models this token's tier can use
|
|
475
|
+
```
|
|
476
|
+
|
|
477
|
+
```yaml
|
|
478
|
+
llm:
|
|
479
|
+
model: openai/gpt-5.6-luna # pro default; probe-validated (see below)
|
|
480
|
+
base_url: https://api.githubcopilot.com
|
|
481
|
+
api_key_secret:
|
|
482
|
+
from: file
|
|
483
|
+
key: ~/.agentic-dev-loop/auth/copilot.json
|
|
484
|
+
json_key: access_token
|
|
485
|
+
headers:
|
|
486
|
+
Copilot-Integration-Id: vscode-chat
|
|
487
|
+
Editor-Version: vscode/1.94.0
|
|
488
|
+
Openai-Intent: conversation-panel
|
|
489
|
+
llms:
|
|
490
|
+
flash:
|
|
491
|
+
model: openai/gpt-4.1 # lite tier; no reasoning_effort (see below)
|
|
492
|
+
base_url: https://api.githubcopilot.com
|
|
493
|
+
api_key_secret:
|
|
494
|
+
from: file
|
|
495
|
+
key: ~/.agentic-dev-loop/auth/copilot.json
|
|
496
|
+
json_key: access_token
|
|
497
|
+
headers:
|
|
498
|
+
Copilot-Integration-Id: vscode-chat
|
|
499
|
+
Editor-Version: vscode/1.94.0
|
|
500
|
+
Openai-Intent: conversation-panel
|
|
501
|
+
```
|
|
502
|
+
|
|
503
|
+
- The `openai/` prefix on model names is required: litellm otherwise routes a
|
|
504
|
+
bare `claude-*` name through Anthropic's wire format, which the gateway
|
|
505
|
+
rejects. The prefix forces the OpenAI-compatible route.
|
|
506
|
+
- The model pins are probe-validated against the live endpoint (2026-11-06).
|
|
507
|
+
`gpt-5.6-luna` routes through the Responses API (the SDK switches
|
|
508
|
+
automatically), accepts `xhigh`, and returns real verdicts on the review
|
|
509
|
+
step. `gpt-4.1` tool-calls cleanly on the chat path but hard-400s on any
|
|
510
|
+
`reasoning_effort`, so lite-tier steps set none.
|
|
511
|
+
- The brief step runs on the pro model at `medium`: no-effort `gpt-4.1`
|
|
512
|
+
cannot satisfy the mechanical brief-coverage gate.
|
|
513
|
+
- The engine forces string-encoded message content for Copilot configs: the
|
|
514
|
+
gateway silently drops ARRAY-encoded tool results, which would leave an
|
|
515
|
+
agent blind to its own terminal output.
|
|
516
|
+
|
|
517
|
+
Known limitations (probe-validated, not speculation):
|
|
518
|
+
|
|
519
|
+
- Anthropic models are unusable for agent steps on this endpoint: the claude
|
|
520
|
+
pins collapse to tool-less narration on review (planning-docs/059), and
|
|
521
|
+
claude-opus-4.8 returns two choices on the non-streamed path the engine
|
|
522
|
+
uses.
|
|
523
|
+
- `gpt-4o-mini` caps chat context at 12288 tokens (it overran on the first
|
|
524
|
+
smoke run).
|
|
525
|
+
- `gpt-4o` and `gpt-4.1` reject any `reasoning_effort` on the chat path.
|
|
526
|
+
- `gpt-5.x` models are Responses-API-only.
|
|
527
|
+
- kimi-k3 accepts only `low`/`high`/`max` effort, and its tool results were
|
|
528
|
+
dropped by the gateway until the string-content policy above.
|
|
529
|
+
- `gpt-4.1` carries a gateway "utility model" rate limit that a sustained dev
|
|
530
|
+
loop can trip; it resets within minutes.
|
|
531
|
+
|
|
532
|
+
See [docs/setup.md](docs/setup.md#copilot-subscription-endpoint-058) for the
|
|
533
|
+
token lifecycle, the probe-validated headers, and the full details.
|
|
534
|
+
|
|
421
535
|
|
|
422
536
|
## Authoring a loop for your own repo
|
|
423
537
|
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "agentic-dev-loop"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.12.1"
|
|
8
8
|
description = "Turn an LLM into a gated, sandboxed, resumable construction loop over your codebase."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.12"
|
|
@@ -10,12 +10,13 @@ from pathlib import Path
|
|
|
10
10
|
from typing import Any, Literal
|
|
11
11
|
|
|
12
12
|
import yaml
|
|
13
|
-
from pydantic import BaseModel, ConfigDict, Field, field_validator,
|
|
13
|
+
from pydantic import (BaseModel, ConfigDict, Field, field_validator,
|
|
14
|
+
model_serializer, model_validator)
|
|
14
15
|
|
|
15
16
|
from agentic_dev_loop.core.paths import _has_wildcards, path_excluded, relative_parts
|
|
16
17
|
|
|
17
18
|
TERMINALS = frozenset({"success", "failure"})
|
|
18
|
-
KNOWN_SECRET_PROVIDERS = frozenset({"env"})
|
|
19
|
+
KNOWN_SECRET_PROVIDERS = frozenset({"env", "file"})
|
|
19
20
|
MOUNT_MODES = frozenset({"rw", "ro"})
|
|
20
21
|
# The egress preset names the engine ships (BR-9). Config validates the
|
|
21
22
|
# name here; the target data lives in engine/presets.py (preset data,
|
|
@@ -183,14 +184,17 @@ def route_target(route: RouteTarget | None) -> str | None:
|
|
|
183
184
|
|
|
184
185
|
class SecretSpec(BaseModel):
|
|
185
186
|
"""One ``secrets:`` ref - the locator only, never a value. ``from`` names
|
|
186
|
-
the provider (``env``
|
|
187
|
+
the provider (``env`` or ``file``); ``key`` is that provider's locator (for
|
|
187
188
|
``env``, the host environment variable to read and the step env name to
|
|
188
|
-
inject
|
|
189
|
+
inject; for ``file``, the JSON file path). ``json_key`` selects a key
|
|
190
|
+
inside the JSON file and is file-provider-only. Unknown keys fail at load
|
|
191
|
+
(extra="forbid")."""
|
|
189
192
|
|
|
190
193
|
model_config = ConfigDict(extra="forbid", populate_by_name=True)
|
|
191
194
|
|
|
192
195
|
from_: str = Field(alias="from")
|
|
193
196
|
key: str
|
|
197
|
+
json_key: str | None = None
|
|
194
198
|
|
|
195
199
|
@field_validator("from_")
|
|
196
200
|
@classmethod
|
|
@@ -208,6 +212,28 @@ class SecretSpec(BaseModel):
|
|
|
208
212
|
raise ValueError("secret key must be a non-empty string")
|
|
209
213
|
return v
|
|
210
214
|
|
|
215
|
+
@field_validator("json_key")
|
|
216
|
+
@classmethod
|
|
217
|
+
def _nonempty_json_key(cls, v: str | None) -> str | None:
|
|
218
|
+
if v is not None and not v.strip():
|
|
219
|
+
raise ValueError("secret json_key must be a non-empty string")
|
|
220
|
+
return v
|
|
221
|
+
|
|
222
|
+
@model_validator(mode="after")
|
|
223
|
+
def _json_key_only_for_file(self) -> "SecretSpec":
|
|
224
|
+
if self.json_key is not None and self.from_ != "file":
|
|
225
|
+
raise ValueError(
|
|
226
|
+
"secret json_key is only valid with the 'file' provider")
|
|
227
|
+
return self
|
|
228
|
+
|
|
229
|
+
@model_serializer(mode="wrap")
|
|
230
|
+
def _omit_absent_json_key(self, handler):
|
|
231
|
+
"""Keep the pre-058 env-provider dump shape byte-identical."""
|
|
232
|
+
data = handler(self)
|
|
233
|
+
if data.get("json_key") is None:
|
|
234
|
+
data.pop("json_key", None)
|
|
235
|
+
return data
|
|
236
|
+
|
|
211
237
|
|
|
212
238
|
class IsolationConfig(BaseModel):
|
|
213
239
|
"""Optional `isolation:` section - configures the always-on sandbox (BR-2).
|
|
@@ -600,13 +626,29 @@ class LlmConfig(BaseModel):
|
|
|
600
626
|
|
|
601
627
|
model: str
|
|
602
628
|
base_url: str | None = None
|
|
603
|
-
|
|
629
|
+
# 058 (D3): exactly one host-side key mechanism - an environment variable
|
|
630
|
+
# (the existing shape) or a file-backed secret spec.
|
|
631
|
+
api_key_env: str | None = None
|
|
632
|
+
api_key_secret: SecretSpec | None = None
|
|
633
|
+
# 058 (D2): extra HTTP headers forwarded verbatim to the SDK LLM
|
|
634
|
+
# (``extra_headers``). Empty = no change for every existing pipeline.
|
|
635
|
+
headers: dict[str, str] = Field(default_factory=dict)
|
|
604
636
|
timeout: float | None = None # omitted → opinionated no-timeout default
|
|
605
637
|
# 047 (D1) / 054 (D6): opt-in passthrough; unset means reasoning is off
|
|
606
638
|
# (the engine forwards none and strips reasoning passback). The SDK
|
|
607
639
|
# "high" default never engages because build_llm always passes the value.
|
|
608
640
|
reasoning_effort: ReasoningEffort | None = None
|
|
609
641
|
|
|
642
|
+
@model_validator(mode="after")
|
|
643
|
+
def _exactly_one_api_key_mechanism(self) -> "LlmConfig":
|
|
644
|
+
has_env = self.api_key_env is not None
|
|
645
|
+
has_secret = self.api_key_secret is not None
|
|
646
|
+
if has_env == has_secret:
|
|
647
|
+
raise ValueError(
|
|
648
|
+
"set exactly one of 'api_key_env' or 'api_key_secret' on the "
|
|
649
|
+
"llm block (neither or both are invalid)")
|
|
650
|
+
return self
|
|
651
|
+
|
|
610
652
|
|
|
611
653
|
class PipelineConfig(BaseModel):
|
|
612
654
|
model_config = ConfigDict(extra="forbid")
|
|
@@ -10,9 +10,12 @@ import json
|
|
|
10
10
|
import os
|
|
11
11
|
import signal
|
|
12
12
|
import sys
|
|
13
|
+
import threading
|
|
13
14
|
from pathlib import Path
|
|
14
15
|
|
|
15
16
|
from agentic_dev_loop.core.config import PipelineConfig, load_config
|
|
17
|
+
from .copilot_auth import (TOKEN_PATH, CopilotAuthError, list_copilot_models,
|
|
18
|
+
login, read_token)
|
|
16
19
|
from .containers import containers_report
|
|
17
20
|
from .isolation import isolation_state_dir, reap_orphaned_containers
|
|
18
21
|
from .loaders import mark_plan_completed
|
|
@@ -46,6 +49,14 @@ def _restore_sigterm_handler(previous) -> None:
|
|
|
46
49
|
signal.signal(signal.SIGTERM, previous)
|
|
47
50
|
|
|
48
51
|
|
|
52
|
+
def _wait_for_interrupt() -> None:
|
|
53
|
+
"""Block until the user presses ctrl+c; a bounded wait keeps the loop
|
|
54
|
+
responsive to signals without requiring a tty."""
|
|
55
|
+
idle = threading.Event()
|
|
56
|
+
while not idle.wait(1.0):
|
|
57
|
+
pass
|
|
58
|
+
|
|
59
|
+
|
|
49
60
|
def _version() -> str:
|
|
50
61
|
"""The installed distribution version - the single source of truth."""
|
|
51
62
|
try:
|
|
@@ -149,6 +160,52 @@ def _print_containers_report(report) -> None:
|
|
|
149
160
|
f"run={run} pid={pid_s}")
|
|
150
161
|
|
|
151
162
|
|
|
163
|
+
def _token_path(value: str | None) -> Path:
|
|
164
|
+
return Path(value).expanduser() if value else TOKEN_PATH.expanduser()
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
def _run_copilot_login(argv: list[str]) -> int:
|
|
168
|
+
parser = argparse.ArgumentParser(
|
|
169
|
+
prog="agentic-dev-loop copilot-login",
|
|
170
|
+
description="Run the GitHub device flow and store the Copilot OAuth "
|
|
171
|
+
"token (~/.agentic-dev-loop/auth/copilot.json, mode 600).")
|
|
172
|
+
parser.add_argument("--token-file", default=None,
|
|
173
|
+
help="token path (default: "
|
|
174
|
+
"~/.agentic-dev-loop/auth/copilot.json)")
|
|
175
|
+
args = parser.parse_args(argv)
|
|
176
|
+
path = _token_path(args.token_file)
|
|
177
|
+
try:
|
|
178
|
+
login(token_path=path)
|
|
179
|
+
except CopilotAuthError as e:
|
|
180
|
+
print(f"copilot-login: {e}", file=sys.stderr)
|
|
181
|
+
return 1
|
|
182
|
+
print(f"Copilot token written to {path}")
|
|
183
|
+
return 0
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
def _run_copilot_models(argv: list[str]) -> int:
|
|
187
|
+
parser = argparse.ArgumentParser(
|
|
188
|
+
prog="agentic-dev-loop copilot-models",
|
|
189
|
+
description="List the Copilot models available to this token's tier.")
|
|
190
|
+
parser.add_argument("--token-file", default=None,
|
|
191
|
+
help="token path (default: "
|
|
192
|
+
"~/.agentic-dev-loop/auth/copilot.json)")
|
|
193
|
+
parser.add_argument("--json", action="store_true",
|
|
194
|
+
help="emit the model list as JSON")
|
|
195
|
+
args = parser.parse_args(argv)
|
|
196
|
+
try:
|
|
197
|
+
models = list_copilot_models(read_token(_token_path(args.token_file)))
|
|
198
|
+
except CopilotAuthError as e:
|
|
199
|
+
print(f"copilot-models: {e}", file=sys.stderr)
|
|
200
|
+
return 1
|
|
201
|
+
if args.json:
|
|
202
|
+
print(json.dumps({"models": models}, sort_keys=True))
|
|
203
|
+
else:
|
|
204
|
+
for model in models:
|
|
205
|
+
print(model)
|
|
206
|
+
return 0
|
|
207
|
+
|
|
208
|
+
|
|
152
209
|
def build_parser(cfg_path) -> argparse.ArgumentParser:
|
|
153
210
|
"""The full parser for one pipeline: core flags + loader-contributed."""
|
|
154
211
|
registry, _ = _registry_for(cfg_path)
|
|
@@ -158,13 +215,17 @@ def build_parser(cfg_path) -> argparse.ArgumentParser:
|
|
|
158
215
|
return parser
|
|
159
216
|
|
|
160
217
|
|
|
161
|
-
def main(argv: list[str] | None = None) -> int:
|
|
218
|
+
def main(argv: list[str] | None = None, *, keep_alive: bool = True) -> int:
|
|
162
219
|
if argv is None:
|
|
163
220
|
argv = sys.argv[1:]
|
|
164
221
|
else:
|
|
165
222
|
argv = list(argv)
|
|
166
223
|
if argv and argv[0] == "containers":
|
|
167
224
|
return _run_containers(argv[1:])
|
|
225
|
+
if argv and argv[0] == "copilot-login":
|
|
226
|
+
return _run_copilot_login(argv[1:])
|
|
227
|
+
if argv and argv[0] == "copilot-models":
|
|
228
|
+
return _run_copilot_models(argv[1:])
|
|
168
229
|
if argv and argv[0] == "init":
|
|
169
230
|
from .onboarding.init import run_init
|
|
170
231
|
return run_init(argv[1:])
|
|
@@ -207,6 +268,12 @@ def main(argv: list[str] | None = None) -> int:
|
|
|
207
268
|
if not success:
|
|
208
269
|
print("agentic-dev-loop: pipeline failed - exiting with code 1",
|
|
209
270
|
file=sys.stderr)
|
|
271
|
+
if keep_alive and not args.no_viz:
|
|
272
|
+
print("Pipeline finished: press ctrl+c to exit")
|
|
273
|
+
try:
|
|
274
|
+
_wait_for_interrupt()
|
|
275
|
+
except KeyboardInterrupt:
|
|
276
|
+
pass
|
|
210
277
|
return 0 if success else 1
|
|
211
278
|
finally:
|
|
212
279
|
_restore_sigterm_handler(previous)
|