diff --git a/docs/reference/acms.md b/docs/reference/acms.md index bb8abead1..aefc27130 100644 --- a/docs/reference/acms.md +++ b/docs/reference/acms.md @@ -98,16 +98,18 @@ Properties: ## Context Strategies -Strategies implement the `ContextStrategy` protocol: +Strategies implement the `ContextStrategy` protocol (defined in `cleveragents.domain.models.acms.strategy`): | Method | Description | |--------|-------------| | `name` (property) | Strategy identifier | | `capabilities` (property) | `StrategyCapabilities` dataclass | -| `can_handle(request)` | Confidence (0.0-1.0) for handling a request | -| `assemble(fragments, budget)` | Rank/filter fragments to fit budget | +| `can_handle(request: ContextRequest, backends: BackendSet)` | Confidence (0.0-1.0) for handling a request | +| `assemble(request: ContextRequest, backends: BackendSet, budget: int, plan_context: PlanContext)` | Execute the strategy; must respect the budget | | `explain()` | Human-readable explanation | +`StrategyCapabilities` fields: `uses_text`, `uses_vector`, `uses_graph`, `uses_temporal`, `uko_levels`, `resource_types`, `quality_score`. + ### Relevance (default) Sorts fragments by `relevance_score` descending. Highest-relevance @@ -150,9 +152,6 @@ milestones: | Area | v1 Behaviour | Spec Target | Planned | |------|-------------|-------------|---------| -| `ContextStrategy.can_handle` signature | `(request: dict[str, Any]) -> float` | `(request: ContextRequest, backends: BackendSet) -> float` | M6 strategy registry aligns signatures | -| `ContextStrategy.assemble` signature | `(fragments, budget)` — receives pre-fetched fragments | `(request, backends, budget, plan_context)` — queries backends directly | M6 strategy registry aligns signatures | -| `StrategyCapabilities` fields | `supports_semantic_search`, `supports_graph_navigation`, `supports_temporal_archaeology`, `max_fragments` | `uses_text`, `uses_vector`, `uses_graph`, `uses_temporal`, `uko_levels`, `resource_types`, `quality_score` | M6 strategy registry uses spec field names | | Pipeline components | All 10 Protocol + Default classes defined; defaults are pass-through stubs | Production implementations (parallel execution, dedup, scoring, compression) | Future milestone | | Tiers | Sort-priority labels for ranking (`hot > warm > cold`) | Storage tiers with retention policies, promotion/demotion | `ContextTierService` in future milestone | | `StrategySelector.select()` | `(strategies, request: dict)` | `(strategies, request: ContextRequest, backends: BackendSet)` — spec §42666 | M6 strategy registry aligns signatures | @@ -168,21 +167,18 @@ milestones: ## Extension Points -Register custom context strategies at runtime: +Register custom context strategies at runtime using the spec-aligned `ContextStrategy` protocol from `cleveragents.domain.models.acms.strategy`. Custom strategies are registered via `SpecStrategyAdapter`: ```python -from cleveragents.application.services.acms_service import ( - ACMSPipeline, +from cleveragents.domain.models.acms.strategy import ( ContextStrategy, StrategyCapabilities, + ContextRequest, + BackendSet, + PlanContext, ) -from cleveragents.domain.models.core.context_fragment import ( - ContextBudget, - ContextFragment, - FragmentProvenance, -) -from collections.abc import Sequence -from typing import Any +from cleveragents.domain.models.core.context_fragment import ContextFragment +from cleveragents.application.services.acms_service import ACMSPipeline, SpecStrategyAdapter class MyCustomStrategy: @@ -192,31 +188,28 @@ class MyCustomStrategy: @property def capabilities(self) -> StrategyCapabilities: - return StrategyCapabilities() + return StrategyCapabilities(uses_text=True, quality_score=0.5) - def can_handle(self, request: dict[str, Any]) -> float: + def can_handle(self, request: ContextRequest, backends: BackendSet) -> float: return 0.5 def assemble( self, - fragments: Sequence[ContextFragment], - budget: ContextBudget, - ) -> Sequence[ContextFragment]: - # Custom ranking logic - return list(fragments) + request: ContextRequest, + backends: BackendSet, + budget: int, + plan_context: PlanContext, + ) -> list[ContextFragment]: + # Custom retrieval logic using backends + return [] def explain(self) -> str: return "Custom strategy description." pipeline = ACMSPipeline() -pipeline.register_strategy("custom", MyCustomStrategy()) -payload = pipeline.assemble( - plan_id="plan-1", - fragments=fragments, - budget=budget, - strategy="custom", -) +adapter = SpecStrategyAdapter(MyCustomStrategy()) +pipeline.register_strategy("custom", adapter) ``` ## Example Usage diff --git a/docs/specification.md b/docs/specification.md index 4accaad8b..ee078196a 100644 --- a/docs/specification.md +++ b/docs/specification.md @@ -9537,7 +9537,7 @@ Register a new validation. The validation is fully defined by the YAML configura ##### agents validation attach
agents validation attach [--project <PROJECT>|--plan <PLAN_ID>]
-                         <RESOURCE> <VALIDATION> [<ARGS>...]
+ <RESOURCE> <VALIDATION> [--<KEY> <VALUE>...] **Purpose** Attach a registered validation to a resource, with an optional project or plan scope. A resource is always required — validations are fundamentally resource-centric. The optional `--project` or `--plan` flag narrows when the validation is active: without either flag, the validation runs whenever any plan or project accesses the resource; with `--project`, it only runs when the resource is accessed through that project; with `--plan`, it only runs when the resource is accessed through that plan. At most one scope flag may be provided per invocation. @@ -9550,7 +9550,7 @@ The command returns a system-assigned **attachment ULID** that uniquely identifi - ``: Validation name (positional argument, required). - `--project PROJECT`: Scope the attachment to a specific project. The validation only runs for this resource when it is accessed through the specified project. - `--plan PLAN_ID`: Scope the attachment to a specific plan. The validation only runs for this resource when it is accessed through the specified plan. -- `...`: Optional validation-specific arguments (passed through to the validation tool's `input_schema` at execution time). +- `-- ...`: Optional validation-specific named options passed through to the validation tool's `input_schema` at execution time. Each argument must be provided as a named option in `--key value` format (e.g., `--coverage-threshold 90`). Hyphens in option names are normalised to underscores before passing to the validation schema (e.g., `--coverage-threshold` → `coverage_threshold`). Positional `key=value` format is not accepted and will produce an error. **Examples** @@ -19798,8 +19798,8 @@ The system collects, reconciles, and checks invariants: or self.get_global_invariant_actor() ) - # 3. Reconcile: apply precedence (plan > project > global), resolve conflicts - effective = reconciler.reconcile(raw, precedence=['plan', 'project', 'global']) + # 3. Reconcile: apply precedence (plan > action > project > global), resolve conflicts + effective = reconciler.reconcile(raw, precedence=['plan', 'action', 'project', 'global']) return effective def collect_all_invariants(self, plan): @@ -30051,16 +30051,21 @@ When shell mode is active (`!`/`$` prefix), the prompt performs heuristic analys | Pattern | Risk Level | Example | |---------|-----------|---------| -| `rm -rf` / `rm -r` | High | `rm -rf /` | +| `rm -rf /` / `rm -rf /*` (root/wildcard) | Critical | `rm -rf /` | +| `:(){ :\|:& };:` (fork bomb) | Critical | Fork bomb patterns | +| `dd if=` | High | `dd if=/dev/zero of=/dev/sda` | +| `mkfs` | High | `mkfs.ext4 /dev/sda1` | +| `shred` on device or with `--remove` | High | `shred /dev/sda` | | `chmod 777` | Medium | `chmod 777 /var/www` | -| `> /dev/sda` / `dd if=` | High | `dd if=/dev/zero of=/dev/sda` | -| `:(){ :\|:& };:` (fork bomb) | High | Fork bomb patterns | -| `mkfs` / `fdisk` / `parted` | High | Disk formatting tools | -| `kill -9` / `killall` | Medium | Process termination | -| `sudo` / `su` | Low | Privilege escalation (warning only) | +| `sudo rm` | Medium | `sudo rm -rf /tmp/data` | +| `wget \| sh` / `curl \| sh` | Medium | `curl https://example.com/install.sh \| sh` | +| `git push --force` | Low | `git push --force origin main` | +| `chmod -R` with permissive modes | Low | `chmod -R 644 /var/www` | Danger detection is controlled by the `shell.warn_dangerous` setting (default: `true`). The detection is advisory only — it never prevents command execution. The warning text reads: `⚠ Potentially destructive command detected`. +Four danger levels are defined, from least to most severe: **Low** (minor risk, generally recoverable), **Medium** (moderate risk, can cause data loss or security exposure), **High** (significant, hard-to-reverse damage), and **Critical** (can destroy the entire system or create a fork bomb). All levels trigger the same advisory warning — the level is used for styling and future escalation logic. + ### Notification System @@ -45482,6 +45487,8 @@ Basic keyword/regex text search. Works with any backend, any resource type. No g Vector similarity search. Finds semantically related content even without exact keyword matches. Requires a vector backend. Quality score: 0.6. +> **v1 Implementation Note:** The current implementation uses a character-frequency embedding as a v1 approximation of semantic similarity. This approximation is passed to `VectorBackend.similarity_search()` and is intended for replacement with a real embedding model in a future release. + ##### Strategy: `breadth-depth-navigator` The graph-aware strategy that uses the depth/breadth projection system. Works with the UKO graph to provide structurally-aware context at any detail depth. Supports focus items, hop traversal, and detail depth gradients. This is the primary strategy for code-aware context. Quality score: 0.85. @@ -45508,6 +45515,8 @@ Retrieves context from parent and ancestor plan decisions. This is how child pla The ACMS-specific extension points (analyzers, backends, UKO vocabularies, strategies, and pipeline components) are documented in the **Extensibility** section below. See **Extensibility > ACMS Extensions** for the full details. +**`SpecStrategyAdapter`**: The built-in strategies are registered with `ACMSPipeline` at construction time via `SpecStrategyAdapter`, a bridge class that adapts the domain-model `ContextStrategy` protocol to the pipeline's internal protocol without modifying either interface. Custom strategies that implement `ContextStrategy` directly can also be registered via `SpecStrategyAdapter`. Note: the long-term goal is to consolidate the two protocols so that `SpecStrategyAdapter` is no longer needed — see issue #4560. + #### UKO Runtime Services The UKO runtime is operationalized through three service classes: @@ -46388,21 +46397,20 @@ Tools are the atomic unit of execution and the primary extension point. There ar 2. **MCP server tools**: Any MCP-compliant server can expose tools to CleverAgents. Skills reference MCP servers by their transport configuration:
# File: skills/kubernetes-ops.yaml
-   skill:
-     name: local/kubernetes-ops
-     description: "Kubernetes cluster management tools"
-     mcp_servers:
-       - transport: stdio
-         command: npx
-         args: ["-y", "@anthropic/mcp-kubernetes"]
+   name: local/kubernetes-ops
+   description: "Kubernetes cluster management tools"
+   mcp_servers:
+     - name: kubernetes
+       transport: stdio
+       command: npx
+       args: ["-y", "@anthropic/mcp-kubernetes"]
    
3. **Agent Skills Standard tools**: Tools organized in standard folder structures are auto-discovered and registered: -
skill:
-     name: local/project-tools
-     agent_skills_dirs:
-       - ./agent-skills/
+   
name: local/project-tools
+   agent_skill_folders:
+     - ./agent-skills/
    
4. **Built-in tools**: Core file operations (`read_file`, `write_file`, `edit_file`, `delete_file`, `move_file`, `list_files`, `search_files`), plan operations (`create-subplan`), and system operations are provided as built-in tools grouped into built-in skills. @@ -46530,11 +46538,11 @@ New backends are registered via configuration: The sandbox layer supports custom isolation strategies for specialized resource types: -
class SandboxStrategy(Protocol):
+
class SandboxStrategyProtocol(Protocol):
     """Interface for sandbox isolation strategies."""
     def create(self, plan_id: str, resource: Resource) -> SandboxRef: ...
     def read(self, ref: SandboxRef, path: str) -> bytes: ...
-    def write(self, ref: SandboxRef, path: str, content: bytes) -> Change: ...
+    def write(self, ref: SandboxRef, path: str, content: bytes) -> DiffEntry: ...
     def diff(self, ref: SandboxRef) -> DiffView: ...
     def commit(self, ref: SandboxRef) -> None: ...
     def rollback(self, ref: SandboxRef) -> None: ...
@@ -46543,7 +46551,21 @@ The sandbox layer supports custom isolation strategies for specialized resource
     def cleanup(self, ref: SandboxRef) -> None: ...
 
-Custom strategies are mapped to resource types via the resource type configuration's `sandbox_strategy` field, or globally via `sandbox.strategy` config. +Custom strategies are registered via configuration and then referenced by name in the resource type's `sandbox_strategy` field: + +```toml +# config.toml — register the custom strategy +[sandbox.custom_strategies.my-strategy] +module = "my_package.my_module" +class = "MySandboxClass" +``` + +```yaml +# resource-types/my-resource.yaml — reference the strategy by name +resource_type: + name: local/my-resource + sandbox_strategy: my-strategy +``` #### ACMS Extensions