agentforge-framework 0.2.2__tar.gz → 0.2.4__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.
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.4}/PKG-INFO +46 -5
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.4}/README.md +45 -4
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.4}/pyproject.toml +1 -1
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.4}/src/agentforge_framework/__init__.py +1 -1
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.4}/src/agentforge_framework/agents/__init__.py +21 -2
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.4}/src/agentforge_framework/agents/architect.py +7 -2
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.4}/src/agentforge_framework/agents/implementer.py +7 -2
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.4}/src/agentforge_framework/agents/orchestrator.py +2 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.4}/src/agentforge_framework/agents/reviewer.py +2 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.4}/src/agentforge_framework/agents/security.py +19 -7
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.4}/src/agentforge_framework/agents/tester.py +15 -2
- agentforge_framework-0.2.4/src/agentforge_framework/core/config.py +158 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.4}/src/agentforge_framework/core/contracts.py +36 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.4}/src/agentforge_framework/core/issues.py +32 -10
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.4}/src/agentforge_framework/core/project.py +8 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.4}/src/agentforge_framework/core/repo.py +15 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.4}/src/agentforge_framework/core/runtime.py +56 -12
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.4}/src/agentforge_framework/providers/base.py +27 -4
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.4}/src/agentforge_framework/providers/claude.py +55 -11
- agentforge_framework-0.2.4/src/agentforge_framework/providers/codex.py +196 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.4}/src/agentforge_framework.egg-info/PKG-INFO +46 -5
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.4}/tests/test_agents.py +2 -2
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.4}/tests/test_architect.py +1 -1
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.4}/tests/test_cli.py +1 -1
- agentforge_framework-0.2.4/tests/test_config.py +162 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.4}/tests/test_docs.py +6 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.4}/tests/test_execution_posture.py +81 -19
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.4}/tests/test_issues.py +38 -9
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.4}/tests/test_project.py +6 -4
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.4}/tests/test_providers.py +70 -2
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.4}/tests/test_reviewer.py +1 -1
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.4}/tests/test_runtime.py +147 -19
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.4}/tests/test_security.py +23 -5
- agentforge_framework-0.2.2/src/agentforge_framework/core/config.py +0 -90
- agentforge_framework-0.2.2/src/agentforge_framework/providers/codex.py +0 -143
- agentforge_framework-0.2.2/tests/test_config.py +0 -77
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.4}/LICENSE +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.4}/setup.cfg +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.4}/src/agentforge_framework/.claude-plugin/plugin.json +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.4}/src/agentforge_framework/agents/decomposer.py +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.4}/src/agentforge_framework/cli.py +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.4}/src/agentforge_framework/context/__init__.py +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.4}/src/agentforge_framework/context/extractors/__init__.py +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.4}/src/agentforge_framework/context/extractors/base.py +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.4}/src/agentforge_framework/context/extractors/python.py +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.4}/src/agentforge_framework/context/extractors/sql.py +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.4}/src/agentforge_framework/context/extractors/yaml.py +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.4}/src/agentforge_framework/context/prompt.py +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.4}/src/agentforge_framework/context/resolver.py +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.4}/src/agentforge_framework/core/__init__.py +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.4}/src/agentforge_framework/core/commands.py +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.4}/src/agentforge_framework/core/gates.py +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.4}/src/agentforge_framework/core/plan_format.py +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.4}/src/agentforge_framework/core/process.py +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.4}/src/agentforge_framework/core/registry.py +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.4}/src/agentforge_framework/core/router.py +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.4}/src/agentforge_framework/core/skills.py +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.4}/src/agentforge_framework/core/workflow.py +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.4}/src/agentforge_framework/plugins/__init__.py +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.4}/src/agentforge_framework/plugins/databricks/__init__.py +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.4}/src/agentforge_framework/plugins/pyspark/__init__.py +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.4}/src/agentforge_framework/plugins/python/__init__.py +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.4}/src/agentforge_framework/plugins/sql/__init__.py +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.4}/src/agentforge_framework/providers/__init__.py +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.4}/src/agentforge_framework/skills/MANIFEST.yaml +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.4}/src/agentforge_framework/skills/NOTICE +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.4}/src/agentforge_framework/skills/domain-modeling/ADR-FORMAT.md +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.4}/src/agentforge_framework/skills/domain-modeling/CONTEXT-FORMAT.md +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.4}/src/agentforge_framework/skills/domain-modeling/SKILL.md +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.4}/src/agentforge_framework/skills/domain-modeling/agents/openai.yaml +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.4}/src/agentforge_framework/skills/grill-with-docs/SKILL.md +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.4}/src/agentforge_framework/skills/grilling/SKILL.md +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.4}/src/agentforge_framework/skills/grilling/agents/openai.yaml +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.4}/src/agentforge_framework/skills/to-spec/SKILL.md +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.4}/src/agentforge_framework/skills/to-spec/agents/openai.yaml +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.4}/src/agentforge_framework/skills/to-tickets/SKILL.md +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.4}/src/agentforge_framework/skills/to-tickets/agents/openai.yaml +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.4}/src/agentforge_framework/skills/unslop/SKILL.md +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.4}/src/agentforge_framework/skills/unslop/evals/fixtures/silhouette/human_reference.json +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.4}/src/agentforge_framework/skills/unslop/scripts/_lang.py +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.4}/src/agentforge_framework/skills/unslop/scripts/banned_phrase_scan.py +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.4}/src/agentforge_framework/skills/unslop/scripts/calibrate_pairs.py +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.4}/src/agentforge_framework/skills/unslop/scripts/calibrate_score.py +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.4}/src/agentforge_framework/skills/unslop/scripts/check_packs.py +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.4}/src/agentforge_framework/skills/unslop/scripts/check_suggestions.py +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.4}/src/agentforge_framework/skills/unslop/scripts/contribute.py +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.4}/src/agentforge_framework/skills/unslop/scripts/diff_check.py +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.4}/src/agentforge_framework/skills/unslop/scripts/extract_constraints.py +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.4}/src/agentforge_framework/skills/unslop/scripts/harvest_classify.py +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.4}/src/agentforge_framework/skills/unslop/scripts/harvest_samples.py +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.4}/src/agentforge_framework/skills/unslop/scripts/readability_metrics.py +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.4}/src/agentforge_framework/skills/unslop/scripts/refresh_status.py +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.4}/src/agentforge_framework/skills/unslop/scripts/silhouette_scan.py +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.4}/src/agentforge_framework/skills/unslop/scripts/structure_scan.py +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.4}/src/agentforge_framework/skills/unslop/scripts/suggest.py +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.4}/src/agentforge_framework/skills/unslop/scripts/validate_preservation.py +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.4}/src/agentforge_framework/skills/unslop/scripts/voice_card.py +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.4}/src/agentforge_framework/skills/unslop/scripts/voice_profile.py +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.4}/src/agentforge_framework/skills/unslop/scripts/voice_score.py +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.4}/src/agentforge_framework/skills/unslop/scripts/wiki_sync.py +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.4}/src/agentforge_framework/skills/write-plainly/SKILL.md +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.4}/src/agentforge_framework/workflows/bugfix.yaml +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.4}/src/agentforge_framework/workflows/feature.yaml +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.4}/src/agentforge_framework/workflows/review.yaml +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.4}/src/agentforge_framework.egg-info/SOURCES.txt +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.4}/src/agentforge_framework.egg-info/dependency_links.txt +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.4}/src/agentforge_framework.egg-info/entry_points.txt +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.4}/src/agentforge_framework.egg-info/requires.txt +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.4}/src/agentforge_framework.egg-info/top_level.txt +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.4}/tests/test_context.py +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.4}/tests/test_contracts.py +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.4}/tests/test_decompose.py +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.4}/tests/test_gates.py +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.4}/tests/test_interview.py +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.4}/tests/test_plan_format.py +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.4}/tests/test_plugins.py +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.4}/tests/test_skills.py +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.4}/tests/test_tester.py +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.4}/tests/test_workflow.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: agentforge-framework
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.4
|
|
4
4
|
Summary: A modular framework for coordinating specialized software agents.
|
|
5
5
|
Author: Yash Mhatre
|
|
6
6
|
License-Expression: Apache-2.0
|
|
@@ -99,7 +99,10 @@ ADR-0015.
|
|
|
99
99
|
|
|
100
100
|
Without `--allow-commands`, the Implementer remains default-deny and the Tester
|
|
101
101
|
reports that it could not run the suite; it never substitutes reading tests and
|
|
102
|
-
claims completion.
|
|
102
|
+
claims completion. That gate is held by the `claude` adapter. The `codex` CLI
|
|
103
|
+
cannot hold it — `codex exec` discards its approval flag — so a denied Run on
|
|
104
|
+
`codex` is refused before it starts rather than run under a guarantee nothing
|
|
105
|
+
enforces (ADR-0007, amended). Security, the Reviewer, and the Architect need no such flag —
|
|
103
106
|
auditing, reviewing, and designing are reading. All six Roles `CONTEXT.md` names
|
|
104
107
|
now run. Plugins have landed, and with them the data-engineering ones: a
|
|
105
108
|
repository whose files import `pyspark` has its Roles told to write DataFrame
|
|
@@ -145,7 +148,7 @@ without notice. See
|
|
|
145
148
|
```console
|
|
146
149
|
$ pip install agentforge-framework
|
|
147
150
|
$ agentforge --version
|
|
148
|
-
agentforge 0.2.
|
|
151
|
+
agentforge 0.2.4
|
|
149
152
|
```
|
|
150
153
|
|
|
151
154
|
That is the whole of it. The two routes below the naming note are for a machine
|
|
@@ -169,7 +172,7 @@ from the [latest release](https://github.com/yashmhatre/AgentForge/releases/late
|
|
|
169
172
|
and install the file:
|
|
170
173
|
|
|
171
174
|
```console
|
|
172
|
-
$ pip install agentforge_framework-0.2.
|
|
175
|
+
$ pip install agentforge_framework-0.2.4-py3-none-any.whl
|
|
173
176
|
```
|
|
174
177
|
|
|
175
178
|
Publishing a release is a wizard rather than a checklist:
|
|
@@ -200,6 +203,14 @@ What each release contains is in [CHANGELOG.md](CHANGELOG.md).
|
|
|
200
203
|
|
|
201
204
|
AgentForge never touches a model API and handles no credentials of its own. Whatever your coding-agent CLI is already authenticated with is what a Run costs.
|
|
202
205
|
|
|
206
|
+
### Editors and IDEs
|
|
207
|
+
|
|
208
|
+
AgentForge is a terminal tool. It runs fine from an IDE's terminal, but the IDE is not a Provider and its assistant is not one either.
|
|
209
|
+
|
|
210
|
+
**Antigravity IDE cannot be a Provider** ([#101](https://github.com/yashmhatre/AgentForge/issues/101)). Checked by running it, so nobody has to derive it again: `antigravity-ide chat` accepts a prompt and reads stdin, which is the right shape for the argument vector — but it is a window launcher. Piping a prompt to it returns exit 0 immediately with `Reading from stdin via: …\code-stdin-XXXX` on stdout and nothing else, ever; the answer goes to a GUI session. A Provider adapter has to read the Agent's result off stdout, so there is nothing for `parse_output` to parse. The same is true of any editor CLI of this shape.
|
|
211
|
+
|
|
212
|
+
**Do not run another agent against a checkout while a Run is going.** A Run commits every change to a file git already tracks, however it got there ([ADR-0015](docs/adr/0015-a-run-commits-what-it-declared.md)) — so a second agent's half-finished edits are committed into the Run's branch and attributed to a Role. Files nothing in the Run claimed are listed in the pull request body ([ADR-0023](docs/adr/0023-a-commit-names-what-nothing-claimed.md)), which makes it visible at Sign-off but does not prevent it. Use a separate clone.
|
|
213
|
+
|
|
203
214
|
## Commands
|
|
204
215
|
|
|
205
216
|
| Command | What it does |
|
|
@@ -274,14 +285,33 @@ defaults are Claude `native` and every other Provider `fragment`, and the
|
|
|
274
285
|
providers:
|
|
275
286
|
claude:
|
|
276
287
|
capability_tier: native
|
|
288
|
+
# Optional. Overrides the adapter's own tier-to-model table; name only the
|
|
289
|
+
# tiers you disagree about, and the rest keep the shipped defaults.
|
|
290
|
+
models:
|
|
291
|
+
deep: claude-opus-5
|
|
277
292
|
codex:
|
|
278
293
|
capability_tier: fragment
|
|
279
294
|
|
|
295
|
+
# Optional. A Role's two declared axes: which class of model runs it, and how
|
|
296
|
+
# hard that model thinks. Both default to the table in ADR-0004.
|
|
297
|
+
roles:
|
|
298
|
+
security:
|
|
299
|
+
tier: deep
|
|
300
|
+
effort: max
|
|
301
|
+
|
|
280
302
|
gates:
|
|
281
303
|
tests:
|
|
282
304
|
suite: pytest
|
|
283
305
|
```
|
|
284
306
|
|
|
307
|
+
There is no `roles.<name>.model` key, and naming one is an error rather than a
|
|
308
|
+
line that gets ignored. A Role declares a tier; the Provider maps that tier onto
|
|
309
|
+
a model. A model named per Role does not survive the next CLI release and does
|
|
310
|
+
not port to another Provider, which is what
|
|
311
|
+
[ADR-0004](docs/adr/0004-model-tiers-declared-not-named.md) exists to prevent --
|
|
312
|
+
override `providers.<name>.models.<tier>` if the mapping is what you disagree
|
|
313
|
+
with.
|
|
314
|
+
|
|
285
315
|
There is no `plugins:` key. Which Plugins answer for a repository is decided per
|
|
286
316
|
Run from the frozen plan's blast radius (ADR-0016), so a repository-level list
|
|
287
317
|
would be inert and misleading -- init prints what it detected instead. The file
|
|
@@ -331,10 +361,21 @@ code rather than the verdict about the old code.
|
|
|
331
361
|
|
|
332
362
|
Read [`CONTEXT.md`](CONTEXT.md) before writing anything, and [`docs/adr/`](docs/adr/) for the decisions that constrain it.
|
|
333
363
|
|
|
334
|
-
##
|
|
364
|
+
## Changing AgentForge
|
|
365
|
+
|
|
366
|
+
Clone it and install that clone editable. Never patch the installed copy.
|
|
335
367
|
|
|
336
368
|
```console
|
|
369
|
+
$ git clone https://github.com/yashmhatre/AgentForge.git
|
|
370
|
+
$ cd AgentForge
|
|
337
371
|
$ pip install -e ".[dev]"
|
|
372
|
+
```
|
|
373
|
+
|
|
374
|
+
Editing `site-packages/agentforge_framework/` appears to work and is the one change nothing will catch: the local install diverges from what ships, so the thing that works is not the thing anybody else gets, and the fix is lost the next time the package is upgraded. If a Run is failing badly enough that patching the install looks like the way forward, that is a bug worth filing — [#101](https://github.com/yashmhatre/AgentForge/issues/101) is what happened the last time somebody reached for it.
|
|
375
|
+
|
|
376
|
+
## Tests
|
|
377
|
+
|
|
378
|
+
```console
|
|
338
379
|
$ pytest
|
|
339
380
|
```
|
|
340
381
|
|
|
@@ -81,7 +81,10 @@ ADR-0015.
|
|
|
81
81
|
|
|
82
82
|
Without `--allow-commands`, the Implementer remains default-deny and the Tester
|
|
83
83
|
reports that it could not run the suite; it never substitutes reading tests and
|
|
84
|
-
claims completion.
|
|
84
|
+
claims completion. That gate is held by the `claude` adapter. The `codex` CLI
|
|
85
|
+
cannot hold it — `codex exec` discards its approval flag — so a denied Run on
|
|
86
|
+
`codex` is refused before it starts rather than run under a guarantee nothing
|
|
87
|
+
enforces (ADR-0007, amended). Security, the Reviewer, and the Architect need no such flag —
|
|
85
88
|
auditing, reviewing, and designing are reading. All six Roles `CONTEXT.md` names
|
|
86
89
|
now run. Plugins have landed, and with them the data-engineering ones: a
|
|
87
90
|
repository whose files import `pyspark` has its Roles told to write DataFrame
|
|
@@ -127,7 +130,7 @@ without notice. See
|
|
|
127
130
|
```console
|
|
128
131
|
$ pip install agentforge-framework
|
|
129
132
|
$ agentforge --version
|
|
130
|
-
agentforge 0.2.
|
|
133
|
+
agentforge 0.2.4
|
|
131
134
|
```
|
|
132
135
|
|
|
133
136
|
That is the whole of it. The two routes below the naming note are for a machine
|
|
@@ -151,7 +154,7 @@ from the [latest release](https://github.com/yashmhatre/AgentForge/releases/late
|
|
|
151
154
|
and install the file:
|
|
152
155
|
|
|
153
156
|
```console
|
|
154
|
-
$ pip install agentforge_framework-0.2.
|
|
157
|
+
$ pip install agentforge_framework-0.2.4-py3-none-any.whl
|
|
155
158
|
```
|
|
156
159
|
|
|
157
160
|
Publishing a release is a wizard rather than a checklist:
|
|
@@ -182,6 +185,14 @@ What each release contains is in [CHANGELOG.md](CHANGELOG.md).
|
|
|
182
185
|
|
|
183
186
|
AgentForge never touches a model API and handles no credentials of its own. Whatever your coding-agent CLI is already authenticated with is what a Run costs.
|
|
184
187
|
|
|
188
|
+
### Editors and IDEs
|
|
189
|
+
|
|
190
|
+
AgentForge is a terminal tool. It runs fine from an IDE's terminal, but the IDE is not a Provider and its assistant is not one either.
|
|
191
|
+
|
|
192
|
+
**Antigravity IDE cannot be a Provider** ([#101](https://github.com/yashmhatre/AgentForge/issues/101)). Checked by running it, so nobody has to derive it again: `antigravity-ide chat` accepts a prompt and reads stdin, which is the right shape for the argument vector — but it is a window launcher. Piping a prompt to it returns exit 0 immediately with `Reading from stdin via: …\code-stdin-XXXX` on stdout and nothing else, ever; the answer goes to a GUI session. A Provider adapter has to read the Agent's result off stdout, so there is nothing for `parse_output` to parse. The same is true of any editor CLI of this shape.
|
|
193
|
+
|
|
194
|
+
**Do not run another agent against a checkout while a Run is going.** A Run commits every change to a file git already tracks, however it got there ([ADR-0015](docs/adr/0015-a-run-commits-what-it-declared.md)) — so a second agent's half-finished edits are committed into the Run's branch and attributed to a Role. Files nothing in the Run claimed are listed in the pull request body ([ADR-0023](docs/adr/0023-a-commit-names-what-nothing-claimed.md)), which makes it visible at Sign-off but does not prevent it. Use a separate clone.
|
|
195
|
+
|
|
185
196
|
## Commands
|
|
186
197
|
|
|
187
198
|
| Command | What it does |
|
|
@@ -256,14 +267,33 @@ defaults are Claude `native` and every other Provider `fragment`, and the
|
|
|
256
267
|
providers:
|
|
257
268
|
claude:
|
|
258
269
|
capability_tier: native
|
|
270
|
+
# Optional. Overrides the adapter's own tier-to-model table; name only the
|
|
271
|
+
# tiers you disagree about, and the rest keep the shipped defaults.
|
|
272
|
+
models:
|
|
273
|
+
deep: claude-opus-5
|
|
259
274
|
codex:
|
|
260
275
|
capability_tier: fragment
|
|
261
276
|
|
|
277
|
+
# Optional. A Role's two declared axes: which class of model runs it, and how
|
|
278
|
+
# hard that model thinks. Both default to the table in ADR-0004.
|
|
279
|
+
roles:
|
|
280
|
+
security:
|
|
281
|
+
tier: deep
|
|
282
|
+
effort: max
|
|
283
|
+
|
|
262
284
|
gates:
|
|
263
285
|
tests:
|
|
264
286
|
suite: pytest
|
|
265
287
|
```
|
|
266
288
|
|
|
289
|
+
There is no `roles.<name>.model` key, and naming one is an error rather than a
|
|
290
|
+
line that gets ignored. A Role declares a tier; the Provider maps that tier onto
|
|
291
|
+
a model. A model named per Role does not survive the next CLI release and does
|
|
292
|
+
not port to another Provider, which is what
|
|
293
|
+
[ADR-0004](docs/adr/0004-model-tiers-declared-not-named.md) exists to prevent --
|
|
294
|
+
override `providers.<name>.models.<tier>` if the mapping is what you disagree
|
|
295
|
+
with.
|
|
296
|
+
|
|
267
297
|
There is no `plugins:` key. Which Plugins answer for a repository is decided per
|
|
268
298
|
Run from the frozen plan's blast radius (ADR-0016), so a repository-level list
|
|
269
299
|
would be inert and misleading -- init prints what it detected instead. The file
|
|
@@ -313,10 +343,21 @@ code rather than the verdict about the old code.
|
|
|
313
343
|
|
|
314
344
|
Read [`CONTEXT.md`](CONTEXT.md) before writing anything, and [`docs/adr/`](docs/adr/) for the decisions that constrain it.
|
|
315
345
|
|
|
316
|
-
##
|
|
346
|
+
## Changing AgentForge
|
|
347
|
+
|
|
348
|
+
Clone it and install that clone editable. Never patch the installed copy.
|
|
317
349
|
|
|
318
350
|
```console
|
|
351
|
+
$ git clone https://github.com/yashmhatre/AgentForge.git
|
|
352
|
+
$ cd AgentForge
|
|
319
353
|
$ pip install -e ".[dev]"
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
Editing `site-packages/agentforge_framework/` appears to work and is the one change nothing will catch: the local install diverges from what ships, so the thing that works is not the thing anybody else gets, and the fix is lost the next time the package is upgraded. If a Run is failing badly enough that patching the install looks like the way forward, that is a bug worth filing — [#101](https://github.com/yashmhatre/AgentForge/issues/101) is what happened the last time somebody reached for it.
|
|
357
|
+
|
|
358
|
+
## Tests
|
|
359
|
+
|
|
360
|
+
```console
|
|
320
361
|
$ pytest
|
|
321
362
|
```
|
|
322
363
|
|
|
@@ -12,7 +12,7 @@ would be the most expensive default in the project.
|
|
|
12
12
|
|
|
13
13
|
from __future__ import annotations
|
|
14
14
|
|
|
15
|
-
from ..core.contracts import ModelTier, Role
|
|
15
|
+
from ..core.contracts import Effort, ModelTier, Role
|
|
16
16
|
from .architect import ARCHITECT, Architect
|
|
17
17
|
from .implementer import IMPLEMENTER, Implementer
|
|
18
18
|
from .orchestrator import ORCHESTRATOR
|
|
@@ -53,12 +53,30 @@ RUNNERS: dict[str, type] = {
|
|
|
53
53
|
KNOWN_TIERS: dict[str, ModelTier] = {
|
|
54
54
|
"orchestrator": ModelTier.DEEP,
|
|
55
55
|
"architect": ModelTier.DEEP,
|
|
56
|
-
"security": ModelTier.DEEP,
|
|
57
56
|
"reviewer": ModelTier.DEEP,
|
|
58
57
|
"implementer": ModelTier.STANDARD,
|
|
58
|
+
"security": ModelTier.STANDARD,
|
|
59
59
|
"tester": ModelTier.CHEAP,
|
|
60
60
|
}
|
|
61
61
|
|
|
62
|
+
#: The second axis, keyed the same way. A tier says which model runs; this says
|
|
63
|
+
#: how hard it thinks, and the two are set independently — see ADR-0004's
|
|
64
|
+
#: 2026-09-03 amendment for why they were ever one thing.
|
|
65
|
+
#:
|
|
66
|
+
#: The Security row is the reason the axes split. It reads `standard` above and
|
|
67
|
+
#: `high` here, which is not a demotion: ADR-0004 bought it `deep` because a
|
|
68
|
+
#: missed finding is silent, and that argument was about reasoning depth. It
|
|
69
|
+
#: keeps the depth — above the Implementer whose work it audits — and stops
|
|
70
|
+
#: buying a frontier model to get it.
|
|
71
|
+
KNOWN_EFFORTS: dict[str, Effort] = {
|
|
72
|
+
"orchestrator": Effort.HIGH,
|
|
73
|
+
"architect": Effort.HIGH,
|
|
74
|
+
"reviewer": Effort.HIGH,
|
|
75
|
+
"security": Effort.HIGH,
|
|
76
|
+
"implementer": Effort.MEDIUM,
|
|
77
|
+
"tester": Effort.MEDIUM,
|
|
78
|
+
}
|
|
79
|
+
|
|
62
80
|
|
|
63
81
|
class UnknownRole(LookupError):
|
|
64
82
|
"""A Roster names a Role this version of AgentForge cannot run."""
|
|
@@ -80,6 +98,7 @@ def resolve_role(name: str) -> Role:
|
|
|
80
98
|
__all__ = [
|
|
81
99
|
"ARCHITECT",
|
|
82
100
|
"IMPLEMENTER",
|
|
101
|
+
"KNOWN_EFFORTS",
|
|
83
102
|
"KNOWN_TIERS",
|
|
84
103
|
"ORCHESTRATOR",
|
|
85
104
|
"REVIEWER",
|
|
@@ -27,7 +27,7 @@ from __future__ import annotations
|
|
|
27
27
|
from pathlib import Path
|
|
28
28
|
|
|
29
29
|
from ..context.prompt import render_context_block
|
|
30
|
-
from ..core.contracts import AgentResult, ContextPack, ModelTier, Plan, Role
|
|
30
|
+
from ..core.contracts import AgentResult, ContextPack, Effort, ModelTier, Plan, Role
|
|
31
31
|
from ..core.plan_format import RESULT_CLOSE, RESULT_OPEN
|
|
32
32
|
from .implementer import render_steps
|
|
33
33
|
|
|
@@ -90,7 +90,12 @@ step and the mismatch in `summary`.\
|
|
|
90
90
|
"""
|
|
91
91
|
|
|
92
92
|
#: The Architect runs `deep`: a boundary in the wrong place outlives the Run.
|
|
93
|
-
ARCHITECT = Role(
|
|
93
|
+
ARCHITECT = Role(
|
|
94
|
+
name="architect",
|
|
95
|
+
tier=ModelTier.DEEP,
|
|
96
|
+
effort=Effort.HIGH,
|
|
97
|
+
instructions=INSTRUCTIONS,
|
|
98
|
+
)
|
|
94
99
|
|
|
95
100
|
|
|
96
101
|
def build_prompt(
|
|
@@ -19,7 +19,7 @@ from __future__ import annotations
|
|
|
19
19
|
from pathlib import Path
|
|
20
20
|
|
|
21
21
|
from ..context.prompt import render_context_block
|
|
22
|
-
from ..core.contracts import AgentResult, ContextPack, ModelTier, Plan, Role
|
|
22
|
+
from ..core.contracts import AgentResult, ContextPack, Effort, ModelTier, Plan, Role
|
|
23
23
|
from ..core.plan_format import RESULT_CLOSE, RESULT_OPEN
|
|
24
24
|
|
|
25
25
|
INSTRUCTIONS = """\
|
|
@@ -77,7 +77,12 @@ actually contains. Change no files when you escalate.\
|
|
|
77
77
|
"""
|
|
78
78
|
|
|
79
79
|
#: The Implementer runs at `standard`: it executes a plan it did not write.
|
|
80
|
-
IMPLEMENTER = Role(
|
|
80
|
+
IMPLEMENTER = Role(
|
|
81
|
+
name="implementer",
|
|
82
|
+
tier=ModelTier.STANDARD,
|
|
83
|
+
effort=Effort.MEDIUM,
|
|
84
|
+
instructions=INSTRUCTIONS,
|
|
85
|
+
)
|
|
81
86
|
|
|
82
87
|
|
|
83
88
|
def render_steps(plan: Plan) -> str:
|
|
@@ -38,6 +38,7 @@ from ..core.contracts import (
|
|
|
38
38
|
DEFAULT_WORKFLOW,
|
|
39
39
|
AgentResult,
|
|
40
40
|
ContextPack,
|
|
41
|
+
Effort,
|
|
41
42
|
ModelTier,
|
|
42
43
|
Outcome,
|
|
43
44
|
Plan,
|
|
@@ -239,6 +240,7 @@ repository itself uses them, and do not start a glossary during an interview.\
|
|
|
239
240
|
ORCHESTRATOR = Role(
|
|
240
241
|
name="orchestrator",
|
|
241
242
|
tier=ModelTier.DEEP,
|
|
243
|
+
effort=Effort.HIGH,
|
|
242
244
|
instructions=INSTRUCTIONS,
|
|
243
245
|
#: What a planning pass works with: the project's vocabulary, the synthesis
|
|
244
246
|
#: of a conversation into a spec, and the breakdown of that spec into work.
|
|
@@ -33,6 +33,7 @@ from ..context.prompt import render_context_block
|
|
|
33
33
|
from ..core.contracts import (
|
|
34
34
|
AgentResult,
|
|
35
35
|
ContextPack,
|
|
36
|
+
Effort,
|
|
36
37
|
ModelTier,
|
|
37
38
|
Outcome,
|
|
38
39
|
Plan,
|
|
@@ -158,6 +159,7 @@ End your reply with this block and nothing after it:
|
|
|
158
159
|
REVIEWER = Role(
|
|
159
160
|
name="reviewer",
|
|
160
161
|
tier=ModelTier.DEEP,
|
|
162
|
+
effort=Effort.HIGH,
|
|
161
163
|
instructions=INSTRUCTIONS,
|
|
162
164
|
skills=WRITING_SKILLS,
|
|
163
165
|
)
|
|
@@ -1,9 +1,15 @@
|
|
|
1
1
|
"""The Security Role: audits the change, and fixes nothing.
|
|
2
2
|
|
|
3
|
-
It runs at `
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
Role that cannot be run cheaply.
|
|
3
|
+
It runs at `standard` and thinks at `high` (ADR-0004, amended 2026-09-03). The
|
|
4
|
+
tier used to be `deep`, on the one argument in the table that was not about
|
|
5
|
+
cost: a missed finding is silent — nobody reviews the audit that did not happen
|
|
6
|
+
— so the Role that looks for what nobody asked about cannot be run cheaply.
|
|
7
|
+
|
|
8
|
+
That argument survives; it was only ever about reasoning. When effort became a
|
|
9
|
+
Role's own axis, the depth it was really buying became purchasable on its own,
|
|
10
|
+
and Security kept it — `high`, above the Implementer whose work it reads —
|
|
11
|
+
while the tier came down. If audits start missing what a human then finds at
|
|
12
|
+
Sign-off, the tier is the half to move back.
|
|
7
13
|
|
|
8
14
|
Its output is a list of Findings rather than prose, because the Gate downstream
|
|
9
15
|
of it has to tell "audited, nothing found" from "did not audit", and a paragraph
|
|
@@ -16,7 +22,7 @@ from __future__ import annotations
|
|
|
16
22
|
from pathlib import Path
|
|
17
23
|
|
|
18
24
|
from ..context.prompt import render_context_block
|
|
19
|
-
from ..core.contracts import AgentResult, ContextPack, ModelTier, Plan, Role
|
|
25
|
+
from ..core.contracts import AgentResult, ContextPack, Effort, ModelTier, Plan, Role
|
|
20
26
|
from ..core.plan_format import RESULT_CLOSE, RESULT_OPEN
|
|
21
27
|
from .implementer import render_steps
|
|
22
28
|
|
|
@@ -86,8 +92,14 @@ Use `"outcome": "escalated"` only when the Plan does not match the repository, \
|
|
|
86
92
|
or when you could not audit the change at all. Say which in `summary`.\
|
|
87
93
|
"""
|
|
88
94
|
|
|
89
|
-
#:
|
|
90
|
-
|
|
95
|
+
#: A finding nobody makes is a finding nobody reviews — bought as depth rather
|
|
96
|
+
#: than as model size. See the module docstring and ADR-0004.
|
|
97
|
+
SECURITY = Role(
|
|
98
|
+
name="security",
|
|
99
|
+
tier=ModelTier.STANDARD,
|
|
100
|
+
effort=Effort.HIGH,
|
|
101
|
+
instructions=INSTRUCTIONS,
|
|
102
|
+
)
|
|
91
103
|
|
|
92
104
|
|
|
93
105
|
def build_prompt(
|
{agentforge_framework-0.2.2 → agentforge_framework-0.2.4}/src/agentforge_framework/agents/tester.py
RENAMED
|
@@ -5,7 +5,15 @@ from __future__ import annotations
|
|
|
5
5
|
from pathlib import Path
|
|
6
6
|
|
|
7
7
|
from ..context.prompt import render_context_block
|
|
8
|
-
from ..core.contracts import
|
|
8
|
+
from ..core.contracts import (
|
|
9
|
+
AgentResult,
|
|
10
|
+
ContextPack,
|
|
11
|
+
Effort,
|
|
12
|
+
ModelTier,
|
|
13
|
+
Outcome,
|
|
14
|
+
Plan,
|
|
15
|
+
Role,
|
|
16
|
+
)
|
|
9
17
|
from ..core.plan_format import RESULT_CLOSE, RESULT_OPEN
|
|
10
18
|
from .implementer import render_steps
|
|
11
19
|
|
|
@@ -66,7 +74,12 @@ from verifying. Reading tests is not a substitute for running them.\
|
|
|
66
74
|
#: recorded in ADR-0004 — reasoning about an edge case nobody wrote a test for is
|
|
67
75
|
#: the part that gets worse here, and it is the part a human reads the findings
|
|
68
76
|
#: for anyway.
|
|
69
|
-
TESTER = Role(
|
|
77
|
+
TESTER = Role(
|
|
78
|
+
name="tester",
|
|
79
|
+
tier=ModelTier.CHEAP,
|
|
80
|
+
effort=Effort.MEDIUM,
|
|
81
|
+
instructions=INSTRUCTIONS,
|
|
82
|
+
)
|
|
70
83
|
|
|
71
84
|
|
|
72
85
|
def build_prompt(
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
"""Read project configuration without creating or changing it."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import shlex
|
|
6
|
+
from dataclasses import dataclass, field
|
|
7
|
+
from enum import StrEnum
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
|
|
10
|
+
import yaml
|
|
11
|
+
|
|
12
|
+
from .contracts import Effort, ModelTier
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class CapabilityTier(StrEnum):
|
|
16
|
+
"""How a Provider receives a Role's declared Vendored Skills."""
|
|
17
|
+
|
|
18
|
+
NATIVE = "native"
|
|
19
|
+
FRAGMENT = "fragment"
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
DEFAULT_CAPABILITIES = {
|
|
23
|
+
"claude": CapabilityTier.NATIVE,
|
|
24
|
+
"codex": CapabilityTier.FRAGMENT,
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
#: What a `tests` Gate runs when the project declares nothing. `pytest` rather
|
|
28
|
+
#: than `python -m pytest`, so that an environment without it is a binary the
|
|
29
|
+
#: Command Runner cannot find — which is the difference between a suite that
|
|
30
|
+
#: could not be run and one that ran and failed, and the Gate reports them
|
|
31
|
+
#: differently.
|
|
32
|
+
DEFAULT_TEST_SUITE = ("pytest",)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
@dataclass(frozen=True)
|
|
36
|
+
class Config:
|
|
37
|
+
"""The read-only project configuration the Workflow runtime reads."""
|
|
38
|
+
|
|
39
|
+
provider_capabilities: dict[str, CapabilityTier] = field(
|
|
40
|
+
default_factory=lambda: dict(DEFAULT_CAPABILITIES)
|
|
41
|
+
)
|
|
42
|
+
#: The argument vector the test-suite Gate runs. An argument vector, not a
|
|
43
|
+
#: Command in the glossary's sense: nothing infers anything from it.
|
|
44
|
+
test_suite: tuple[str, ...] = DEFAULT_TEST_SUITE
|
|
45
|
+
|
|
46
|
+
#: Whether the Context Pack comment publishes the symbols and import graph
|
|
47
|
+
#: it resolved, or only their counts. Off by default: the Issue already
|
|
48
|
+
#: carries the pack's file paths in the frozen Plan, and nothing else on it
|
|
49
|
+
#: carries private symbol names. A tracker whose audience matches the
|
|
50
|
+
#: code's turns this on. See ADR-0024.
|
|
51
|
+
publish_pack_inventory: bool = False
|
|
52
|
+
|
|
53
|
+
#: Per-Provider overrides of the adapter's tier-to-model table, keyed by
|
|
54
|
+
#: provider name and then by tier. ADR-0004 promised this in its first
|
|
55
|
+
#: paragraph and nothing read it until 2026-09-03. A tier absent here keeps
|
|
56
|
+
#: the adapter's default, so a project pins the one tier it disagrees about
|
|
57
|
+
#: rather than restating all three.
|
|
58
|
+
provider_models: dict[str, dict[ModelTier, str]] = field(default_factory=dict)
|
|
59
|
+
|
|
60
|
+
#: Per-Role overrides of the two declared axes. The Role still never names a
|
|
61
|
+
#: model — it names a tier, and the Provider maps that. `roles.x.model` is
|
|
62
|
+
#: deliberately not a key: a model named per Role does not survive a release
|
|
63
|
+
#: and does not port across Providers, which is the whole of ADR-0004.
|
|
64
|
+
role_tiers: dict[str, ModelTier] = field(default_factory=dict)
|
|
65
|
+
role_efforts: dict[str, Effort] = field(default_factory=dict)
|
|
66
|
+
|
|
67
|
+
def capability_for(self, provider: str) -> CapabilityTier:
|
|
68
|
+
return self.provider_capabilities.get(provider, CapabilityTier.FRAGMENT)
|
|
69
|
+
|
|
70
|
+
def model_for(self, provider: str, tier: ModelTier) -> str | None:
|
|
71
|
+
"""The configured model for a tier, or None to keep the adapter's own."""
|
|
72
|
+
return self.provider_models.get(provider, {}).get(tier)
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def load_config(root: Path | str) -> Config:
|
|
76
|
+
"""Read ``.agentforge/config.yaml``, or return documented defaults."""
|
|
77
|
+
path = Path(root) / ".agentforge" / "config.yaml"
|
|
78
|
+
if not path.is_file():
|
|
79
|
+
return Config()
|
|
80
|
+
|
|
81
|
+
data = yaml.safe_load(path.read_text(encoding="utf-8")) or {}
|
|
82
|
+
capabilities = dict(DEFAULT_CAPABILITIES)
|
|
83
|
+
models: dict[str, dict[ModelTier, str]] = {}
|
|
84
|
+
for name, provider in (data.get("providers") or {}).items():
|
|
85
|
+
if "capability_tier" in provider:
|
|
86
|
+
capabilities[str(name)] = CapabilityTier(provider["capability_tier"])
|
|
87
|
+
if provider.get("models"):
|
|
88
|
+
models[str(name)] = {
|
|
89
|
+
ModelTier(tier): str(slug) for tier, slug in provider["models"].items()
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
tiers, efforts = _role_overrides(data)
|
|
93
|
+
return Config(
|
|
94
|
+
provider_capabilities=capabilities,
|
|
95
|
+
test_suite=_test_suite(data),
|
|
96
|
+
publish_pack_inventory=bool(
|
|
97
|
+
(data.get("context") or {}).get("publish_inventory", False)
|
|
98
|
+
),
|
|
99
|
+
provider_models=models,
|
|
100
|
+
role_tiers=tiers,
|
|
101
|
+
role_efforts=efforts,
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def _role_overrides(data: dict) -> tuple[dict[str, ModelTier], dict[str, Effort]]:
|
|
106
|
+
"""`roles.<name>.tier` and `roles.<name>.effort`, the two declared axes.
|
|
107
|
+
|
|
108
|
+
A `model` key here is refused rather than ignored. Silently dropping it
|
|
109
|
+
would leave a project believing it had pinned a model per Role, which reads
|
|
110
|
+
as working right up until a Provider changes — and the whole reason
|
|
111
|
+
ADR-0004 gives a Role a tier is that the failure is otherwise invisible.
|
|
112
|
+
"""
|
|
113
|
+
tiers: dict[str, ModelTier] = {}
|
|
114
|
+
efforts: dict[str, Effort] = {}
|
|
115
|
+
for name, role in (data.get("roles") or {}).items():
|
|
116
|
+
if "model" in role:
|
|
117
|
+
raise ValueError(
|
|
118
|
+
f"`roles.{name}.model` names a model per Role, which ADR-0004 "
|
|
119
|
+
f"does not allow; set `roles.{name}.tier` and override "
|
|
120
|
+
f"`providers.<name>.models.<tier>` if the mapping is wrong"
|
|
121
|
+
)
|
|
122
|
+
if "tier" in role:
|
|
123
|
+
tiers[str(name)] = ModelTier(role["tier"])
|
|
124
|
+
if "effort" in role:
|
|
125
|
+
efforts[str(name)] = Effort(role["effort"])
|
|
126
|
+
return tiers, efforts
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def _test_suite(data: dict) -> tuple[str, ...]:
|
|
130
|
+
"""`gates.tests.suite`: what the test-suite Gate runs in this repository.
|
|
131
|
+
|
|
132
|
+
A string is split the way a shell would; a list is taken as written, which
|
|
133
|
+
is how a path with a space in it gets named. Nothing here consults the tree
|
|
134
|
+
— a project that runs `npm test` says so, rather than being guessed at.
|
|
135
|
+
|
|
136
|
+
ADR-0007 defers a general execution allowlist to M5. This is the bounded
|
|
137
|
+
case that ADR names as the appropriate thing to persist: one suite, declared
|
|
138
|
+
by the project, run by AgentForge itself rather than by a Role.
|
|
139
|
+
"""
|
|
140
|
+
value = ((data.get("gates") or {}).get("tests") or {}).get("suite")
|
|
141
|
+
if value is None:
|
|
142
|
+
return DEFAULT_TEST_SUITE
|
|
143
|
+
|
|
144
|
+
if isinstance(value, str):
|
|
145
|
+
parts = tuple(shlex.split(value))
|
|
146
|
+
elif isinstance(value, list):
|
|
147
|
+
parts = tuple(str(part) for part in value)
|
|
148
|
+
else:
|
|
149
|
+
raise TypeError(
|
|
150
|
+
f"`gates.tests.suite` must be a string or a list, not {type(value).__name__}"
|
|
151
|
+
)
|
|
152
|
+
|
|
153
|
+
if not parts:
|
|
154
|
+
raise ValueError("`gates.tests.suite` is empty; name the command that runs the suite")
|
|
155
|
+
return parts
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
__all__ = ["DEFAULT_TEST_SUITE", "CapabilityTier", "Config", "load_config"]
|
{agentforge_framework-0.2.2 → agentforge_framework-0.2.4}/src/agentforge_framework/core/contracts.py
RENAMED
|
@@ -45,6 +45,28 @@ class ModelTier(StrEnum):
|
|
|
45
45
|
CHEAP = "cheap"
|
|
46
46
|
|
|
47
47
|
|
|
48
|
+
class Effort(StrEnum):
|
|
49
|
+
"""How hard a Role thinks, named by intent and independent of its tier.
|
|
50
|
+
|
|
51
|
+
The second axis ADR-0004 originally folded into the first. A Model Tier
|
|
52
|
+
chooses which model runs; an Effort chooses how much reasoning it spends
|
|
53
|
+
getting there, and the two are not the same purchase -- the Security Role
|
|
54
|
+
is the case that proves it, auditing at `standard` and thinking at `high`.
|
|
55
|
+
|
|
56
|
+
The levels are the intersection of what the shipped adapters' CLIs accept:
|
|
57
|
+
`claude --effort` takes low through max, and every slug in a real codex
|
|
58
|
+
install takes the same five. `ultra` is offered by one codex model alone
|
|
59
|
+
and is deliberately absent -- a level a Role could declare and most
|
|
60
|
+
Providers could not honor is not an intent-named level.
|
|
61
|
+
"""
|
|
62
|
+
|
|
63
|
+
LOW = "low"
|
|
64
|
+
MEDIUM = "medium"
|
|
65
|
+
HIGH = "high"
|
|
66
|
+
XHIGH = "xhigh"
|
|
67
|
+
MAX = "max"
|
|
68
|
+
|
|
69
|
+
|
|
48
70
|
class Outcome(StrEnum):
|
|
49
71
|
"""How an Agent finished.
|
|
50
72
|
|
|
@@ -278,11 +300,25 @@ class Role:
|
|
|
278
300
|
tier: ModelTier
|
|
279
301
|
instructions: str = ""
|
|
280
302
|
skills: tuple[str, ...] = ()
|
|
303
|
+
#: How much reasoning the Role spends, independent of which model runs it.
|
|
304
|
+
#: `medium` rather than a tier-derived value: a Role that has not thought
|
|
305
|
+
#: about the question gets the level both CLIs already default most models
|
|
306
|
+
#: to, and the ones that have thought about it say so.
|
|
307
|
+
effort: Effort = Effort.MEDIUM
|
|
281
308
|
|
|
282
309
|
def at_tier(self, tier: ModelTier) -> Role:
|
|
283
310
|
"""The same Role with its tier overridden, per user request or config."""
|
|
284
311
|
return replace(self, tier=tier)
|
|
285
312
|
|
|
313
|
+
def at_effort(self, effort: Effort) -> Role:
|
|
314
|
+
"""The same Role with its effort overridden, per config.
|
|
315
|
+
|
|
316
|
+
Separate from `at_tier` because the axes move separately -- overriding
|
|
317
|
+
one has never implied anything about the other, which is the whole
|
|
318
|
+
reason there are two.
|
|
319
|
+
"""
|
|
320
|
+
return replace(self, effort=effort)
|
|
321
|
+
|
|
286
322
|
|
|
287
323
|
@dataclass(frozen=True)
|
|
288
324
|
class Roster:
|