agentframe-py 0.1.0__tar.gz → 0.2.0__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.
- {agentframe_py-0.1.0 → agentframe_py-0.2.0}/AGENTS.md +133 -29
- agentframe_py-0.2.0/CHANGELOG.md +195 -0
- agentframe_py-0.1.0/README.md → agentframe_py-0.2.0/PKG-INFO +130 -25
- agentframe_py-0.1.0/PKG-INFO → agentframe_py-0.2.0/README.md +94 -56
- {agentframe_py-0.1.0 → agentframe_py-0.2.0}/docs/README.md +4 -2
- {agentframe_py-0.1.0 → agentframe_py-0.2.0}/docs/api/README.md +12 -12
- {agentframe_py-0.1.0 → agentframe_py-0.2.0}/docs/api/agents.md +3 -2
- {agentframe_py-0.1.0 → agentframe_py-0.2.0}/docs/api/authorization.md +348 -9
- {agentframe_py-0.1.0 → agentframe_py-0.2.0}/docs/api/configuration.md +10 -7
- {agentframe_py-0.1.0 → agentframe_py-0.2.0}/docs/api/decorators.md +1 -1
- agentframe_py-0.2.0/docs/api/discovery.md +1095 -0
- {agentframe_py-0.1.0 → agentframe_py-0.2.0}/docs/api/hosting.md +207 -33
- {agentframe_py-0.1.0 → agentframe_py-0.2.0}/docs/api/runtime.md +81 -7
- {agentframe_py-0.1.0 → agentframe_py-0.2.0}/docs/design/base-classes.md +3 -5
- {agentframe_py-0.1.0 → agentframe_py-0.2.0}/docs/design/discovery.md +181 -109
- agentframe_py-0.2.0/docs/design/evaluation.md +487 -0
- {agentframe_py-0.1.0 → agentframe_py-0.2.0}/docs/design/mcp-skills.md +1 -1
- agentframe_py-0.2.0/docs/design/orchestration.md +579 -0
- {agentframe_py-0.1.0 → agentframe_py-0.2.0}/docs/design/tracing.md +23 -0
- {agentframe_py-0.1.0 → agentframe_py-0.2.0}/docs/guides/agent-examples.md +74 -21
- {agentframe_py-0.1.0 → agentframe_py-0.2.0}/docs/guides/annotations.md +1 -1
- {agentframe_py-0.1.0 → agentframe_py-0.2.0}/docs/guides/authorization.md +92 -5
- agentframe_py-0.2.0/docs/guides/development.md +214 -0
- {agentframe_py-0.1.0 → agentframe_py-0.2.0}/docs/guides/limits.md +73 -6
- {agentframe_py-0.1.0 → agentframe_py-0.2.0}/docs/guides/releasing.md +5 -4
- {agentframe_py-0.1.0 → agentframe_py-0.2.0}/docs/roadmap.md +120 -30
- agentframe_py-0.2.0/examples/multi_agent/__init__.py +1 -0
- agentframe_py-0.2.0/examples/multi_agent/agentframe.yaml +10 -0
- agentframe_py-0.2.0/examples/multi_agent/planner.py +129 -0
- agentframe_py-0.2.0/examples/multi_agent/research.py +108 -0
- {agentframe_py-0.1.0 → agentframe_py-0.2.0}/examples/smoke.py +45 -1
- {agentframe_py-0.1.0 → agentframe_py-0.2.0}/pyproject.toml +9 -2
- {agentframe_py-0.1.0 → agentframe_py-0.2.0}/src/agentframe/__init__.py +47 -3
- agentframe_py-0.2.0/src/agentframe/__main__.py +5 -0
- {agentframe_py-0.1.0 → agentframe_py-0.2.0}/src/agentframe/authoring/context.py +4 -0
- {agentframe_py-0.1.0 → agentframe_py-0.2.0}/src/agentframe/authoring/definitions.py +20 -7
- {agentframe_py-0.1.0 → agentframe_py-0.2.0}/src/agentframe/cli.py +73 -20
- agentframe_py-0.2.0/src/agentframe/compose.py +230 -0
- agentframe_py-0.2.0/src/agentframe/dev.py +180 -0
- agentframe_py-0.2.0/src/agentframe/dev_server.py +302 -0
- agentframe_py-0.2.0/src/agentframe/discovery/__init__.py +1 -0
- agentframe_py-0.2.0/src/agentframe/discovery/cards.py +181 -0
- agentframe_py-0.2.0/src/agentframe/discovery/directory.py +376 -0
- agentframe_py-0.2.0/src/agentframe/discovery/foundry.py +232 -0
- agentframe_py-0.2.0/src/agentframe/discovery/identity.py +317 -0
- agentframe_py-0.2.0/src/agentframe/discovery/registry.py +291 -0
- agentframe_py-0.2.0/src/agentframe/discovery/remote.py +840 -0
- {agentframe_py-0.1.0 → agentframe_py-0.2.0}/src/agentframe/errors.py +58 -0
- {agentframe_py-0.1.0 → agentframe_py-0.2.0}/src/agentframe/hosting/a2a_executor.py +161 -47
- {agentframe_py-0.1.0 → agentframe_py-0.2.0}/src/agentframe/hosting/app.py +129 -23
- {agentframe_py-0.1.0 → agentframe_py-0.2.0}/src/agentframe/hosting/card.py +9 -6
- {agentframe_py-0.1.0 → agentframe_py-0.2.0}/src/agentframe/hosting/host.py +48 -8
- agentframe_py-0.2.0/src/agentframe/hosting/skill_call.py +222 -0
- agentframe_py-0.2.0/src/agentframe/project.py +127 -0
- agentframe_py-0.2.0/src/agentframe/runtime/correlation.py +147 -0
- {agentframe_py-0.1.0 → agentframe_py-0.2.0}/src/agentframe/runtime/generation.py +16 -2
- {agentframe_py-0.1.0 → agentframe_py-0.2.0}/src/agentframe/runtime/limits.py +7 -0
- {agentframe_py-0.1.0 → agentframe_py-0.2.0}/src/agentframe/runtime/runtime.py +163 -8
- {agentframe_py-0.1.0 → agentframe_py-0.2.0}/src/agentframe/security/authentication.py +7 -1
- {agentframe_py-0.1.0 → agentframe_py-0.2.0}/src/agentframe/security/entra.py +109 -7
- agentframe_py-0.2.0/tests/integration/test_agentframe_dev.py +219 -0
- agentframe_py-0.2.0/tests/integration/test_compose_containers.py +131 -0
- agentframe_py-0.2.0/tests/integration/test_directory_localhost.py +56 -0
- agentframe_py-0.2.0/tests/integration/test_foundry_agents.py +136 -0
- agentframe_py-0.2.0/tests/integration/test_multi_agent_interop.py +283 -0
- agentframe_py-0.2.0/tests/integration/test_multi_agent_release.py +325 -0
- agentframe_py-0.2.0/tests/integration/test_on_behalf_of.py +390 -0
- {agentframe_py-0.1.0 → agentframe_py-0.2.0}/tests/integration/test_providers.py +37 -14
- agentframe_py-0.2.0/tests/integration/test_remote_agents.py +327 -0
- agentframe_py-0.2.0/tests/integration/test_remote_budgets.py +399 -0
- agentframe_py-0.2.0/tests/integration/test_remote_context.py +362 -0
- agentframe_py-0.2.0/tests/integration/test_remote_correlation.py +419 -0
- agentframe_py-0.2.0/tests/integration/test_remote_identity.py +190 -0
- {agentframe_py-0.1.0 → agentframe_py-0.2.0}/tests/support/a2a_client.py +23 -13
- agentframe_py-0.2.0/tests/support/a2a_peer.py +128 -0
- agentframe_py-0.2.0/tests/support/dev_targets.py +69 -0
- {agentframe_py-0.1.0 → agentframe_py-0.2.0}/tests/support/fake_entra.py +74 -1
- {agentframe_py-0.1.0 → agentframe_py-0.2.0}/tests/support/local_server.py +5 -2
- agentframe_py-0.2.0/tests/support/release_targets.py +113 -0
- agentframe_py-0.2.0/tests/unit/discovery/test_directory.py +546 -0
- agentframe_py-0.2.0/tests/unit/discovery/test_directory_find.py +220 -0
- agentframe_py-0.2.0/tests/unit/discovery/test_foundry_registry.py +414 -0
- agentframe_py-0.2.0/tests/unit/discovery/test_outbound_identity.py +148 -0
- agentframe_py-0.2.0/tests/unit/discovery/test_registry.py +294 -0
- agentframe_py-0.2.0/tests/unit/discovery/test_remote.py +1065 -0
- agentframe_py-0.2.0/tests/unit/golden/Dockerfile +9 -0
- agentframe_py-0.2.0/tests/unit/golden/compose.yaml +68 -0
- {agentframe_py-0.1.0 → agentframe_py-0.2.0}/tests/unit/hosting/test_admission.py +2 -1
- {agentframe_py-0.1.0 → agentframe_py-0.2.0}/tests/unit/hosting/test_app.py +29 -1
- agentframe_py-0.2.0/tests/unit/hosting/test_caller_context.py +458 -0
- {agentframe_py-0.1.0 → agentframe_py-0.2.0}/tests/unit/hosting/test_card.py +8 -1
- agentframe_py-0.2.0/tests/unit/runtime/test_correlation.py +106 -0
- {agentframe_py-0.1.0 → agentframe_py-0.2.0}/tests/unit/runtime/test_options.py +2 -0
- {agentframe_py-0.1.0 → agentframe_py-0.2.0}/tests/unit/security/test_entra.py +61 -0
- agentframe_py-0.2.0/tests/unit/test_compose.py +182 -0
- agentframe_py-0.2.0/tests/unit/test_dev.py +135 -0
- {agentframe_py-0.1.0 → agentframe_py-0.2.0}/tests/unit/test_package.py +1 -1
- agentframe_py-0.2.0/tests/unit/test_project.py +62 -0
- {agentframe_py-0.1.0 → agentframe_py-0.2.0}/uv.lock +63 -1
- agentframe_py-0.1.0/CHANGELOG.md +0 -48
- agentframe_py-0.1.0/docs/api/discovery.md +0 -760
- agentframe_py-0.1.0/docs/guides/development.md +0 -117
- {agentframe_py-0.1.0 → agentframe_py-0.2.0}/.github/actions/check-wheel/action.yml +0 -0
- {agentframe_py-0.1.0 → agentframe_py-0.2.0}/.github/workflows/ci.yml +0 -0
- {agentframe_py-0.1.0 → agentframe_py-0.2.0}/.github/workflows/foundry.yml +0 -0
- {agentframe_py-0.1.0 → agentframe_py-0.2.0}/.github/workflows/release.yml +0 -0
- {agentframe_py-0.1.0 → agentframe_py-0.2.0}/.gitignore +0 -0
- {agentframe_py-0.1.0 → agentframe_py-0.2.0}/.pre-commit-config.yaml +0 -0
- {agentframe_py-0.1.0 → agentframe_py-0.2.0}/.python-version +0 -0
- {agentframe_py-0.1.0 → agentframe_py-0.2.0}/LICENSE +0 -0
- {agentframe_py-0.1.0 → agentframe_py-0.2.0}/docs/api/models.md +0 -0
- {agentframe_py-0.1.0 → agentframe_py-0.2.0}/docs/api/streaming.md +0 -0
- {agentframe_py-0.1.0 → agentframe_py-0.2.0}/docs/design/prompt-safeguarding.md +0 -0
- {agentframe_py-0.1.0 → agentframe_py-0.2.0}/docs/design/streaming.md +0 -0
- {agentframe_py-0.1.0 → agentframe_py-0.2.0}/examples/__init__.py +0 -0
- {agentframe_py-0.1.0 → agentframe_py-0.2.0}/examples/assistant.py +0 -0
- {agentframe_py-0.1.0 → agentframe_py-0.2.0}/examples/echo.py +0 -0
- {agentframe_py-0.1.0 → agentframe_py-0.2.0}/examples/orders.py +0 -0
- {agentframe_py-0.1.0 → agentframe_py-0.2.0}/examples/routed_assistant.py +0 -0
- {agentframe_py-0.1.0 → agentframe_py-0.2.0}/src/agentframe/authoring/__init__.py +0 -0
- {agentframe_py-0.1.0 → agentframe_py-0.2.0}/src/agentframe/authoring/agent.py +0 -0
- {agentframe_py-0.1.0 → agentframe_py-0.2.0}/src/agentframe/authoring/decorators.py +0 -0
- {agentframe_py-0.1.0 → agentframe_py-0.2.0}/src/agentframe/authoring/services.py +0 -0
- {agentframe_py-0.1.0 → agentframe_py-0.2.0}/src/agentframe/hosting/__init__.py +0 -0
- {agentframe_py-0.1.0 → agentframe_py-0.2.0}/src/agentframe/hosting/admission.py +0 -0
- {agentframe_py-0.1.0 → agentframe_py-0.2.0}/src/agentframe/hosting/dedup.py +0 -0
- {agentframe_py-0.1.0 → agentframe_py-0.2.0}/src/agentframe/hosting/draining_server.py +0 -0
- {agentframe_py-0.1.0 → agentframe_py-0.2.0}/src/agentframe/hosting/lifecycle.py +0 -0
- {agentframe_py-0.1.0 → agentframe_py-0.2.0}/src/agentframe/hosting/model_router.py +0 -0
- {agentframe_py-0.1.0 → agentframe_py-0.2.0}/src/agentframe/hosting/request_size.py +0 -0
- {agentframe_py-0.1.0 → agentframe_py-0.2.0}/src/agentframe/hosting/routing.py +0 -0
- {agentframe_py-0.1.0 → agentframe_py-0.2.0}/src/agentframe/models/__init__.py +0 -0
- {agentframe_py-0.1.0 → agentframe_py-0.2.0}/src/agentframe/models/base.py +0 -0
- {agentframe_py-0.1.0 → agentframe_py-0.2.0}/src/agentframe/models/foundry.py +0 -0
- {agentframe_py-0.1.0 → agentframe_py-0.2.0}/src/agentframe/models/ollama.py +0 -0
- {agentframe_py-0.1.0 → agentframe_py-0.2.0}/src/agentframe/models/strict_schema.py +0 -0
- {agentframe_py-0.1.0 → agentframe_py-0.2.0}/src/agentframe/py.typed +0 -0
- {agentframe_py-0.1.0 → agentframe_py-0.2.0}/src/agentframe/runtime/__init__.py +0 -0
- {agentframe_py-0.1.0 → agentframe_py-0.2.0}/src/agentframe/runtime/options.py +0 -0
- {agentframe_py-0.1.0 → agentframe_py-0.2.0}/src/agentframe/runtime/tools.py +0 -0
- {agentframe_py-0.1.0 → agentframe_py-0.2.0}/src/agentframe/runtime/usage.py +0 -0
- {agentframe_py-0.1.0 → agentframe_py-0.2.0}/src/agentframe/security/__init__.py +0 -0
- {agentframe_py-0.1.0 → agentframe_py-0.2.0}/src/agentframe/security/authorization.py +0 -0
- {agentframe_py-0.1.0 → agentframe_py-0.2.0}/src/agentframe/security/execution_policy.py +0 -0
- {agentframe_py-0.1.0 → agentframe_py-0.2.0}/src/agentframe/security/principal.py +0 -0
- {agentframe_py-0.1.0 → agentframe_py-0.2.0}/src/agentframe/testing.py +0 -0
- {agentframe_py-0.1.0 → agentframe_py-0.2.0}/tests/conftest.py +0 -0
- {agentframe_py-0.1.0 → agentframe_py-0.2.0}/tests/integration/test_entra_interoperability.py +0 -0
- {agentframe_py-0.1.0 → agentframe_py-0.2.0}/tests/integration/test_example.py +0 -0
- {agentframe_py-0.1.0 → agentframe_py-0.2.0}/tests/integration/test_graceful_shutdown.py +0 -0
- {agentframe_py-0.1.0 → agentframe_py-0.2.0}/tests/integration/test_hardening.py +0 -0
- {agentframe_py-0.1.0 → agentframe_py-0.2.0}/tests/integration/test_interoperability.py +0 -0
- {agentframe_py-0.1.0 → agentframe_py-0.2.0}/tests/integration/test_routing_example.py +0 -0
- {agentframe_py-0.1.0 → agentframe_py-0.2.0}/tests/integration/test_smoke.py +0 -0
- {agentframe_py-0.1.0 → agentframe_py-0.2.0}/tests/integration/test_structured_example.py +0 -0
- {agentframe_py-0.1.0 → agentframe_py-0.2.0}/tests/integration/test_success_criterion.py +0 -0
- {agentframe_py-0.1.0 → agentframe_py-0.2.0}/tests/integration/test_tools_example.py +0 -0
- {agentframe_py-0.1.0 → agentframe_py-0.2.0}/tests/support/cli_targets.py +0 -0
- {agentframe_py-0.1.0 → agentframe_py-0.2.0}/tests/unit/authoring/test_decorators.py +0 -0
- {agentframe_py-0.1.0 → agentframe_py-0.2.0}/tests/unit/authoring/test_services.py +0 -0
- {agentframe_py-0.1.0 → agentframe_py-0.2.0}/tests/unit/authoring/test_tool_registration.py +0 -0
- {agentframe_py-0.1.0 → agentframe_py-0.2.0}/tests/unit/authoring/test_tools.py +0 -0
- {agentframe_py-0.1.0 → agentframe_py-0.2.0}/tests/unit/hosting/test_a2a_binding.py +0 -0
- {agentframe_py-0.1.0 → agentframe_py-0.2.0}/tests/unit/hosting/test_a2a_executor.py +0 -0
- {agentframe_py-0.1.0 → agentframe_py-0.2.0}/tests/unit/hosting/test_dedup.py +0 -0
- {agentframe_py-0.1.0 → agentframe_py-0.2.0}/tests/unit/hosting/test_limits_ceiling.py +0 -0
- {agentframe_py-0.1.0 → agentframe_py-0.2.0}/tests/unit/hosting/test_model_router.py +0 -0
- {agentframe_py-0.1.0 → agentframe_py-0.2.0}/tests/unit/hosting/test_routing.py +0 -0
- {agentframe_py-0.1.0 → agentframe_py-0.2.0}/tests/unit/hosting/test_shutdown.py +0 -0
- {agentframe_py-0.1.0 → agentframe_py-0.2.0}/tests/unit/hosting/test_size_limits.py +0 -0
- {agentframe_py-0.1.0 → agentframe_py-0.2.0}/tests/unit/hosting/test_task_operations.py +0 -0
- {agentframe_py-0.1.0 → agentframe_py-0.2.0}/tests/unit/models/test_foundry.py +0 -0
- {agentframe_py-0.1.0 → agentframe_py-0.2.0}/tests/unit/models/test_models.py +0 -0
- {agentframe_py-0.1.0 → agentframe_py-0.2.0}/tests/unit/models/test_ollama.py +0 -0
- {agentframe_py-0.1.0 → agentframe_py-0.2.0}/tests/unit/models/test_strict_schema.py +0 -0
- {agentframe_py-0.1.0 → agentframe_py-0.2.0}/tests/unit/runtime/test_generation.py +0 -0
- {agentframe_py-0.1.0 → agentframe_py-0.2.0}/tests/unit/runtime/test_limits.py +0 -0
- {agentframe_py-0.1.0 → agentframe_py-0.2.0}/tests/unit/runtime/test_retries.py +0 -0
- {agentframe_py-0.1.0 → agentframe_py-0.2.0}/tests/unit/runtime/test_runtime.py +0 -0
- {agentframe_py-0.1.0 → agentframe_py-0.2.0}/tests/unit/runtime/test_structured_output.py +0 -0
- {agentframe_py-0.1.0 → agentframe_py-0.2.0}/tests/unit/runtime/test_tool_loop.py +0 -0
- {agentframe_py-0.1.0 → agentframe_py-0.2.0}/tests/unit/runtime/test_tool_replay.py +0 -0
- {agentframe_py-0.1.0 → agentframe_py-0.2.0}/tests/unit/runtime/test_validation.py +0 -0
- {agentframe_py-0.1.0 → agentframe_py-0.2.0}/tests/unit/security/test_authentication.py +0 -0
- {agentframe_py-0.1.0 → agentframe_py-0.2.0}/tests/unit/security/test_authorization.py +0 -0
- {agentframe_py-0.1.0 → agentframe_py-0.2.0}/tests/unit/security/test_execution_policy.py +0 -0
- {agentframe_py-0.1.0 → agentframe_py-0.2.0}/tests/unit/security/test_tool_authorization.py +0 -0
- {agentframe_py-0.1.0 → agentframe_py-0.2.0}/tests/unit/test_cli.py +0 -0
- {agentframe_py-0.1.0 → agentframe_py-0.2.0}/tests/unit/test_testing.py +0 -0
|
@@ -12,13 +12,16 @@ release (M8+), not an optional add-on or a hosting platform.
|
|
|
12
12
|
|
|
13
13
|
This repository contains the M0 development tooling, M1 model-free
|
|
14
14
|
authoring/local `Runtime`, M2 native A2A hosting, M3 Ollama/Foundry text
|
|
15
|
-
generation, M4 typed structured outputs, M5 bounded tool calling
|
|
16
|
-
opt-in model routing alongside design documentation.
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
15
|
+
generation, M4 typed structured outputs, M5 bounded tool calling, M6 built-in
|
|
16
|
+
opt-in model routing and M7 hardening alongside design documentation. Version
|
|
17
|
+
0.1.0, the first usable single-agent release, is published on PyPI as
|
|
18
|
+
`agentframe-py` (import `agentframe`). Pre-1.0 there is no stable API. The
|
|
19
|
+
M8+ multi-agent stories (#83-#97) are complete and form the 0.2.0
|
|
20
|
+
multi-agent release; later features (MCP, telemetry, streaming) are not implemented. Keep design API examples beyond
|
|
21
|
+
the implemented surface labeled as unimplemented; explicitly approved
|
|
22
|
+
names/defaults are decisions, not still-unresolved recommendations. Do not
|
|
23
|
+
scaffold implementation or add dependencies merely to make design snippets
|
|
24
|
+
executable.
|
|
22
25
|
|
|
23
26
|
## Code quality and readability
|
|
24
27
|
|
|
@@ -85,12 +88,13 @@ Folders stay one level deep. Put new code in the group that owns the concept:
|
|
|
85
88
|
| Path | Contents |
|
|
86
89
|
| --- | --- |
|
|
87
90
|
| `src/agentframe/__init__.py` | Public API re-exports; every public name is importable from `agentframe`. |
|
|
88
|
-
| `src/agentframe/` (root) | `errors.py`, `testing.py` (`ScriptedModel`), `cli.py` (`agentframe run`). |
|
|
91
|
+
| `src/agentframe/` (root) | `errors.py`, `testing.py` (`ScriptedModel`), `cli.py` (`agentframe run` / `dev` / `compose`), `project.py` (`agentframe.yaml`), `dev.py` (`dev_settings()`), `dev_server.py` (`agentframe dev` supervisor), `compose.py` (`agentframe compose` generator). |
|
|
89
92
|
| `src/agentframe/authoring/` | `Agent`, `@agent` / `@skill`, definitions, `RunContext`, `Inject` / `Services`. |
|
|
90
93
|
| `src/agentframe/runtime/` | `Runtime`, generation, `RunLimits`, `RunOptions`, `Usage`. |
|
|
91
94
|
| `src/agentframe/models/` | `base.py` model protocol and settings; provider adapters `ollama.py`, `foundry.py`; `strict_schema.py` (OpenAI strict JSON-schema converter). |
|
|
92
95
|
| `src/agentframe/hosting/` | `AgentApp`, `HostConfig`, Agent Card, A2A executor, routing. |
|
|
93
96
|
| `src/agentframe/security/` | `Principal`, authentication, authorization. |
|
|
97
|
+
| `src/agentframe/discovery/` | `AgentRegistry`, `AgentReference`, `AgentQuery`, file and environment registries (M8+). |
|
|
94
98
|
| `tests/unit/<group>/` | Unit tests mirroring the source groups; root-module tests in `tests/unit/`. |
|
|
95
99
|
| `tests/integration/` | Interoperability, real providers, success criterion, README example. |
|
|
96
100
|
| `tests/support/` | Shared helpers, on pytest's `pythonpath` (`from local_server import ...`). |
|
|
@@ -145,6 +149,17 @@ AGENTFRAME_TEST_FOUNDRY=1 AGENTFRAME_FOUNDRY_ENDPOINT=<base-url> \
|
|
|
145
149
|
|
|
146
150
|
An enabled provider with a missing required variable fails rather than skips.
|
|
147
151
|
|
|
152
|
+
`tests/integration/test_foundry_agents.py` has opt-in `foundry` tests for
|
|
153
|
+
`FoundryAgentRegistry` (#86): it lists a real project with one A2A-enabled
|
|
154
|
+
agent and fetches that agent's card, read-only. It runs only with
|
|
155
|
+
`AGENTFRAME_TEST_FOUNDRY_AGENTS=1`, `AGENTFRAME_FOUNDRY_PROJECT_ENDPOINT`
|
|
156
|
+
(`https://<account>.services.ai.azure.com/api/projects/<project>`) and
|
|
157
|
+
`AGENTFRAME_FOUNDRY_A2A_AGENT` (the agent's name), after `az login`. Setup and
|
|
158
|
+
roles are in `docs/api/discovery.md#foundryagentregistry`. The same agent is the
|
|
159
|
+
real non-AgentFrame server for #94: one test `ask`s it once, another checks
|
|
160
|
+
`call` is refused before sending. In CI, `test_multi_agent_interop.py` proves
|
|
161
|
+
both directions against a plain SDK peer (`tests/support/a2a_peer.py`).
|
|
162
|
+
|
|
148
163
|
`tests/integration/test_entra_interoperability.py` has one opt-in `entra` test
|
|
149
164
|
using a real token from `az account get-access-token`, enabled with
|
|
150
165
|
`AGENTFRAME_TEST_ENTRA=1`, `AGENTFRAME_ENTRA_TENANT_ID` and
|
|
@@ -248,6 +263,20 @@ downgrade to prompts. Implemented wire format (M2 #5): activate the card's
|
|
|
248
263
|
skill-call URI via `A2A-Extensions` (echoed in the response), skill ID in message
|
|
249
264
|
metadata `agentframe.skill`, exactly one JSON-object data part; malformed ->
|
|
250
265
|
JSON-RPC InvalidParams, no task. Not a standard A2A `skill` field.
|
|
266
|
+
Implemented (#89): the same URI carries caller context; an activated message
|
|
267
|
+
without `agentframe.skill` is free text. Cards list `params.context`
|
|
268
|
+
(`deadline_ms`, `hops`, `max_total_tokens` since #90); senders send only
|
|
269
|
+
listed fields (0.1.0 lists none).
|
|
270
|
+
Receiver timeout = min(own, `agentframe.deadline_ms`); `RunContext.hops`;
|
|
271
|
+
`RunLimits.max_remote_hops=5`: caller fails before sending, deeper request
|
|
272
|
+
REJECTED with base `LimitExceededError` message; depth is cooperative.
|
|
273
|
+
Implemented (#91): context fields `root_invocation_id`/`parent_invocation_id`
|
|
274
|
+
(lowercase UUID strings; invalid -> ignored, new root, never echoed or an
|
|
275
|
+
error). Hosted runs log `Run started`/`Run completed` (INFO) with `extra`
|
|
276
|
+
`agent`, `invocation_id`, `root_invocation_id`, `parent_invocation_id`; no
|
|
277
|
+
log format set; root not exposed on `RunContext`. Valid W3C `traceparent`
|
|
278
|
+
(version 00) plus valid `tracestate` forwarded unchanged as headers to every
|
|
279
|
+
peer, invalid dropped; no spans or trace IDs created. Never authentication.
|
|
251
280
|
Free-text/extension-unaware callers use custom or opt-in model routing plus
|
|
252
281
|
validated extraction. Ambiguity/missing arguments asks back through
|
|
253
282
|
`TASK_STATE_INPUT_REQUIRED`, never guesses; the caller answers in the same task,
|
|
@@ -400,9 +429,11 @@ call specialists over A2A, not a workflow/graph engine.
|
|
|
400
429
|
`AgentRegistry` is a small read-only abstract base with proposed async
|
|
401
430
|
`get(agent_id) -> AgentReference` and `list(query: AgentQuery) ->
|
|
402
431
|
list[AgentReference]`; pagination is unresolved. Environment/file adapters read
|
|
403
|
-
explicit sources. `
|
|
404
|
-
|
|
405
|
-
|
|
432
|
+
explicit sources. Implemented (#83): `AgentReference(name, card_url)` (the Agent
|
|
433
|
+
Card URL), `AgentQuery(names=...)`; `FileAgentRegistry("agents.yaml")` reads a
|
|
434
|
+
YAML `name`/`url` list on each call (1 MiB, `safe_load`, PyYAML core dep);
|
|
435
|
+
`EnvironmentAgentRegistry("AGENTFRAME_AGENTS")` reads comma-separated
|
|
436
|
+
`name=url` pairs. Config problems and unknown names raise `DefinitionError`.
|
|
406
437
|
`FoundryAgentRegistry` uses the chosen Foundry project agents API through
|
|
407
438
|
`azure-ai-projects` 2.x (2.10.0 observed), project endpoint
|
|
408
439
|
`https://{account}.services.ai.azure.com/api/projects/{project}`. Incoming A2A
|
|
@@ -416,15 +447,26 @@ Foundry A2A connections are a possible additional reference source, not decided.
|
|
|
416
447
|
Model deployments are not agent endpoints; no AgentFrame-to-Foundry deployment
|
|
417
448
|
or tested registry integration is claimed.
|
|
418
449
|
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
450
|
+
Implemented (#84): `AgentDirectory(sources=[...], card_ttl_seconds=300,
|
|
451
|
+
allow_stale=False, allow_http_hosts=(), http_client=None)` owns SDK card
|
|
452
|
+
fetch/validation/cache/refresh via an internal fetcher (no public
|
|
453
|
+
`AgentCardResolver`/`CardCacheConfig`). Sources read on every `get`; cards
|
|
454
|
+
cached per card URL, refetched when stale; failed refresh raises unless
|
|
455
|
+
`allow_stale=True` (`DiscoveredAgent.stale`, warning). https only except
|
|
456
|
+
loopback/`allow_http_hosts`, no redirects, 10 s, 1 MiB, interface URL same
|
|
457
|
+
origin as the registry `card_url`; no caller credentials. Remote failures
|
|
458
|
+
raise `RemoteAgentError`; duplicate names across sources `DefinitionError`.
|
|
459
|
+
No construction I/O or background tasks; closes only its own client.
|
|
424
460
|
Compose `AgentDirectory(sources=[...])`, inject via `AgentApp(directory=...)`,
|
|
425
|
-
and validate configured `Remote["name"]` bindings at startup.
|
|
461
|
+
and validate configured `Remote[Literal["name"]]` bindings at startup.
|
|
426
462
|
`directory.find(skill="find_sources")` / `find(tags=["research"])` returns
|
|
427
463
|
matching native cards from bounded validated enrichment, not assumed reference fields.
|
|
464
|
+
Implemented (#85): `find(*, skill=None, tags=())`, at least one filter; skill
|
|
465
|
+
ID and all-of tags must match on the same card skill; every source listed,
|
|
466
|
+
cards resolved through the cache, at most 8 fetches at a time; any source/card
|
|
467
|
+
failure raises (honouring `allow_stale`); source order; no pagination (small
|
|
468
|
+
curated sources). Found cards are not callable; orchestrators call configured
|
|
469
|
+
remotes, dynamic invocation is deferred.
|
|
428
470
|
Inject directories as application dependencies;
|
|
429
471
|
caller-owned resources stay caller-owned unless explicitly transferred. No
|
|
430
472
|
registration writes, deploy/start/stop/delete, leases/federation, or A2A task
|
|
@@ -440,11 +482,47 @@ references/cards/source files; enforce application-selected outbound auth and
|
|
|
440
482
|
destination/redirect policy, bounded reads/deadlines/cancellation and safe
|
|
441
483
|
telemetry. Discovery does not delegate or auto-expose skills as tools. A separate
|
|
442
484
|
`RemoteAgentClient` may remain underlying/explicit client with construction open.
|
|
443
|
-
Author skills use `Remote["name"]` (excluded from external schemas),
|
|
485
|
+
Author skills use `Remote[Literal["name"]]` (excluded from external schemas),
|
|
444
486
|
`call("skill", ..., output=list[Source], max_total_tokens=...)` for structured
|
|
445
487
|
A2A, or `ask(text)` for any compatible peer. Both wait within caller deadline;
|
|
446
488
|
remote input-required raises `ClarificationNeededError` with question, no
|
|
447
|
-
automatic back-and-forth.
|
|
489
|
+
automatic back-and-forth. Implemented (#87): `class Remote[N: str]` read from the
|
|
490
|
+
`Literal` (alias `type Research = Remote[Literal["research-agent"]]`; string form
|
|
491
|
+
fails mypy), `Runtime(directory=)`/`AgentApp(directory=)` (app-owned; no
|
|
492
|
+
directory -> construction `DefinitionError`), `Runtime.validate_remotes` at
|
|
493
|
+
startup, bound per run inside its deadline. `ask` sends via the SDK
|
|
494
|
+
`JsonRpcTransport` with the directory client to `interface_url` only,
|
|
495
|
+
`return_immediately` then `GetTask` polling (0.25 s -> 1 s), per-request
|
|
496
|
+
`A2A-Version`/timeout headers; deadline or cancellation sends best-effort
|
|
497
|
+
`CancelTask`.
|
|
498
|
+
Implemented (#88): `call(skill, /, *, output=None, **arguments)` checks the
|
|
499
|
+
card declares the skill-call extension and skill (else `RemoteAgentError`, no
|
|
500
|
+
send, no `ask` fallback), sends the M2 wire format with per-request
|
|
501
|
+
`A2A-Extensions`, reuses `ask`'s wait/cancel, decodes text or `{"value": ...}`
|
|
502
|
+
and validates `output` (`OutputValidationError`, no remote data).
|
|
503
|
+
Implemented (#90): `ask`/`call` take `max_total_tokens=` (default: all the run
|
|
504
|
+
has left), held atomically in the run's `RunAccounting` before sending and sent
|
|
505
|
+
as `agentframe.max_total_tokens` only to peers listing it in `params.context`
|
|
506
|
+
(older receivers reject unknown keys). Receiver uses min(own, incoming). The
|
|
507
|
+
task's usage snapshot `total_tokens` replaces the hold, even if higher;
|
|
508
|
+
non-advertising peers hold nothing and make usage unknown (`None`). Over the
|
|
509
|
+
remaining budget -> `TokenLimitExceededError` before sending (default-ceiling
|
|
510
|
+
message says to pass `max_total_tokens` per call for fan-out).
|
|
511
|
+
Implemented (#92): `remotes={name: Service(credential=...)}` on
|
|
512
|
+
`Runtime`/`AgentApp` (same rule as `services=`); `OutboundCredential` protocol
|
|
513
|
+
`async headers()` + `satisfies(scheme)`, built-ins `APIKeyCredential(key,
|
|
514
|
+
header="X-API-Key")`, `EntraAppCredential(credential, scope=...)`. Headers per
|
|
515
|
+
request (send, polls, cancel) to `interface_url` only; no redirects. Startup:
|
|
516
|
+
card security must be met by the entry, else `DefinitionError`; open cards need
|
|
517
|
+
none. Implemented (#93): `OnBehalfOf(scope=...)` entries; credential on
|
|
518
|
+
inbound `EntraIDAuth(client_id=audience, client_credential=secret|cert bytes|
|
|
519
|
+
assertion func)`; delegated tokens (`scp`, not `idtyp=app`) kept privately as
|
|
520
|
+
`VerifiedCaller.user_assertion`; one azure `OnBehalfOfCredential` per run.
|
|
521
|
+
Non-Entra host/no credential -> startup `DefinitionError`; no user (app-only,
|
|
522
|
+
local run) -> `PermissionDeniedError`; failed exchange -> `AuthenticationError`;
|
|
523
|
+
never a `Service` fallback. Opt-in `entra` OBO test uses two registrations.
|
|
524
|
+
`ClarificationNeededError(question, task_id, context_id)` fails a hosted
|
|
525
|
+
orchestrator task. See `docs/design/discovery.md` and `docs/api/discovery.md`.
|
|
448
526
|
|
|
449
527
|
Plan cross-hop verified identity/delegation without authority amplification,
|
|
450
528
|
permissions, deadlines, remaining token allowance and trace/correlation;
|
|
@@ -467,11 +545,32 @@ specialist with propagated context, and non-AgentFrame A2A client/server interop
|
|
|
467
545
|
Development support starts with running N agents locally, then container
|
|
468
546
|
packaging; Foundry/Kubernetes templates are later. M2 `agentframe run module:create_app`
|
|
469
547
|
serves the app's own configured `AgentApp` (`--dev module:Class` is localhost-only
|
|
470
|
-
anonymous; Uvicorn is the `server` extra).
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
548
|
+
anonymous; Uvicorn is the `server` extra). Implemented (#95): `agentframe dev`
|
|
549
|
+
reads agentframe.yaml (`agents: {name: {app: module:create_app, port}}`,
|
|
550
|
+
`project.py`), starts each as `python -m agentframe run` on 127.0.0.1 in file
|
|
551
|
+
order after the previous `/ready`, writes `.agentframe/agents.yaml` (name/url
|
|
552
|
+
only, self-gitignored), generates one random key per agent per session (an
|
|
553
|
+
agent calls others with its own key, verified as its name), passes keys only
|
|
554
|
+
via child env (`AGENTFRAME_DEV`, `AGENTFRAME_PUBLIC_URL`,
|
|
555
|
+
`AGENTFRAME_DEV_REGISTRY`, `AGENTFRAME_DEV_API_KEYS`,
|
|
556
|
+
`AGENTFRAME_DEV_REMOTE_KEYS`), never disk/logs; optional
|
|
557
|
+
`AGENTFRAME_DEV_CLIENT_KEY` is caller `developer`. Factories call
|
|
558
|
+
`dev_settings()` (refuses without `AGENTFRAME_DEV=1`; directory has its own
|
|
559
|
+
http client; reads `AGENTFRAME_ALLOW_HTTP_HOSTS`). Output prefixed
|
|
560
|
+
`[agent-name]`, agents log INFO; failures stop all and exit 1; Ctrl+C sends
|
|
561
|
+
one SIGTERM (children in own session), kills after 35 s; a client key with
|
|
562
|
+
`,`/`=` is refused. Implemented (#96): `agentframe compose [--config]
|
|
563
|
+
[--force]` (`compose.py`) writes development `compose.yaml` (one service per
|
|
564
|
+
agent, `agentframe run APP --host 0.0.0.0 --port P`, `127.0.0.1:P:P`, `/ready`
|
|
565
|
+
healthcheck, depends on earlier agents healthy; env = dev contract with
|
|
566
|
+
`AGENTFRAME_PUBLIC_URL=http://name:P`, `AGENTFRAME_AGENTS` (read by
|
|
567
|
+
`dev_settings()` as an `EnvironmentAgentRegistry`), `AGENTFRAME_DEV=1`,
|
|
568
|
+
`AGENTFRAME_ALLOW_HTTP_HOSTS`=service names; keys only
|
|
569
|
+
`${NAME_API_KEY:?...}`/`${AGENTFRAME_DEV_CLIENT_KEY:?...}` from user `.env`)
|
|
570
|
+
and, only if missing, a `Dockerfile` (digest-pinned `python:3.13-slim`,
|
|
571
|
+
`dist/*.whl`, `USER 10001:10001`); `--force` overwrites only `compose.yaml`.
|
|
572
|
+
Golden files in `tests/unit/golden/`; opt-in `docker` test
|
|
573
|
+
(`AGENTFRAME_TEST_DOCKER=1`, skipped without a running Docker).
|
|
475
574
|
No hosting platform is implied. See `docs/guides/development.md`.
|
|
476
575
|
|
|
477
576
|
### Initial release boundaries
|
|
@@ -504,11 +603,15 @@ A2A failures use safe failed/rejected states as appropriate; no raw provider
|
|
|
504
603
|
exception, stack trace or secret goes to callers. Log diagnostics with redaction:
|
|
505
604
|
failed runs log ID, class and safe message; skill/tool/router tracebacks stay in
|
|
506
605
|
operator logs with the ID, so authors keep secrets out of exception messages.
|
|
507
|
-
`tests/integration/test_hardening.py` is the M7 end-to-end acceptance suite
|
|
606
|
+
`tests/integration/test_hardening.py` is the M7 end-to-end acceptance suite;
|
|
607
|
+
`tests/integration/test_multi_agent_release.py` is M8+'s, running
|
|
608
|
+
`examples/multi_agent` as separate `agentframe run` processes.
|
|
508
609
|
Network host examples must include clearly labeled application-supplied
|
|
509
610
|
development authentication/authorization placeholders where appropriate;
|
|
510
611
|
loopback/omission is not access policy or a production-safe default.
|
|
511
|
-
M8+ adds `
|
|
612
|
+
M8+ adds `RemoteAgentError` (#84; failures caused by another agent, optional
|
|
613
|
+
`task_id`/`task_state`) and `ClarificationNeededError` under
|
|
614
|
+
`AgentFrameError`; local client
|
|
512
615
|
input-required question is not a failed remote task or durable resumption.
|
|
513
616
|
|
|
514
617
|
Use explicit model selection, finite execution limits, validated inputs/outputs,
|
|
@@ -572,9 +675,10 @@ Read `README.md` and `docs/README.md` first. Keep `docs/roadmap.md`, the
|
|
|
572
675
|
guides (`docs/guides/annotations.md`, `agent-examples.md`, `limits.md`,
|
|
573
676
|
`authorization.md`, `development.md`) and the design docs
|
|
574
677
|
(`docs/design/base-classes.md`, `tracing.md`, `mcp-skills.md`,
|
|
575
|
-
`prompt-safeguarding.md`, `discovery.md`, `streaming.md`
|
|
576
|
-
discovery/streaming API pages and
|
|
577
|
-
confirmed product direction and
|
|
678
|
+
`prompt-safeguarding.md`, `discovery.md`, `streaming.md`, `evaluation.md`,
|
|
679
|
+
`orchestration.md`) consistent, including discovery/streaming API pages and
|
|
680
|
+
navigation. Preserve the distinction between confirmed product direction and
|
|
681
|
+
illustrative API details.
|
|
578
682
|
|
|
579
683
|
Maintain `docs/api/README.md` and its linked surface reference pages alongside
|
|
580
684
|
those design documents. Give every proposed decorator, class, and known public
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to AgentFrame are listed here. The format follows
|
|
4
|
+
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
|
5
|
+
|
|
6
|
+
Pre-1.0: public APIs may change in minor releases; changes are listed in the
|
|
7
|
+
changelog.
|
|
8
|
+
|
|
9
|
+
## [Unreleased]
|
|
10
|
+
|
|
11
|
+
## [0.2.0] - 2026-11-14
|
|
12
|
+
|
|
13
|
+
The multi-agent release (M8+): independently deployed AgentFrame agents find
|
|
14
|
+
each other through registries and an `AgentDirectory`, and orchestrator skills
|
|
15
|
+
call them over A2A with `Remote.call` and `Remote.ask`, carrying deadlines,
|
|
16
|
+
token budgets, hop limits, correlation and caller identity across hops.
|
|
17
|
+
`agentframe dev` and `agentframe compose` run several agents locally or in
|
|
18
|
+
containers. Calls interoperate with non-AgentFrame A2A agents, including
|
|
19
|
+
Microsoft Foundry agents.
|
|
20
|
+
|
|
21
|
+
### Added
|
|
22
|
+
|
|
23
|
+
- **Multi-agent (M8+):** `examples/multi_agent`, a planner orchestrator and a
|
|
24
|
+
research agent with an `agentframe.yaml`. The planner finds the research
|
|
25
|
+
agent with `directory.find`, then fans out a structured `call` and a
|
|
26
|
+
free-text `ask` per topic in one `asyncio.TaskGroup`, each with its own
|
|
27
|
+
`max_total_tokens`. The release acceptance test runs both as separate
|
|
28
|
+
`agentframe run` processes; the examples smoke runner checks the example.
|
|
29
|
+
- **Multi-agent (M8+):** `agentframe compose` writes a development
|
|
30
|
+
`compose.yaml`, one container per agent of `agentframe.yaml`, and, if
|
|
31
|
+
missing, a `Dockerfile` template (`python:3.13-slim` pinned by digest,
|
|
32
|
+
non-root, installs the project's wheels from `dist/`). Services bind
|
|
33
|
+
`0.0.0.0` inside their container, publish on `127.0.0.1` only, check
|
|
34
|
+
`/ready` and get the `agentframe dev` environment with service-name URLs
|
|
35
|
+
and the explicit `AGENTFRAME_DEV=1` / `AGENTFRAME_ALLOW_HTTP_HOSTS` opt-in.
|
|
36
|
+
Keys appear only as `${VAR}` references to a `.env` file the user creates.
|
|
37
|
+
Existing files are never overwritten without `--force`, and a `Dockerfile`
|
|
38
|
+
never. `dev_settings()` now also reads an `AGENTFRAME_AGENTS` registry, and
|
|
39
|
+
`agentframe dev` refuses an `AGENTFRAME_DEV_CLIENT_KEY` containing `,` or
|
|
40
|
+
`=` without showing it.
|
|
41
|
+
- **Multi-agent (M8+):** `agentframe dev` runs every agent listed in
|
|
42
|
+
`agentframe.yaml` (`agents: {name: {app: module:create_app, port: int}}`)
|
|
43
|
+
as its own `agentframe run` process on `127.0.0.1`, wired together. It
|
|
44
|
+
checks ports first, starts agents in file order once each is ready, writes
|
|
45
|
+
`.agentframe/agents.yaml` with local card URLs, and generates one random
|
|
46
|
+
API key per agent per session, passed only through the agents'
|
|
47
|
+
environment, never written or logged. Every agent also accepts an optional
|
|
48
|
+
`AGENTFRAME_DEV_CLIENT_KEY` as caller `developer`. Output is combined with
|
|
49
|
+
an `[agent-name]` prefix, with each run's invocation IDs. Failures stop
|
|
50
|
+
every agent and exit 1; Ctrl+C stops them gracefully. App factories read the
|
|
51
|
+
wiring with the new `dev_settings()`, which refuses to run without
|
|
52
|
+
`AGENTFRAME_DEV=1`. `python -m agentframe` now runs the command line.
|
|
53
|
+
- **Multi-agent (M8+):** cross-agent token budgets. `Remote.ask` and
|
|
54
|
+
`Remote.call` take `max_total_tokens=` (default: everything the run has
|
|
55
|
+
left), held atomically against the run's `RunLimits.max_total_tokens`
|
|
56
|
+
before sending and sent as `agentframe.max_total_tokens` to peers whose
|
|
57
|
+
card lists it in `params.context` (now `deadline_ms`, `hops`,
|
|
58
|
+
`max_total_tokens`). Receivers use the smaller of it and their own limit.
|
|
59
|
+
The usage the peer's task reports is charged to the run once, so
|
|
60
|
+
`RunResult.usage` includes every hop; peers that do not list the field hold
|
|
61
|
+
nothing and leave usage unknown (`None`), never zero. A ceiling over what is
|
|
62
|
+
left raises `TokenLimitExceededError` before sending.
|
|
63
|
+
- **Multi-agent (M8+):** `OnBehalfOf(scope=...)` in `remotes=` calls a remote
|
|
64
|
+
agent on behalf of the signed-in user through the Microsoft Entra
|
|
65
|
+
on-behalf-of flow; the incoming token is never forwarded. `EntraIDAuth`
|
|
66
|
+
gains `client_id=`, `client_credential=` and `token_transport=`, and keeps a
|
|
67
|
+
delegated user token privately as `VerifiedCaller.user_assertion`. Startup
|
|
68
|
+
refuses `OnBehalfOf` behind non-Entra hosts or without a client credential;
|
|
69
|
+
app-only callers and local runs fail with `PermissionDeniedError`, failed
|
|
70
|
+
exchanges with `AuthenticationError`, never falling back to `Service`. The
|
|
71
|
+
`entra` extra now pins `azure-identity==1.27.0` and `aiohttp==3.14.5`.
|
|
72
|
+
- **Multi-agent (M8+):** cross-agent correlation. Hosted runs log `Run
|
|
73
|
+
started` / `Run completed` at `INFO`, and their log records carry `agent`,
|
|
74
|
+
`invocation_id`, `root_invocation_id` and `parent_invocation_id` in
|
|
75
|
+
`logging` `extra` (AgentFrame sets no format). `Remote.ask` and `call` send
|
|
76
|
+
`agentframe.root_invocation_id` and `agentframe.parent_invocation_id` to
|
|
77
|
+
peers whose card lists them in `params.context`, so every agent of one
|
|
78
|
+
request logs the same root. Values that are not lowercase UUID strings are
|
|
79
|
+
ignored (the run becomes a new root), never echoed or trusted. A valid W3C
|
|
80
|
+
`traceparent` (and valid `tracestate`) is forwarded unchanged to every peer;
|
|
81
|
+
invalid ones are dropped. None of it authenticates or grants access, and
|
|
82
|
+
task metadata is unchanged.
|
|
83
|
+
- **Multi-agent (M8+):** `Remote.ask` sends the run's remaining time
|
|
84
|
+
(`agentframe.deadline_ms`) and depth (`agentframe.hops`) to peers whose card
|
|
85
|
+
lists them in the skill-call extension's new `params.context`; others get
|
|
86
|
+
plain A2A. Receivers cap their timeout by the caller's deadline and reject
|
|
87
|
+
requests deeper than the new `RunLimits.max_remote_hops` (default 5); a run
|
|
88
|
+
at that depth fails before sending. New `RunContext.hops`. With the
|
|
89
|
+
extension activated, a message without `agentframe.skill` is now free text
|
|
90
|
+
instead of an `InvalidParams` error.
|
|
91
|
+
- **Discovery (M8+):** read-only `AgentRegistry` base with `AgentReference`
|
|
92
|
+
(agent name and Agent Card URL) and `AgentQuery` (name filter), plus
|
|
93
|
+
`FileAgentRegistry` (YAML `name`/`url` list) and `EnvironmentAgentRegistry`
|
|
94
|
+
(comma-separated `name=url` pairs). PyYAML is a new core dependency.
|
|
95
|
+
- **Discovery (M8+):** `AgentDirectory(sources=[...])` returns
|
|
96
|
+
`DiscoveredAgent` results with validated official SDK Agent Cards, cached
|
|
97
|
+
for 300 seconds, with `allow_stale`, `refresh`, `allow_http_hosts` and an
|
|
98
|
+
optional caller-owned `http_client`. New `RemoteAgentError` for failures
|
|
99
|
+
caused by another agent. `httpx` is now an exactly pinned core dependency.
|
|
100
|
+
- **Discovery (M8+):** `AgentDirectory.find(skill=..., tags=...)` returns the
|
|
101
|
+
Agent Cards of listed agents with a skill matching the ID and every tag,
|
|
102
|
+
in source order. It fetches at most eight cards at a time through the cache
|
|
103
|
+
and raises on any source or card failure instead of returning fewer cards.
|
|
104
|
+
- **Discovery (M8+):** `FoundryAgentRegistry(project_endpoint=...,
|
|
105
|
+
credential=...)` lists a Foundry project's A2A-enabled agents, mapped to
|
|
106
|
+
their A2A 1.0 Agent Card URLs. New optional `AgentRegistry.card_headers`
|
|
107
|
+
lets a source send its own credentials with its card requests; Foundry
|
|
108
|
+
sends a bearer token from its credential. The `foundry` extra now pins
|
|
109
|
+
`azure-ai-projects==2.11.0`.
|
|
110
|
+
- **Remote agents (M8+):** skills declare another A2A agent as a
|
|
111
|
+
`Remote[Literal["name"]]` parameter (excluded from arguments, the Agent Card
|
|
112
|
+
and schemas), found through `Runtime(directory=...)` or
|
|
113
|
+
`AgentApp(directory=...)`; server startup checks every name with
|
|
114
|
+
`Runtime.validate_remotes`. `await remote.ask(text)` sends plain A2A text and
|
|
115
|
+
returns the text answer within the run's deadline, cancelling the remote
|
|
116
|
+
task on deadline or cancellation. New `ClarificationNeededError` when the
|
|
117
|
+
remote agent asks a question.
|
|
118
|
+
- **Remote agents (M8+):** `await remote.call("skill", output=Type, **arguments)`
|
|
119
|
+
runs a remote AgentFrame agent's skill by ID through the skill-call
|
|
120
|
+
extension and validates its result into `output` (`OutputValidationError`
|
|
121
|
+
on mismatch). A peer whose card lacks the extension or the skill raises
|
|
122
|
+
`RemoteAgentError` before anything is sent, with no free-text fallback.
|
|
123
|
+
`output` and `max_total_tokens` cannot be remote argument names.
|
|
124
|
+
- **Outbound identity (M8+):** `remotes={name: Service(credential=...)}` on
|
|
125
|
+
`Runtime` and `AgentApp` authenticates remote calls with the calling
|
|
126
|
+
agent's own identity: `APIKeyCredential(key, header="X-API-Key")` or
|
|
127
|
+
`EntraAppCredential(credential, scope=...)` (any `azure.core` async token
|
|
128
|
+
credential, sent as a bearer token), or any `OutboundCredential`.
|
|
129
|
+
Credentials go per request only to the card's validated interface origin,
|
|
130
|
+
never with redirects, and incoming caller credentials are never forwarded.
|
|
131
|
+
Agents whose card declares security, including Foundry A2A agents, can now
|
|
132
|
+
be called; startup fails with `DefinitionError` when the configured
|
|
133
|
+
credential cannot satisfy the card's security.
|
|
134
|
+
|
|
135
|
+
### Fixed
|
|
136
|
+
|
|
137
|
+
- `agentframe dev` no longer passes an exported `AGENTFRAME_AGENTS` to its
|
|
138
|
+
agents, which made `dev_settings()` list every agent twice and fail startup
|
|
139
|
+
with a duplicate-name `DefinitionError`.
|
|
140
|
+
- URLs with a non-numeric or out-of-range port are refused as
|
|
141
|
+
`HostConfig.public_url` and registry card URLs.
|
|
142
|
+
- `FileAgentRegistry` strips whitespace around names and URLs, like
|
|
143
|
+
`EnvironmentAgentRegistry`.
|
|
144
|
+
- Credential headers (`Authorization`, `Proxy-Authorization`, `Cookie`,
|
|
145
|
+
`X-API-Key`) are dropped from the A2A SDK's call context after
|
|
146
|
+
authentication, so the SDK's debug logs no longer contain bearer tokens or
|
|
147
|
+
API keys.
|
|
148
|
+
- `FoundryAgentRegistry` finds A2A-enabled agents again: the live Foundry API
|
|
149
|
+
lists them as `agent_endpoint.protocols: ["a2a", ...]`, which
|
|
150
|
+
`azure-ai-projects` 2.11.0 does not model, and its list response omits
|
|
151
|
+
`agent_endpoint` entirely, so every agent was skipped and lookups raised.
|
|
152
|
+
`list` now reads each queried agent's details (one call per agent), and the
|
|
153
|
+
`protocol_configuration.a2a` shape is still accepted.
|
|
154
|
+
|
|
155
|
+
## [0.1.0] - 2026-11-13
|
|
156
|
+
|
|
157
|
+
The first usable single-agent release, distributed on PyPI as `agentframe-py`
|
|
158
|
+
and imported as `agentframe`.
|
|
159
|
+
|
|
160
|
+
### Added
|
|
161
|
+
|
|
162
|
+
- **Authoring:** `Agent` subclasses with `@agent` identity and one async
|
|
163
|
+
`@skill` method per advertised skill; Python types and Pydantic models as
|
|
164
|
+
validated input and output contracts; `Inject[T]` services registered with
|
|
165
|
+
`Services.add` / `add_scoped`; per-invocation `RunContext`.
|
|
166
|
+
- **Local runtime:** `Runtime.invoke` returns a validated value and
|
|
167
|
+
`Runtime.run` a `RunResult` with invocation ID, usage, model and tool calls,
|
|
168
|
+
duration and finish reason. `RunLimits`, `ModelSettings` and `RunOptions`
|
|
169
|
+
resolve field by field from library to agent, skill and run.
|
|
170
|
+
- **A2A 1.0 hosting:** `AgentApp` serves a generated native Agent Card and a
|
|
171
|
+
JSON-RPC endpoint through the official `a2a-sdk`. Structured requests name a
|
|
172
|
+
skill through the Agent Card's skill-call extension; free text uses a custom
|
|
173
|
+
`SkillRouter` or the opt-in `ModelRouter`, which asks back with
|
|
174
|
+
`TASK_STATE_INPUT_REQUIRED` when a request is ambiguous.
|
|
175
|
+
`agentframe run module:create_app` serves an application (`server` extra).
|
|
176
|
+
- **Models:** `OllamaModel` (local Llama, `ollama` extra) and `FoundryModel`
|
|
177
|
+
(Microsoft Foundry GPT deployments, `foundry` extra), switched by
|
|
178
|
+
configuration only. One `ctx.generate` returns text, a validated Pydantic
|
|
179
|
+
instance with `output=`, or runs registered `@tool` functions with
|
|
180
|
+
`tools=[...]` within `max_tool_calls`. Refusal, truncation and unsupported
|
|
181
|
+
capabilities raise typed errors. `ScriptedModel` in `agentframe.testing`
|
|
182
|
+
scripts model replies for tests.
|
|
183
|
+
- **Security:** `HostConfig(auth=...)` with `APIKeyAuth`, `EntraIDAuth`
|
|
184
|
+
(`entra` extra), explicit `AllowAnonymous` or a custom `Authenticator`;
|
|
185
|
+
skill `requires_permissions`, role mapping and an injected
|
|
186
|
+
`AuthorizationPolicy`, failing closed; optional `ExecutionPolicy` input,
|
|
187
|
+
model-request, tool and output checks.
|
|
188
|
+
- **Hardening:** host ceilings for concurrent runs, request and response size,
|
|
189
|
+
and graceful shutdown; `/health` and `/ready`; A2A message deduplication by
|
|
190
|
+
caller and message ID; safe, correlated errors with redacted operator logs.
|
|
191
|
+
- **Examples:** runnable examples in `examples/`, each checked against the
|
|
192
|
+
installed wheel by `python -m examples.smoke`.
|
|
193
|
+
|
|
194
|
+
[0.2.0]: https://github.com/malcmiller/agentframe/releases/tag/v0.2.0
|
|
195
|
+
[0.1.0]: https://github.com/malcmiller/agentframe/releases/tag/v0.1.0
|