diff --git a/CHANGELOG.md b/CHANGELOG.md index b29698451..d64e33132 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,63 @@ The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). --- +## [3.8.1] — 2026-04-05 + +### Added + +- **ACMS — Skeleton context inheritance for child plans**: `ACMSPipeline.assemble()` + and `ContextAssemblyPipeline.assemble()` now accept `skeleton_ratio: float = 0.15` + and `parent_fragments: tuple[ContextFragment, ...] | None = None` parameters. + When `parent_fragments` is provided, the `SkeletonCompressor` is invoked in + Phase 3 to compress parent context to the skeleton budget + (`int(budget.available_tokens * skeleton_ratio)`). Compressed skeleton fragments + are returned in the new `ContextPayload.skeleton_fragments` field, enabling child + plans to inherit a compressed representation of the parent plan's accumulated + context. Includes 4 Behave unit test scenarios and 1 Robot Framework integration + test. (#3563) + +### Fixed + +- **CLI — `agents actor add` YAML-first persistence path**: The `actor add` command + now routes through `ActorRegistry.add()` instead of the legacy + `registry.upsert_actor()` path, ensuring the original YAML text, + `schema_version`, and `compiled_metadata` are preserved in the database. + A `_load_config_text()` helper returns both raw text and parsed dict; the + service fallback path (no registry) is unchanged. (#3426) + +- **CLI — `agents diagnostics` extended to all 9 providers**: The diagnostics + command now checks all nine supported providers (OpenAI, Anthropic, Google, + Azure OpenAI, OpenRouter, Gemini, Cohere, Groq, Together) for credential + presence and reports which are configured. (#3422) + +- **Executor — Automatic per-tool-write and event-based checkpoint triggers**: + The execution engine now creates checkpoints automatically at four trigger + points: `on_tool_write` (before each write-tool call), `on_tool_write_complete` + (after each write-tool call), `on_subplan_spawn` (before first subplan attempt), + and `on_error` (when Execute phase fails). Triggers are configurable via + `core.checkpoints.auto_create_on`. `CheckpointService` is injected as an + optional parameter into `ToolRunner`, `SubplanExecutionService`, and + `PlanExecutor`. (#3439) + +- **CLI — `--container-id` flag on `agents resource add container-instance`**: + The `resource add` command for container-instance resources now accepts a + `--container-id` flag to specify the Docker/Podman container ID explicitly, + rather than requiring auto-discovery. (#2598) + +- **Domain — `ToolLifecycle` execute hook**: The `ToolLifecycle` model now + includes the `execute` hook to satisfy the spec's four-stage lifecycle + (`activate → validate → execute → deactivate`). (#2820) + +- **MCP — `MCPToolResult.data` type annotation**: The `data` field type + annotation on `MCPToolResult` has been corrected to match the MCP 1.4.0 + content list format. (#2743) + +- **CLI — `automation-profile list` output structure**: The `automation-profile + list` command now renders the correct Rich table structure and output format + matching the specification. (#2064) + +--- + ## [3.8.0] — 2026-04-05 ### Added diff --git a/docs/api/actor.md b/docs/api/actor.md index 329d47311..e6a0e46d9 100644 --- a/docs/api/actor.md +++ b/docs/api/actor.md @@ -68,6 +68,25 @@ actor = registry.get("openai/gpt-4o") In-memory registry for actor configurations. Thread-safe. +### `ActorRegistry.add()` — YAML-first persistence path + +The `agents actor add` CLI command routes through `ActorRegistry.add()` to +ensure the original YAML text, `schema_version`, and `compiled_metadata` are +preserved in the database. + +```python +registry.add(yaml_text="name: openai/gpt-4o\n...", update=False) +``` + +| Parameter | Type | Description | +|-----------|------|-------------| +| `yaml_text` | `str` | Raw YAML text of the actor configuration | +| `update` | `bool` | When `True`, overwrite an existing actor with the same name | + +> **Note:** The legacy `registry.upsert_actor()` path is still available for +> programmatic use but is no longer called by the CLI. Use `registry.add()` +> for all new code that needs to persist actor configurations. + --- ## `compile_actor` / `CompiledActor` diff --git a/docs/architecture.md b/docs/architecture.md index e355f0c5e..c46af61fb 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -217,6 +217,34 @@ Key UKO capabilities: - **Graph persistence** — `UKOGraphPersistence` serialises/restores state via JSON-file or in-memory backends across application restarts +### Skeleton Context Inheritance + +When a parent plan spawns a child plan, the ACMS pipeline compresses the +parent's accumulated context fragments into a **skeleton** for propagation. +This prevents child plans from starting with an empty context while keeping +the inherited payload within budget. + +``` +Parent Plan Context + │ + ▼ SkeletonCompressor (skeleton_ratio=0.15) + │ + ▼ skeleton_fragments (top-relevance, budget-bounded) + │ + ▼ Child Plan ContextPayload.skeleton_fragments +``` + +Key parameters: + +| Parameter | Default | Description | +|-----------|---------|-------------| +| `skeleton_ratio` | `0.15` | Fraction of the child's token budget allocated to inherited skeleton context | +| `parent_fragments` | `None` | Parent plan's accumulated `ContextFragment` tuple; `None` disables skeleton compression | + +The `ContextPayload.skeleton_fragments` field carries the compressed result. +See [`docs/reference/skeleton_compressor.md`](reference/skeleton_compressor.md) +for the full compression algorithm and configuration reference. + --- ## Invariant Reconciliation diff --git a/docs/modules/acms-skeleton-context.md b/docs/modules/acms-skeleton-context.md new file mode 100644 index 000000000..5281e7e25 --- /dev/null +++ b/docs/modules/acms-skeleton-context.md @@ -0,0 +1,163 @@ +# ACMS Skeleton Context Inheritance + +## Overview + +When a parent plan spawns a child plan, the child plan starts with no +accumulated context. Without inherited context, the child's actor must +re-discover information the parent already gathered, wasting tokens and +execution time. + +**Skeleton context inheritance** solves this by compressing the parent +plan's accumulated context fragments into a budget-bounded **skeleton** +that is passed to the child plan's context assembly pipeline. + +This feature was introduced in v3.8.1 (issue #3563). + +--- + +## How It Works + +``` +Parent Plan + ├── ContextFragment (relevance=0.9, tokens=800) + ├── ContextFragment (relevance=0.7, tokens=600) + └── ContextFragment (relevance=0.3, tokens=400) + │ + ▼ SkeletonCompressor + │ skeleton_ratio=0.15 + │ skeleton_budget = available_tokens * 0.15 + │ + ▼ Compressed skeleton (top-relevance, budget-bounded) + │ +Child Plan ContextPayload + ├── fragments — child's own assembled context + └── skeleton_fragments — inherited from parent (compressed) +``` + +The `SkeletonCompressor` sorts parent fragments by relevance descending +and greedily packs them into the skeleton budget. The compressed result +is returned in `ContextPayload.skeleton_fragments`. + +--- + +## API + +### `ACMSPipeline.assemble()` + +```python +from cleveragents.application.services.acms_service import ACMSPipeline +from cleveragents.domain.models.core.context_fragment import ContextFragment + +pipeline = ACMSPipeline() +payload = pipeline.assemble( + plan_id="child-plan-id", + fragments=child_fragments, + budget=budget, + strategy="relevance", + # Skeleton inheritance parameters: + skeleton_ratio=0.15, # fraction of budget for skeleton + parent_fragments=parent_frags, # parent's accumulated fragments +) + +# Access inherited skeleton +for frag in payload.skeleton_fragments: + print(frag.uko_node, frag.token_count) +``` + +**New parameters (v3.8.1+):** + +| Parameter | Type | Default | Description | +|-----------|------|---------|-------------| +| `skeleton_ratio` | `float` | `0.15` | Fraction of `budget.available_tokens` allocated to the skeleton | +| `parent_fragments` | `tuple[ContextFragment, ...] \| None` | `None` | Parent plan's accumulated fragments; `None` disables skeleton compression | + +When `parent_fragments` is `None`, `ContextPayload.skeleton_fragments` is +an empty tuple and no compression is performed. + +### `ContextAssemblyPipeline.assemble()` + +The same `skeleton_ratio` and `parent_fragments` parameters are available +on `ContextAssemblyPipeline.assemble()` for consistency across the two +pipeline entry points. + +### `ContextPayload.skeleton_fragments` + +```python +@dataclass(frozen=True) +class ContextPayload: + ... + skeleton_fragments: tuple[ContextFragment, ...] = () +``` + +Immutable tuple of compressed parent context fragments. Empty when no +parent context was provided. + +--- + +## Configuration + +### Default skeleton ratio + +The default `skeleton_ratio` of `0.15` means the skeleton budget is 15% +of the child plan's available token budget. For a 4096-token budget with +512 reserved tokens, the skeleton budget is: + +``` +skeleton_budget = (4096 - 512) * 0.15 = 537 tokens +``` + +### Per-project override + +Set a custom skeleton ratio for a project's context policy: + +```bash +agents project context set --skeleton-ratio 0.20 +``` + +This overrides the default for all child plans spawned within that project. + +--- + +## Relationship to `SkeletonCompressorService` + +The `SkeletonCompressorService` (registered in the DI container as +`skeleton_compressor_service`) is the underlying service that performs +the compression. The ACMS pipeline calls it internally during Phase 3 +(Context Finalization). + +For direct use of the compressor outside the pipeline, see +[`docs/reference/skeleton_compressor.md`](../reference/skeleton_compressor.md). + +--- + +## Subplan Spawning Integration + +The `SubplanService.spawn()` method passes the parent plan's accumulated +context as `parent_fragments` when assembling the child plan's initial +context. This is the primary production path for skeleton inheritance. + +```python +# Conceptual — internal to SubplanService +child_payload = pipeline.assemble( + plan_id=child_plan_id, + fragments=child_fragments, + budget=child_budget, + parent_fragments=parent_plan.accumulated_fragments, + skeleton_ratio=project_context_policy.skeleton_ratio, +) +``` + +--- + +## Gotchas + +- **`skeleton_ratio=0.0`** disables compression entirely — all parent + fragments pass through unchanged. Use this only when you want the full + parent context in the child (rare; usually exceeds budget). +- **`skeleton_ratio=1.0`** keeps only the single highest-relevance fragment. + Useful for very tight token budgets. +- The skeleton budget is computed from `budget.available_tokens`, not + `budget.max_tokens`. Reserved tokens are already excluded. +- Skeleton fragments are **not** deduplicated against the child's own + assembled fragments. If the same resource appears in both, it will be + present twice. A deduplication pass is planned for a future milestone.