|
1 | 1 | # Release History - open AEA |
2 | 2 |
|
3 | | -## Unreleased |
| 3 | +## 2.2.7 (2026-05-27) |
4 | 4 |
|
5 | 5 | Framework: |
6 | 6 |
|
7 | | -- `aea.configurations.loader`: exposes `parse_service_yaml(file_pointer) -> (head, overrides)` as a public module-level helper that parses a service YAML stream into the head document and the trailing override documents without schema validation. `ConfigLoader._load_service_config` now delegates to it. Lets downstream packagers (e.g. open-autonomy, which needs to substitute environment variables in the head document before validating it through its own `Service` config class) drop a hand-maintained copy of the parse-and-split logic. Raises `ValueError("Service configuration file was empty.")` whenever the head document is not a mapping — covers empty streams, bare `---`, and YAML scalars (`false`, `0`, `""`, `[]`) — so the failure surfaces at parse time rather than as an opaque `AttributeError` from the downstream validator. |
8 | | -- `aea.cli.scaffold.scaffold_item`: saves `ctx.cwd` on entry and restores it via `try ... finally`. Previously the `--to-local-registry` branch swapped `ctx.cwd` to `<registry_path>/<author>` to make the inner `fingerprint_item` call resolve correctly, but never restored it, leaking the registry path back to callers. The post-fingerprint steps in the same function (notably the `--with-symlinks` block, which builds vendor symlinks from `ctx.cwd`) also see the original value again. Downstream wrappers that had to bracket `scaffold_item` with their own `preserve_cwd = ctx.cwd; ...; ctx.cwd = preserve_cwd` can drop the workaround. |
9 | | -- `aea.contracts.base.Contract.from_config` and `aea.connections.base.Connection.from_config`: pass the canonical packages-prefixed dotted path (`packages.<author>.contracts.<name>.contract` / `packages.<author>.connections.<name>.connection`) to `load_module` instead of the bare `"contracts"` / `"connection_module"` constants. With the new `sys.modules` registration in `load_module` (below), the bare keys would have collided across contracts and across connections loaded in the same process. Caller-visible behaviour is unchanged because `load_module` always returns a fresh module object. |
10 | | -- `aea.skills.base._SkillComponentLoader`: makes the dotted module path returned by `_compute_module_dotted_path` the canonical one (`packages.<author>.skills.<name>.<file>`), drops the now-redundant "exclude classes whose `__module__` starts with this skill's dotted path" condition from `_filter_classes`, and updates the unused-class warning filter at `_print_warning_message_for_unused_classes` to keep classes that belong to this skill (both submodule classes and those defined at the skill's own `__init__.py` level) instead of dropping every `packages.`-prefixed class. Cross-skill re-exports (the canonical FSM composition idiom — `class_name: AbciDialogues` in `skill.yaml` bound to an `AbciDialogues` imported from a parent skill) still pass the filter because their `__module__` starts with another skill's prefix, not `aea.`. The legacy `_parse_module` filter (used by `Behaviour.parse_module` / `Handler.parse_module` / `Model.parse_module`) drops the same local-prefix exclusion clause and switches its `load_module` call to the canonical packages-prefixed dotted path so its `sys.modules` cache entries do not collide on bare keys (`"behaviours"` / `"handlers"` / `"models"`) across skills loaded in the same process. The remaining difference vs. `_filter_classes` is the legacy filter's name-allowlist gate (`x[0] in component_names`), which is unchanged. `aea.helpers.base.load_module` now registers the loaded module in `sys.modules` under its dotted path before `exec_module` and rolls the entry back if `exec_module` raises, so (a) a subsequent ordinary `from packages.<author>.skills.<name>.<file> import X` returns the same module object instead of re-executing the file and producing a second copy of every class defined in it, and (b) a transient module-level error does not permanently poison `sys.modules` with a half-initialised stub. Together these eliminate the dual short-name / long-name class objects that downstream code (open-autonomy's `_MetaPayload.registry`) previously had to paper over with a key-rewrite loop. `_compute_module_dotted_path` is no longer a `@classmethod` (it reads `self.skill_dotted_path`) — internal API, no out-of-tree caller. |
| 7 | +- `aea.configurations.loader`: exposes `parse_service_yaml(file_pointer) -> (head, overrides)` as a public module-level helper that parses a service YAML stream into the head document and the trailing override documents without schema validation. `ConfigLoader._load_service_config` now delegates to it. Lets downstream packagers (e.g. open-autonomy, which needs to substitute environment variables in the head document before validating it through its own `Service` config class) drop a hand-maintained copy of the parse-and-split logic. Raises `ValueError("Service configuration file was empty.")` whenever the head document is not a mapping — covers empty streams, bare `---`, and YAML scalars (`false`, `0`, `""`, `[]`) — so the failure surfaces at parse time rather than as an opaque `AttributeError` from the downstream validator. #918 |
| 8 | +- `aea.cli.scaffold.scaffold_item`: saves `ctx.cwd` on entry and restores it via `try ... finally`. Previously the `--to-local-registry` branch swapped `ctx.cwd` to `<registry_path>/<author>` to make the inner `fingerprint_item` call resolve correctly, but never restored it, leaking the registry path back to callers. The post-fingerprint steps in the same function (notably the `--with-symlinks` block, which builds vendor symlinks from `ctx.cwd`) also see the original value again. Downstream wrappers that had to bracket `scaffold_item` with their own `preserve_cwd = ctx.cwd; ...; ctx.cwd = preserve_cwd` can drop the workaround. #918 |
| 9 | +- `aea.contracts.base.Contract.from_config` and `aea.connections.base.Connection.from_config`: pass the canonical packages-prefixed dotted path (`packages.<author>.contracts.<name>.contract` / `packages.<author>.connections.<name>.connection`) to `load_module` instead of the bare `"contracts"` / `"connection_module"` constants. With the new `sys.modules` registration in `load_module` (below), the bare keys would have collided across contracts and across connections loaded in the same process. Caller-visible behaviour is unchanged because `load_module` always returns a fresh module object. #918 |
| 10 | +- `aea.skills.base._SkillComponentLoader`: makes the dotted module path returned by `_compute_module_dotted_path` the canonical one (`packages.<author>.skills.<name>.<file>`), drops the now-redundant "exclude classes whose `__module__` starts with this skill's dotted path" condition from `_filter_classes`, and updates the unused-class warning filter at `_print_warning_message_for_unused_classes` to keep classes that belong to this skill (both submodule classes and those defined at the skill's own `__init__.py` level) instead of dropping every `packages.`-prefixed class. Cross-skill re-exports (the canonical FSM composition idiom — `class_name: AbciDialogues` in `skill.yaml` bound to an `AbciDialogues` imported from a parent skill) still pass the filter because their `__module__` starts with another skill's prefix, not `aea.`. The legacy `_parse_module` filter (used by `Behaviour.parse_module` / `Handler.parse_module` / `Model.parse_module`) drops the same local-prefix exclusion clause and switches its `load_module` call to the canonical packages-prefixed dotted path so its `sys.modules` cache entries do not collide on bare keys (`"behaviours"` / `"handlers"` / `"models"`) across skills loaded in the same process. The remaining difference vs. `_filter_classes` is the legacy filter's name-allowlist gate (`x[0] in component_names`), which is unchanged. `aea.helpers.base.load_module` now registers the loaded module in `sys.modules` under its dotted path before `exec_module` (reusing any module already registered at that path so a subsequent ordinary `from packages.<author>.skills.<name>.<file> import X` returns the same object instead of re-executing the file and producing a second copy of every class defined in it), and rolls the entry back if `exec_module` raises. Together these eliminate the dual short-name / long-name class objects that downstream code (open-autonomy's `_MetaPayload.registry`) previously had to paper over with a key-rewrite loop. `_compute_module_dotted_path` is no longer a `@classmethod` (it reads `self.skill_dotted_path`) — internal API, no out-of-tree caller. #918 |
11 | 11 |
|
12 | 12 | - `aea.cli` / `aea.helpers.protocols`: removes the two module-top `logging.basicConfig(...)` calls that fired as a side effect of `import aea.cli` and attached a stderr `StreamHandler` to the root logger. That orphan root handler survived the agent's `logging.config.dictConfig` (the config block has no `root:` key) and caught every `aea.agent.*` record a second time via propagation, producing the `] [INFO]` vs `][INFO]` doubling seen in operator captures. Fix moves the `basicConfig` in `aea/cli/generate_all_protocols.py` into the click command body and switches `aea/helpers/protocols.py` from `setup_logger(__name__)` to `logging.getLogger(__name__)`. Behaviour change for operators: third-party library `INFO`/`DEBUG` logs (e.g. from `web3`, `requests` in the ledger plugins) that the orphan handler previously surfaced now go silent under an agent config without a `root:` block in `logging_config`. `WARNING` and above still appear on stderr via Python's `logging.lastResort` handler, though with that handler's bare-message format (just `%(message)s`, no level or logger-name prefix and no timestamp) rather than the formatted one the orphan installed. Operators who want the previous behaviour (timestamped third-party `INFO`/`DEBUG`) can add a `root:` block to `logging_config` in the agent's `aea-config.yaml`. #914 #917 |
| 13 | +- `aea.helpers.env_vars.generate_env_vars_recursively`: adds an `is_strict_dict`-style branch that JSON-encodes a whole dict as a single env var when any key fails the bash identifier rule (`^[A-Za-z_][A-Za-z0-9_]*$`), mirroring the existing `is_strict_list` handling. Previously a dict keyed by bash-unsafe strings (e.g. probability thresholds like `"0.0"`, `"0.6"`, `"1.0"`) flattened per key to env var names such as `..._BET_AMOUNT_PER_THRESHOLD_0.0` that bash rejects at export time, so the override silently never reached the agent (see [open-autonomy#2243](https://github.com/valory-xyz/open-autonomy/issues/2243)). Also fixes a latent key-corruption bug where the per-key path round-tripped numeric-looking string keys through `json.loads` inside `parse_list`. All-safe-key dicts still flatten per key (no regression); mixed-key dicts now travel as one JSON-encoded env var. Agent-side reads through `convert_value_str_to_type` JSON-decode the `:dict:`/`:list:` placeholders unchanged. #915 |
| 14 | + |
| 15 | +Plugins: |
| 16 | + |
| 17 | +- `open-aea-ci-helpers` (`aea-ci check-dependencies`): `PyProjectToml.load` now reads `[tool.poetry.group.*.dependencies]` tables in addition to the main `[tool.poetry.dependencies]` block, and accepts dict-form entries without an `extras` key (so `{version = "...", optional = true}` deps are no longer silently dropped). Implementation refactored around two helpers (`_normalize_version`, `_dependency_from_spec`) that handle string and dict forms uniformly; main-deps entries still win on name collisions. New unit tests at `plugins/aea-ci-helpers/tests/test_check_dependencies.py` cover the four entry formats, `dev` + `docs` groups, main-vs-group precedence, missing `[tool.poetry]` table, and the `python` ignore filter. Removes a sharp edge for anyone using `aea-ci check-dependencies` locally against a project with grouped or optional-without-extras deps. #916 |
| 18 | + |
| 19 | +Documentation: |
| 20 | + |
| 21 | +- `docs/connection-resilience.md` rewrite addressing @DavidMinarsch's review of the original v2.2.6 doc. Adds a "What the protocol's speech acts imply" section that walks through the protocols in the repo and groups them by failure-channel shape (typed-error, status-with-classification, response-only, routing-failure) — making explicit how a protocol's `speech_acts` and reply rules pin down what the skill can decide. Terminology aligned with the framework convention (`speech_acts` over "vocabulary", with FIPA's "performatives" as a one-line synonym). Cross-links added to Design principles, Architectural diagram, Skills, Connections, Protocols, Message routing, Decision maker, and Ledger & Crypto APIs. Accuracy pass on the original draft (`fetchai/default` routing failures are surfaced by the `fetchai/error` skill, not the multiplexer; protocols decide failure-signal richness, not which layer retries; ACN reply rules clarified per performative). Net 261 → 91 lines for the same coverage plus the new protocol section. #912 |
13 | 22 |
|
14 | 23 | ## 2.2.6 (2026-05-14) |
15 | 24 |
|
|
0 commit comments