Files
cleveragents-core/docs/specification.md
T
HAL9000 510cb03d99
CI / push-validation (push) Successful in 19s
CI / helm (push) Successful in 23s
CI / build (push) Successful in 36s
CI / e2e_tests (push) Successful in 3m20s
CI / lint (push) Successful in 3m20s
CI / security (push) Successful in 4m5s
CI / quality (push) Successful in 4m19s
CI / typecheck (push) Successful in 4m25s
CI / integration_tests (push) Successful in 9m23s
CI / unit_tests (push) Has been cancelled
CI / coverage (push) Has been cancelled
CI / docker (push) Has been cancelled
CI / status-check (push) Has been cancelled
docs(spec): add v3.8.0 Server Implementation milestone plan and update status table (#7701)
Co-authored-by: CleverThis <hal9000@cleverthis.com>
Co-committed-by: CleverThis <hal9000@cleverthis.com>
2026-04-14 16:46:31 +00:00

3.0 MiB
Raw Blame History

CleverAgents Documentation (Detailed Spec)

Overview

CleverAgents is your command center for AI agents — a unified platform for orchestrating any task you want agents to accomplish, from developing large software projects to writing comprehensive technical papers, administering databases, managing cloud infrastructure, or any complex multi-step workflow.

The core value proposition is enabling long-running, complex, large-scale tasks to execute autonomously with minimal human intervention, making it ideal for building entire software systems, producing extensive documentation, or managing sophisticated operations largely hands-off.

!!! info "Server Mode"

In **server mode**, CleverAgents becomes a collaborative hub where teams can share resources — prompts, actors, actions, and projects — while executing plans in the cloud. This enables a consistent experience across all your devices: start a complex task on your laptop, check progress from your phone, and review results from any machine.

!!! note "Runtime Foundation"

While CleverAgents leverages LangGraph and LangChain for the underlying LLM runtime primitives (tool calling, graphs, routing), its value lies in what it builds on top of these foundations.

!!! success "Core Capabilities"

- [x] A **first-class plan lifecycle** (Action templates driving Strategize / Execute / Apply phases) for breaking down and tracking complex work
- [x] A **project + resource model** for grounding tasks in real codebases, databases, documents, and infrastructure
- [x] A consistent **actor abstraction** for defining and composing intelligent agents
- [x] An independently registered **resource abstraction** for representing anything that can be read, written, or queried
- [x] An independently registered **tool abstraction** for reusable, callable operations with resource bindings
- [x] A **validation abstraction** that extends the tool concept with pass/fail semantics and resource-centric attachment with optional project/plan scoping
- [x] A consistent **skill abstraction** for organizing tools into composable capability collections
- [x] A **sandbox + checkpoint** safety model for safe, reversible execution
- [x] A **CLI/TUI/Web UX** for controlling and monitoring large multi-step autonomous work

!!! example "Advanced Subsystems"

- A scalable **Advanced Context Management System (ACMS)** with a Universal Knowledge Ontology (UKO), a demand-driven Context Request Protocol (CRP), pluggable context strategies, a fusion coordinator, and hot/warm/cold tiers with per-actor views.
- **Invariants** as first-class constraints (global, project, action, and plan scoped) that flow into the decision tree, with precedence-based conflict resolution via the Invariant Reconciliation Actor.
- A future-facing ==correction model== where the user can "edit the decision tree" and only recompute affected subtrees.

Standards Alignment

CleverAgents deliberately adopts open, versioned protocols wherever possible so that clients, tools, and skills can interoperate without bespoke integrations.

!!! tip "Guiding Principles"

1. **Prefer open protocols** — align with community standards to keep integrations portable and reduce vendor lock-in.
2. **Keep adapters at the edge** — standards map into stable internal domain models so core logic remains protocol-agnostic.

The following standards are integrated into the architecture:

Standard Role Key Benefit
A2A (Agent-to-Agent Protocol) Versioned client-server contract for messaging, task lifecycle, plan management, registry access, and event streaming Clients are interchangeable; Agent Card discovery enables ecosystem interoperability; reliable remote execution in server mode
MCP (Model Context Protocol) Discovering and invoking external tools over a server boundary Plug-and-play access to a growing ecosystem of tool providers
LSP (Language Server Protocol) Attaching language intelligence (diagnostics, type info, symbol navigation, completions) to actors and agents Actors gain semantic code understanding from the mature LSP ecosystem without bespoke language analysis
Agent Skills (AgentSkills.io) Packaging instruction-driven, multi-step workflows as SKILL.md with progressive disclosure Teaches agents how to accomplish complex tasks, complementing MCP tools

??? info "Agent-to-Agent Protocol (A2A) -- Details"

CleverAgents adopts the external **Agent-to-Agent (A2A) Protocol** standard ([a2a-protocol.org](https://a2a-protocol.org)) as the **sole** communication protocol for all client-server interaction. A2A is the successor to the Agent Client Protocol (ACP), which is now deprecated; A2A retains backward compatibility with ACP's JSON-RPC 2.0 foundation. A2A is built on **JSON-RPC 2.0** (with additional gRPC and REST bindings available) and defines the **fundamental boundary between the Presentation and Application layers** — every client operation flows through A2A regardless of deployment mode. The standard provides operations for messaging (`message/send`, `message/stream`), task lifecycle management, streaming updates via SSE, and **Agent Card**-based capability discovery. CleverAgents extends the standard with `_cleveragents/`-prefixed extension methods (declared via the A2A extension mechanism) for platform operations: plan lifecycle, registry CRUD, entity sync, namespace management, and diagnostics. In local mode, A2A flows over **stdio** via the JSON-RPC binding (agent as subprocess) with platform operations resolved in-process via `A2aLocalFacade`. In server mode, A2A flows over **HTTP** to the CleverAgents server. Both transports use the A2A Python SDK. All clients — CLI, TUI, IDE plugin, and third-party — communicate exclusively through A2A. See [ADR-047](adr/ADR-047-acp-standard-adoption.md), [ADR-048](adr/ADR-048-server-application-architecture.md), and [Server and Client Architecture](#server-and-client-architecture) for the full detail.

??? info "Model Context Protocol (MCP) -- Details"

The standard for discovering and invoking external tools over a server boundary. MCP servers are bridged into the Tool Registry via adapters, giving CleverAgents plug-and-play access to a growing ecosystem of tool providers.

??? info "Language Server Protocol (LSP) -- Details"

The standard for attaching language intelligence to actors and agents. LSP servers are registered in a global LSP Registry (namespaced like all other entities) and bound to actor graph nodes via YAML configuration. When an actor activates, the LSP Runtime starts the appropriate language servers for the actor's bound languages and workspace resources. LSP capabilities — diagnostics, type information, symbol navigation, completions, references, rename, code actions, and more — are exposed to the actor as callable tools (via the `LSPToolAdapter`) and as automatic context enrichment (diagnostics and type annotations injected into the ACMS hot context). Actors can bind LSP servers explicitly by name, by language, or automatically based on the languages detected in their project's resources. Different nodes in an actor's graph can have different LSP bindings, enabling fine-grained control over which agents receive which language intelligence capabilities. See [LSP Integration](#lsp-integration) for the full architectural detail and [ADR-027](adr/ADR-027-language-server-protocol.md) for the decision record.

??? info "Agent Skills (AgentSkills.io) -- Details"

The standard for packaging instruction-driven, multi-step workflows as `SKILL.md` with progressive disclosure. Agent Skills complement MCP tools by teaching agents *how* to accomplish complex tasks rather than simply exposing callable functions.

Glossary

???+ abstract "Plan Lifecycle"

Plan
:   A ==ULID-identified==, hierarchical unit of work instantiated from an Action template. Progresses through four phases — **Action**, **Strategize**, **Execute**, **Apply** — persisting a decision tree at each step. May spawn child plans. Scoped to one or more projects. Top-level plans carry a namespaced name (`[[server:]namespace/]name`); child plans are identified solely by their plan ID (ULID). All plans are referenced by plan ID when precision is needed, especially in hierarchies containing subplans.

Action
:   A YAML-defined, reusable plan template specifying a description, definition of done, strategy/execution actors, typed arguments, and optional invariants. Project-agnostic until bound to one or more projects via `agents plan use`, which instantiates a Plan. Namespaced as `[[server:]namespace/]name`.

Strategize
:   Second phase of the plan lifecycle (following the Action phase). The first phase where active processing occurs. ==Read-only==: the strategy actor produces the initial decision tree — strategy choices, invariant enforcement records, resource selections, child plan blueprints — without modifying any resources.

Execute
:   Third phase of the plan lifecycle. The execution actor carries out decisions from Strategize — invoking tools, spawning child plans, producing artifacts, creating checkpoints — with all mutations confined to a sandbox. May also create additional decisions constrained by Strategize-phase decisions, based on runtime discoveries.

Apply
:   Fourth and final phase of the plan lifecycle. Merges the sandbox changeset into real project resources. Terminal states: `applied` (success), `constrained` (cannot complete — may revert to Strategize), `errored` (failed), `cancelled` (user/system cancelled).

Decision
:   A persisted choice point in a plan's decision tree, created during Strategize or Execute. Records the question, chosen option, alternatives, confidence score, rationale, context snapshot, and downstream dependencies. Types: `prompt_definition`, `invariant_enforced`, `strategy_choice`, `subplan_spawn`, `subplan_parallel_spawn`, among others. Supports targeted correction with selective subtree recomputation.

Invariant
:   A natural-language constraint on plan execution scoped to global, project, action, or plan level. The runtime precedence chain is four-tier: ==plan > action > project > global==. Exception: global invariants marked `non_overridable` always win regardless of scope. Reconciled by the Invariant Reconciliation Actor at the start of Strategize; recorded as `invariant_enforced` decisions that propagate to child plans.

Automation Profile
:   A named set of confidence thresholds (each `0.0``1.0`) gating which plan operations proceed automatically versus requiring human approval. `0.0` = always automatic; `1.0` = always manual. Eight built-in profiles (`manual` through `full-auto`). Custom profiles namespaced as `[[server:]namespace/]name`. Each profile composes a **Safety Profile** that controls hard safety constraints (sandbox, checkpoint, unsafe-tool gating, skill restrictions, cost/retry limits).

Safety Profile
:   A composed sub-model of an Automation Profile that groups all hard safety constraints: `require_sandbox`, `require_checkpoints`, `allow_unsafe_tools`, `require_human_approval`, `allowed_skill_categories`, `max_cost_per_plan`, `max_retries_per_step`, and `max_total_cost`. Safety profiles can also be referenced standalone on Actions when only safety constraints (without full autonomy thresholds) are needed. See [ADR-041](adr/ADR-041-safety-profile-extraction.md).

???+ abstract "Projects & Resources"

Project
:   A named scope linking resources (from the Resource Registry), context policies, invariants, and validation attachments. Does not own resources; a single resource may be linked to multiple projects. Identified solely by its namespaced name (`[[server:]namespace/]name`) — no ULID is generated. May be local or remote.

Resource
:   A ULID-identified entity registered in the Resource Registry representing anything readable, writable, or queryable (git repos, filesystems, databases, etc.). Classified as physical or virtual, typed by a Resource Type, and organized in a DAG via parent/child links.

Resource Type
:   A schema-level definition constraining a category of resources. Specifies accepted CLI arguments, physical/virtual classification, permitted parent/child type relationships, auto-discovery rules, sandbox strategy, and handler implementation. Built-in types (e.g., `git-checkout`, `fs-mount`) are unnamespaced; custom types are namespaced as `[[server:]namespace/]name`. Resource types support single inheritance via the `inherits` field — see [ADR-042](adr/ADR-042-resource-type-inheritance.md).

Resource Type Inheritance
:   A mechanism allowing one resource type to inherit properties, capabilities, child types, sandbox strategy, and handler behavior from another type via the `inherits` field in the type definition. Subtypes can selectively override or extend any inherited field. Tools bound to a parent type automatically work with all subtypes (polymorphic matching). Auto-discovery child type matching and DAG queries are also polymorphic. Single inheritance only; maximum chain depth of 5 levels. See [ADR-042](adr/ADR-042-resource-type-inheritance.md).

Devcontainer
:   A container execution environment defined by a `.devcontainer/devcontainer.json` configuration file per the [Development Containers Specification](https://containers.dev). Auto-discovered when a `git-checkout` or `fs-directory` resource contains a `.devcontainer/` directory. Represented as a `devcontainer-instance` resource type that inherits from `container-instance`. Uses lazy activation — the container is only built when first needed by a plan. See [ADR-043](adr/ADR-043-devcontainer-integration.md).

Execution Environment
:   The runtime context in which tools execute — either the host system or a specific container. Configurable at project scope (`execution_environment` preference), plan scope (`--execution-environment` flag), and resource scope (auto-detected devcontainers). Precedence resolution determines which environment is used when multiple are configured, with `priority: override` forcing a specific container and `priority: fallback` deferring to auto-detected devcontainers. See [ADR-043](adr/ADR-043-devcontainer-integration.md).

Physical Resource
:   A resource bound to a concrete, located artifact — a specific file at a specific path, a specific repo at a specific URL. Directly readable and writable by tools. Two physical resources with identical content remain distinct instances.

Virtual Resource
:   A resource representing an abstract equivalence identity that links physical resources sharing the same content, hash, or name. ==Has no location; cannot be directly read or written.== Equivalence relationships update when underlying physical resources diverge.

Resource Binding
:   The association between a tool and the resources it operates on, declared via typed resource slots. Slots resolve through **contextual binding** (from the plan's project), **static binding** (hardcoded at registration), or **parameter binding** (passed at invocation).

Resource Registry
:   The persistent catalog of all registered resources and their DAG relationships (parent/child links). One of the core registries, alongside the Tool Registry, Skill Registry, Actor Registry, Provider Registry, and LSP Registry.

???+ abstract "Tools & Skills"

Tool
:   The ==atomic unit of execution==: a namespaced, independently registered callable operation. Defined by JSON Schema inputs/outputs, capability metadata (`read_only`, `writes`, `checkpointable`), and a four-stage lifecycle (`discover` / `activate` / `execute` / `deactivate`). Sources: MCP servers, Agent Skills folders, built-ins, or custom Python. Namespaced as `[[server:]namespace/]name`.

Validation
:   A Tool subtype adding: a `mode` (`required` | `informational`) controlling whether failure blocks execution; a structured JSON return with mandatory `passed` boolean, optional `data`, and optional `message`. ==Always read-only== (`writes = false`, `checkpointable = false`). May wrap an existing Tool via `wraps` + `transform`. Managed via `agents validation add/attach/detach`; always attached to a resource, optionally scoped to a project or plan.

Anonymous Tool
:   An inline tool definition embedded in a skill YAML or actor graph node. Same schema as a named tool but unregistered, unnamespaced, and scoped only to its defining context.

Skill
:   A composable, namespaced collection of tools assembled by referencing named tools, defining inline anonymous tools, including other skills, or exposing MCP server tools and Agent Skills ([AgentSkills.io](https://AgentSkills.io)) tools. Actors reference skills by name to acquire capabilities. Namespaced as `[[server:]namespace/]name`.

MCP (Model Context Protocol)
:   A standard for exposing tools and resources over a server boundary via JSON-RPC. CleverAgents discovers MCP tools through the `MCPToolAdapter` and registers them in the Tool Registry with extended capability metadata. MCP tools can be composed into skills and used as tool nodes in actor graphs.

Agent Skills ([AgentSkills.io](https://AgentSkills.io))
:   A standard for packaging instruction-driven, multi-step workflows as `SKILL.md` files with optional `scripts/`, `references/`, and `assets/` directories, using progressive disclosure. Surfaced as tools inside skills and loaded on demand by the actor runtime.

???+ abstract "Actors & Sessions"

Actor
:   A YAML-configured conversational unit — either a single LLM/agent or a composed LangGraph of actors and tool nodes. Specialized roles: strategy actor, execution actor, estimation actor, invariant reconciliation actor. Namespaced as `[[server:]namespace/]name`.

Session
:   A persistent conversation thread tied to an orchestrator actor. Maintains message history across plans and serves as the user's natural-language interface.

Server
:   An optional shared backend providing multi-user storage, namespace resolution, and remote plan execution.

???+ abstract "Protocols & Standards"

A2A (Agent-to-Agent Protocol)
:   The external **Agent-to-Agent (A2A) Protocol** standard ([a2a-protocol.org](https://a2a-protocol.org)), built on **JSON-RPC 2.0** (with gRPC and REST bindings available), used as the **sole** communication protocol for all client-server interaction. A2A is the successor to the Agent Client Protocol (ACP), which is now deprecated; A2A retains backward compatibility with ACP's JSON-RPC 2.0 foundation. A2A defines the **fundamental boundary between the Presentation and Application layers**. Standard A2A operations handle agent messaging (`message/send`, `message/stream`), task lifecycle, streaming updates, and Agent Card-based discovery. CleverAgents `_cleveragents/`-prefixed extension methods handle platform operations (plan lifecycle, registry CRUD, entity sync, namespace management, diagnostics). In local mode A2A flows over stdio via JSON-RPC (agent as subprocess); in server mode over HTTP. Every CLI command maps to an A2A operation. See [Core Concepts > Server > A2A](#agent-to-agent-protocol-a2a) and [Server and Client Architecture](#server-and-client-architecture) for full detail.

LSP (Language Server Protocol)
:   A standard protocol for language intelligence, used in CleverAgents to attach semantic code understanding to actors and agents. LSP servers are registered in the global **LSP Registry** (namespaced as `[[server:]namespace/]name`) and bound to actor graph nodes via YAML configuration. Capabilities — diagnostics, type information, symbol navigation, completions, references, rename, code actions — are exposed as tools (via `LSPToolAdapter`) and as automatic context enrichment. Actors can bind LSP servers explicitly by name, by language, or automatically based on detected resource languages. The LSP Runtime in the Infrastructure layer manages server lifecycle, workspace mapping, and file synchronization. See [LSP Integration](#lsp-integration) for full detail.

???+ abstract "Naming & Identity"

Namespace
:   The scoping segment in the name format `[[server:]namespace/]name`. Defaults to `local/` when omitted. `local/` is reserved for local-only items. Non-`local/` namespaces with server omitted assume the default configured server. Built-in LLM actors use provider prefixes (e.g., `openai/`, `anthropic/`). Built-in resource types are unnamespaced.

ULID
:   ==Universally Unique Lexicographically Sortable Identifier.== Assigned to plans, decisions, resources, correction attempts, and validation attachments. Projects, actions, skills, and tools use their namespaced name as sole identifier instead.

???+ abstract "Context Management (ACMS)"

ACMS (Advanced Context Management System)
:   The pluggable, strategy-driven framework for assembling actor context. Comprises the UKO, CRP, pluggable context strategies, the Context Assembly Pipeline, and hot/warm/cold tiered storage with per-actor scoped views.

UKO (Universal Knowledge Ontology)
:   An RDF-based, inheritance-driven ontology representing resources at multiple abstraction levels with provenance and temporal versioning. Four layers: universal foundation, domain specializations (software, documents, data schemas, infrastructure), paradigm/format specializations (procedural programming, markdown), and technology-specific (Python, PostgreSQL). ==Semantically aware — implicit relationships are inferred from content analysis.==

CRP (Context Request Protocol)
:   A structured vocabulary through which actors declare needed information, desired detail depth, and scope.

Context Strategy
:   A pluggable retrieval component that searches for and assembles ContextFragments using a specific approach (keyword search, semantic embedding, graph navigation, temporal archaeology, etc.). Registered with the Context Assembly Pipeline; executed in parallel by the StrategyExecutor.

Context Assembly Pipeline
:   The central ACMS orchestrator. Ten pluggable Protocol-defined components in three phases: **Strategy Orchestration**, **Fragment Fusion**, and **Context Finalization**. Each component ships with a default implementation and is overridable at global, project, or plan scope.

Skeleton (context)
:   A compressed representation of a plan's accumulated context produced by the SkeletonCompressor. Propagated from parent plans to child plans as inherited context. Size governed by the `skeleton_ratio` budget parameter.

CLI Commands

!!! adr "Architecture Decision" The CLI command structure, output rendering, and interaction patterns are defined in ADR-021: CLI and Output Rendering.

Command Synopsis


agents|cleveragents  [--data-dir <DATA_PATH>] [--config-path <CONFIG_PATH>]
                     [--format (rich|color|table|plain|json|yaml)]
                     [--help|-h] [--version]
                     [--install-completion [<INST_SHELL>]] [--show-completion [<SHOW_SHELL>]]
                     [-v...] <COMMAND> [<ARGS>...]

agents version agents info agents diagnostics agents init [--yes|-y]

agents session create [--actor <ACTOR>] agents session list agents session show <SESSION_ID> agents session delete [--yes|-y] <SESSION_ID> agents session export [(--output|-o) <FILE>] <SESSION_ID> agents session import (--input|-i) <FILE> agents session tell --session <SESSION_ID> [--actor <ACTOR>] [--stream] <PROMPT>

agents project create [(--description|-d) <DESC>] [--resource <RESOURCE>]... [--invariant <INVARIANT>]... [--invariant-actor <ACTOR>] <NAME> agents project link-resource [--read-only] <PROJECT> <RESOURCE> agents project unlink-resource [--yes|-y] <PROJECT> <RESOURCE_NAME> agents project list [(--namespace|-n) NS] [<REGEX>] agents project show <PROJECT> agents project delete [--force|-f] [--yes|-y] <NAME> agents project context set[--view (strategize|execute|apply|default)] [--include-resource <INCLUDE_RESOURCE>]... [--exclude-resource <EXCLUDE_RESOURCE>]... [--include-path <INCLUDE_GLOB>]... [--exclude-path <EXCLUDE_GLOB>]... [--hot-max-tokens <N>] [--warm-max-decisions <N_WARM_MAX>] [--cold-max-decisions <N_COLD_MAX>] [--query-limit <N>] [--max-file-size <MAX_FILE_BYTES>] [--max-total-size <MAX_TOTAL_BYTES>] [--summarize|--no-summarize] [--summary-max-tokens <N>] [--strategy <STRATEGY>]... [--default-breadth <N>] [--default-depth <INT_OR_NAME>] [--depth-gradient <HOP:INT_OR_NAME>]... [--skeleton-ratio <FLOAT>] [--temporal-scope (current|recent|all)] [--auto-refresh|--no-auto-refresh] [--execution-environment <RESOURCE_NAME>] [--execution-env-priority (fallback|override)] [--clear] <PROJECT> agents project context show [--view (strategize|execute|apply|default)] <PROJECT> agents project context inspect [--view (strategize|execute|apply|default)] [--strategy <STRATEGY>] [--focus <UKO_URI>]... [--breadth <N>] [--depth <INT_OR_NAME>] <PROJECT> agents project context simulate [--view (strategize|execute|apply|default)] [--budget <TOKENS>] [--focus <UKO_URI>]... [--strategy <STRATEGY>]... <PROJECT>

agents validation add --config|-c <FILE> [--update] agents validation attach [--project <PROJECT>|--plan <PLAN_ID>] <RESOURCE> <VALIDATION> [<ARGS>...] agents validation detach [--yes|-y] <ATTACHMENT_ID>

agents actor run [(--output|-o) <OUTPUT_FILE>] [-v...] [--unsafe|-u] [--context <CONTEXT_NAME>] [--context-dir <CONTEXT_PATH>] [--load-context <LOAD_CONTEXT_NAME>] [(--temperature|-t) <TEMP>] [--allow-rxpy-in-run-mode] [--skill <SKILL>]... <NAME> <PROMPT> agents actor add --config|-c <FILE> [--update] agents actor remove <NAME> agents actor list agents actor show <NAME> agents actor context remove [--yes|-y] (--all|-a|<NAME>) agents actor context list [<REGEX>] agents actor context show <NAME> agents actor context export (--output|-o) <FILE> <NAME> agents actor context import [--update] (--input|-i) <FILE> [<NAME>] agents actor context clear [--yes|-y] (--all|-a|<NAME>)

agents skill add --config|-c <FILE> [--update] agents skill remove [--yes|-y] <NAME> agents skill list [(--namespace|-n) <NS>] [--source <SOURCE>] agents skill show <NAME> agents skill tools <NAME>

agents tool add --config|-c <FILE> [--update] agents tool remove [--yes|-y] <NAME> agents tool list [(--namespace|-n) <NS>] [--source <SOURCE>] [--type (tool|validation)] [<REGEX>] agents tool show <NAME>

agents lsp add --config|-c <FILE> [--update] agents lsp remove [--yes|-y] <NAME> agents lsp list [(--namespace|-n) <NS>] [--language <LANG>] agents lsp show <NAME> agents lsp serve [--log-level <LEVEL>]

agents resource type add --config|-c <FILE> [--update] agents resource type remove [--yes|-y] <NAME> agents resource type list [<REGEX>] agents resource type show <NAME>

agents resource add [(--description|-d) <DESC>] [--update] <TYPE> <NAME> [type-specific-flags...] agents resource remove [--yes|-y] <NAME> agents resource list [--all] [(--type|-t) <TYPE>] agents resource show <RESOURCE> agents resource inspect [--tree] [--file <PATH>] <RESOURCE> agents resource tree [(--depth|-d) <N>] [(--type|-t) <TYPE>] <RESOURCE> agents resource link-child <PARENT> <CHILD> agents resource unlink-child [--yes|-y] <PARENT> <CHILD> agents resource stop <NAME> agents resource rebuild <NAME>

agents plan list [--phase <PHASE>] [--state <STATE>] [--project <PROJECT>] [--action <ACTION>] [<REGEX>] agents plan use [--automation-profile <PROFILE>] [--invariant <INVARIANT>]... [--strategy-actor <STRATEGY_ACTOR>] [--execution-actor <EXEC_ACTOR>] [--estimation-actor <EST_ACTOR>] [--invariant-actor <INV_ACTOR>] [--execution-environment <RESOURCE_NAME>] [--execution-env-priority (fallback|override)] [--arg/-a name=value]... <ACTION> <PROJECT>... agents plan execute <PLAN_ID> agents plan apply [--yes|-y] <PLAN_ID> agents plan status <PLAN_ID> agents plan cancel [(--reason|-r) <REASON>] <PLAN_ID> agents plan tree [--show-superseded] <PLAN_ID> agents plan explain [--show-context] [--show-reasoning] <DECISION_ID> agents plan correct --mode (revert|append) (--guidance|-g) <GUIDANCE> [--dry-run] [--yes|-y] <DECISION_ID> agents plan diff (--correction <CORRECTION_ATTEMPT_ID>|<PLAN_ID>) agents plan artifacts <PLAN_ID> agents plan prompt <PLAN_ID> <GUIDANCE> agents plan rollback [--yes|-y] <PLAN_ID> <CHECKPOINT_ID> agents plan errors <PLAN_ID>

agents action create --config|-c <CFG_FILE> agents action list [(--namespace|-n) <NS>] [(--state|-s) <STATE>] [<REGEX>] agents action show <ACTION_NAME> agents action archive <ACTION_NAME>

agents automation-profile add --config|-c <FILE> [--update] agents automation-profile remove [--yes|-y] <NAME> agents automation-profile list [<REGEX>] agents automation-profile show <NAME>

agents config set <key> <value> agents config get <key> agents config list [--filter-values <REGEX>] [<REGEX>]

agents invariant add [--global] [(--project|-p) PROJECT] [--plan PLAN_ID]... [--action ACTION]... <INVARIANT_TEXT> agents invariant list [--global] [(--project|-p) PROJECT] [--plan PLAN_ID] [--action ACTION] [--effective] [<REGEX>] agents invariant remove [--yes|-y] <INVARIANT_ID>

Command Reference

!!! info "Output Format Tabs" Each command example below is shown in multiple output formats using content tabs. Select a tab to see how the same command renders in that format. The table format is visually identical to rich in static documentation (the difference is live animation). The color format has the same layout as plain but with ANSI color codes applied to headers, keys, and values. See Output Rendering Framework for full format specifications.

Global Options

!!! note "Purpose"

Configure global state locations and shell integration for every command.

Arguments

  • --data-dir PATH: Overrides the global data directory (database, caches, sessions, logs). When omitted, the default data location is used.

  • --config-path PATH: Overrides the global configuration file path. When omitted, the default config path is used.

  • --format rich|color|table|plain|json|yaml: Set the output rendering format for all subcommands. When omitted, the value is read from the global config key core.format. If not set in config either, defaults to rich. See Output Rendering Framework for full details on each format.

    ??? tip "Available Output Formats"

      | Format | Description | Best For |
      | :----- | :---------- | :------- |
      | `rich` | Modern rich CLI elements with dynamic effects, animated spinners, progress bars, and generous color | Interactive terminal use **(default)** |
      | `color` | Plain scrolling text with ANSI color codes | Terminals that support color but not advanced rendering |
      | `table` | ASCII box-drawing characters to create structured tables and panels with color | Structured visual output similar to examples in this document |
      | `plain` | Plain text with no color codes or non-ASCII characters | Piping into files, logs, or non-terminal consumers |
      | `json` | Structured JSON output for programmatic consumption | Scripts, CI/CD pipelines, programmatic consumption |
      | `yaml` | Structured YAML output for programmatic consumption | Configuration, programmatic consumption |
    
  • --help, -h: Print help for the current command.

  • --version: Print the version and exit.

  • --install-completion [SHELL]: Install shell completion for the given shell.

  • --show-completion [SHELL]: Show the completion script for the given shell.

  • -v: Increase log verbosity for a single invocation (repeatable). By default (no -v), only normal command output is shown on stdout; logging output is suppressed except on fatal errors where the application exits entirely and displays the error. Each additional -v raises the verbosity one level: -v = ERROR (non-fatal, recoverable errors), -vv = WARN (warnings that may indicate a degraded system but are not necessarily errors), -vvv = INFO (routine informative messages useful to common users), -vvvv = DEBUG (coarse debugging output), -vvvvv = TRACE (the most granular logging level available). Increased verbosity writes to both the log file and stderr by default, keeping log output separate from normal program output on stdout. The destination for log output raised by -v is configurable: it can be sent to the terminal (stderr by default, or redirected to another stream such as stdin or a device), to the log file only, or to both. See core.log.terminal, core.log.terminal-stream, and core.log.file-enabled configuration keys for details. This flag does not persist — it only affects the current invocation.

Examples

=== "Rich"

<div class="highlight"><pre><code>
<span style="color: #66cc66; font-weight: 600;">$</span> <span style="color: cyan; font-weight: 600;">agents</span> <span style="color: cyan;">--data-dir</span> /srv/cleveragents <span style="color: cyan;">--config-path</span> /srv/cleveragents/config.toml info

╭─ System Snapshot ─────────────────╮
│ <span style="color: cyan; font-weight: 600;">CleverAgents</span> 1.0.0                │
│ <span style="color: #5599ff; font-weight: 600;">Mode:</span> local                       │
│ <span style="color: magenta; font-weight: 600;">Automation:</span> review                │
│ <span style="color: #66cc66; font-weight: 600;">Status:</span> ready                     │
╰───────────────────────────────────╯

╭─ Paths ───────────────────────────────╮
│ <span style="color: #5599ff; font-weight: 600;">Data Dir:</span> /srv/cleveragents           │
│ <span style="color: #5599ff; font-weight: 600;">Config:</span> /srv/cleveragents/config.toml │
│ <span style="color: #5599ff; font-weight: 600;">Logs:</span> /srv/cleveragents/logs          │
│ <span style="color: #5599ff; font-weight: 600;">Cache:</span> /srv/cleveragents/cache        │
│ <span style="color: #5599ff; font-weight: 600;">Database:</span> /srv/cleveragents/agents.db │
╰───────────────────────────────────────╯

╭─ Runtime ──────────╮
│ <span style="color: yellow; font-weight: 600;">PID:</span> 4127          │
│ <span style="color: #66cc66; font-weight: 600;">Uptime:</span> 00:14:32   │
│ <span style="color: #5599ff; font-weight: 600;">Python:</span> 3.13.1     │
│ <span style="color: #5599ff; font-weight: 600;">Host:</span> devbox.local │
│ <span style="color: #5599ff; font-weight: 600;">Platform:</span> linux    │
╰────────────────────╯

╭─ Projects & Sessions ─╮
│ <span style="color: #5599ff; font-weight: 600;">Projects:</span> 2           │
│ <span style="color: #5599ff; font-weight: 600;">Sessions:</span> 1 active    │
│ <span style="color: #5599ff; font-weight: 600;">Active Plans:</span> 0       │
│ <span style="color: #5599ff; font-weight: 600;">Actors:</span> 3             │
╰───────────────────────╯

<span style="color: #66cc66; font-weight: 600;">✓ OK</span> Environment loaded
</code></pre></div>

=== "Plain"

```
$ agents --data-dir /srv/cleveragents --config-path /srv/cleveragents/config.toml info

System Snapshot
  CleverAgents 1.0.0
  Mode: local
  Automation: review
  Status: ready

Paths
  Data Dir: /srv/cleveragents
  Config: /srv/cleveragents/config.toml
  Logs: /srv/cleveragents/logs
  Cache: /srv/cleveragents/cache
  Database: /srv/cleveragents/agents.db

Runtime
  PID: 4127
  Uptime: 00:14:32
  Python: 3.13.1
  Host: devbox.local
  Platform: linux

Projects & Sessions
  Projects: 2
  Sessions: 1 active
  Active Plans: 0
  Actors: 3

[OK] Environment loaded
```

=== "JSON"

```json
{
  "command": "info",
  "status": "ok",
  "exit_code": 0,
  "data": {
    "system_snapshot": {
      "name": "CleverAgents",
      "version": "1.0.0",
      "mode": "local",
      "automation": "review",
      "status": "ready"
    },
    "paths": {
      "data_dir": "/srv/cleveragents",
      "config": "/srv/cleveragents/config.toml",
      "logs": "/srv/cleveragents/logs",
      "cache": "/srv/cleveragents/cache",
      "database": "/srv/cleveragents/agents.db"
    },
    "runtime": {
      "pid": 4127,
      "uptime": "00:14:32",
      "python": "3.13.1",
      "host": "devbox.local",
      "platform": "linux"
    },
    "projects_and_sessions": {
      "projects": 2,
      "sessions": "1 active",
      "active_plans": 0,
      "actors": 3
    }
  },
  "timing": {
    "duration_ms": 12
  },
  "messages": [
    { "level": "ok", "text": "Environment loaded" }
  ]
}
```

=== "YAML"

```yaml
command: info
status: ok
exit_code: 0
data:
  system_snapshot:
    name: CleverAgents
    version: "1.0.0"
    mode: local
    automation: review
    status: ready
  paths:
    data_dir: /srv/cleveragents
    config: /srv/cleveragents/config.toml
    logs: /srv/cleveragents/logs
    cache: /srv/cleveragents/cache
    database: /srv/cleveragents/agents.db
  runtime:
    pid: 4127
    uptime: "00:14:32"
    python: "3.13.1"
    host: devbox.local
    platform: linux
  projects_and_sessions:
    projects: 2
    sessions: 1 active
    active_plans: 0
    actors: 3
timing:
  duration_ms: 12
messages:
  - level: ok
    text: Environment loaded
```

agents version

agents version

Purpose Print the current CLI version.

Arguments

None.

Examples

=== "Rich"

<div class="highlight"><pre><code>
<span style="color: #66cc66; font-weight: 600;">$</span> <span style="color: cyan; font-weight: 600;">agents</span> version

╭─ CLI Version ────╮
│ <span style="color: cyan; font-weight: 600;">CleverAgents CLI</span> │
│ <span style="color: #5599ff; font-weight: 600;">Version:</span> 1.0.0   │
│ <span style="color: #5599ff; font-weight: 600;">Channel:</span> stable  │
│ <span style="color: #5599ff; font-weight: 600;">Python:</span> 3.13     │
╰──────────────────╯

╭─ Build ────────────────╮
│ <span style="color: #66cc66; font-weight: 600;">Build Date:</span> 2026-02-08 │
│ <span style="color: magenta; font-weight: 600;">Commit:</span> a17c3f9        │
│ <span style="color: #5599ff; font-weight: 600;">Schema:</span> v3             │
│ <span style="color: #5599ff; font-weight: 600;">Platform:</span> linux-x86_64 │
╰────────────────────────╯

╭─ Dependencies ─────────────────╮
│ <span style="color: #5599ff; font-weight: 600;">LangGraph:</span> 0.2.60              │
│ <span style="color: #5599ff; font-weight: 600;">LangChain:</span> 0.3.18              │
│ <span style="color: #5599ff; font-weight: 600;">MCP SDK:</span> 1.4.0                 │
│ <span style="color: #5599ff; font-weight: 600;">Pydantic:</span> 2.10.4               │
╰────────────────────────────────╯

<span style="color: #66cc66; font-weight: 600;">✓ OK</span> Version reported
</code></pre></div>

=== "Plain"

```
$ agents version

CLI Version
  CleverAgents CLI
  Version: 1.0.0
  Channel: stable
  Python: 3.13

Build
  Build Date: 2026-02-08
  Commit: a17c3f9
  Schema: v3
  Platform: linux-x86_64

Dependencies
  LangGraph: 0.2.60
  LangChain: 0.3.18
  MCP SDK: 1.4.0
  Pydantic: 2.10.4

[OK] Version reported
```

=== "JSON"

```json
{
  "command": "version",
  "status": "ok",
  "exit_code": 0,
  "data": {
    "cli": {
      "name": "CleverAgents CLI",
      "version": "1.0.0",
      "channel": "stable",
      "python": "3.13"
    },
    "build": {
      "build_date": "2026-02-08",
      "commit": "a17c3f9",
      "schema": "v3",
      "platform": "linux-x86_64"
    },
    "dependencies": {
      "langgraph": "0.2.60",
      "langchain": "0.3.18",
      "mcp_sdk": "1.4.0",
      "pydantic": "2.10.4"
    }
  },
  "timing": {
    "duration_ms": 8
  },
  "messages": [
    { "level": "ok", "text": "Version reported" }
  ]
}
```

=== "YAML"

```yaml
command: version
status: ok
exit_code: 0
data:
  cli:
    name: CleverAgents CLI
    version: "1.0.0"
    channel: stable
    python: "3.13"
  build:
    build_date: "2026-02-08"
    commit: a17c3f9
    schema: v3
    platform: linux-x86_64
  dependencies:
    langgraph: "0.2.60"
    langchain: "0.3.18"
    mcp_sdk: "1.4.0"
    pydantic: "2.10.4"
timing:
  duration_ms: 8
messages:
  - level: ok
    text: Version reported
```

agents info

agents info

Purpose Show configuration and runtime information useful for debugging or support.

Arguments

None.

Examples

=== "Rich"

<div class="highlight"><pre><code>
<span style="color: #66cc66; font-weight: 600;">$</span> <span style="color: cyan; font-weight: 600;">agents</span> info

╭─ Environment ───────────────────────────────────────────────╮
│ <span style="color: #5599ff; font-weight: 600;">Data Dir:</span> /home/alex/.cleveragents                          │
│ <span style="color: #5599ff; font-weight: 600;">Config:</span> /home/alex/.cleveragents/config.toml                │
│ <span style="color: #66cc66; font-weight: 600;">Database:</span> sqlite:///home/alex/.cleveragents/cleveragents.db │
│ <span style="color: yellow; font-weight: 600;">Server Mode:</span> disabled                                       │
│ <span style="color: #5599ff; font-weight: 600;">Platform:</span> Linux 6.8.0 (x86_64)                              │
╰─────────────────────────────────────────────────────────────╯

╭─ Runtime ─────────────────────────╮
│ <span style="color: magenta; font-weight: 600;">Automation:</span> review                │
│ <span style="color: #5599ff; font-weight: 600;">Providers:</span> 3 configured           │
│ <span style="color: yellow; font-weight: 600;">Sessions:</span> 2 active                │
│ <span style="color: #5599ff; font-weight: 600;">Active Plans:</span> 1                   │
╰───────────────────────────────────╯

╭─ Storage ─────╮
│ <span style="color: #5599ff; font-weight: 600;">Cache:</span> 118 MB │
│ <span style="color: #5599ff; font-weight: 600;">Logs:</span> 42 MB   │
│ <span style="color: yellow; font-weight: 600;">Backups:</span> 3    │
│ <span style="color: #5599ff; font-weight: 600;">DB Size:</span> 8 MB │
╰───────────────╯

╭─ Indexing ─────────────╮
│ <span style="color: #66cc66; font-weight: 600;">Text Index:</span> ready      │
│ <span style="color: #66cc66; font-weight: 600;">Vector Index:</span> ready    │
│ <span style="color: yellow; font-weight: 600;">Graph Store:</span> disabled  │
│ <span style="color: #5599ff; font-weight: 600;">Indexed Files:</span> 1,247   │
╰────────────────────────╯

<span style="color: #66cc66; font-weight: 600;">✓ OK</span> Environment details ready
</code></pre></div>

=== "Plain"

```
$ agents info

Environment
  Data Dir: /home/alex/.cleveragents
  Config: /home/alex/.cleveragents/config.toml
  Database: sqlite:///home/alex/.cleveragents/cleveragents.db
  Server Mode: disabled
  Platform: Linux 6.8.0 (x86_64)

Runtime
  Automation: review
  Providers: 3 configured
  Sessions: 2 active
  Active Plans: 1

Storage
  Cache: 118 MB
  Logs: 42 MB
  Backups: 3
  DB Size: 8 MB

Indexing
  Text Index: ready
  Vector Index: ready
  Graph Store: disabled
  Indexed Files: 1,247

[OK] Environment details ready
```

=== "JSON"

```json
{
  "command": "info",
  "status": "ok",
  "exit_code": 0,
  "data": {
    "environment": {
      "data_dir": "/home/alex/.cleveragents",
      "config": "/home/alex/.cleveragents/config.toml",
      "database": "sqlite:///home/alex/.cleveragents/cleveragents.db",
      "server_mode": "disabled",
      "platform": "Linux 6.8.0 (x86_64)"
    },
    "runtime": {
      "automation": "review",
      "providers": 3,
      "sessions": "2 active",
      "active_plans": 1
    },
    "storage": {
      "cache_mb": 118,
      "logs_mb": 42,
      "backups": 3,
      "db_size_mb": 8
    },
    "indexing": {
      "text_index": "ready",
      "vector_index": "ready",
      "graph_store": "disabled",
      "indexed_files": 1247
    }
  },
  "timing": {
    "duration_ms": 18
  },
  "messages": [
    { "level": "ok", "text": "Environment details ready" }
  ]
}
```

=== "YAML"

```yaml
command: info
status: ok
exit_code: 0
data:
  environment:
    data_dir: /home/alex/.cleveragents
    config: /home/alex/.cleveragents/config.toml
    database: sqlite:///home/alex/.cleveragents/cleveragents.db
    server_mode: disabled
    platform: Linux 6.8.0 (x86_64)
  runtime:
    automation: review
    providers: 3
    sessions: 2 active
    active_plans: 1
  storage:
    cache_mb: 118
    logs_mb: 42
    backups: 3
    db_size_mb: 8
  indexing:
    text_index: ready
    vector_index: ready
    graph_store: disabled
    indexed_files: 1247
timing:
  duration_ms: 18
messages:
  - level: ok
    text: Environment details ready
```

agents diagnostics

agents diagnostics

Purpose Run health checks for configuration, providers, and filesystem permissions.

Arguments

None.

Examples

=== "Rich"

<div class="highlight"><pre><code>
<span style="color: #66cc66; font-weight: 600;">$</span> <span style="color: cyan; font-weight: 600;">agents</span> diagnostics

╭─ Checks ────────────────────────────────╮
│ <span style="color: cyan; font-weight: 600;">Check</span>            <span style="color: cyan; font-weight: 600;">Status</span>  <span style="color: cyan; font-weight: 600;">Details</span>        │
│ <span style="opacity: 0.7;">───────────────  ──────  ──────────────</span> │
│ Config file      <span style="color: #66cc66; font-weight: 600;">OK</span>      readable       │
│ Database         <span style="color: #66cc66; font-weight: 600;">OK</span>      writable       │
│ OPENAI_API_KEY   <span style="color: yellow; font-weight: 600;">WARN</span>    missing        │
│ Anthropic key    <span style="color: #66cc66; font-weight: 600;">OK</span>      configured     │
│ Disk space       <span style="color: #66cc66; font-weight: 600;">OK</span>      2.1 GB free    │
│ Text index       <span style="color: #66cc66; font-weight: 600;">OK</span>      tantivy 0.22   │
│ Vector index     <span style="color: #66cc66; font-weight: 600;">OK</span>      faiss (CPU)    │
│ Graph store      <span style="color: yellow; font-weight: 600;">WARN</span>    not configured │
│ File permissions <span style="color: #66cc66; font-weight: 600;">OK</span>      data dir r/w   │
│ Git              <span style="color: #66cc66; font-weight: 600;">OK</span>      git 2.43.0     │
╰─────────────────────────────────────────╯

╭─ Summary ─────────╮
│ <span style="color: #5599ff; font-weight: 600;">Checks:</span> 10 total  │
│ <span style="color: yellow; font-weight: 600;">Warnings:</span> 2       │
│ <span style="color: #ff6666; font-weight: 600;">Errors:</span> 0         │
│ <span style="color: #66cc66; font-weight: 600;">Duration:</span> 0.6s    │
╰───────────────────╯

╭─ Recommendations ─────────────────────────────────────────────╮
│ - Set OPENAI_API_KEY to enable OpenAI models                  │
│ - Configure a graph store backend for structural code queries │
│ - Verify provider credentials via config                      │
╰───────────────────────────────────────────────────────────────╯

<span style="color: yellow; font-weight: 600;">⚠ WARN</span> 2 warnings require attention
</code></pre></div>

=== "Plain"

```
$ agents diagnostics

Checks
  Check            Status  Details
  ---------------  ------  --------------
  Config file      OK      readable
  Database         OK      writable
  OPENAI_API_KEY   WARN    missing
  Anthropic key    OK      configured
  Disk space       OK      2.1 GB free
  Text index       OK      tantivy 0.22
  Vector index     OK      faiss (CPU)
  Graph store      WARN    not configured
  File permissions OK      data dir r/w
  Git              OK      git 2.43.0

Summary
  Checks: 10 total
  Warnings: 2
  Errors: 0
  Duration: 0.6s

Recommendations
  - Set OPENAI_API_KEY to enable OpenAI models
  - Configure a graph store backend for structural code queries
  - Verify provider credentials via config

[WARN] 2 warnings require attention
```

=== "JSON"

```json
{
  "command": "diagnostics",
  "status": "ok",
  "exit_code": 0,
  "data": {
    "checks": [
      { "check": "Config file", "status": "ok", "details": "readable" },
      { "check": "Database", "status": "ok", "details": "writable" },
      { "check": "OPENAI_API_KEY", "status": "warn", "details": "missing" },
      { "check": "Anthropic key", "status": "ok", "details": "configured" },
      { "check": "Disk space", "status": "ok", "details": "2.1 GB free" },
      { "check": "Text index", "status": "ok", "details": "tantivy 0.22" },
      { "check": "Vector index", "status": "ok", "details": "faiss (CPU)" },
      { "check": "Graph store", "status": "warn", "details": "not configured" },
      { "check": "File permissions", "status": "ok", "details": "data dir r/w" },
      { "check": "Git", "status": "ok", "details": "git 2.43.0" }
    ],
    "summary": {
      "total": 10,
      "warnings": 2,
      "errors": 0,
      "duration_s": 0.6
    },
    "recommendations": [
      "Set OPENAI_API_KEY to enable OpenAI models",
      "Configure a graph store backend for structural code queries",
      "Verify provider credentials via config"
    ]
  },
  "timing": {
    "duration_ms": 600
  },
  "messages": [
    { "level": "warn", "text": "2 warnings require attention" }
  ]
}
```

=== "YAML"

```yaml
command: diagnostics
status: ok
exit_code: 0
data:
  checks:
    - check: Config file
      status: ok
      details: readable
    - check: Database
      status: ok
      details: writable
    - check: OPENAI_API_KEY
      status: warn
      details: missing
    - check: Anthropic key
      status: ok
      details: configured
    - check: Disk space
      status: ok
      details: 2.1 GB free
    - check: Text index
      status: ok
      details: tantivy 0.22
    - check: Vector index
      status: ok
      details: faiss (CPU)
    - check: Graph store
      status: warn
      details: not configured
    - check: File permissions
      status: ok
      details: data dir r/w
    - check: Git
      status: ok
      details: git 2.43.0
  summary:
    total: 10
    warnings: 2
    errors: 0
    duration_s: 0.6
  recommendations:
    - Set OPENAI_API_KEY to enable OpenAI models
    - Configure a graph store backend for structural code queries
    - Verify provider credentials via config
timing:
  duration_ms: 600
messages:
  - level: warn
    text: 2 warnings require attention
```

When critical checks fail, diagnostics reports errors:

=== "Rich"

<div class="highlight"><pre><code>
<span style="color: #66cc66; font-weight: 600;">$</span> <span style="color: cyan; font-weight: 600;">agents</span> diagnostics

╭─ Checks ────────────────────────────────────────────╮
│ <span style="color: cyan; font-weight: 600;">Check</span>            <span style="color: cyan; font-weight: 600;">Status</span>   <span style="color: cyan; font-weight: 600;">Details</span>                   │
│ <span style="opacity: 0.7;">───────────────  ───────  ────────────────────</span>      │
│ Config file      <span style="color: #66cc66; font-weight: 600;">OK</span>       readable                  │
│ Database         <span style="color: #ff6666; font-weight: 600;">ERROR</span>    locked by another process │
│ OPENAI_API_KEY   <span style="color: #66cc66; font-weight: 600;">OK</span>       configured                │
│ Anthropic key    <span style="color: #ff6666; font-weight: 600;">ERROR</span>    invalid key format        │
│ Disk space       <span style="color: yellow; font-weight: 600;">WARN</span>     312 MB free (low)         │
│ Text index       <span style="color: #66cc66; font-weight: 600;">OK</span>       tantivy 0.22              │
│ Vector index     <span style="color: #ff6666; font-weight: 600;">ERROR</span>    FAISS library not found   │
│ Graph store      <span style="color: #66cc66; font-weight: 600;">OK</span>       neo4j 5.15                │
│ File permissions <span style="color: #66cc66; font-weight: 600;">OK</span>       data dir r/w              │
│ Git              <span style="color: #66cc66; font-weight: 600;">OK</span>       git 2.43.0                │
╰─────────────────────────────────────────────────────╯

╭─ Summary ─────────╮
│ <span style="color: #5599ff; font-weight: 600;">Checks:</span> 10 total  │
│ <span style="color: yellow; font-weight: 600;">Warnings:</span> 1       │
│ <span style="color: #ff6666; font-weight: 600;">Errors:</span> 3         │
│ <span style="color: #66cc66; font-weight: 600;">Duration:</span> 1.2s    │
╰───────────────────╯

╭─ Errors (must fix) ─────────────────────────────────────────────────╮
│ <span style="color: #ff6666; font-weight: 600;">1.</span> Database is locked by PID 12847 — stop the other process or      │
│    delete the lock file at ~/.cleveragents/agents.db-lock           │
│ <span style="color: #ff6666; font-weight: 600;">2.</span> Anthropic key starts with <span style="color: #66cc66;">"pk-"</span> — expected <span style="color: #66cc66;">"sk-ant-"</span> prefix      │
│    Run: agents config set provider.anthropic.api-key <correct-key>               │
│ <span style="color: #ff6666; font-weight: 600;">3.</span> FAISS library not installed — vector search will not work        │
│    Run: pip install faiss-cpu                                       │
╰─────────────────────────────────────────────────────────────────────╯

<span style="color: #ff6666; font-weight: 600;">✗ ERROR</span> 3 errors must be resolved before CleverAgents can operate
</code></pre></div>

=== "Plain"

```
$ agents diagnostics

Checks
  Check            Status   Details
  ---------------  -------  ------------------------
  Config file      OK       readable
  Database         ERROR    locked by another process
  OPENAI_API_KEY   OK       configured
  Anthropic key    ERROR    invalid key format
  Disk space       WARN     312 MB free (low)
  Text index       OK       tantivy 0.22
  Vector index     ERROR    FAISS library not found
  Graph store      OK       neo4j 5.15
  File permissions OK       data dir r/w
  Git              OK       git 2.43.0

Summary
  Checks: 10 total
  Warnings: 1
  Errors: 3
  Duration: 1.2s

Errors (must fix)
  1. Database is locked by PID 12847 -- stop the other process or
     delete the lock file at ~/.cleveragents/agents.db-lock
  2. Anthropic key starts with "pk-" -- expected "sk-ant-" prefix
     Run: agents config set provider.anthropic.api-key <correct-key>
  3. FAISS library not installed -- vector search will not work
     Run: pip install faiss-cpu

[ERROR] 3 errors must be resolved before CleverAgents can operate
```

=== "JSON"

```json
{
  "command": "diagnostics",
  "status": "error",
  "exit_code": 1,
  "data": {
    "checks": [
      { "check": "Config file", "status": "ok", "details": "readable" },
      { "check": "Database", "status": "error", "details": "locked by another process" },
      { "check": "OPENAI_API_KEY", "status": "ok", "details": "configured" },
      { "check": "Anthropic key", "status": "error", "details": "invalid key format" },
      { "check": "Disk space", "status": "warn", "details": "312 MB free (low)" },
      { "check": "Text index", "status": "ok", "details": "tantivy 0.22" },
      { "check": "Vector index", "status": "error", "details": "FAISS library not found" },
      { "check": "Graph store", "status": "ok", "details": "neo4j 5.15" },
      { "check": "File permissions", "status": "ok", "details": "data dir r/w" },
      { "check": "Git", "status": "ok", "details": "git 2.43.0" }
    ],
    "summary": {
      "total": 10,
      "warnings": 1,
      "errors": 3,
      "duration_s": 1.2
    },
    "errors": [
      {
        "index": 1,
        "message": "Database is locked by PID 12847 -- stop the other process or delete the lock file at ~/.cleveragents/agents.db-lock"
      },
      {
        "index": 2,
        "message": "Anthropic key starts with \"pk-\" -- expected \"sk-ant-\" prefix",
        "fix": "agents config set provider.anthropic.api-key <correct-key>"
      },
      {
        "index": 3,
        "message": "FAISS library not installed -- vector search will not work",
        "fix": "pip install faiss-cpu"
      }
    ]
  },
  "timing": {
    "duration_ms": 1200
  },
  "messages": [
    { "level": "error", "text": "3 errors must be resolved before CleverAgents can operate" }
  ]
}
```

=== "YAML"

```yaml
command: diagnostics
status: error
exit_code: 1
data:
  checks:
    - check: Config file
      status: ok
      details: readable
    - check: Database
      status: error
      details: locked by another process
    - check: OPENAI_API_KEY
      status: ok
      details: configured
    - check: Anthropic key
      status: error
      details: invalid key format
    - check: Disk space
      status: warn
      details: 312 MB free (low)
    - check: Text index
      status: ok
      details: tantivy 0.22
    - check: Vector index
      status: error
      details: FAISS library not found
    - check: Graph store
      status: ok
      details: neo4j 5.15
    - check: File permissions
      status: ok
      details: data dir r/w
    - check: Git
      status: ok
      details: git 2.43.0
  summary:
    total: 10
    warnings: 1
    errors: 3
    duration_s: 1.2
  errors:
    - index: 1
      message: >-
        Database is locked by PID 12847 -- stop the other process or
        delete the lock file at ~/.cleveragents/agents.db-lock
    - index: 2
      message: Anthropic key starts with "pk-" -- expected "sk-ant-" prefix
      fix: agents config set provider.anthropic.api-key <correct-key>
    - index: 3
      message: FAISS library not installed -- vector search will not work
      fix: pip install faiss-cpu
timing:
  duration_ms: 1200
messages:
  - level: error
    text: 3 errors must be resolved before CleverAgents can operate
```

agents init

agents init [--yes|-y]

!!! danger "Destructive Operation"

This command ==wipes all existing data== and re-creates the global config and database. A backup is created automatically, but all sessions, plans, and registry entries will be removed.

Purpose Initialize or reset the global CleverAgents environment. This wipes any existing data and re-creates the global config and database.

Arguments

  • --yes: Skip the confirmation prompt and proceed with the wipe.

Examples

=== "Rich"

<div class="highlight"><pre><code>
<span style="color: #66cc66; font-weight: 600;">$</span> <span style="color: cyan; font-weight: 600;">agents</span> init

<span style="color: yellow; font-weight: 600;">Warning</span>: This will remove all data in /home/alex/.cleveragents
Continue? [y/N]: y

╭─ Environment Reset ────────────────────────────────╮
│ <span style="color: #5599ff; font-weight: 600;">Config:</span> /home/alex/.cleveragents/config.toml       │
│ <span style="color: #66cc66; font-weight: 600;">Database:</span> /home/alex/.cleveragents/cleveragents.db │
│ <span style="color: yellow; font-weight: 600;">Backup:</span> /home/alex/.cleveragents.backup-2026-02-08 │
│ <span style="color: #66cc66; font-weight: 600;">Status:</span> ready                                      │
╰────────────────────────────────────────────────────╯

╭─ Defaults ──────────────────────────────────╮
│ <span style="color: magenta; font-weight: 600;">Automation Profile:</span> supervised              │
│ <span style="color: #5599ff; font-weight: 600;">Built-in Profiles:</span> 8 loaded                 │
╰─────────────────────────────────────────────╯

╭─ Created ─────────────────╮
│ <span style="color: #5599ff; font-weight: 600;">Config:</span> config.toml       │
│ <span style="color: #66cc66; font-weight: 600;">Database:</span> cleveragents.db │
│ <span style="color: #5599ff; font-weight: 600;">Logs:</span> logs/               │
│ <span style="color: #5599ff; font-weight: 600;">Cache:</span> cache/             │
│ <span style="color: #5599ff; font-weight: 600;">Backups:</span> backups/         │
╰───────────────────────────╯

╭─ Schema ───────────────────────╮
│ <span style="color: #5599ff; font-weight: 600;">Version:</span> v3                    │
│ <span style="color: #66cc66; font-weight: 600;">Tables:</span> 12 created             │
│ <span style="color: #5599ff; font-weight: 600;">Migrations:</span> up to date         │
╰────────────────────────────────╯

<span style="color: #66cc66; font-weight: 600;">✓ OK</span> Environment initialized
</code></pre></div>

=== "Plain"

```
$ agents init

Warning: This will remove all data in /home/alex/.cleveragents
Continue? [y/N]: y

Environment Reset
  Config: /home/alex/.cleveragents/config.toml
  Database: /home/alex/.cleveragents/cleveragents.db
  Backup: /home/alex/.cleveragents.backup-2026-02-08
  Status: ready

Defaults
  Automation Profile: supervised
  Built-in Profiles: 8 loaded

Created
  Config: config.toml
  Database: cleveragents.db
  Logs: logs/
  Cache: cache/
  Backups: backups/

Schema
  Version: v3
  Tables: 12 created
  Migrations: up to date

[OK] Environment initialized
```

=== "JSON"

```json
{
  "command": "init",
  "status": "ok",
  "exit_code": 0,
  "data": {
    "environment_reset": {
      "config": "/home/alex/.cleveragents/config.toml",
      "database": "/home/alex/.cleveragents/cleveragents.db",
      "backup": "/home/alex/.cleveragents.backup-2026-02-08",
      "status": "ready"
    },
    "defaults": {
      "automation_profile": "supervised",
      "builtin_profiles": 8
    },
    "created": {
      "config": "config.toml",
      "database": "cleveragents.db",
      "logs": "logs/",
      "cache": "cache/",
      "backups": "backups/"
    },
    "schema": {
      "version": "v3",
      "tables": 12,
      "migrations": "up to date"
    }
  },
  "timing": {
    "duration_ms": 340
  },
  "messages": [
    { "level": "ok", "text": "Environment initialized" }
  ]
}
```

=== "YAML"

```yaml
command: init
status: ok
exit_code: 0
data:
  environment_reset:
    config: /home/alex/.cleveragents/config.toml
    database: /home/alex/.cleveragents/cleveragents.db
    backup: /home/alex/.cleveragents.backup-2026-02-08
    status: ready
  defaults:
    automation_profile: supervised
    builtin_profiles: 8
  created:
    config: config.toml
    database: cleveragents.db
    logs: logs/
    cache: cache/
    backups: backups/
  schema:
    version: v3
    tables: 12
    migrations: up to date
timing:
  duration_ms: 340
messages:
  - level: ok
    text: Environment initialized
```

Non-interactive initialization using --yes (useful in scripts and CI):

=== "Rich"

<div class="highlight"><pre><code>
<span style="color: #66cc66; font-weight: 600;">$</span> <span style="color: cyan; font-weight: 600;">agents</span> init <span style="color: cyan;">--yes</span>

╭─ Initialized ──────────────────────────────────────────╮
│ <span style="color: #66cc66; font-weight: 600;">Data Dir:</span> /home/alex/.cleveragents (created)           │
│ <span style="color: #66cc66; font-weight: 600;">Config:</span> /home/alex/.cleveragents/config.toml           │
│ <span style="color: #66cc66; font-weight: 600;">Database:</span> initialized (schema v3)                      │
│ <span style="color: #66cc66; font-weight: 600;">Directories:</span> logs, cache, sessions, contexts           │
╰────────────────────────────────────────────────────────╯

<span style="color: #66cc66; font-weight: 600;">✓ OK</span> Initialized (non-interactive)
</code></pre></div>

=== "Plain"

```
$ agents init --yes

Initialized
  Data Dir: /home/alex/.cleveragents (created)
  Config: /home/alex/.cleveragents/config.toml
  Database: initialized (schema v3)
  Directories: logs, cache, sessions, contexts

[OK] Initialized (non-interactive)
```

=== "JSON"

```json
{
  "command": "init --yes",
  "status": "ok",
  "exit_code": 0,
  "data": {
    "initialized": {
      "data_dir": "/home/alex/.cleveragents",
      "config": "/home/alex/.cleveragents/config.toml",
      "database": "initialized (schema v3)",
      "directories": ["logs", "cache", "sessions", "contexts"]
    }
  },
  "timing": {
    "duration_ms": 280
  },
  "messages": [
    { "level": "ok", "text": "Initialized (non-interactive)" }
  ]
}
```

=== "YAML"

```yaml
command: init --yes
status: ok
exit_code: 0
data:
  initialized:
    data_dir: /home/alex/.cleveragents
    config: /home/alex/.cleveragents/config.toml
    database: initialized (schema v3)
    directories:
      - logs
      - cache
      - sessions
      - contexts
timing:
  duration_ms: 280
messages:
  - level: ok
    text: Initialized (non-interactive)
```

agents session

Purpose Manage interactive sessions that hold a conversation history and orchestrator state.

agents session create
agents session create [--actor <ACTOR>]

Purpose Create a new session for interactive work.

Arguments

  • --actor ACTOR: Orchestrator actor to use for session tell.

Examples

=== "Rich"

<div class="highlight"><pre><code>
<span style="color: #66cc66; font-weight: 600;">$</span> <span style="color: cyan; font-weight: 600;">agents</span> session create <span style="color: cyan;">--actor</span> local/orchestrator

╭─ Session ───────────────────────╮
│ <span style="color: #5599ff; font-weight: 600;">ID:</span> 01HXM2A6K1P2E9Q9D4GQ7J4S7Z  │
│ <span style="color: magenta; font-weight: 600;">Actor:</span> local/orchestrator       │
│ <span style="color: #66cc66; font-weight: 600;">Created:</span> 2026-02-08 12:44       │
│ <span style="color: #5599ff; font-weight: 600;">Namespace:</span> local                │
╰─────────────────────────────────╯

╭─ Settings ─────────────╮
│ <span style="color: magenta; font-weight: 600;">Automation:</span> review     │
│ <span style="color: #5599ff; font-weight: 600;">Streaming:</span> off         │
│ <span style="color: #5599ff; font-weight: 600;">Context:</span> default       │
│ <span style="color: #5599ff; font-weight: 600;">Memory:</span> enabled        │
│ <span style="color: yellow; font-weight: 600;">Max History:</span> 50 turns  │
╰────────────────────────╯

╭─ Actor Details ───────────────────╮
│ <span style="color: #5599ff; font-weight: 600;">Provider:</span> anthropic               │
│ <span style="color: magenta; font-weight: 600;">Model:</span> claude-3.5                 │
│ <span style="color: #5599ff; font-weight: 600;">Temperature:</span> 0.7                  │
│ <span style="color: #5599ff; font-weight: 600;">Context Window:</span> 200K tokens       │
╰───────────────────────────────────╯

<span style="color: #66cc66; font-weight: 600;">✓ OK</span> Session created
</code></pre></div>

=== "Plain"

```
$ agents session create --actor local/orchestrator

Session
  ID: 01HXM2A6K1P2E9Q9D4GQ7J4S7Z
  Actor: local/orchestrator
  Created: 2026-02-08 12:44
  Namespace: local

Settings
  Automation: review
  Streaming: off
  Context: default
  Memory: enabled
  Max History: 50 turns

Actor Details
  Provider: anthropic
  Model: claude-3.5
  Temperature: 0.7
  Context Window: 200K tokens

[OK] Session created
```

=== "JSON"

```json
{
  "command": "agents session create --actor local/orchestrator",
  "status": "ok",
  "exit_code": 0,
  "data": {
    "session": {
      "id": "01HXM2A6K1P2E9Q9D4GQ7J4S7Z",
      "actor": "local/orchestrator",
      "created": "2026-02-08T12:44:00Z",
      "namespace": "local"
    },
    "settings": {
      "automation": "review",
      "streaming": "off",
      "context": "default",
      "memory": "enabled",
      "max_history": 50
    },
    "actor_details": {
      "provider": "anthropic",
      "model": "claude-3.5",
      "temperature": 0.7,
      "context_window": "200K tokens"
    }
  },
  "timing": { "duration_ms": 120 },
  "messages": [{ "level": "ok", "text": "Session created" }]
}
```

=== "YAML"

```yaml
command: agents session create --actor local/orchestrator
status: ok
exit_code: 0
data:
  session:
    id: 01HXM2A6K1P2E9Q9D4GQ7J4S7Z
    actor: local/orchestrator
    created: "2026-02-08T12:44:00Z"
    namespace: local
  settings:
    automation: review
    streaming: "off"
    context: default
    memory: enabled
    max_history: 50
  actor_details:
    provider: anthropic
    model: claude-3.5
    temperature: 0.7
    context_window: 200K tokens
timing:
  duration_ms: 120
messages:
  - level: ok
    text: Session created
```
agents session list
agents session list

Purpose List sessions available on the local machine.

Arguments

None.

Examples

=== "Rich"

<div class="highlight"><pre><code>
<span style="color: #66cc66; font-weight: 600;">$</span> <span style="color: cyan; font-weight: 600;">agents</span> <span style="color: cyan;">--format</span> table session list

╭─ Sessions ───────────────────────────────────────────────────────────────────╮
│ <span style="color: cyan; font-weight: 600;">ID</span>        <span style="color: cyan; font-weight: 600;">Name</span>             <span style="color: cyan; font-weight: 600;">Actor</span>               <span style="color: cyan; font-weight: 600;">Messages</span>  <span style="color: cyan; font-weight: 600;">Updated</span>             │
│ <span style="opacity: 0.7;">────────  ───────────────  ──────────────────  ────────  ────────────────</span>    │
│ 01HXM2A6  weekly-planning  local/orchestrator  6         2026-02-08 12:44    │
│ 01HXM1F2  refactor-sprint  local/orchestrator  14        2026-02-07 18:11    │
╰──────────────────────────────────────────────────────────────────────────────╯

╭─ Summary ────────────────────╮
│ <span style="color: yellow; font-weight: 600;">Total:</span> 2                     │
│ <span style="color: #66cc66; font-weight: 600;">Most Recent:</span> weekly-planning │
│ <span style="color: #5599ff; font-weight: 600;">Oldest:</span> refactor-sprint      │
│ <span style="color: #5599ff; font-weight: 600;">Total Messages:</span> 20           │
│ <span style="color: #5599ff; font-weight: 600;">Storage:</span> 42 KB               │
╰──────────────────────────────╯

<span style="color: #66cc66; font-weight: 600;">✓ OK</span> 2 sessions listed
</code></pre></div>

=== "Plain"

```
$ agents --format table session list

Sessions
ID        Name             Actor               Messages  Updated
--------  ---------------  ------------------  --------  ----------------
01HXM2A6  weekly-planning  local/orchestrator  6         2026-02-08 12:44
01HXM1F2  refactor-sprint  local/orchestrator  14        2026-02-07 18:11

Summary
  Total: 2
  Most Recent: weekly-planning
  Oldest: refactor-sprint
  Total Messages: 20
  Storage: 42 KB

[OK] 2 sessions listed
```

=== "JSON"

```json
{
  "command": "agents --format table session list",
  "status": "ok",
  "exit_code": 0,
  "data": {
    "sessions": [
      {
        "id": "01HXM2A6",
        "name": "weekly-planning",
        "actor": "local/orchestrator",
        "messages": 6,
        "updated": "2026-02-08T12:44:00Z"
      },
      {
        "id": "01HXM1F2",
        "name": "refactor-sprint",
        "actor": "local/orchestrator",
        "messages": 14,
        "updated": "2026-02-07T18:11:00Z"
      }
    ],
    "summary": {
      "total": 2,
      "most_recent": "weekly-planning",
      "oldest": "refactor-sprint",
      "total_messages": 20,
      "storage": "42 KB"
    }
  },
  "timing": { "duration_ms": 85 },
  "messages": [{ "level": "ok", "text": "2 sessions listed" }]
}
```

=== "YAML"

```yaml
command: agents --format table session list
status: ok
exit_code: 0
data:
  sessions:
    - id: 01HXM2A6
      name: weekly-planning
      actor: local/orchestrator
      messages: 6
      updated: "2026-02-08T12:44:00Z"
    - id: 01HXM1F2
      name: refactor-sprint
      actor: local/orchestrator
      messages: 14
      updated: "2026-02-07T18:11:00Z"
  summary:
    total: 2
    most_recent: weekly-planning
    oldest: refactor-sprint
    total_messages: 20
    storage: 42 KB
timing:
  duration_ms: 85
messages:
  - level: ok
    text: 2 sessions listed
```
agents session show
agents session show <SESSION_ID>

Purpose Show details and recent messages for a session.

Arguments

  • <SESSION_ID>: The session identifier.

Examples

=== "Rich"

<div class="highlight"><pre><code>
<span style="color: #66cc66; font-weight: 600;">$</span> <span style="color: cyan; font-weight: 600;">agents</span> session show 01HXM2A6K1P2E9Q9D4GQ7J4S7Z

╭─ Session Summary ───────────────╮
│ <span style="color: #5599ff; font-weight: 600;">ID:</span> 01HXM2A6K1P2E9Q9D4GQ7J4S7Z  │
│ <span style="color: magenta; font-weight: 600;">Actor:</span> local/orchestrator       │
│ <span style="color: yellow; font-weight: 600;">Messages:</span> 6                     │
│ <span style="color: #66cc66; font-weight: 600;">Created:</span> 2026-02-08 12:30       │
│ <span style="color: #66cc66; font-weight: 600;">Updated:</span> 2026-02-08 12:44       │
│ <span style="color: #5599ff; font-weight: 600;">Automation:</span> review              │
╰─────────────────────────────────╯

╭─ Recent Messages ──────────────────────────────────╮
│ <span style="opacity: 0.7;">user</span>  Create an action to refresh dependency locks │
│ <span style="opacity: 0.7;">assistant</span>  Plan created, running commands...       │
│ <span style="opacity: 0.7;">assistant</span>  Completed 2 commands                    │
╰────────────────────────────────────────────────────╯

╭─ Linked Plans ────────────────────────────────╮
│ <span style="color: cyan; font-weight: 600;">Plan ID</span>                     <span style="color: cyan; font-weight: 600;">Phase</span>   <span style="color: cyan; font-weight: 600;">State</span>     │
│ <span style="opacity: 0.7;">──────────────────────────  ──────  ────────</span>  │
│ 01HXM8C2ZK4Q7C2B3F2R4VYV6J  execute  complete │
╰───────────────────────────────────────────────╯

╭─ Token Usage ──────────────╮
│ <span style="color: #5599ff; font-weight: 600;">Input Tokens:</span> 3,420        │
│ <span style="color: #5599ff; font-weight: 600;">Output Tokens:</span> 1,185       │
│ <span style="color: yellow; font-weight: 600;">Estimated Cost:</span> $0.0184    │
╰────────────────────────────╯

<span style="color: #66cc66; font-weight: 600;">✓ OK</span> Session details loaded
</code></pre></div>

=== "Plain"

```
$ agents session show 01HXM2A6K1P2E9Q9D4GQ7J4S7Z

Session Summary
  ID: 01HXM2A6K1P2E9Q9D4GQ7J4S7Z
  Actor: local/orchestrator
  Messages: 6
  Created: 2026-02-08 12:30
  Updated: 2026-02-08 12:44
  Automation: review

Recent Messages
  user       Create an action to refresh dependency locks
  assistant  Plan created, running commands...
  assistant  Completed 2 commands

Linked Plans
Plan ID                     Phase    State
--------------------------  -------  --------
01HXM8C2ZK4Q7C2B3F2R4VYV6J  execute  complete

Token Usage
  Input Tokens: 3,420
  Output Tokens: 1,185
  Estimated Cost: $0.0184

[OK] Session details loaded
```

=== "JSON"

```json
{
  "command": "agents session show 01HXM2A6K1P2E9Q9D4GQ7J4S7Z",
  "status": "ok",
  "exit_code": 0,
  "data": {
    "session_summary": {
      "id": "01HXM2A6K1P2E9Q9D4GQ7J4S7Z",
      "actor": "local/orchestrator",
      "messages": 6,
      "created": "2026-02-08T12:30:00Z",
      "updated": "2026-02-08T12:44:00Z",
      "automation": "review"
    },
    "recent_messages": [
      { "role": "user", "text": "Create an action to refresh dependency locks" },
      { "role": "assistant", "text": "Plan created, running commands..." },
      { "role": "assistant", "text": "Completed 2 commands" }
    ],
    "linked_plans": [
      {
        "plan_id": "01HXM8C2ZK4Q7C2B3F2R4VYV6J",
        "phase": "execute",
        "state": "complete"
      }
    ],
    "token_usage": {
      "input_tokens": 3420,
      "output_tokens": 1185,
      "estimated_cost": "$0.0184"
    }
  },
  "timing": { "duration_ms": 95 },
  "messages": [{ "level": "ok", "text": "Session details loaded" }]
}
```

=== "YAML"

```yaml
command: agents session show 01HXM2A6K1P2E9Q9D4GQ7J4S7Z
status: ok
exit_code: 0
data:
  session_summary:
    id: 01HXM2A6K1P2E9Q9D4GQ7J4S7Z
    actor: local/orchestrator
    messages: 6
    created: "2026-02-08T12:30:00Z"
    updated: "2026-02-08T12:44:00Z"
    automation: review
  recent_messages:
    - role: user
      text: Create an action to refresh dependency locks
    - role: assistant
      text: Plan created, running commands...
    - role: assistant
      text: Completed 2 commands
  linked_plans:
    - plan_id: 01HXM8C2ZK4Q7C2B3F2R4VYV6J
      phase: execute
      state: complete
  token_usage:
    input_tokens: 3420
    output_tokens: 1185
    estimated_cost: "$0.0184"
timing:
  duration_ms: 95
messages:
  - level: ok
    text: Session details loaded
```
agents session delete
agents session delete [--yes|-y] <SESSION_ID>

!!! danger "Irreversible Deletion" This command ==permanently deletes== a session and its stored conversation history. Messages and context cannot be recovered after deletion. Use --yes to skip the confirmation prompt.

Arguments

  • <SESSION_ID>: The session identifier.
  • --yes, -y: Skip the confirmation prompt.

Examples

=== "Rich"

<div class="highlight"><pre><code>
<span style="color: #66cc66; font-weight: 600;">$</span> <span style="color: cyan; font-weight: 600;">agents</span> session delete 01HXM2A6K1P2E9Q9D4GQ7J4S7Z

Delete session 01HXM2A6K1P2E9Q9D4GQ7J4S7Z? [y/N]: y

╭─ Deletion Summary ──────────────────╮
│ <span style="color: cyan; font-weight: 600;">Session:</span> 01HXM2A6K1P2E9Q9D4GQ7J4S7Z │
│ <span style="color: #5599ff; font-weight: 600;">ID:</span> 01HXM2A6K1P2E9Q9D4GQ7J4S7Z      │
│ <span style="color: yellow; font-weight: 600;">Messages:</span> 6 removed                 │
│ <span style="color: #5599ff; font-weight: 600;">Storage:</span> 18 KB freed                │
│ <span style="color: #5599ff; font-weight: 600;">Plans Orphaned:</span> 0                   │
╰─────────────────────────────────────╯

╭─ Cleanup ───────────╮
│ <span style="color: #66cc66; font-weight: 600;">Backups:</span> none       │
│ <span style="color: #5599ff; font-weight: 600;">Logs:</span> preserved     │
│ <span style="color: #5599ff; font-weight: 600;">Context:</span> cleared    │
│ <span style="color: #5599ff; font-weight: 600;">Checkpoints:</span> none   │
╰─────────────────────╯

<span style="color: #66cc66; font-weight: 600;">✓ OK</span> Session deleted
</code></pre></div>

=== "Plain"

```
$ agents session delete 01HXM2A6K1P2E9Q9D4GQ7J4S7Z

Delete session 01HXM2A6K1P2E9Q9D4GQ7J4S7Z? [y/N]: y

Deletion Summary
  Session: 01HXM2A6K1P2E9Q9D4GQ7J4S7Z
  ID: 01HXM2A6K1P2E9Q9D4GQ7J4S7Z
  Messages: 6 removed
  Storage: 18 KB freed
  Plans Orphaned: 0

Cleanup
  Backups: none
  Logs: preserved
  Context: cleared
  Checkpoints: none

[OK] Session deleted
```

=== "JSON"

```json
{
  "command": "agents session delete 01HXM2A6K1P2E9Q9D4GQ7J4S7Z",
  "status": "ok",
  "exit_code": 0,
  "data": {
    "deletion_summary": {
      "session": "01HXM2A6K1P2E9Q9D4GQ7J4S7Z",
      "id": "01HXM2A6K1P2E9Q9D4GQ7J4S7Z",
      "messages_removed": 6,
      "storage_freed": "18 KB",
      "plans_orphaned": 0
    },
    "cleanup": {
      "backups": "none",
      "logs": "preserved",
      "context": "cleared",
      "checkpoints": "none"
    }
  },
  "timing": { "duration_ms": 62 },
  "messages": [{ "level": "ok", "text": "Session deleted" }]
}
```

=== "YAML"

```yaml
command: agents session delete 01HXM2A6K1P2E9Q9D4GQ7J4S7Z
status: ok
exit_code: 0
data:
  deletion_summary:
    session: 01HXM2A6K1P2E9Q9D4GQ7J4S7Z
    id: 01HXM2A6K1P2E9Q9D4GQ7J4S7Z
    messages_removed: 6
    storage_freed: 18 KB
    plans_orphaned: 0
  cleanup:
    backups: none
    logs: preserved
    context: cleared
    checkpoints: none
timing:
  duration_ms: 62
messages:
  - level: ok
    text: Session deleted
```
agents session export
agents session export [(--output|-o) <FILE>] <SESSION_ID>

Purpose Export a session as a portable JSON file.

Arguments

  • <SESSION_ID>: The session identifier.
  • --output/-o FILE: Output file path (optional).

Examples

=== "Rich"

<div class="highlight"><pre><code>
<span style="color: #66cc66; font-weight: 600;">$</span> <span style="color: cyan; font-weight: 600;">agents</span> session export <span style="color: cyan;">--output</span> /tmp/weekly-planning.json 01HXM2A6K1P2E9Q9D4GQ7J4S7Z

╭─ Session Export ────────────────────╮
│ <span style="color: cyan; font-weight: 600;">Session:</span> 01HXM2A6K1P2E9Q9D4GQ7J4S7Z │
│ <span style="color: #5599ff; font-weight: 600;">Output:</span> /tmp/weekly-planning.json   │
│ <span style="color: yellow; font-weight: 600;">Messages:</span> 6                         │
│ <span style="color: #66cc66; font-weight: 600;">Size:</span> 24 KB                         │
│ <span style="color: #5599ff; font-weight: 600;">Format:</span> JSON                        │
╰─────────────────────────────────────╯

╭─ Contents ─────────────────╮
│ <span style="color: #5599ff; font-weight: 600;">Messages:</span> 6                │
│ <span style="color: #5599ff; font-weight: 600;">Plan References:</span> 1         │
│ <span style="color: #5599ff; font-weight: 600;">Metadata Keys:</span> 2           │
│ <span style="color: yellow; font-weight: 600;">Actor Config:</span> included     │
│ <span style="color: #5599ff; font-weight: 600;">Schema Version:</span> v3         │
╰────────────────────────────╯

╭─ Integrity ──────────────────╮
│ <span style="color: magenta; font-weight: 600;">Checksum:</span> sha256:7a9b...42c1 │
│ <span style="color: yellow; font-weight: 600;">Encrypted:</span> no                │
╰──────────────────────────────╯

<span style="color: #66cc66; font-weight: 600;">✓ OK</span> Export completed
</code></pre></div>

=== "Plain"

```
$ agents session export --output /tmp/weekly-planning.json 01HXM2A6K1P2E9Q9D4GQ7J4S7Z

Session Export
  Session: 01HXM2A6K1P2E9Q9D4GQ7J4S7Z
  Output: /tmp/weekly-planning.json
  Messages: 6
  Size: 24 KB
  Format: JSON

Contents
  Messages: 6
  Plan References: 1
  Metadata Keys: 2
  Actor Config: included
  Schema Version: v3

Integrity
  Checksum: sha256:7a9b...42c1
  Encrypted: no

[OK] Export completed
```

=== "JSON"

```json
{
  "command": "agents session export --output /tmp/weekly-planning.json 01HXM2A6K1P2E9Q9D4GQ7J4S7Z",
  "status": "ok",
  "exit_code": 0,
  "data": {
    "session_export": {
      "session": "01HXM2A6K1P2E9Q9D4GQ7J4S7Z",
      "output": "/tmp/weekly-planning.json",
      "messages": 6,
      "size": "24 KB",
      "format": "JSON"
    },
    "contents": {
      "messages": 6,
      "plan_references": 1,
      "metadata_keys": 2,
      "actor_config": "included",
      "schema_version": "v3"
    },
    "integrity": {
      "checksum": "sha256:7a9b...42c1",
      "encrypted": false
    }
  },
  "timing": { "duration_ms": 140 },
  "messages": [{ "level": "ok", "text": "Export completed" }]
}
```

=== "YAML"

```yaml
command: agents session export --output /tmp/weekly-planning.json 01HXM2A6K1P2E9Q9D4GQ7J4S7Z
status: ok
exit_code: 0
data:
  session_export:
    session: 01HXM2A6K1P2E9Q9D4GQ7J4S7Z
    output: /tmp/weekly-planning.json
    messages: 6
    size: 24 KB
    format: JSON
  contents:
    messages: 6
    plan_references: 1
    metadata_keys: 2
    actor_config: included
    schema_version: v3
  integrity:
    checksum: "sha256:7a9b...42c1"
    encrypted: false
timing:
  duration_ms: 140
messages:
  - level: ok
    text: Export completed
```
agents session import
agents session import (--input|-i) <FILE>

Purpose Import a session JSON file.

Arguments

  • --input/-i FILE: Input JSON file.

Examples

=== "Rich"

<div class="highlight"><pre><code>
<span style="color: #66cc66; font-weight: 600;">$</span> <span style="color: cyan; font-weight: 600;">agents</span> session import <span style="color: cyan;">--input</span> /tmp/weekly-planning.json

╭─ Session Import ────────────────────────╮
│ <span style="color: #5599ff; font-weight: 600;">Input:</span> /tmp/weekly-planning.json        │
│ <span style="color: #5599ff; font-weight: 600;">Session ID:</span> 01HXM3D3B2W4CQYQ3P4ZB8A5T1  │
│ <span style="color: yellow; font-weight: 600;">Messages:</span> 6                             │
│ <span style="color: #5599ff; font-weight: 600;">Schema:</span> v3                              │
╰─────────────────────────────────────────╯

╭─ Validation ────────────╮
│ <span style="color: #66cc66; font-weight: 600;">Checksum:</span> verified      │
│ <span style="color: #66cc66; font-weight: 600;">Schema:</span> compatible      │
│ <span style="color: #66cc66; font-weight: 600;">Actor Ref:</span> resolved     │
╰─────────────────────────╯

╭─ Merge ──────────────╮
│ <span style="color: #66cc66; font-weight: 600;">Existing:</span> none       │
│ <span style="color: #5599ff; font-weight: 600;">Strategy:</span> create new │
╰──────────────────────╯

<span style="color: #66cc66; font-weight: 600;">✓ OK</span> Import completed
</code></pre></div>

=== "Plain"

```
$ agents session import --input /tmp/weekly-planning.json

Session Import
  Input: /tmp/weekly-planning.json
  Session ID: 01HXM3D3B2W4CQYQ3P4ZB8A5T1
  Messages: 6
  Schema: v3

Validation
  Checksum: verified
  Schema: compatible
  Actor Ref: resolved

Merge
  Existing: none
  Strategy: create new

[OK] Import completed
```

=== "JSON"

```json
{
  "command": "agents session import --input /tmp/weekly-planning.json",
  "status": "ok",
  "exit_code": 0,
  "data": {
    "session_import": {
      "input": "/tmp/weekly-planning.json",
      "session_id": "01HXM3D3B2W4CQYQ3P4ZB8A5T1",
      "messages": 6,
      "schema": "v3"
    },
    "validation": {
      "checksum": "verified",
      "schema": "compatible",
      "actor_ref": "resolved"
    },
    "merge": {
      "existing": "none",
      "strategy": "create new"
    }
  },
  "timing": { "duration_ms": 210 },
  "messages": [{ "level": "ok", "text": "Import completed" }]
}
```

=== "YAML"

```yaml
command: agents session import --input /tmp/weekly-planning.json
status: ok
exit_code: 0
data:
  session_import:
    input: /tmp/weekly-planning.json
    session_id: 01HXM3D3B2W4CQYQ3P4ZB8A5T1
    messages: 6
    schema: v3
  validation:
    checksum: verified
    schema: compatible
    actor_ref: resolved
  merge:
    existing: none
    strategy: create new
timing:
  duration_ms: 210
messages:
  - level: ok
    text: Import completed
```
agents session tell
agents session tell --session <SESSION_ID> [--actor <ACTOR>] [--stream] <PROMPT>

!!! tip "Primary User Interface"

This is the main natural-language interface for interacting with CleverAgents. The orchestrator interprets your request and issues the necessary CleverAgents commands under the hood — creating actions, plans, or project changes as needed. Use `--stream` to see responses in real time.

Purpose Send a natural-language request to the orchestrator. The orchestrator can create actions, plans, or project changes by issuing the necessary CleverAgents commands under the hood.

Arguments

  • <PROMPT>: Instruction text (positional argument).
  • --session SESSION_ID: Session to use (required).
  • --actor ACTOR: Override the session actor for this request.
  • --stream: Stream progress as the orchestrator works.

Examples

=== "Rich"

<div class="highlight"><pre><code>
<span style="color: #66cc66; font-weight: 600;">$</span> <span style="color: cyan; font-weight: 600;">agents</span> session tell <span style="color: #66cc66;">&quot;Create an action to refresh dependency locks and add it to the platform project&quot;</span> \
  <span style="color: cyan;">--session</span> 01HXM2A6K1P2E9Q9D4GQ7J4S7Z

╭─ Plan Request ──────────────────────────────────────────╮
│ <span style="color: magenta; font-weight: 600;">Actor:</span> local/orchestrator                               │
│ <span style="color: cyan; font-weight: 600;">Session:</span> 01HXM2A6K1P2E9Q9D4GQ7J4S7Z                     │
│ <span style="color: magenta; font-weight: 600;">Automation:</span> review                                      │
│ <span style="color: #5599ff; font-weight: 600;">Prompt:</span> Create an action to refresh dependency locks... │
╰─────────────────────────────────────────────────────────╯

╭─ Commands Executed ─────────────────────────────────────────────────────────────────────────────────────────╮
│ - agents action create <span style="color: cyan;">--config</span> ./actions/refresh-locks.yaml                                                │
│ - agents resource add git-checkout local/platform-repo <span style="color: cyan;">--path</span> /repos/platform                               │
│ - agents project link-resource local/platform local/platform-repo                                           │
╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────╯

╭─ Result ──────────────────────────╮
│ <span style="color: #66cc66; font-weight: 600;">Action:</span> local/refresh-locks       │
│ <span style="color: #5599ff; font-weight: 600;">Project:</span> local/platform           │
│ <span style="color: magenta; font-weight: 600;">Resource:</span> local/platform-repo     │
╰───────────────────────────────────╯

╭─ Usage ─────────────────────╮
│ <span style="color: #5599ff; font-weight: 600;">Input Tokens:</span> 1,842         │
│ <span style="color: #5599ff; font-weight: 600;">Output Tokens:</span> 624          │
│ <span style="color: yellow; font-weight: 600;">Cost:</span> $0.0094               │
│ <span style="color: #66cc66; font-weight: 600;">Duration:</span> 3.2s              │
│ <span style="color: #5599ff; font-weight: 600;">Tool Calls:</span> 3               │
╰─────────────────────────────╯

<span style="color: #66cc66; font-weight: 600;">✓ OK</span> Orchestrator completed 3 commands
</code></pre></div>

=== "Plain"

```
$ agents session tell "Create an action to refresh dependency locks and add it to the platform project" \
  --session 01HXM2A6K1P2E9Q9D4GQ7J4S7Z

Plan Request
  Actor: local/orchestrator
  Session: 01HXM2A6K1P2E9Q9D4GQ7J4S7Z
  Automation: review
  Prompt: Create an action to refresh dependency locks...

Commands Executed
  - agents action create --config ./actions/refresh-locks.yaml
  - agents resource add git-checkout local/platform-repo --path /repos/platform
  - agents project link-resource local/platform local/platform-repo

Result
  Action: local/refresh-locks
  Project: local/platform
  Resource: local/platform-repo

Usage
  Input Tokens: 1,842
  Output Tokens: 624
  Cost: $0.0094
  Duration: 3.2s
  Tool Calls: 3

[OK] Orchestrator completed 3 commands
```

=== "JSON"

```json
{
  "command": "agents session tell \"Create an action to refresh dependency locks and add it to the platform project\" --session 01HXM2A6K1P2E9Q9D4GQ7J4S7Z",
  "status": "ok",
  "exit_code": 0,
  "data": {
    "plan_request": {
      "actor": "local/orchestrator",
      "session": "01HXM2A6K1P2E9Q9D4GQ7J4S7Z",
      "automation": "review",
      "prompt": "Create an action to refresh dependency locks..."
    },
    "commands_executed": [
      "agents action create --config ./actions/refresh-locks.yaml",
      "agents resource add git-checkout local/platform-repo --path /repos/platform",
      "agents project link-resource local/platform local/platform-repo"
    ],
    "result": {
      "action": "local/refresh-locks",
      "project": "local/platform",
      "resource": "local/platform-repo"
    },
    "usage": {
      "input_tokens": 1842,
      "output_tokens": 624,
      "cost": "$0.0094",
      "duration_s": 3.2,
      "tool_calls": 3
    }
  },
  "timing": { "duration_ms": 3200 },
  "messages": [{ "level": "ok", "text": "Orchestrator completed 3 commands" }]
}
```

=== "YAML"

```yaml
command: agents session tell "Create an action to refresh dependency locks and add it to the platform project" --session 01HXM2A6K1P2E9Q9D4GQ7J4S7Z
status: ok
exit_code: 0
data:
  plan_request:
    actor: local/orchestrator
    session: 01HXM2A6K1P2E9Q9D4GQ7J4S7Z
    automation: review
    prompt: Create an action to refresh dependency locks...
  commands_executed:
    - agents action create --config ./actions/refresh-locks.yaml
    - agents resource add git-checkout local/platform-repo --path /repos/platform
    - agents project link-resource local/platform local/platform-repo
  result:
    action: local/refresh-locks
    project: local/platform
    resource: local/platform-repo
  usage:
    input_tokens: 1842
    output_tokens: 624
    cost: "$0.0094"
    duration_s: 3.2
    tool_calls: 3
timing:
  duration_ms: 3200
messages:
  - level: ok
    text: Orchestrator completed 3 commands
```

Using --stream to see the response as it is generated (token by token):

=== "Rich"

<div class="highlight"><pre><code>
<span style="color: #66cc66; font-weight: 600;">$</span> <span style="color: cyan; font-weight: 600;">agents</span> session tell <span style="color: cyan;">--session</span> 01HXM2A6K1 <span style="color: cyan;">--stream</span> <span style="color: #66cc66;">&quot;What files were changed in the last plan?&quot;</span>

╭─ Session ──────────────────────────╮
│ <span style="color: cyan; font-weight: 600;">ID:</span> 01HXM2A6K1P2E9Q9D4GQ7J4S7Z     │
│ <span style="color: #5599ff; font-weight: 600;">Actor:</span> local/orchestrator          │
│ <span style="color: #5599ff; font-weight: 600;">Mode:</span> streaming                    │
╰────────────────────────────────────╯

<span style="color: #66cc66;">▍</span> The last plan (01HXM8C2ZK) modified 6 files in the auth module:

  1. `src/auth/session.py` — refactored session validation
  2. `src/auth/tokens.py` — updated token expiry to 7200s
  3. `src/auth/__init__.py` — updated exports
  4. `tests/test_auth.py` — added 12 new test cases
  5. `tests/test_session.py` — updated session fixtures
  6. `docs/auth.md` — updated API documentation

All changes passed validation (24/24 tests, lint clean).

╭─ Usage ──────────────────╮
│ <span style="color: #5599ff; font-weight: 600;">Tokens:</span> 1,240 (stream)   │
│ <span style="color: #5599ff; font-weight: 600;">Duration:</span> 3.1s           │
│ <span style="color: #5599ff; font-weight: 600;">Tool Calls:</span> 2            │
╰──────────────────────────╯

<span style="color: #66cc66; font-weight: 600;">✓ OK</span> Stream complete
</code></pre></div>

=== "Plain"

```
$ agents session tell --session 01HXM2A6K1 --stream "What files were changed in the last plan?"

Session
  ID: 01HXM2A6K1P2E9Q9D4GQ7J4S7Z
  Actor: local/orchestrator
  Mode: streaming

The last plan (01HXM8C2ZK) modified 6 files in the auth module:

  1. src/auth/session.py -- refactored session validation
  2. src/auth/tokens.py -- updated token expiry to 7200s
  3. src/auth/__init__.py -- updated exports
  4. tests/test_auth.py -- added 12 new test cases
  5. tests/test_session.py -- updated session fixtures
  6. docs/auth.md -- updated API documentation

All changes passed validation (24/24 tests, lint clean).

Usage
  Tokens: 1,240 (stream)
  Duration: 3.1s
  Tool Calls: 2

[OK] Stream complete
```

=== "JSON"

```json
{
  "command": "agents session tell --session 01HXM2A6K1 --stream \"What files were changed in the last plan?\"",
  "status": "ok",
  "exit_code": 0,
  "data": {
    "session": {
      "id": "01HXM2A6K1P2E9Q9D4GQ7J4S7Z",
      "actor": "local/orchestrator",
      "mode": "streaming"
    },
    "response": "The last plan (01HXM8C2ZK) modified 6 files in the auth module:\n\n1. src/auth/session.py \u2014 refactored session validation\n2. src/auth/tokens.py \u2014 updated token expiry to 7200s\n3. src/auth/__init__.py \u2014 updated exports\n4. tests/test_auth.py \u2014 added 12 new test cases\n5. tests/test_session.py \u2014 updated session fixtures\n6. docs/auth.md \u2014 updated API documentation\n\nAll changes passed validation (24/24 tests, lint clean).",
    "files_changed": [
      "src/auth/session.py",
      "src/auth/tokens.py",
      "src/auth/__init__.py",
      "tests/test_auth.py",
      "tests/test_session.py",
      "docs/auth.md"
    ],
    "usage": {
      "tokens": 1240,
      "duration_s": 3.1,
      "tool_calls": 2
    }
  },
  "timing": { "duration_ms": 3100 },
  "messages": [{ "level": "ok", "text": "Stream complete" }]
}
```

=== "YAML"

```yaml
command: agents session tell --session 01HXM2A6K1 --stream "What files were changed in the last plan?"
status: ok
exit_code: 0
data:
  session:
    id: 01HXM2A6K1P2E9Q9D4GQ7J4S7Z
    actor: local/orchestrator
    mode: streaming
  response: >
    The last plan (01HXM8C2ZK) modified 6 files in the auth module:
    1. src/auth/session.py -- refactored session validation
    2. src/auth/tokens.py -- updated token expiry to 7200s
    3. src/auth/__init__.py -- updated exports
    4. tests/test_auth.py -- added 12 new test cases
    5. tests/test_session.py -- updated session fixtures
    6. docs/auth.md -- updated API documentation
    All changes passed validation (24/24 tests, lint clean).
  files_changed:
    - src/auth/session.py
    - src/auth/tokens.py
    - src/auth/__init__.py
    - tests/test_auth.py
    - tests/test_session.py
    - docs/auth.md
  usage:
    tokens: 1240
    duration_s: 3.1
    tool_calls: 2
timing:
  duration_ms: 3100
messages:
  - level: ok
    text: Stream complete
```

agents project

Purpose Manage projects and their resources.

agents project create
agents project create [(--description|-d) <DESC>] [--resource <RESOURCE>]...
                     [--invariant <INVARIANT>]... [--invariant-actor <ACTOR>] <NAME>

Purpose Create a new project record.

Arguments

  • <NAME>: Namespaced project name (positional argument).
  • --description/-d TEXT: Optional description.
  • --resource RESOURCE: Resource name or ULID to link to the project at creation time (repeatable).
  • --invariant TEXT: Invariant to attach to this project (repeatable). These invariants apply to all plans targeting this project.
  • --invariant-actor ACTOR: Invariant Reconciliation Actor for this project. Used to reconcile project-level invariants against global invariants for all plans targeting this project (unless the plan overrides it).

Examples

=== "Rich"

<div class="highlight"><pre><code>
<span style="color: #66cc66; font-weight: 600;">$</span> <span style="color: cyan; font-weight: 600;">agents</span> project create <span style="color: cyan;">--description</span> <span style="color: #66cc66;">&quot;Backend API&quot;</span> local/api-service

╭─ Project ──────────────────────╮
│ <span style="color: cyan; font-weight: 600;">Name:</span> local/api-service        │
│ <span style="color: #5599ff; font-weight: 600;">Description:</span> Backend API       │
│ <span style="color: #5599ff; font-weight: 600;">Type:</span> local                    │
│ <span style="color: #66cc66; font-weight: 600;">Created:</span> 2026-02-08 12:46      │
╰────────────────────────────────╯

╭─ Paths ────────────────────────────────────╮
│ <span style="color: #5599ff; font-weight: 600;">Root:</span> /repos/api-service                   │
│ <span style="color: #5599ff; font-weight: 600;">Data Dir:</span> /repos/api-service/.cleveragents │
╰────────────────────────────────────────────╯

╭─ Defaults ──────────────────────────────╮
│ <span style="color: #5599ff; font-weight: 600;">Sandbox:</span> git_worktree                   │
│ <span style="color: #66cc66; font-weight: 600;">Validations:</span> 0                          │
│ <span style="color: yellow; font-weight: 600;">Context Filters:</span> none                   │
│ <span style="color: magenta; font-weight: 600;">Automation Profile:</span> (inherits global)   │
╰─────────────────────────────────────────╯

╭─ Resources ──────╮
│ <span style="color: yellow; font-weight: 600;">Total:</span> 0         │
│ <span style="color: #5599ff; font-weight: 600;">Indexed:</span> 0       │
│ <span style="color: #5599ff; font-weight: 600;">Sandboxable:</span> 0   │
╰──────────────────╯

<span style="color: #66cc66; font-weight: 600;">✓ OK</span> Project created
</code></pre></div>

=== "Plain"

```
$ agents project create --description "Backend API" local/api-service

Project
  Name: local/api-service
  Description: Backend API
  Type: local
  Created: 2026-02-08 12:46

Paths
  Root: /repos/api-service
  Data Dir: /repos/api-service/.cleveragents

Defaults
  Sandbox: git_worktree
  Validations: 0
  Context Filters: none
  Automation Profile: (inherits global)

Resources
  Total: 0
  Indexed: 0
  Sandboxable: 0

[OK] Project created
```

=== "JSON"

```json
{
  "command": "agents project create --description \"Backend API\" local/api-service",
  "status": "ok",
  "exit_code": 0,
  "data": {
    "project": {
      "name": "local/api-service",
      "description": "Backend API",
      "type": "local",
      "created": "2026-02-08T12:46:00Z"
    },
    "paths": {
      "root": "/repos/api-service",
      "data_dir": "/repos/api-service/.cleveragents"
    },
    "defaults": {
      "sandbox": "git_worktree",
      "validations": 0,
      "context_filters": null,
      "automation_profile": null
    },
    "resources": {
      "total": 0,
      "indexed": 0,
      "sandboxable": 0
    }
  },
  "timing": { "duration_ms": 85 },
  "messages": [{ "level": "ok", "text": "Project created" }]
}
```

=== "YAML"

```yaml
command: agents project create --description "Backend API" local/api-service
status: ok
exit_code: 0
data:
  project:
    name: local/api-service
    description: Backend API
    type: local
    created: "2026-02-08T12:46:00Z"
  paths:
    root: /repos/api-service
    data_dir: /repos/api-service/.cleveragents
  defaults:
    sandbox: git_worktree
    validations: 0
    context_filters: null
    automation_profile: null
  resources:
    total: 0
    indexed: 0
    sandboxable: 0
timing:
  duration_ms: 85
messages:
  - level: ok
    text: Project created
```

Creating a project with resources and invariants in one command:

=== "Rich"

<div class="highlight"><pre><code>
<span style="color: #66cc66; font-weight: 600;">$</span> <span style="color: cyan; font-weight: 600;">agents</span> project create <span style="color: cyan;">-d</span> <span style="color: #66cc66;">&quot;Frontend web application&quot;</span> \
  <span style="color: cyan;">--resource</span> local/web-repo <span style="color: cyan;">--resource</span> local/web-db \
  <span style="color: cyan;">--invariant</span> <span style="color: #66cc66;">&quot;All components must have unit tests&quot;</span> \
  <span style="color: cyan;">--invariant</span> <span style="color: #66cc66;">&quot;CSS must pass stylelint checks&quot;</span> \
  <span style="color: cyan;">--invariant-actor</span> local/invariant-resolver \
  local/web-app

╭─ Project Created ─────────────────────╮
│ <span style="color: cyan; font-weight: 600;">Name:</span> local/web-app                   │
│ <span style="color: #5599ff; font-weight: 600;">Description:</span> Frontend web app         │
│ <span style="color: #5599ff; font-weight: 600;">Remote:</span> no                            │
╰───────────────────────────────────────╯

╭─ Linked Resources ───────────────────────────────────────╮
│ <span style="color: cyan; font-weight: 600;">Name</span>               <span style="color: cyan; font-weight: 600;">Type</span>          <span style="color: cyan; font-weight: 600;">Read-Only</span>               │
│ <span style="opacity: 0.7;">─────────────────  ──────────────  ─────────</span>             │
│ local/web-repo     git-checkout    no                    │
│ local/web-db       local/database  no                    │
╰──────────────────────────────────────────────────────────╯

╭─ Invariants ────────────────────────────────────╮
│ <span style="color: magenta; font-weight: 600;">1.</span> All components must have unit tests          │
│ <span style="color: magenta; font-weight: 600;">2.</span> CSS must pass stylelint checks               │
│ <span style="color: #5599ff; font-weight: 600;">Reconciliation Actor:</span> local/invariant-resolver  │
╰─────────────────────────────────────────────────╯

<span style="color: #66cc66; font-weight: 600;">✓ OK</span> Project created
</code></pre></div>

=== "Plain"

```
$ agents project create -d "Frontend web application" \
  --resource local/web-repo --resource local/web-db \
  --invariant "All components must have unit tests" \
  --invariant "CSS must pass stylelint checks" \
  --invariant-actor local/invariant-resolver \
  local/web-app

Project Created
  Name: local/web-app
  Description: Frontend web app
  Remote: no

Linked Resources
  Name               Type            Read-Only
  -----------------  --------------  ---------
  local/web-repo     git-checkout    no
  local/web-db       local/database  no

Invariants
  1. All components must have unit tests
  2. CSS must pass stylelint checks
  Reconciliation Actor: local/invariant-resolver

[OK] Project created
```

=== "JSON"

```json
{
  "command": "agents project create -d \"Frontend web application\" --resource local/web-repo --resource local/web-db --invariant \"All components must have unit tests\" --invariant \"CSS must pass stylelint checks\" --invariant-actor local/invariant-resolver local/web-app",
  "status": "ok",
  "exit_code": 0,
  "data": {
    "project": {
      "name": "local/web-app",
      "description": "Frontend web app",
      "remote": false
    },
    "linked_resources": [
      { "name": "local/web-repo", "type": "git-checkout", "read_only": false },
      { "name": "local/web-db", "type": "local/database", "read_only": false }
    ],
    "invariants": {
      "items": [
        "All components must have unit tests",
        "CSS must pass stylelint checks"
      ],
      "reconciliation_actor": "local/invariant-resolver"
    }
  },
  "timing": { "duration_ms": 120 },
  "messages": [{ "level": "ok", "text": "Project created" }]
}
```

=== "YAML"

```yaml
command: agents project create -d "Frontend web application" --resource local/web-repo --resource local/web-db --invariant "All components must have unit tests" --invariant "CSS must pass stylelint checks" --invariant-actor local/invariant-resolver local/web-app
status: ok
exit_code: 0
data:
  project:
    name: local/web-app
    description: Frontend web app
    remote: false
  linked_resources:
    - name: local/web-repo
      type: git-checkout
      read_only: false
    - name: local/web-db
      type: local/database
      read_only: false
  invariants:
    items:
      - All components must have unit tests
      - CSS must pass stylelint checks
    reconciliation_actor: local/invariant-resolver
timing:
  duration_ms: 120
messages:
  - level: ok
    text: Project created
```
agents project link-resource [--read-only] <PROJECT> <RESOURCE>

Purpose Link a registered resource to a project. The resource must already be registered via agents resource add. A resource can be linked to multiple projects. Optionally, the resource can be marked as read-only within this project context, and given an alias for convenience.

Arguments

  • <PROJECT>: Project name (positional argument).
  • <RESOURCE>: Resource name or ULID (positional argument).
  • --read-only: Mark the resource as read-only within this project (even if the resource itself is writable).

Examples

=== "Rich"

<div class="highlight"><pre><code>
<span style="color: #66cc66; font-weight: 600;">$</span> <span style="color: cyan; font-weight: 600;">agents</span> resource add git-checkout local/api-repo <span style="color: cyan;">--path</span> /repos/api <span style="color: cyan;">--branch</span> main
<span style="color: #66cc66; font-weight: 600;">$</span> <span style="color: cyan; font-weight: 600;">agents</span> project link-resource local/api-service local/api-repo

╭─ Resource Linked ───────────────────────╮
│ <span style="color: cyan; font-weight: 600;">Project:</span> local/api-service              │
│ <span style="color: #5599ff; font-weight: 600;">Resource:</span> local/api-repo                │
│ <span style="color: #5599ff; font-weight: 600;">Type:</span> git-checkout                      │
│ <span style="color: #5599ff; font-weight: 600;">Read-Only:</span> no                           │
╰─────────────────────────────────────────╯

╭─ Access ─────────────────╮
│ <span style="color: #66cc66; font-weight: 600;">Read:</span> allowed            │
│ <span style="color: yellow; font-weight: 600;">Write:</span> allowed           │
│ <span style="color: #5599ff; font-weight: 600;">Apply:</span> requires approval │
╰──────────────────────────╯

╭─ Indexing ─────────────────────╮
│ <span style="color: cyan; font-weight: 600;">Status:</span> indexing...            │
│ <span style="color: #5599ff; font-weight: 600;">Files Found:</span> 347               │
│ <span style="color: #5599ff; font-weight: 600;">Language:</span> Python (primary)     │
│ <span style="color: #5599ff; font-weight: 600;">Estimated Time:</span> ~20 seconds    │
╰────────────────────────────────╯

<span style="color: #66cc66; font-weight: 600;">✓ OK</span> Resource linked to project
</code></pre></div>

=== "Plain"

```
$ agents resource add git-checkout local/api-repo --path /repos/api --branch main
$ agents project link-resource local/api-service local/api-repo

Resource Linked
  Project: local/api-service
  Resource: local/api-repo
  Type: git-checkout
  Read-Only: no

Access
  Read: allowed
  Write: allowed
  Apply: requires approval

Indexing
  Status: indexing...
  Files Found: 347
  Language: Python (primary)
  Estimated Time: ~20 seconds

[OK] Resource linked to project
```

=== "JSON"

```json
{
  "command": "agents project link-resource local/api-service local/api-repo",
  "status": "ok",
  "exit_code": 0,
  "data": {
    "resource_linked": {
      "project": "local/api-service",
      "resource": "local/api-repo",
      "type": "git-checkout",
      "read_only": false
    },
    "access": {
      "read": "allowed",
      "write": "allowed",
      "apply": "requires approval"
    },
    "indexing": {
      "status": "indexing",
      "files_found": 347,
      "language": "Python (primary)",
      "estimated_time_seconds": 20
    }
  },
  "timing": { "duration_ms": 92 },
  "messages": [{ "level": "ok", "text": "Resource linked to project" }]
}
```

=== "YAML"

```yaml
command: agents project link-resource local/api-service local/api-repo
status: ok
exit_code: 0
data:
  resource_linked:
    project: local/api-service
    resource: local/api-repo
    type: git-checkout
    read_only: false
  access:
    read: allowed
    write: allowed
    apply: requires approval
  indexing:
    status: indexing
    files_found: 347
    language: Python (primary)
    estimated_time_seconds: 20
timing:
  duration_ms: 92
messages:
  - level: ok
    text: Resource linked to project
```
agents project unlink-resource [--yes|-y] <PROJECT> <RESOURCE_NAME>

Purpose Unlink a resource from a project. The resource itself remains registered in the Resource Registry and can still be linked to other projects. Active plans using this resource in this project context will be warned.

Arguments

  • <PROJECT>: Project name (positional argument).
  • <RESOURCE_NAME>: Resource name (positional argument). Only user-added (named) resources can be unlinked.
  • --yes, -y: Skip confirmation prompt.

Examples

=== "Rich"

<div class="highlight"><pre><code>
<span style="color: #66cc66; font-weight: 600;">$</span> <span style="color: cyan; font-weight: 600;">agents</span> project unlink-resource local/api-service local/api-repo

Unlink local/api-repo from local/api-service? [y/N]: y

╭─ Resource Unlinked ─────────────────────────────────╮
│ <span style="color: cyan; font-weight: 600;">Project:</span> local/api-service                          │
│ <span style="color: #5599ff; font-weight: 600;">Resource:</span> local/api-repo                            │
│ <span style="color: #5599ff; font-weight: 600;">Type:</span> git-checkout                                  │
╰─────────────────────────────────────────────────────╯

╭─ Index Cleanup ──────────╮
│ <span style="color: yellow; font-weight: 600;">Text Index:</span> 347 removed  │
│ <span style="color: yellow; font-weight: 600;">Vectors:</span> 892 removed     │
│ <span style="color: yellow; font-weight: 600;">Graph Triples:</span> cleared   │
│ <span style="color: #66cc66; font-weight: 600;">Duration:</span> 0.3s           │
╰──────────────────────────╯

╭─ Project Summary ──────────────╮
│ <span style="color: yellow; font-weight: 600;">Linked Resources:</span> 1 remaining  │
│ <span style="color: #66cc66; font-weight: 600;">Last Updated:</span> 2026-02-09 10:30 │
│ <span style="color: #5599ff; font-weight: 600;">Active Plans:</span> 0                │
╰────────────────────────────────╯

<span style="color: #66cc66; font-weight: 600;">✓ OK</span> Resource unlinked from project
</code></pre></div>

=== "Plain"

```
$ agents project unlink-resource local/api-service local/api-repo

Unlink local/api-repo from local/api-service? [y/N]: y

Resource Unlinked
  Project: local/api-service
  Resource: local/api-repo
  Type: git-checkout

Index Cleanup
  Text Index: 347 removed
  Vectors: 892 removed
  Graph Triples: cleared
  Duration: 0.3s

Project Summary
  Linked Resources: 1 remaining
  Last Updated: 2026-02-09 10:30
  Active Plans: 0

[OK] Resource unlinked from project
```

=== "JSON"

```json
{
  "command": "agents project unlink-resource local/api-service local/api-repo",
  "status": "ok",
  "exit_code": 0,
  "data": {
    "resource_unlinked": {
      "project": "local/api-service",
      "resource": "local/api-repo",
      "type": "git-checkout"
    },
    "index_cleanup": {
      "text_index": "347 removed",
      "vectors": "892 removed",
      "graph_triples": "cleared",
      "duration_seconds": 0.3
    },
    "project_summary": {
      "linked_resources": 1,
      "last_updated": "2026-02-09T10:30:00Z",
      "active_plans": 0
    }
  },
  "timing": { "duration_ms": 340 },
  "messages": [{ "level": "ok", "text": "Resource unlinked from project" }]
}
```

=== "YAML"

```yaml
command: agents project unlink-resource local/api-service local/api-repo
status: ok
exit_code: 0
data:
  resource_unlinked:
    project: local/api-service
    resource: local/api-repo
    type: git-checkout
  index_cleanup:
    text_index: 347 removed
    vectors: 892 removed
    graph_triples: cleared
    duration_seconds: 0.3
  project_summary:
    linked_resources: 1
    last_updated: "2026-02-09T10:30:00Z"
    active_plans: 0
timing:
  duration_ms: 340
messages:
  - level: ok
    text: Resource unlinked from project
```
agents project list
agents project list [(--namespace|-n) NS] [<REGEX>]

Purpose List projects with optional filters.

Arguments

  • --namespace/-n NS: Filter by namespace.

Examples

=== "Rich"

<div class="highlight"><pre><code>
<span style="color: #66cc66; font-weight: 600;">$</span> <span style="color: cyan; font-weight: 600;">agents</span> <span style="color: cyan;">--format</span> table project list

╭─ Projects ────────────────────────────────────────────────────╮
│ <span style="color: cyan; font-weight: 600;">Name</span>               <span style="color: cyan; font-weight: 600;">Resources</span>  <span style="color: cyan; font-weight: 600;">Remote</span>  <span style="color: cyan; font-weight: 600;">Active Plans</span>            │
│ <span style="opacity: 0.7;">─────────────────  ─────────  ──────  ────────────</span>            │
│ local/api-service  2          No      1                       │
│ local/docs         1          No      0                       │
╰───────────────────────────────────────────────────────────────╯

╭─ Summary ──────────────────╮
│ <span style="color: yellow; font-weight: 600;">Total:</span> 2                   │
│ <span style="color: #66cc66; font-weight: 600;">With Resources:</span> 2          │
│ <span style="color: #5599ff; font-weight: 600;">Remote:</span> 0                  │
│ <span style="color: #5599ff; font-weight: 600;">Total Resources:</span> 3         │
│ <span style="color: #5599ff; font-weight: 600;">Indexed Files:</span> 1,247       │
│ <span style="color: yellow; font-weight: 600;">Active Plans:</span> 1            │
╰────────────────────────────╯

<span style="color: #66cc66; font-weight: 600;">✓ OK</span> 2 projects listed
</code></pre></div>

=== "Plain"

```
$ agents --format table project list

Projects
  Name               Resources  Remote  Active Plans
  -----------------  ---------  ------  ------------
  local/api-service  2          No      1
  local/docs         1          No      0

Summary
  Total: 2
  With Resources: 2
  Remote: 0
  Total Resources: 3
  Indexed Files: 1,247
  Active Plans: 1

[OK] 2 projects listed
```

=== "JSON"

```json
{
  "command": "agents --format table project list",
  "status": "ok",
  "exit_code": 0,
  "data": {
    "projects": [
      { "name": "local/api-service", "resources": 2, "remote": false, "active_plans": 1 },
      { "name": "local/docs", "resources": 1, "remote": false, "active_plans": 0 }
    ],
    "summary": {
      "total": 2,
      "with_resources": 2,
      "remote": 0,
      "total_resources": 3,
      "indexed_files": 1247,
      "active_plans": 1
    }
  },
  "timing": { "duration_ms": 45 },
  "messages": [{ "level": "ok", "text": "2 projects listed" }]
}
```

=== "YAML"

```yaml
command: agents --format table project list
status: ok
exit_code: 0
data:
  projects:
    - name: local/api-service
      resources: 2
      remote: false
      active_plans: 1
    - name: local/docs
      resources: 1
      remote: false
      active_plans: 0
  summary:
    total: 2
    with_resources: 2
    remote: 0
    total_resources: 3
    indexed_files: 1247
    active_plans: 1
timing:
  duration_ms: 45
messages:
  - level: ok
    text: 2 projects listed
```
agents project show
agents project show <PROJECT>

Purpose Show full project details.

Arguments

  • <NAME>: Project name.

Examples

=== "Rich"

<div class="highlight"><pre><code>
<span style="color: #66cc66; font-weight: 600;">$</span> <span style="color: cyan; font-weight: 600;">agents</span> project show local/api-service

╭─ Project Details ──────────────╮
│ <span style="color: cyan; font-weight: 600;">Name:</span> local/api-service        │
│ <span style="color: #5599ff; font-weight: 600;">Description:</span> Backend API       │
│ <span style="color: yellow; font-weight: 600;">Resources:</span> 2                   │
│ <span style="color: #66cc66; font-weight: 600;">Remote:</span> no                     │
│ <span style="color: #66cc66; font-weight: 600;">Created:</span> 2026-02-08 12:46      │
╰────────────────────────────────╯

╭─ Linked Resources ──────────────────────────────────────────────────────╮
│ <span style="color: cyan; font-weight: 600;">Resource</span>          <span style="color: cyan; font-weight: 600;">Type</span>            <span style="color: cyan; font-weight: 600;">Sandbox</span>              <span style="color: cyan; font-weight: 600;">Read-Only</span>        │
│ <span style="opacity: 0.7;">────────────────  ──────────────  ────────────────────  ─────────</span>       │
│ local/api-repo   git-checkout    git_worktree         no                │
│ local/staging-db local/database  transaction_rollback yes               │
╰─────────────────────────────────────────────────────────────────────────╯

╭─ Validations (3) ──────────────────────────────────────────────────────────────────╮
│ <span style="color: cyan; font-weight: 600;">local/run-tests</span>          Run unit tests with coverage    <span style="color: #66cc66; font-weight: 600;">required</span>                  │
│   <span style="opacity: 0.7;">resource: local/api-repo  scope: project  (attachment: 01HXM5A1B2C3D4E5F6G7…)</span>    │
│ <span style="color: cyan; font-weight: 600;">local/lint-check</span>         Lint check                     <span style="color: #66cc66; font-weight: 600;">required</span>                   │
│   <span style="opacity: 0.7;">resource: local/api-repo  scope: direct (always active)  (attachment: 01HXM5…)</span>   │
│ <span style="color: cyan; font-weight: 600;">local/check-bundle-size</span>  Check bundle size (advisory)   <span style="color: yellow; font-weight: 600;">informational</span>              │
│   <span style="opacity: 0.7;">resource: local/api-repo  scope: project  (attachment: 01HXM5D4E5F6G7H8J9…)</span>      │
╰────────────────────────────────────────────────────────────────────────────────────╯

╭─ Context ───────────────────╮
│ <span style="color: #66cc66; font-weight: 600;">Include:</span> repo               │
│ <span style="color: yellow; font-weight: 600;">Exclude:</span> **/node_modules/**     │
│ <span style="color: #5599ff; font-weight: 600;">Max File Size:</span> 1 MB         │
╰─────────────────────────────╯

╭─ Indexing Status ──────────╮
│ <span style="color: #66cc66; font-weight: 600;">Text Index:</span> ready          │
│ <span style="color: #66cc66; font-weight: 600;">Vector Index:</span> ready        │
│ <span style="color: yellow; font-weight: 600;">Graph Store:</span> disabled      │
│ <span style="color: #5599ff; font-weight: 600;">Indexed Files:</span> 347         │
│ <span style="color: #5599ff; font-weight: 600;">Last Indexed:</span> 12:48        │
╰────────────────────────────╯

╭─ Active Plans ──────────────────────────╮
│ <span style="color: cyan; font-weight: 600;">Plan ID</span>   <span style="color: cyan; font-weight: 600;">Action</span>               <span style="color: cyan; font-weight: 600;">Phase</span>    │
│ <span style="opacity: 0.7;">────────  ───────────────────  ───────</span>  │
│ 01HXM7A9  local/code-coverage  execute  │
╰─────────────────────────────────────────╯

<span style="color: #66cc66; font-weight: 600;">✓ OK</span> Project loaded
</code></pre></div>

=== "Plain"

```
$ agents project show local/api-service

Project Details
  Name: local/api-service
  Description: Backend API
  Resources: 2
  Remote: no
  Created: 2026-02-08 12:46

Linked Resources
  Resource          Type            Sandbox               Read-Only
  ----------------  --------------  --------------------  ---------
  local/api-repo    git-checkout    git_worktree          no
  local/staging-db  local/database  transaction_rollback  yes

Validations (3)
  local/run-tests          Run unit tests with coverage    required
    resource: local/api-repo  scope: project  (attachment: 01HXM5A1B2C3D4E5F6G7...)
  local/lint-check         Lint check                     required
    resource: local/api-repo  scope: direct (always active)  (attachment: 01HXM5...)
  local/check-bundle-size  Check bundle size (advisory)   informational
    resource: local/api-repo  scope: project  (attachment: 01HXM5D4E5F6G7H8J9...)

Context
  Include: repo
  Exclude: **/node_modules/**
  Max File Size: 1 MB

Indexing Status
  Text Index: ready
  Vector Index: ready
  Graph Store: disabled
  Indexed Files: 347
  Last Indexed: 12:48

Active Plans
  Plan ID   Action               Phase
  --------  -------------------  -------
  01HXM7A9  local/code-coverage  execute

[OK] Project loaded
```

=== "JSON"

```json
{
  "command": "agents project show local/api-service",
  "status": "ok",
  "exit_code": 0,
  "data": {
    "project": {
      "name": "local/api-service",
      "description": "Backend API",
      "resources": 2,
      "remote": false,
      "created": "2026-02-08T12:46:00Z"
    },
    "linked_resources": [
      { "name": "local/api-repo", "type": "git-checkout", "sandbox": "git_worktree", "read_only": false },
      { "name": "local/staging-db", "type": "local/database", "sandbox": "transaction_rollback", "read_only": true }
    ],
    "validations": [
      {
        "name": "local/run-tests",
        "description": "Run unit tests with coverage",
        "mode": "required",
        "resource": "local/api-repo",
        "scope": "project",
        "attachment_id": "01HXM5A1B2C3D4E5F6G7"
      },
      {
        "name": "local/lint-check",
        "description": "Lint check",
        "mode": "required",
        "resource": "local/api-repo",
        "scope": "direct",
        "attachment_id": "01HXM5"
      },
      {
        "name": "local/check-bundle-size",
        "description": "Check bundle size (advisory)",
        "mode": "informational",
        "resource": "local/api-repo",
        "scope": "project",
        "attachment_id": "01HXM5D4E5F6G7H8J9"
      }
    ],
    "context": {
      "include": ["repo"],
      "exclude": ["**/node_modules/**"],
      "max_file_size_bytes": 1048576
    },
    "indexing_status": {
      "text_index": "ready",
      "vector_index": "ready",
      "graph_store": "disabled",
      "indexed_files": 347,
      "last_indexed": "12:48"
    },
    "active_plans": [
      { "plan_id": "01HXM7A9", "action": "local/code-coverage", "phase": "execute" }
    ]
  },
  "timing": { "duration_ms": 62 },
  "messages": [{ "level": "ok", "text": "Project loaded" }]
}
```

=== "YAML"

```yaml
command: agents project show local/api-service
status: ok
exit_code: 0
data:
  project:
    name: local/api-service
    description: Backend API
    resources: 2
    remote: false
    created: "2026-02-08T12:46:00Z"
  linked_resources:
    - name: local/api-repo
      type: git-checkout
      sandbox: git_worktree
      read_only: false
    - name: local/staging-db
      type: local/database
      sandbox: transaction_rollback
      read_only: true
  validations:
    - name: local/run-tests
      description: Run unit tests with coverage
      mode: required
      resource: local/api-repo
      scope: project
      attachment_id: 01HXM5A1B2C3D4E5F6G7
    - name: local/lint-check
      description: Lint check
      mode: required
      resource: local/api-repo
      scope: direct
      attachment_id: 01HXM5
    - name: local/check-bundle-size
      description: Check bundle size (advisory)
      mode: informational
      resource: local/api-repo
      scope: project
      attachment_id: 01HXM5D4E5F6G7H8J9
  context:
    include:
      - repo
    exclude:
      - "**/node_modules/**"
    max_file_size_bytes: 1048576
  indexing_status:
    text_index: ready
    vector_index: ready
    graph_store: disabled
    indexed_files: 347
    last_indexed: "12:48"
  active_plans:
    - plan_id: 01HXM7A9
      action: local/code-coverage
      phase: execute
timing:
  duration_ms: 62
messages:
  - level: ok
    text: Project loaded
```
agents project validations

Note: Validations are managed as a top-level entity via agents validation commands (see the agents validation section below). A validation is always attached to a resource, but can optionally be scoped to a project via agents validation attach --project <PROJECT> <RESOURCE> <VALIDATION>. When scoped to a project, the validation only runs when that resource is accessed through the specified project. The agents project show command displays all validations relevant to the project — both directly-attached validations on linked resources and project-scoped validations.

See: agents validation for full details on creating, managing, and attaching validations.

agents project delete
agents project delete [--force|-f] [--yes|-y] <NAME>

!!! warning "Irreversible Action"

Deleting a project ==cannot be undone==. All resource links, validation attachments, and context policies will be removed. The resources themselves remain in the Resource Registry. Use `--force` to also cancel active plans targeting this project.

Purpose Delete a project and unlink all associated resources. The resources themselves remain in the Resource Registry.

Arguments

  • <NAME>: Project name.
  • --force/-f: Delete even if active plans exist.
  • --yes: Skip confirmation prompt.

Examples

=== "Rich"

<div class="highlight"><pre><code>
<span style="color: #66cc66; font-weight: 600;">$</span> <span style="color: cyan; font-weight: 600;">agents</span> project delete local/docs

Delete project local/docs? This cannot be undone. [y/N]: y

╭─ Deletion Summary ──────────────────╮
│ <span style="color: cyan; font-weight: 600;">Project:</span> local/docs                 │
│ <span style="color: yellow; font-weight: 600;">Resources:</span> 1 unlinked               │
│ <span style="color: #5599ff; font-weight: 600;">Data Dir:</span> /repos/docs/.cleveragents │
╰─────────────────────────────────────╯

╭─ Index Cleanup ────────╮
│ <span style="color: yellow; font-weight: 600;">Text Index:</span> cleared    │
│ <span style="color: yellow; font-weight: 600;">Vectors:</span> 240 removed   │
│ <span style="color: yellow; font-weight: 600;">Graph Triples:</span> none    │
│ <span style="color: #5599ff; font-weight: 600;">Storage Freed:</span> 12 MB   │
╰────────────────────────╯

╭─ Backups ────────────────────────────────────╮
│ <span style="color: #66cc66; font-weight: 600;">Snapshot:</span> /backups/local-docs-2026-02-08.tgz │
│ <span style="color: yellow; font-weight: 600;">Retention:</span> 7 days                            │
╰──────────────────────────────────────────────╯

<span style="color: #66cc66; font-weight: 600;">✓ OK</span> Project deleted
</code></pre></div>

=== "Plain"

```
$ agents project delete local/docs

Delete project local/docs? This cannot be undone. [y/N]: y

Deletion Summary
  Project: local/docs
  Resources: 1 unlinked
  Data Dir: /repos/docs/.cleveragents

Index Cleanup
  Text Index: cleared
  Vectors: 240 removed
  Graph Triples: none
  Storage Freed: 12 MB

Backups
  Snapshot: /backups/local-docs-2026-02-08.tgz
  Retention: 7 days

[OK] Project deleted
```

=== "JSON"

```json
{
  "command": "agents project delete local/docs",
  "status": "ok",
  "exit_code": 0,
  "data": {
    "deletion_summary": {
      "project": "local/docs",
      "resources_unlinked": 1,
      "data_dir": "/repos/docs/.cleveragents"
    },
    "index_cleanup": {
      "text_index": "cleared",
      "vectors": "240 removed",
      "graph_triples": "none",
      "storage_freed_mb": 12
    },
    "backups": {
      "snapshot": "/backups/local-docs-2026-02-08.tgz",
      "retention_days": 7
    }
  },
  "timing": { "duration_ms": 530 },
  "messages": [{ "level": "ok", "text": "Project deleted" }]
}
```

=== "YAML"

```yaml
command: agents project delete local/docs
status: ok
exit_code: 0
data:
  deletion_summary:
    project: local/docs
    resources_unlinked: 1
    data_dir: /repos/docs/.cleveragents
  index_cleanup:
    text_index: cleared
    vectors: 240 removed
    graph_triples: none
    storage_freed_mb: 12
  backups:
    snapshot: /backups/local-docs-2026-02-08.tgz
    retention_days: 7
timing:
  duration_ms: 530
messages:
  - level: ok
    text: Project deleted
```

Attempting to delete a project that has active plans (without --force):

=== "Rich"

<div class="highlight"><pre><code>
<span style="color: #66cc66; font-weight: 600;">$</span> <span style="color: cyan; font-weight: 600;">agents</span> project delete local/api-service

Delete project local/api-service? [y/N]: y

╭─ Delete Blocked ──────────────────────────────────────────╮
│ <span style="color: #ff6666; font-weight: 600;">Cannot delete:</span> project has active plans                   │
│ <span style="color: #5599ff; font-weight: 600;">Active Plans:</span> 2                                           │
│   <span style="opacity: 0.7;">01HXM7A9 — execute/processing (local/code-coverage)</span>     │
│   <span style="opacity: 0.7;">01HXM4J1 — strategize/processing (local/refactor-api)</span>   │
╰───────────────────────────────────────────────────────────╯

╭─ Resolution ──────────────────────────────────────────────────────╮
│ - Cancel or complete active plans first, or                       │
│ - Use <span style="color: cyan;">--force</span> to cancel all active plans and delete the project   │
╰───────────────────────────────────────────────────────────────────╯

<span style="color: #ff6666; font-weight: 600;">✗ ERROR</span> Delete blocked — 2 active plans
</code></pre></div>

=== "Plain"

```
$ agents project delete local/api-service

Delete project local/api-service? [y/N]: y

Delete Blocked
  Cannot delete: project has active plans
  Active Plans: 2
    01HXM7A9 -- execute/processing (local/code-coverage)
    01HXM4J1 -- strategize/processing (local/refactor-api)

Resolution
  - Cancel or complete active plans first, or
  - Use --force to cancel all active plans and delete the project

[ERROR] Delete blocked -- 2 active plans
```

=== "JSON"

```json
{
  "command": "agents project delete local/api-service",
  "status": "error",
  "exit_code": 1,
  "data": {
    "delete_blocked": {
      "reason": "project has active plans",
      "active_plans": [
        { "plan_id": "01HXM7A9", "phase": "execute/processing", "action": "local/code-coverage" },
        { "plan_id": "01HXM4J1", "phase": "strategize/processing", "action": "local/refactor-api" }
      ]
    },
    "resolution": [
      "Cancel or complete active plans first",
      "Use --force to cancel all active plans and delete the project"
    ]
  },
  "timing": { "duration_ms": 38 },
  "messages": [{ "level": "error", "text": "Delete blocked — 2 active plans" }]
}
```

=== "YAML"

```yaml
command: agents project delete local/api-service
status: error
exit_code: 1
data:
  delete_blocked:
    reason: project has active plans
    active_plans:
      - plan_id: 01HXM7A9
        phase: execute/processing
        action: local/code-coverage
      - plan_id: 01HXM4J1
        phase: strategize/processing
        action: local/refactor-api
  resolution:
    - Cancel or complete active plans first
    - Use --force to cancel all active plans and delete the project
timing:
  duration_ms: 38
messages:
  - level: error
    text: "Delete blocked \u2014 2 active plans"
```

Force-deleting a project with active plans:

=== "Rich"

<div class="highlight"><pre><code>
<span style="color: #66cc66; font-weight: 600;">$</span> <span style="color: cyan; font-weight: 600;">agents</span> project delete <span style="color: cyan;">--force</span> <span style="color: cyan;">--yes</span> local/api-service

╭─ Force Delete ─────────────────────────────────────────╮
│ <span style="color: yellow; font-weight: 600;">Cancelling:</span> 2 active plans                             │
│   <span style="opacity: 0.7;">01HXM7A9 — cancelled (was execute/processing)</span>        │
│   <span style="opacity: 0.7;">01HXM4J1 — cancelled (was strategize/processing)</span>     │
╰────────────────────────────────────────────────────────╯

╭─ Deleted ────────────────────╮
│ <span style="color: cyan; font-weight: 600;">Project:</span> local/api-service   │
│ <span style="color: #5599ff; font-weight: 600;">Resources Unlinked:</span> 2        │
│ <span style="color: #5599ff; font-weight: 600;">Validations Detached:</span> 3      │
│ <span style="color: #5599ff; font-weight: 600;">Plans Cancelled:</span> 2           │
│ <span style="color: #5599ff; font-weight: 600;">Context Policies:</span> cleared    │
╰──────────────────────────────╯

<span style="color: #66cc66; font-weight: 600;">✓ OK</span> Project force-deleted
</code></pre></div>

=== "Plain"

```
$ agents project delete --force --yes local/api-service

Force Delete
  Cancelling: 2 active plans
    01HXM7A9 -- cancelled (was execute/processing)
    01HXM4J1 -- cancelled (was strategize/processing)

Deleted
  Project: local/api-service
  Resources Unlinked: 2
  Validations Detached: 3
  Plans Cancelled: 2
  Context Policies: cleared

[OK] Project force-deleted
```

=== "JSON"

```json
{
  "command": "agents project delete --force --yes local/api-service",
  "status": "ok",
  "exit_code": 0,
  "data": {
    "force_delete": {
      "cancelled_plans": [
        { "plan_id": "01HXM7A9", "previous_state": "execute/processing" },
        { "plan_id": "01HXM4J1", "previous_state": "strategize/processing" }
      ]
    },
    "deleted": {
      "project": "local/api-service",
      "resources_unlinked": 2,
      "validations_detached": 3,
      "plans_cancelled": 2,
      "context_policies": "cleared"
    }
  },
  "timing": { "duration_ms": 245 },
  "messages": [{ "level": "ok", "text": "Project force-deleted" }]
}
```

=== "YAML"

```yaml
command: agents project delete --force --yes local/api-service
status: ok
exit_code: 0
data:
  force_delete:
    cancelled_plans:
      - plan_id: 01HXM7A9
        previous_state: execute/processing
      - plan_id: 01HXM4J1
        previous_state: strategize/processing
  deleted:
    project: local/api-service
    resources_unlinked: 2
    validations_detached: 3
    plans_cancelled: 2
    context_policies: cleared
timing:
  duration_ms: 245
messages:
  - level: ok
    text: Project force-deleted
```
agents project context

Purpose Manage ACMS context policies for the hot/warm/cold tiers, per-view context selection, strategy configuration, UKO depth/breadth parameters, and context budget tuning.

agents project context set
agents project context set[--view (strategize|execute|apply|default)]
                          [--include-resource <INCLUDE_RESOURCE>]...
                          [--exclude-resource <EXCLUDE_RESOURCE>]...
                          [--include-path <INCLUDE_GLOB>]...
                          [--exclude-path <EXCLUDE_GLOB>]...
                          [--hot-max-tokens <N>]
                          [--warm-max-decisions <N_WARM_MAX>]
                          [--cold-max-decisions <N_COLD_MAX>]
                          [--query-limit <N>]
                          [--max-file-size <MAX_FILE_BYTES>]
                          [--max-total-size <MAX_TOTAL_BYTES>]
                          [--summarize|--no-summarize]
                          [--summary-max-tokens <N>]
                          [--strategy <STRATEGY>]...
                          [--default-breadth <N>]
                          [--default-depth <INT_OR_NAME>]
                          [--depth-gradient <HOP:INT_OR_NAME>]...
                          [--skeleton-ratio <FLOAT>]
                          [--temporal-scope (current|recent|all)]
                          [--auto-refresh|--no-auto-refresh]
                          [--execution-environment <RESOURCE_NAME>]
                          [--execution-env-priority (fallback|override)]
                          [--clear] <PROJECT>

Purpose Set the context policy for a project and (optionally) a specific view.

Arguments

  • <PROJECT>: Project name (positional argument, at end of command).
  • --view strategize|execute|apply|default: Which view this policy applies to.
  • --include-resource NAME: Resource allowlist (repeatable).
  • --exclude-resource NAME: Resource denylist (repeatable).
  • --include-path GLOB: Path allowlist (repeatable).
  • --exclude-path GLOB: Path denylist (repeatable).
  • --hot-max-tokens N: Maximum token budget for hot context. This is a soft cap and may be null. The actor/LLM hard limit can be lower; the effective hot context is the lesser of the two.
  • --warm-max-decisions N: Maximum number of decisions kept in warm context.
  • --cold-max-decisions N: Maximum number of decisions kept in cold context.
  • --query-limit N: Max number of retrieval results per query.
  • --max-file-size BYTES: Max file size included in context.
  • --max-total-size BYTES: Max total size across included files.
  • --summarize/--no-summarize: Enable or disable summarization for large context segments.
  • --summary-max-tokens N: Token limit for generated summaries.
  • --strategy STRATEGY: ACMS context strategy to enable for this view (repeatable). Overrides the global context.strategies.enabled list. Built-in: simple-keyword, semantic-embedding, breadth-depth-navigator, arce, temporal-archaeology, plan-decision-context.
  • --default-breadth N: Default number of hops from focus nodes in the UKO graph. 0 = focus nodes only. Default: 2.
  • --default-depth INT_OR_NAME: Default detail depth for context fragments. Accepts a raw integer (e.g., 4) or a named level from the active domain's DetailLevelMap (e.g., SIGNATURES, MEMBER_SUMMARY). Named levels are resolved to integers via the DetailLevelMap inheritance chain. Default: 3 (typically MEMBER_SUMMARY / FULL_TOC / TYPED_COLUMNS depending on domain).
  • --depth-gradient HOP:INT_OR_NAME: Per-hop detail depth override (repeatable). Format: 0:9, 1:4, 2:0 or 0:FULL_SOURCE, 1:SIGNATURES, 2:MODULE_LISTING. Hops not specified use --default-depth.
  • --skeleton-ratio FLOAT: Fraction of context budget reserved for inherited plan skeleton. Range: 0.01.0. Default: 0.15.
  • --temporal-scope current|recent|all: Temporal scope for UKO node resolution. Default: current.
  • --auto-refresh/--no-auto-refresh: Enable or disable automatic context re-assembly on budget changes.
  • --execution-environment RESOURCE_NAME: Name of a container-instance or devcontainer-instance resource to use as the default execution environment for this project. When set, tool invocations during plan execution are routed to this container unless overridden at the plan level. See ADR-043 §3.3.
  • --execution-env-priority fallback|override: Priority semantics for the project-level execution environment. fallback (default): defers to auto-detected devcontainers when present; used only when no closer-scoped environment exists. override: always uses the specified environment, bypassing devcontainer auto-detection. See §Execution Environment Routing.
  • --clear: Clear the policy for the selected view.

Examples

=== "Rich"

<div class="highlight"><pre><code>
<span style="color: #66cc66; font-weight: 600;">$</span> <span style="color: cyan; font-weight: 600;">agents</span> project context set <span style="color: cyan;">--view</span> strategize \
  <span style="color: cyan;">--include-resource</span> repo <span style="color: cyan;">--exclude-path</span> <span style="color: #66cc66;">&quot;**/node_modules/**&quot;</span> \
  <span style="color: cyan;">--hot-max-tokens</span> 12000 <span style="color: cyan;">--warm-max-decisions</span> 50 <span style="color: cyan;">--cold-max-decisions</span> 200 \
  <span style="color: cyan;">--summarize</span> <span style="color: cyan;">--summary-max-tokens</span> 800 local/api-service

╭─ Context Policy ────────────╮
│ <span style="color: cyan; font-weight: 600;">Project:</span> local/api-service  │
│ <span style="color: #5599ff; font-weight: 600;">View:</span> strategize            │
│ <span style="color: #66cc66; font-weight: 600;">Include:</span> repo               │
│ <span style="color: yellow; font-weight: 600;">Exclude:</span> **/node_modules/**     │
╰─────────────────────────────╯

╭─ Limits ─────────────────────╮
│ <span style="color: magenta; font-weight: 600;">Hot Tokens:</span> 12000 (soft cap) │
│ <span style="color: magenta; font-weight: 600;">Warm Decisions:</span> 50           │
│ <span style="color: magenta; font-weight: 600;">Cold Decisions:</span> 200          │
│ <span style="color: #5599ff; font-weight: 600;">Query Limit:</span> 20              │
│ <span style="color: #5599ff; font-weight: 600;">Max File Size:</span> 1 MB          │
│ <span style="color: #5599ff; font-weight: 600;">Max Total Size:</span> 50 MB        │
╰──────────────────────────────╯

╭─ Summarization ─╮
│ <span style="color: #66cc66; font-weight: 600;">Enabled:</span> yes    │
│ <span style="color: #5599ff; font-weight: 600;">Max Tokens:</span> 800 │
╰─────────────────╯

╭─ Other Views ────────╮
│ <span style="color: #5599ff; font-weight: 600;">execute:</span> (default)   │
│ <span style="color: #5599ff; font-weight: 600;">apply:</span> (default)     │
│ <span style="color: #5599ff; font-weight: 600;">default:</span> (unset)     │
╰──────────────────────╯

<span style="color: #66cc66; font-weight: 600;">✓ OK</span> Context policy updated
</code></pre></div>

=== "Plain"

```
$ agents project context set --view strategize \
  --include-resource repo --exclude-path "**/node_modules/**" \
  --hot-max-tokens 12000 --warm-max-decisions 50 --cold-max-decisions 200 \
  --summarize --summary-max-tokens 800 local/api-service

Context Policy
  Project: local/api-service
  View: strategize
  Include: repo
  Exclude: **/node_modules/**

Limits
  Hot Tokens: 12000 (soft cap)
  Warm Decisions: 50
  Cold Decisions: 200
  Query Limit: 20
  Max File Size: 1 MB
  Max Total Size: 50 MB

Summarization
  Enabled: yes
  Max Tokens: 800

Other Views
  execute: (default)
  apply: (default)
  default: (unset)

[OK] Context policy updated
```

=== "JSON"

```json
{
  "command": "project context set",
  "status": "ok",
  "exit_code": 0,
  "data": {
    "context_policy": {
      "project": "local/api-service",
      "view": "strategize",
      "include_resources": ["repo"],
      "exclude_paths": ["**/node_modules/**"]
    },
    "limits": {
      "hot_max_tokens": 12000,
      "warm_max_decisions": 50,
      "cold_max_decisions": 200,
      "query_limit": 20,
      "max_file_size": "1 MB",
      "max_total_size": "50 MB"
    },
    "summarization": {
      "enabled": true,
      "max_tokens": 800
    },
    "other_views": {
      "execute": "(default)",
      "apply": "(default)",
      "default": "(unset)"
    }
  },
  "timing": {
    "duration_ms": 42
  },
  "messages": ["Context policy updated"]
}
```

=== "YAML"

```yaml
command: project context set
status: ok
exit_code: 0
data:
  context_policy:
    project: local/api-service
    view: strategize
    include_resources:
      - repo
    exclude_paths:
      - "**/node_modules/**"
  limits:
    hot_max_tokens: 12000
    warm_max_decisions: 50
    cold_max_decisions: 200
    query_limit: 20
    max_file_size: 1 MB
    max_total_size: 50 MB
  summarization:
    enabled: true
    max_tokens: 800
  other_views:
    execute: (default)
    apply: (default)
    default: (unset)
timing:
  duration_ms: 42
messages:
  - Context policy updated
```
agents project context show
agents project context show [--view (strategize|execute|apply|default)]
                            <PROJECT>

Purpose Show the active context policy for a project.

Arguments

  • <PROJECT>: Project name (positional argument, at end of command).
  • --view strategize|execute|apply|default: View to display.

Examples

=== "Rich"

<div class="highlight"><pre><code>
<span style="color: #66cc66; font-weight: 600;">$</span> <span style="color: cyan; font-weight: 600;">agents</span> project context show <span style="color: cyan;">--view</span> strategize local/api-service

╭─ Context Policy ────────────╮
│ <span style="color: cyan; font-weight: 600;">Project:</span> local/api-service  │
│ <span style="color: #5599ff; font-weight: 600;">View:</span> strategize            │
│ <span style="color: #66cc66; font-weight: 600;">Include:</span> repo               │
│ <span style="color: yellow; font-weight: 600;">Exclude:</span> **/node_modules/**     │
╰─────────────────────────────╯

╭─ Limits ─────────────────────╮
│ <span style="color: magenta; font-weight: 600;">Hot Tokens:</span> 12000 (soft cap) │
│ <span style="color: magenta; font-weight: 600;">Warm Decisions:</span> 50           │
│ <span style="color: magenta; font-weight: 600;">Cold Decisions:</span> 200          │
│ <span style="color: #5599ff; font-weight: 600;">Query Limit:</span> 20              │
│ <span style="color: #5599ff; font-weight: 600;">Max File Size:</span> 1 MB          │
│ <span style="color: #5599ff; font-weight: 600;">Max Total Size:</span> 50 MB        │
╰──────────────────────────────╯

╭─ Summarization ─╮
│ <span style="color: #66cc66; font-weight: 600;">Enabled:</span> yes    │
│ <span style="color: #5599ff; font-weight: 600;">Max Tokens:</span> 800 │
╰─────────────────╯

╭─ Current Usage ──────────────╮
│ <span style="color: #5599ff; font-weight: 600;">Hot Context:</span> 8,420 / 12,000  │
│ <span style="color: #5599ff; font-weight: 600;">Warm Entries:</span> 12 / 50        │
│ <span style="color: #5599ff; font-weight: 600;">Cold Entries:</span> 47 / 200       │
│ <span style="color: #66cc66; font-weight: 600;">Indexed Resources:</span> 1         │
╰──────────────────────────────╯

<span style="color: #66cc66; font-weight: 600;">✓ OK</span> Context policy loaded
</code></pre></div>

=== "Plain"

```
$ agents project context show --view strategize local/api-service

Context Policy
  Project: local/api-service
  View: strategize
  Include: repo
  Exclude: **/node_modules/**

Limits
  Hot Tokens: 12000 (soft cap)
  Warm Decisions: 50
  Cold Decisions: 200
  Query Limit: 20
  Max File Size: 1 MB
  Max Total Size: 50 MB

Summarization
  Enabled: yes
  Max Tokens: 800

Current Usage
  Hot Context: 8,420 / 12,000
  Warm Entries: 12 / 50
  Cold Entries: 47 / 200
  Indexed Resources: 1

[OK] Context policy loaded
```

=== "JSON"

```json
{
  "command": "project context show",
  "status": "ok",
  "exit_code": 0,
  "data": {
    "context_policy": {
      "project": "local/api-service",
      "view": "strategize",
      "include_resources": ["repo"],
      "exclude_paths": ["**/node_modules/**"]
    },
    "limits": {
      "hot_max_tokens": 12000,
      "warm_max_decisions": 50,
      "cold_max_decisions": 200,
      "query_limit": 20,
      "max_file_size": "1 MB",
      "max_total_size": "50 MB"
    },
    "summarization": {
      "enabled": true,
      "max_tokens": 800
    },
    "current_usage": {
      "hot_context_tokens": 8420,
      "hot_context_limit": 12000,
      "warm_entries": 12,
      "warm_limit": 50,
      "cold_entries": 47,
      "cold_limit": 200,
      "indexed_resources": 1
    }
  },
  "timing": {
    "duration_ms": 38
  },
  "messages": ["Context policy loaded"]
}
```

=== "YAML"

```yaml
command: project context show
status: ok
exit_code: 0
data:
  context_policy:
    project: local/api-service
    view: strategize
    include_resources:
      - repo
    exclude_paths:
      - "**/node_modules/**"
  limits:
    hot_max_tokens: 12000
    warm_max_decisions: 50
    cold_max_decisions: 200
    query_limit: 20
    max_file_size: 1 MB
    max_total_size: 50 MB
  summarization:
    enabled: true
    max_tokens: 800
  current_usage:
    hot_context_tokens: 8420
    hot_context_limit: 12000
    warm_entries: 12
    warm_limit: 50
    cold_entries: 47
    cold_limit: 200
    indexed_resources: 1
timing:
  duration_ms: 38
messages:
  - Context policy loaded
```
agents project context inspect
agents project context inspect [--view (strategize|execute|apply|default)]
                               [--strategy <STRATEGY>]
                               [--focus <UKO_URI>]...
                               [--breadth <N>] [--depth <INT_OR_NAME>]
                               <PROJECT>

Purpose Inspect the current state of the ACMS context assembly for a project. Shows the UKO graph structure, active strategies, indexed resources, context budget usage, and the most recent fusion result. Useful for debugging context quality issues.

Arguments

  • <PROJECT>: Project name (positional argument, at end of command).
  • --view strategize|execute|apply|default: View to inspect. Default: default.
  • --strategy STRATEGY: Filter to show results from a specific strategy only.
  • --focus UKO_URI: Show the UKO subgraph rooted at these focus nodes (repeatable). When omitted, shows a summary of the full indexed graph.
  • --breadth N: Override the breadth (hop count) for the focus node expansion. Default: uses the view's default_breadth.
  • --depth INT_OR_NAME: Override the detail depth for the display. Accepts a raw integer or a named level from the active DetailLevelMap. Default: uses the view's default_depth.

Examples

=== "Rich"

<div class="highlight"><pre><code>
<span style="color: #66cc66; font-weight: 600;">$</span> <span style="color: cyan; font-weight: 600;">agents</span> project context inspect <span style="color: cyan;">--view</span> execute <span style="color: cyan;">--focus</span> uko-py:module/src.auth <span style="color: cyan;">--breadth</span> 2 local/api-service

╭─ ACMS Context Inspection ────────────────────────────────────────╮
│ <span style="color: cyan; font-weight: 600;">Project:</span> local/api-service                                       │
│ <span style="color: #5599ff; font-weight: 600;">View:</span> execute                                                    │
│ <span style="color: #66cc66; font-weight: 600;">Focus:</span> uko-py:module/src.auth                                    │
│ <span style="color: magenta; font-weight: 600;">Breadth:</span> 2 hops  │  <span style="color: magenta; font-weight: 600;">Depth:</span> 3 (MEMBER_SUMMARY)                    │
╰──────────────────────────────────────────────────────────────────╯

╭─ UKO Graph (2-hop neighborhood) ─────────────────────────────────╮
│ <span style="color: cyan; font-weight: 600;">uko-py:module/src.auth</span> (depth 9/FULL_SOURCE, 1,240 tokens)       │
│   ├─ contains → <span style="color: #5599ff;">uko-py:class/AuthHandler</span> (depth 3, 320 tokens)   │
│   ├─ contains → <span style="color: #5599ff;">uko-py:class/TokenValidator</span> (depth 3, 280 tok)   │
│   ├─ imports  → <span style="color: #5599ff;">uko-py:module/src.db</span> (depth 0, 90 tokens)        │
│   └─ imports  → <span style="color: #5599ff;">uko-py:module/src.config</span> (depth 0, 60 tok)       │
╰──────────────────────────────────────────────────────────────────╯

╭─ Active Strategies ──────────────────────────────────────────────╮
│ <span style="color: #66cc66; font-weight: 600;">breadth-depth-navigator</span>  quality=0.85  budget=45%  4 fragments   │
│ <span style="color: #66cc66; font-weight: 600;">semantic-embedding</span>       quality=0.60  budget=35%  6 fragments   │
│ <span style="color: #66cc66; font-weight: 600;">simple-keyword</span>           quality=0.30  budget=20%  3 fragments   │
╰──────────────────────────────────────────────────────────────────╯

╭─ Budget ─────────────────────────────╮
│ <span style="color: #5599ff; font-weight: 600;">Model Window:</span> 128,000 tokens         │
│ <span style="color: #5599ff; font-weight: 600;">Response Reserve:</span> 4,096 tokens       │
│ <span style="color: #5599ff; font-weight: 600;">Tool Definitions:</span> 2,400 tokens       │
│ <span style="color: #5599ff; font-weight: 600;">System Prompt:</span> 1,200 tokens          │
│ <span style="color: #5599ff; font-weight: 600;">Skeleton Reserve:</span> 18,046 (15%)       │
│ <span style="color: magenta; font-weight: 600;">Available Budget:</span> 102,258 tokens     │
│ <span style="color: #66cc66; font-weight: 600;">Used:</span> 1,990 / 102,258 (1.9%)         │
╰──────────────────────────────────────╯

<span style="color: #66cc66; font-weight: 600;">✓ OK</span> Context inspection complete
</code></pre></div>

=== "Plain"

```
$ agents project context inspect --view execute --focus uko-py:module/src.auth --breadth 2 local/api-service

ACMS Context Inspection
  Project: local/api-service
  View: execute
  Focus: uko-py:module/src.auth
  Breadth: 2 hops    Depth: 3 (MEMBER_SUMMARY)

UKO Graph (2-hop neighborhood)
  uko-py:module/src.auth (depth 9/FULL_SOURCE, 1,240 tokens)
    contains -> uko-py:class/AuthHandler (depth 3, 320 tokens)
    contains -> uko-py:class/TokenValidator (depth 3, 280 tok)
    imports  -> uko-py:module/src.db (depth 0, 90 tokens)
    imports  -> uko-py:module/src.config (depth 0, 60 tok)

Active Strategies
  Strategy                   Quality  Budget  Fragments
  -------------------------  -------  ------  ---------
  breadth-depth-navigator    0.85     45%     4
  semantic-embedding         0.60     35%     6
  simple-keyword             0.30     20%     3

Budget
  Model Window: 128,000 tokens
  Response Reserve: 4,096 tokens
  Tool Definitions: 2,400 tokens
  System Prompt: 1,200 tokens
  Skeleton Reserve: 18,046 (15%)
  Available Budget: 102,258 tokens
  Used: 1,990 / 102,258 (1.9%)

[OK] Context inspection complete
```

=== "JSON"

```json
{
  "command": "project context inspect",
  "status": "ok",
  "exit_code": 0,
  "data": {
    "inspection": {
      "project": "local/api-service",
      "view": "execute",
      "focus": ["uko-py:module/src.auth"],
      "breadth": 2,
      "depth": 3,
      "depth_name": "MEMBER_SUMMARY"
    },
    "uko_graph": {
      "root": "uko-py:module/src.auth",
      "root_depth": 9,
      "root_depth_name": "FULL_SOURCE",
      "root_tokens": 1240,
      "edges": [
        { "relation": "contains", "target": "uko-py:class/AuthHandler", "depth": 3, "tokens": 320 },
        { "relation": "contains", "target": "uko-py:class/TokenValidator", "depth": 3, "tokens": 280 },
        { "relation": "imports", "target": "uko-py:module/src.db", "depth": 0, "tokens": 90 },
        { "relation": "imports", "target": "uko-py:module/src.config", "depth": 0, "tokens": 60 }
      ]
    },
    "active_strategies": [
      { "name": "breadth-depth-navigator", "quality": 0.85, "budget_pct": 0.45, "fragments": 4 },
      { "name": "semantic-embedding", "quality": 0.60, "budget_pct": 0.35, "fragments": 6 },
      { "name": "simple-keyword", "quality": 0.30, "budget_pct": 0.20, "fragments": 3 }
    ],
    "budget": {
      "model_window": 128000,
      "response_reserve": 4096,
      "tool_definitions": 2400,
      "system_prompt": 1200,
      "skeleton_reserve": 18046,
      "skeleton_ratio": 0.15,
      "available_budget": 102258,
      "used": 1990,
      "utilization": 0.019
    }
  },
  "timing": {
    "duration_ms": 185
  },
  "messages": ["Context inspection complete"]
}
```

=== "YAML"

```yaml
command: project context inspect
status: ok
exit_code: 0
data:
  inspection:
    project: local/api-service
    view: execute
    focus:
      - uko-py:module/src.auth
    breadth: 2
    depth: 3
    depth_name: MEMBER_SUMMARY
  uko_graph:
    root: uko-py:module/src.auth
    root_depth: 9
    root_depth_name: FULL_SOURCE
    root_tokens: 1240
    edges:
      - relation: contains
        target: uko-py:class/AuthHandler
        depth: 3
        tokens: 320
      - relation: contains
        target: uko-py:class/TokenValidator
        depth: 3
        tokens: 280
      - relation: imports
        target: uko-py:module/src.db
        depth: 0
        tokens: 90
      - relation: imports
        target: uko-py:module/src.config
        depth: 0
        tokens: 60
  active_strategies:
    - name: breadth-depth-navigator
      quality: 0.85
      budget_pct: 0.45
      fragments: 4
    - name: semantic-embedding
      quality: 0.60
      budget_pct: 0.35
      fragments: 6
    - name: simple-keyword
      quality: 0.30
      budget_pct: 0.20
      fragments: 3
  budget:
    model_window: 128000
    response_reserve: 4096
    tool_definitions: 2400
    system_prompt: 1200
    skeleton_reserve: 18046
    skeleton_ratio: 0.15
    available_budget: 102258
    used: 1990
    utilization: 0.019
timing:
  duration_ms: 185
messages:
  - Context inspection complete
```
agents project context simulate
agents project context simulate [--view (strategize|execute|apply|default)]
                                [--budget <TOKENS>]
                                [--focus <UKO_URI>]...
                                [--strategy <STRATEGY>]...
                                <PROJECT>

Purpose Simulate a context assembly run without invoking an actor. Produces a dry-run report showing what the Context Assembly Pipeline would include in the context payload, which strategies contributed, how the budget was allocated, and what was excluded. Useful for tuning context view configurations before running plans.

Arguments

  • <PROJECT>: Project name (positional argument, at end of command).
  • --view strategize|execute|apply|default: View to simulate. Default: default.
  • --budget TOKENS: Override the context budget in tokens. When omitted, uses the dynamic budget calculation.
  • --focus UKO_URI: Focus nodes for the simulation (repeatable). When omitted, uses a default focus derived from recent plan activity.
  • --strategy STRATEGY: Override which strategies to run (repeatable). When omitted, uses the view's configured strategies.

Examples

=== "Rich"

<div class="highlight"><pre><code>
<span style="color: #66cc66; font-weight: 600;">$</span> <span style="color: cyan; font-weight: 600;">agents</span> project context simulate <span style="color: cyan;">--view</span> strategize <span style="color: cyan;">--budget</span> 16000 \
    <span style="color: cyan;">--focus</span> uko-py:module/src.auth <span style="color: cyan;">--focus</span> uko-py:module/src.db \
    local/api-service

╭─ ACMS Simulation ────────────────────────────────────────────────╮
│ <span style="color: cyan; font-weight: 600;">Project:</span> local/api-service                                       │
│ <span style="color: #5599ff; font-weight: 600;">View:</span> strategize                                                 │
│ <span style="color: magenta; font-weight: 600;">Budget:</span> 16,000 tokens (override)                                 │
│ <span style="color: #66cc66; font-weight: 600;">Focus:</span> uko-py:module/src.auth, uko-py:module/src.db              │
╰──────────────────────────────────────────────────────────────────╯

╭─ Strategy Results ───────────────────────────────────────────────╮
│ <span style="color: cyan; font-weight: 600;">breadth-depth-navigator</span>                                          │
│   Confidence: 0.85  │  Budget: 7,200 tokens  │  12 fragments     │
│   Top: src/auth/__init__.py (depth 9, 1,240t, score=0.95)            │
│        src/db/models.py (depth 3, 480t, score=0.88)              │
│                                                                  │
│ <span style="color: cyan; font-weight: 600;">semantic-embedding</span>                                               │
│   Confidence: 0.60  │  Budget: 5,600 tokens  │  8 fragments      │
│   Top: src/auth/jwt.py (depth 3, 380t, score=0.82)               │
│        src/middleware/auth_check.py (depth 3, 290t, score=0.79)  │
│                                                                  │
│ <span style="color: cyan; font-weight: 600;">simple-keyword</span>                                                   │
│   Confidence: 0.30  │  Budget: 3,200 tokens  │  5 fragments      │
│   Top: src/auth/utils.py (depth 6, 210t, score=0.71)             │
╰──────────────────────────────────────────────────────────────────╯

╭─ Fusion Result ──────────────────────────────────────────────────╮
│ <span style="color: #66cc66; font-weight: 600;">Included:</span> 18 fragments (14,820 tokens, 92.6% of budget)          │
│ <span style="color: yellow; font-weight: 600;">Excluded:</span> 7 fragments (3,200 tokens, below budget cutoff)        │
│ <span style="color: #5599ff; font-weight: 600;">Deduplicated:</span> 4 fragments merged                                 │
│ <span style="color: #5599ff; font-weight: 600;">Skeleton:</span> 2,400 tokens (15% reserve)                             │
│ <span style="color: magenta; font-weight: 600;">Preamble:</span> 180 tokens                                             │
╰──────────────────────────────────────────────────────────────────╯

<span style="color: #66cc66; font-weight: 600;">✓ OK</span> Simulation complete (dry run — no context was assembled)
</code></pre></div>

=== "Plain"

```
$ agents project context simulate --view strategize --budget 16000 \
    --focus uko-py:module/src.auth --focus uko-py:module/src.db \
    local/api-service

ACMS Simulation
  Project: local/api-service
  View: strategize
  Budget: 16,000 tokens (override)
  Focus: uko-py:module/src.auth, uko-py:module/src.db

Strategy Results
  breadth-depth-navigator
    Confidence: 0.85    Budget: 7,200 tokens    12 fragments
    Top: src/auth/__init__.py (depth 9, 1,240t, score=0.95)
         src/db/models.py (depth 3, 480t, score=0.88)

  semantic-embedding
    Confidence: 0.60    Budget: 5,600 tokens    8 fragments
    Top: src/auth/jwt.py (depth 3, 380t, score=0.82)
         src/middleware/auth_check.py (depth 3, 290t, score=0.79)

  simple-keyword
    Confidence: 0.30    Budget: 3,200 tokens    5 fragments
    Top: src/auth/utils.py (depth 6, 210t, score=0.71)

Fusion Result
  Included: 18 fragments (14,820 tokens, 92.6% of budget)
  Excluded: 7 fragments (3,200 tokens, below budget cutoff)
  Deduplicated: 4 fragments merged
  Skeleton: 2,400 tokens (15% reserve)
  Preamble: 180 tokens

[OK] Simulation complete (dry run -- no context was assembled)
```

=== "JSON"

```json
{
  "command": "project context simulate",
  "status": "ok",
  "exit_code": 0,
  "data": {
    "simulation": {
      "project": "local/api-service",
      "view": "strategize",
      "budget": 16000,
      "budget_source": "override",
      "focus": ["uko-py:module/src.auth", "uko-py:module/src.db"]
    },
    "strategy_results": [
      {
        "name": "breadth-depth-navigator",
        "confidence": 0.85,
        "budget_tokens": 7200,
        "fragments": 12,
        "top": [
          { "path": "src/auth/__init__.py", "depth": 9, "tokens": 1240, "score": 0.95 },
          { "path": "src/db/models.py", "depth": 3, "tokens": 480, "score": 0.88 }
        ]
      },
      {
        "name": "semantic-embedding",
        "confidence": 0.60,
        "budget_tokens": 5600,
        "fragments": 8,
        "top": [
          { "path": "src/auth/jwt.py", "depth": 3, "tokens": 380, "score": 0.82 },
          { "path": "src/middleware/auth_check.py", "depth": 3, "tokens": 290, "score": 0.79 }
        ]
      },
      {
        "name": "simple-keyword",
        "confidence": 0.30,
        "budget_tokens": 3200,
        "fragments": 5,
        "top": [
          { "path": "src/auth/utils.py", "depth": 6, "tokens": 210, "score": 0.71 }
        ]
      }
    ],
    "fusion_result": {
      "included_fragments": 18,
      "included_tokens": 14820,
      "budget_utilization": 0.926,
      "excluded_fragments": 7,
      "excluded_tokens": 3200,
      "deduplicated_fragments": 4,
      "skeleton_tokens": 2400,
      "skeleton_ratio": 0.15,
      "preamble_tokens": 180
    }
  },
  "timing": {
    "duration_ms": 310
  },
  "messages": ["Simulation complete (dry run -- no context was assembled)"]
}
```

=== "YAML"

```yaml
command: project context simulate
status: ok
exit_code: 0
data:
  simulation:
    project: local/api-service
    view: strategize
    budget: 16000
    budget_source: override
    focus:
      - uko-py:module/src.auth
      - uko-py:module/src.db
  strategy_results:
    - name: breadth-depth-navigator
      confidence: 0.85
      budget_tokens: 7200
      fragments: 12
      top:
        - path: src/auth/__init__.py
          depth: 9
          tokens: 1240
          score: 0.95
        - path: src/db/models.py
          depth: 3
          tokens: 480
          score: 0.88
    - name: semantic-embedding
      confidence: 0.60
      budget_tokens: 5600
      fragments: 8
      top:
        - path: src/auth/jwt.py
          depth: 3
          tokens: 380
          score: 0.82
        - path: src/middleware/auth_check.py
          depth: 3
          tokens: 290
          score: 0.79
    - name: simple-keyword
      confidence: 0.30
      budget_tokens: 3200
      fragments: 5
      top:
        - path: src/auth/utils.py
          depth: 6
          tokens: 210
          score: 0.71
  fusion_result:
    included_fragments: 18
    included_tokens: 14820
    budget_utilization: 0.926
    excluded_fragments: 7
    excluded_tokens: 3200
    deduplicated_fragments: 4
    skeleton_tokens: 2400
    skeleton_ratio: 0.15
    preamble_tokens: 180
timing:
  duration_ms: 310
messages:
  - "Simulation complete (dry run -- no context was assembled)"
```

agents actor

Purpose Manage actors and run actor configurations directly.

agents actor run
agents actor run [(--output|-o) <OUTPUT_FILE>]
                 [-v...] [--unsafe|-u] [--context <CONTEXT_NAME>]
                 [--context-dir <CONTEXT_PATH>] [--load-context <LOAD_CONTEXT_NAME>]
                 [(--temperature|-t) <TEMP>] [--allow-rxpy-in-run-mode]
                 [--skill <SKILL>]... <NAME> <PROMPT>

Purpose Run a named actor in isolation with simple, manual context.

Arguments

  • <NAME>: The name of the actor to run (required).
  • <PROMPT>: Prompt text (positional argument).
  • --output/-o FILE: Output file path.
  • --verbose/-v: Increase log verbosity (repeatable). Same semantics as the global -v flag: no -v = normal output only, -v = ERROR, -vv = WARN, -vvv = INFO, -vvvv = DEBUG, -vvvvv = TRACE.
  • --unsafe/-u: Allow unsafe configs.
  • --context NAME: Named actor context to attach.
  • --context-dir PATH: Context storage location.
  • --load-context FILE: Load context from JSON.
  • --temperature/-t FLOAT: Override temperature.
  • --skill NAME: Skill to attach (repeatable).
  • --allow-rxpy-in-run-mode: Allow RxPy routes in run mode.

Examples

=== "Rich"

<div class="highlight"><pre><code>
<span style="color: #66cc66; font-weight: 600;">$</span> <span style="color: cyan; font-weight: 600;">agents</span> actor run <span style="color: cyan;">--context</span> docs local/code_reader <span style="color: #66cc66;">&quot;Summarize the README&quot;</span>

╭─ Run Summary ─────────────────╮
│ <span style="color: cyan; font-weight: 600;">Actor:</span> local/code_reader      │
│ <span style="color: #5599ff; font-weight: 600;">Context:</span> docs                 │
│ <span style="color: yellow; font-weight: 600;">Temperature:</span> 0.2              │
│ <span style="color: #5599ff; font-weight: 600;">Provider:</span> anthropic           │
│ <span style="color: #5599ff; font-weight: 600;">Model:</span> claude-3.5             │
╰───────────────────────────────╯

╭─ Inputs ─────────────────────╮
│ <span style="color: #5599ff; font-weight: 600;">Prompt:</span> Summarize the README │
│ <span style="color: #66cc66; font-weight: 600;">Context Files:</span> 3             │
│ <span style="color: #5599ff; font-weight: 600;">Context Size:</span> 12.4 KB        │
╰──────────────────────────────╯

╭─ Result Metrics ───────╮
│ <span style="color: #66cc66; font-weight: 600;">Output:</span> stdout         │
│ <span style="color: #5599ff; font-weight: 600;">Input Tokens:</span> 1,524    │
│ <span style="color: #5599ff; font-weight: 600;">Output Tokens:</span> 842     │
│ <span style="color: #5599ff; font-weight: 600;">Duration:</span> 1.8s         │
│ <span style="color: yellow; font-weight: 600;">Cost:</span> $0.0021          │
│ <span style="color: #5599ff; font-weight: 600;">Tool Calls:</span> 0          │
╰────────────────────────╯

<span style="color: #66cc66; font-weight: 600;">✓ OK</span> Summary generated
</code></pre></div>

=== "Plain"

```
$ agents actor run --context docs local/code_reader "Summarize the README"

Run Summary
  Actor: local/code_reader
  Context: docs
  Temperature: 0.2
  Provider: anthropic
  Model: claude-3.5

Inputs
  Prompt: Summarize the README
  Context Files: 3
  Context Size: 12.4 KB

Result Metrics
  Output: stdout
  Input Tokens: 1,524
  Output Tokens: 842
  Duration: 1.8s
  Cost: $0.0021
  Tool Calls: 0

[OK] Summary generated
```

=== "JSON"

```json
{
  "command": "agents actor run --context docs local/code_reader \"Summarize the README\"",
  "status": "ok",
  "exit_code": 0,
  "data": {
    "run_summary": {
      "actor": "local/code_reader",
      "context": "docs",
      "temperature": 0.2,
      "provider": "anthropic",
      "model": "claude-3.5"
    },
    "inputs": {
      "prompt": "Summarize the README",
      "context_files": 3,
      "context_size": "12.4 KB"
    },
    "result_metrics": {
      "output": "stdout",
      "input_tokens": 1524,
      "output_tokens": 842,
      "duration_s": 1.8,
      "cost": "$0.0021",
      "tool_calls": 0
    }
  },
  "timing": {
    "duration_ms": 1800
  },
  "messages": [
    { "level": "ok", "text": "Summary generated" }
  ]
}
```

=== "YAML"

```yaml
command: agents actor run --context docs local/code_reader "Summarize the README"
status: ok
exit_code: 0
data:
  run_summary:
    actor: local/code_reader
    context: docs
    temperature: 0.2
    provider: anthropic
    model: claude-3.5
  inputs:
    prompt: Summarize the README
    context_files: 3
    context_size: 12.4 KB
  result_metrics:
    output: stdout
    input_tokens: 1524
    output_tokens: 842
    duration_s: 1.8
    cost: "$0.0021"
    tool_calls: 0
timing:
  duration_ms: 1800
messages:
  - level: ok
    text: Summary generated
```

Running an actor with a custom temperature and skill attachment:

=== "Rich"

<div class="highlight"><pre><code>
<span style="color: #66cc66; font-weight: 600;">$</span> <span style="color: cyan; font-weight: 600;">agents</span> actor run <span style="color: cyan;">--temperature</span> 0.2 <span style="color: cyan;">--skill</span> local/code-analysis \
  local/reviewer <span style="color: #66cc66;">&quot;Review the auth module for security issues&quot;</span>

╭─ Actor Run ─────────────────────────╮
│ <span style="color: cyan; font-weight: 600;">Actor:</span> local/reviewer               │
│ <span style="color: #5599ff; font-weight: 600;">Type:</span> agent                         │
│ <span style="color: #5599ff; font-weight: 600;">Temperature:</span> 0.2                    │
│ <span style="color: #5599ff; font-weight: 600;">Skill:</span> local/code-analysis          │
╰─────────────────────────────────────╯

╭─ Response ────────────────────────────────────────────────────────╮
│ I identified 3 potential security concerns in the auth module:    │
│                                                                   │
│ 1. <span style="color: yellow; font-weight: 600;">Token expiry not validated</span> in `validate_session()` at              │
│    src/auth/session.py:42. The JWT expiry claim is decoded but    │
│    not checked against current time.                              │
│                                                                   │
│ 2. <span style="color: yellow; font-weight: 600;">Missing rate limiting</span> on the `/auth/refresh` endpoint.            │
│    An attacker could brute-force refresh tokens.                  │
│                                                                   │
│ 3. <span style="color: #66cc66; font-weight: 600;">Low severity:</span> Password hashing uses bcrypt with cost=10.       │
│    Consider cost=12 for production deployments.                   │
╰───────────────────────────────────────────────────────────────────╯

╭─ Usage ──────────────╮
│ <span style="color: #5599ff; font-weight: 600;">Tokens:</span> 2,840        │
│ <span style="color: #5599ff; font-weight: 600;">Duration:</span> 4.2s       │
│ <span style="color: #5599ff; font-weight: 600;">Cost:</span> $0.009         │
│ <span style="color: #5599ff; font-weight: 600;">Tool Calls:</span> 6        │
╰──────────────────────╯

<span style="color: #66cc66; font-weight: 600;">✓ OK</span> Actor run completed
</code></pre></div>

=== "Plain"

```
$ agents actor run --temperature 0.2 --skill local/code-analysis \
  local/reviewer "Review the auth module for security issues"

Actor Run
  Actor: local/reviewer
  Type: agent
  Temperature: 0.2
  Skill: local/code-analysis

Response
  I identified 3 potential security concerns in the auth module:

  1. Token expiry not validated in validate_session() at
     src/auth/session.py:42. The JWT expiry claim is decoded but
     not checked against current time.

  2. Missing rate limiting on the /auth/refresh endpoint.
     An attacker could brute-force refresh tokens.

  3. Low severity: Password hashing uses bcrypt with cost=10.
     Consider cost=12 for production deployments.

Usage
  Tokens: 2,840
  Duration: 4.2s
  Cost: $0.009
  Tool Calls: 6

[OK] Actor run completed
```

=== "JSON"

```json
{
  "command": "agents actor run --temperature 0.2 --skill local/code-analysis local/reviewer \"Review the auth module for security issues\"",
  "status": "ok",
  "exit_code": 0,
  "data": {
    "actor_run": {
      "actor": "local/reviewer",
      "type": "agent",
      "temperature": 0.2,
      "skill": "local/code-analysis"
    },
    "response": {
      "text": "I identified 3 potential security concerns in the auth module:\n\n1. Token expiry not validated in validate_session() at src/auth/session.py:42. The JWT expiry claim is decoded but not checked against current time.\n\n2. Missing rate limiting on the /auth/refresh endpoint. An attacker could brute-force refresh tokens.\n\n3. Low severity: Password hashing uses bcrypt with cost=10. Consider cost=12 for production deployments.",
      "issues_found": 3
    },
    "usage": {
      "tokens": 2840,
      "duration_s": 4.2,
      "cost": "$0.009",
      "tool_calls": 6
    }
  },
  "timing": {
    "duration_ms": 4200
  },
  "messages": [
    { "level": "ok", "text": "Actor run completed" }
  ]
}
```

=== "YAML"

```yaml
command: >-
  agents actor run --temperature 0.2 --skill local/code-analysis
  local/reviewer "Review the auth module for security issues"
status: ok
exit_code: 0
data:
  actor_run:
    actor: local/reviewer
    type: agent
    temperature: 0.2
    skill: local/code-analysis
  response:
    text: >-
      I identified 3 potential security concerns in the auth module:

      1. Token expiry not validated in validate_session() at
      src/auth/session.py:42. The JWT expiry claim is decoded but
      not checked against current time.

      2. Missing rate limiting on the /auth/refresh endpoint.
      An attacker could brute-force refresh tokens.

      3. Low severity: Password hashing uses bcrypt with cost=10.
      Consider cost=12 for production deployments.
    issues_found: 3
  usage:
    tokens: 2840
    duration_s: 4.2
    cost: "$0.009"
    tool_calls: 6
timing:
  duration_ms: 4200
messages:
  - level: ok
    text: Actor run completed
```

Saving actor output to a file:

=== "Rich"

<div class="highlight"><pre><code>
<span style="color: #66cc66; font-weight: 600;">$</span> <span style="color: cyan; font-weight: 600;">agents</span> actor run <span style="color: cyan;">--output</span> review.md local/reviewer <span style="color: #66cc66;">&quot;Write a code review report&quot;</span>

╭─ Actor Run ───────────────╮
│ <span style="color: cyan; font-weight: 600;">Actor:</span> local/reviewer     │
│ <span style="color: #5599ff; font-weight: 600;">Output:</span> review.md         │
│ <span style="color: #5599ff; font-weight: 600;">Tokens:</span> 4,120             │
│ <span style="color: #5599ff; font-weight: 600;">Duration:</span> 6.8s            │
╰───────────────────────────╯

<span style="color: #66cc66; font-weight: 600;">✓ OK</span> Output written to review.md (2.4 KB)
</code></pre></div>

=== "Plain"

```
$ agents actor run --output review.md local/reviewer "Write a code review report"

Actor Run
  Actor: local/reviewer
  Output: review.md
  Tokens: 4,120
  Duration: 6.8s

[OK] Output written to review.md (2.4 KB)
```

=== "JSON"

```json
{
  "command": "agents actor run --output review.md local/reviewer \"Write a code review report\"",
  "status": "ok",
  "exit_code": 0,
  "data": {
    "actor_run": {
      "actor": "local/reviewer",
      "output": "review.md",
      "tokens": 4120,
      "duration_s": 6.8
    }
  },
  "timing": {
    "duration_ms": 6800
  },
  "messages": [
    { "level": "ok", "text": "Output written to review.md (2.4 KB)" }
  ]
}
```

=== "YAML"

```yaml
command: agents actor run --output review.md local/reviewer "Write a code review report"
status: ok
exit_code: 0
data:
  actor_run:
    actor: local/reviewer
    output: review.md
    tokens: 4120
    duration_s: 6.8
timing:
  duration_ms: 6800
messages:
  - level: ok
    text: Output written to review.md (2.4 KB)
```
agents actor add
agents actor add --config|-c <FILE> [--update]

Purpose Add a new actor configuration, or replace an existing one with --update. The actor is fully defined by the YAML configuration file specified with --config. If a local actor with the same name already exists, the command fails unless the --update flag is provided, which replaces the existing actor.

Arguments

  • --config/-c FILE: Actor config file (required). The file must fully define the actor, including the name field which determines the actor's registered name.
  • --update: Replace an existing local actor with the same name. Without this flag, attempting to add an actor whose name is already registered will fail.

Examples

=== "Rich"

<div class="highlight"><pre><code>
<span style="color: #66cc66; font-weight: 600;">$</span> <span style="color: cyan; font-weight: 600;">agents</span> actor add <span style="color: cyan;">--config</span> ./actors/reviewer.yaml

╭─ Actor Added ────────╮
│ <span style="color: cyan; font-weight: 600;">Name:</span> local/reviewer │
│ <span style="color: #5599ff; font-weight: 600;">Provider:</span> openai     │
│ <span style="color: magenta; font-weight: 600;">Model:</span> gpt-4         │
│ <span style="color: yellow; font-weight: 600;">Default:</span> yes         │
│ <span style="color: #ff6666; font-weight: 600;">Unsafe:</span> no           │
│ <span style="color: #5599ff; font-weight: 600;">Type:</span> graph          │
╰──────────────────────╯

╭─ Config ─────────────────────╮
│ <span style="color: #5599ff; font-weight: 600;">Path:</span> ./actors/reviewer.yaml │
│ <span style="color: magenta; font-weight: 600;">Hash:</span> 8b3f3d2                │
│ <span style="color: #66cc66; font-weight: 600;">Options:</span> 4                   │
│ <span style="color: #5599ff; font-weight: 600;">Nodes:</span> 3                     │
│ <span style="color: #5599ff; font-weight: 600;">Edges:</span> 4                     │
╰──────────────────────────────╯

╭─ Capabilities ───────╮
│ - code review        │
│ - diff summarization │
│ - lint guidance      │
╰──────────────────────╯

╭─ Tools ────────────────────────╮
│ <span style="color: cyan; font-weight: 600;">Tool</span>          <span style="color: cyan; font-weight: 600;">Read-Only</span>  <span style="color: cyan; font-weight: 600;">Safe</span>  │
│ <span style="opacity: 0.7;">────────────  ─────────  ────</span>  │
│ read_file     yes        yes   │
│ search_files  yes        yes   │
│ git_diff      yes        yes   │
╰────────────────────────────────╯

<span style="color: #66cc66; font-weight: 600;">✓ OK</span> Actor added
</code></pre></div>

=== "Plain"

```
$ agents actor add --config ./actors/reviewer.yaml

Actor Added
  Name: local/reviewer
  Provider: openai
  Model: gpt-4
  Default: yes
  Unsafe: no
  Type: graph

Config
  Path: ./actors/reviewer.yaml
  Hash: 8b3f3d2
  Options: 4
  Nodes: 3
  Edges: 4

Capabilities
  - code review
  - diff summarization
  - lint guidance

Tools
  Tool          Read-Only  Safe
  ------------  ---------  ----
  read_file     yes        yes
  search_files  yes        yes
  git_diff      yes        yes

[OK] Actor added
```

=== "JSON"

```json
{
  "command": "agents actor add --config ./actors/reviewer.yaml",
  "status": "ok",
  "exit_code": 0,
  "data": {
    "actor_added": {
      "name": "local/reviewer",
      "provider": "openai",
      "model": "gpt-4",
      "default": true,
      "unsafe": false,
      "type": "graph"
    },
    "config": {
      "path": "./actors/reviewer.yaml",
      "hash": "8b3f3d2",
      "options": 4,
      "nodes": 3,
      "edges": 4
    },
    "capabilities": [
      "code review",
      "diff summarization",
      "lint guidance"
    ],
    "tools": [
      { "tool": "read_file", "read_only": true, "safe": true },
      { "tool": "search_files", "read_only": true, "safe": true },
      { "tool": "git_diff", "read_only": true, "safe": true }
    ]
  },
  "timing": {
    "duration_ms": 95
  },
  "messages": [
    { "level": "ok", "text": "Actor added" }
  ]
}
```

=== "YAML"

```yaml
command: agents actor add --config ./actors/reviewer.yaml
status: ok
exit_code: 0
data:
  actor_added:
    name: local/reviewer
    provider: openai
    model: gpt-4
    default: true
    unsafe: false
    type: graph
  config:
    path: ./actors/reviewer.yaml
    hash: 8b3f3d2
    options: 4
    nodes: 3
    edges: 4
  capabilities:
    - code review
    - diff summarization
    - lint guidance
  tools:
    - tool: read_file
      read_only: true
      safe: true
    - tool: search_files
      read_only: true
      safe: true
    - tool: git_diff
      read_only: true
      safe: true
timing:
  duration_ms: 95
messages:
  - level: ok
    text: Actor added
```

Attempting to register an actor that already exists (without --update):

=== "Rich"

<div class="highlight"><pre><code>
<span style="color: #66cc66; font-weight: 600;">$</span> <span style="color: cyan; font-weight: 600;">agents</span> actor add <span style="color: cyan;">--config</span> ./actors/reviewer.yaml

╭─ Error ─────────────────────────────────────────────────╮
│ <span style="color: #ff6666; font-weight: 600;">Actor already exists:</span> local/reviewer                    │
│ <span style="color: #5599ff; font-weight: 600;">Registered:</span> 2026-02-07 14:22                            │
│ <span style="opacity: 0.7;">Use <span style="color: cyan;">--update</span> to replace the existing actor definition.</span>  │
╰─────────────────────────────────────────────────────────╯

<span style="color: #ff6666; font-weight: 600;">✗ ERROR</span> Actor already registered — use --update to replace
</code></pre></div>

=== "Plain"

```
$ agents actor add --config ./actors/reviewer.yaml

Error
  Actor already exists: local/reviewer
  Registered: 2026-02-07 14:22
  Use --update to replace the existing actor definition.

[ERROR] Actor already registered -- use --update to replace
```

=== "JSON"

```json
{
  "command": "agents actor add --config ./actors/reviewer.yaml",
  "status": "error",
  "exit_code": 1,
  "data": {
    "error": {
      "message": "Actor already exists: local/reviewer",
      "registered": "2026-02-07T14:22:00Z",
      "hint": "Use --update to replace the existing actor definition."
    }
  },
  "timing": {
    "duration_ms": 42
  },
  "messages": [
    { "level": "error", "text": "Actor already registered -- use --update to replace" }
  ]
}
```

=== "YAML"

```yaml
command: agents actor add --config ./actors/reviewer.yaml
status: error
exit_code: 1
data:
  error:
    message: "Actor already exists: local/reviewer"
    registered: "2026-02-07T14:22:00Z"
    hint: Use --update to replace the existing actor definition.
timing:
  duration_ms: 42
messages:
  - level: error
    text: Actor already registered -- use --update to replace
```

Updating an existing actor with --update:

=== "Rich"

<div class="highlight"><pre><code>
<span style="color: #66cc66; font-weight: 600;">$</span> <span style="color: cyan; font-weight: 600;">agents</span> actor add <span style="color: cyan;">--config</span> ./actors/reviewer.yaml <span style="color: cyan;">--update</span>

╭─ Actor Updated ─────────────────────╮
│ <span style="color: cyan; font-weight: 600;">Name:</span> local/reviewer                │
│ <span style="color: #5599ff; font-weight: 600;">Type:</span> agent                         │
│ <span style="color: yellow; font-weight: 600;">Status:</span> updated                     │
╰─────────────────────────────────────╯

╭─ Changes ────────────────────────────╮
│ <span style="color: #5599ff; font-weight: 600;">Skills:</span> +1 (local/code-analysis)     │
│ <span style="color: #5599ff; font-weight: 600;">Model:</span> unchanged                     │
│ <span style="color: #5599ff; font-weight: 600;">Graph:</span> updated                       │
╰──────────────────────────────────────╯

<span style="color: #66cc66; font-weight: 600;">✓ OK</span> Actor updated
</code></pre></div>

=== "Plain"

```
$ agents actor add --config ./actors/reviewer.yaml --update

Actor Updated
  Name: local/reviewer
  Type: agent
  Status: updated

Changes
  Skills: +1 (local/code-analysis)
  Model: unchanged
  Graph: updated

[OK] Actor updated
```

=== "JSON"

```json
{
  "command": "agents actor add --config ./actors/reviewer.yaml --update",
  "status": "ok",
  "exit_code": 0,
  "data": {
    "actor_updated": {
      "name": "local/reviewer",
      "type": "agent",
      "status": "updated"
    },
    "changes": {
      "skills": "+1 (local/code-analysis)",
      "model": "unchanged",
      "graph": "updated"
    }
  },
  "timing": {
    "duration_ms": 88
  },
  "messages": [
    { "level": "ok", "text": "Actor updated" }
  ]
}
```

=== "YAML"

```yaml
command: agents actor add --config ./actors/reviewer.yaml --update
status: ok
exit_code: 0
data:
  actor_updated:
    name: local/reviewer
    type: agent
    status: updated
  changes:
    skills: "+1 (local/code-analysis)"
    model: unchanged
    graph: updated
timing:
  duration_ms: 88
messages:
  - level: ok
    text: Actor updated
```
agents actor remove
agents actor remove <NAME>

!!! warning "Destructive Operation" Removes a custom actor registration. Active plans and actions referencing this actor will lose their actor binding. Check the Impact section in the output to verify no active plans or sessions are affected.

Arguments

  • <NAME>: Actor name.

Examples

=== "Rich"

<div class="highlight"><pre><code>
<span style="color: #66cc66; font-weight: 600;">$</span> <span style="color: cyan; font-weight: 600;">agents</span> actor remove local/reviewer

╭─ Actor Removed ──────╮
│ <span style="color: cyan; font-weight: 600;">Name:</span> local/reviewer │
│ <span style="color: #5599ff; font-weight: 600;">Provider:</span> openai     │
│ <span style="color: magenta; font-weight: 600;">Model:</span> gpt-4         │
╰──────────────────────╯

╭─ Impact ───────────────────────────────────╮
│ <span style="color: #5599ff; font-weight: 600;">Sessions:</span> 0 affected                       │
│ <span style="color: #5599ff; font-weight: 600;">Active Plans:</span> 0 affected                   │
│ <span style="color: #5599ff; font-weight: 600;">Actions Referencing:</span> 0                     │
╰────────────────────────────────────────────╯

╭─ Cleanup ──────────────╮
│ <span style="color: #5599ff; font-weight: 600;">Config:</span> kept on disk   │
│ <span style="color: yellow; font-weight: 600;">Contexts:</span> 1 orphaned   │
╰────────────────────────╯

<span style="color: #66cc66; font-weight: 600;">✓ OK</span> Actor removed
</code></pre></div>

=== "Plain"

```
$ agents actor remove local/reviewer

Actor Removed
  Name: local/reviewer
  Provider: openai
  Model: gpt-4

Impact
  Sessions: 0 affected
  Active Plans: 0 affected
  Actions Referencing: 0

Cleanup
  Config: kept on disk
  Contexts: 1 orphaned

[OK] Actor removed
```

=== "JSON"

```json
{
  "command": "agents actor remove local/reviewer",
  "status": "ok",
  "exit_code": 0,
  "data": {
    "actor_removed": {
      "name": "local/reviewer",
      "provider": "openai",
      "model": "gpt-4"
    },
    "impact": {
      "sessions": 0,
      "active_plans": 0,
      "actions_referencing": 0
    },
    "cleanup": {
      "config": "kept on disk",
      "contexts": "1 orphaned"
    }
  },
  "timing": {
    "duration_ms": 65
  },
  "messages": [
    { "level": "ok", "text": "Actor removed" }
  ]
}
```

=== "YAML"

```yaml
command: agents actor remove local/reviewer
status: ok
exit_code: 0
data:
  actor_removed:
    name: local/reviewer
    provider: openai
    model: gpt-4
  impact:
    sessions: 0
    active_plans: 0
    actions_referencing: 0
  cleanup:
    config: kept on disk
    contexts: 1 orphaned
timing:
  duration_ms: 65
messages:
  - level: ok
    text: Actor removed
```
agents actor list
agents actor list

Purpose List all actors.

Arguments

None.

Examples

=== "Rich"

<div class="highlight"><pre><code>
<span style="color: #66cc66; font-weight: 600;">$</span> <span style="color: cyan; font-weight: 600;">agents</span> actor list

╭─ Actors ──────────────────────────────────────────────────────────────╮
│ <span style="color: cyan; font-weight: 600;">Name</span>            <span style="color: cyan; font-weight: 600;">Provider</span>   <span style="color: cyan; font-weight: 600;">Model</span>       <span style="color: cyan; font-weight: 600;">Default</span>  <span style="color: cyan; font-weight: 600;">Built-in</span>  <span style="color: cyan; font-weight: 600;">Unsafe</span>      │
│ <span style="opacity: 0.7;">──────────────  ─────────  ──────────  ───────  ────────  ──────</span>      │
│ local/reviewer  openai     gpt-4       ✓                  no          │
│ openai/gpt-4    openai     gpt-4                ✓         no          │
│ anthropic/3.5   anthropic  claude-3.5           ✓         no          │
╰───────────────────────────────────────────────────────────────────────╯

╭─ Summary ──────────────╮
│ <span style="color: yellow; font-weight: 600;">Total:</span> 3               │
│ <span style="color: #66cc66; font-weight: 600;">Built-in:</span> 2            │
│ <span style="color: #5599ff; font-weight: 600;">Custom:</span> 1              │
│ <span style="color: #ff6666; font-weight: 600;">Unsafe:</span> 0              │
│ <span style="color: #5599ff; font-weight: 600;">Providers Used:</span> 2      │
╰────────────────────────╯

<span style="color: #66cc66; font-weight: 600;">✓ OK</span> 3 actors listed
</code></pre></div>

=== "Plain"

```
$ agents actor list

Actors
  Name            Provider   Model       Default  Built-in  Unsafe
  --------------  ---------  ----------  -------  --------  ------
  local/reviewer  openai     gpt-4       yes                no
  openai/gpt-4    openai     gpt-4                yes       no
  anthropic/3.5   anthropic  claude-3.5           yes       no

Summary
  Total: 3
  Built-in: 2
  Custom: 1
  Unsafe: 0
  Providers Used: 2

[OK] 3 actors listed
```

=== "JSON"

```json
{
  "command": "agents actor list",
  "status": "ok",
  "exit_code": 0,
  "data": {
    "actors": [
      {
        "name": "local/reviewer",
        "provider": "openai",
        "model": "gpt-4",
        "default": true,
        "builtin": false,
        "unsafe": false
      },
      {
        "name": "openai/gpt-4",
        "provider": "openai",
        "model": "gpt-4",
        "default": false,
        "builtin": true,
        "unsafe": false
      },
      {
        "name": "anthropic/3.5",
        "provider": "anthropic",
        "model": "claude-3.5",
        "default": false,
        "builtin": true,
        "unsafe": false
      }
    ],
    "summary": {
      "total": 3,
      "builtin": 2,
      "custom": 1,
      "unsafe": 0,
      "providers_used": 2
    }
  },
  "timing": {
    "duration_ms": 55
  },
  "messages": [
    { "level": "ok", "text": "3 actors listed" }
  ]
}
```

=== "YAML"

```yaml
command: agents actor list
status: ok
exit_code: 0
data:
  actors:
    - name: local/reviewer
      provider: openai
      model: gpt-4
      default: true
      builtin: false
      unsafe: false
    - name: openai/gpt-4
      provider: openai
      model: gpt-4
      default: false
      builtin: true
      unsafe: false
    - name: anthropic/3.5
      provider: anthropic
      model: claude-3.5
      default: false
      builtin: true
      unsafe: false
  summary:
    total: 3
    builtin: 2
    custom: 1
    unsafe: 0
    providers_used: 2
timing:
  duration_ms: 55
messages:
  - level: ok
    text: 3 actors listed
```
agents actor show
agents actor show <NAME>

Purpose Show details for a single actor.

Arguments

  • <NAME>: Actor name.

Examples

=== "Rich"

<div class="highlight"><pre><code>
<span style="color: #66cc66; font-weight: 600;">$</span> <span style="color: cyan; font-weight: 600;">agents</span> actor show local/reviewer

╭─ Actor Details ────────────────────╮
│ <span style="color: cyan; font-weight: 600;">Name:</span> local/reviewer               │
│ <span style="color: #5599ff; font-weight: 600;">Provider:</span> openai                   │
│ <span style="color: magenta; font-weight: 600;">Model:</span> gpt-4                       │
│ <span style="color: yellow; font-weight: 600;">Default:</span> yes                       │
│ <span style="color: #66cc66; font-weight: 600;">Built-in:</span> no                       │
│ <span style="color: #ff6666; font-weight: 600;">Unsafe:</span> no                         │
│ <span style="color: #5599ff; font-weight: 600;">Type:</span> graph                        │
│ <span style="color: #66cc66; font-weight: 600;">Created:</span> 2026-02-08 12:35          │
│ <span style="color: #66cc66; font-weight: 600;">Updated:</span> 2026-02-08 12:40          │
│ <span style="color: #5599ff; font-weight: 600;">Config:</span> ./actors/reviewer.yaml     │
│ <span style="color: magenta; font-weight: 600;">Config Hash:</span> 9c4e2a1               │
╰────────────────────────────────────╯

╭─ Options ──────────╮
│ - temperature: 0.2 │
│ - max_tokens: 2048 │
│ - top_p: 1.0       │
╰────────────────────╯

╭─ Graph Structure ─╮
│ <span style="color: #5599ff; font-weight: 600;">Nodes:</span> 3          │
│ <span style="color: #5599ff; font-weight: 600;">Edges:</span> 4          │
│ <span style="color: #5599ff; font-weight: 600;">Entry:</span> analyze    │
│ <span style="color: #5599ff; font-weight: 600;">Exit:</span> report      │
╰───────────────────╯

╭─ Tools ────────────────────────╮
│ <span style="color: cyan; font-weight: 600;">Tool</span>          <span style="color: cyan; font-weight: 600;">Read-Only</span>  <span style="color: cyan; font-weight: 600;">Safe</span>  │
│ <span style="opacity: 0.7;">────────────  ─────────  ────</span>  │
│ read_file     yes        yes   │
│ search_files  yes        yes   │
│ git_diff      yes        yes   │
╰────────────────────────────────╯

╭─ Access ────────────╮
│ <span style="color: #ff6666; font-weight: 600;">Unsafe:</span> no          │
│ <span style="color: #66cc66; font-weight: 600;">Filesystem:</span> allowed │
│ <span style="color: yellow; font-weight: 600;">Network:</span> restricted │
╰─────────────────────╯

╭─ Usage ─────────────────────────────────────────╮
│ <span style="color: #5599ff; font-weight: 600;">Referenced by Actions:</span> 1 (local/code-coverage)  │
│ <span style="color: #5599ff; font-weight: 600;">Active in Sessions:</span> 0                           │
│ <span style="color: #5599ff; font-weight: 600;">Total Runs:</span> 14                                  │
│ <span style="color: #5599ff; font-weight: 600;">Avg Cost/Run:</span> $0.0032                           │
╰─────────────────────────────────────────────────╯

<span style="color: #66cc66; font-weight: 600;">✓ OK</span> Actor loaded
</code></pre></div>

=== "Plain"

```
$ agents actor show local/reviewer

Actor Details
  Name: local/reviewer
  Provider: openai
  Model: gpt-4
  Default: yes
  Built-in: no
  Unsafe: no
  Type: graph
  Created: 2026-02-08 12:35
  Updated: 2026-02-08 12:40
  Config: ./actors/reviewer.yaml
  Config Hash: 9c4e2a1

Options
  - temperature: 0.2
  - max_tokens: 2048
  - top_p: 1.0

Graph Structure
  Nodes: 3
  Edges: 4
  Entry: analyze
  Exit: report

Tools
  Tool          Read-Only  Safe
  ------------  ---------  ----
  read_file     yes        yes
  search_files  yes        yes
  git_diff      yes        yes

Access
  Unsafe: no
  Filesystem: allowed
  Network: restricted

Usage
  Referenced by Actions: 1 (local/code-coverage)
  Active in Sessions: 0
  Total Runs: 14
  Avg Cost/Run: $0.0032

[OK] Actor loaded
```

=== "JSON"

```json
{
  "command": "agents actor show local/reviewer",
  "status": "ok",
  "exit_code": 0,
  "data": {
    "actor_details": {
      "name": "local/reviewer",
      "provider": "openai",
      "model": "gpt-4",
      "default": true,
      "builtin": false,
      "unsafe": false,
      "type": "graph",
      "created": "2026-02-08T12:35:00Z",
      "updated": "2026-02-08T12:40:00Z",
      "config": "./actors/reviewer.yaml",
      "config_hash": "9c4e2a1"
    },
    "options": {
      "temperature": 0.2,
      "max_tokens": 2048,
      "top_p": 1.0
    },
    "graph_structure": {
      "nodes": 3,
      "edges": 4,
      "entry": "analyze",
      "exit": "report"
    },
    "tools": [
      { "tool": "read_file", "read_only": true, "safe": true },
      { "tool": "search_files", "read_only": true, "safe": true },
      { "tool": "git_diff", "read_only": true, "safe": true }
    ],
    "access": {
      "unsafe": false,
      "filesystem": "allowed",
      "network": "restricted"
    },
    "usage": {
      "referenced_by_actions": "1 (local/code-coverage)",
      "active_in_sessions": 0,
      "total_runs": 14,
      "avg_cost_per_run": "$0.0032"
    }
  },
  "timing": {
    "duration_ms": 78
  },
  "messages": [
    { "level": "ok", "text": "Actor loaded" }
  ]
}
```

=== "YAML"

```yaml
command: agents actor show local/reviewer
status: ok
exit_code: 0
data:
  actor_details:
    name: local/reviewer
    provider: openai
    model: gpt-4
    default: true
    builtin: false
    unsafe: false
    type: graph
    created: "2026-02-08T12:35:00Z"
    updated: "2026-02-08T12:40:00Z"
    config: ./actors/reviewer.yaml
    config_hash: 9c4e2a1
  options:
    temperature: 0.2
    max_tokens: 2048
    top_p: 1.0
  graph_structure:
    nodes: 3
    edges: 4
    entry: analyze
    exit: report
  tools:
    - tool: read_file
      read_only: true
      safe: true
    - tool: search_files
      read_only: true
      safe: true
    - tool: git_diff
      read_only: true
      safe: true
  access:
    unsafe: false
    filesystem: allowed
    network: restricted
  usage:
    referenced_by_actions: 1 (local/code-coverage)
    active_in_sessions: 0
    total_runs: 14
    avg_cost_per_run: "$0.0032"
timing:
  duration_ms: 78
messages:
  - level: ok
    text: Actor loaded
```
agents actor context

Purpose Manage manual context for actor runs. These commands mirror the legacy context behavior but are scoped to an actor context name.

agents actor context remove
agents actor context remove [--yes|-y] (--all|-a|<NAME>)

!!! warning "Context Data Loss" Remove files or directories from an actor context. Use --all to clear all contexts at once. Context data is not recoverable after removal.

Arguments

  • <NAME>: Context name (positional argument). Use --all/-a to target all contexts.
  • --all, -a: Target all contexts instead of a named one.
  • --yes, -y: Skip confirmation prompt.

Examples

=== "Rich"

<div class="highlight"><pre><code>
<span style="color: #66cc66; font-weight: 600;">$</span> <span style="color: cyan; font-weight: 600;">agents</span> actor context remove docs

╭─ Context Removed ───────────╮
│ <span style="color: cyan; font-weight: 600;">Context:</span> docs               │
│ <span style="color: #66cc66; font-weight: 600;">Status:</span> removed             │
╰─────────────────────────────╯

╭─ Stats ───────────────────╮
│ <span style="color: #5599ff; font-weight: 600;">Remaining Size:</span> 48 KB     │
│ <span style="color: #66cc66; font-weight: 600;">Updated:</span> 2026-02-08 13:06 │
╰───────────────────────────╯

<span style="color: #66cc66; font-weight: 600;">✓ OK</span> Context updated
</code></pre></div>

=== "Plain"

```
$ agents actor context remove docs

Context Removed
  Context: docs
  Status: removed

Stats
  Remaining Size: 48 KB
  Updated: 2026-02-08 13:06

[OK] Context updated
```

=== "JSON"

```json
{
  "command": "agents actor context remove docs",
  "status": "ok",
  "exit_code": 0,
  "data": {
    "context_removed": {
      "context": "docs",
      "status": "removed"
    },
    "stats": {
      "remaining_size_kb": 48,
      "updated": "2026-02-08T13:06:00Z"
    }
  },
  "timing": { "duration_ms": 65 },
  "messages": [{ "level": "ok", "text": "Context updated" }]
}
```

=== "YAML"

```yaml
command: agents actor context remove docs
status: ok
exit_code: 0
data:
  context_removed:
    context: docs
    status: removed
  stats:
    remaining_size_kb: 48
    updated: "2026-02-08T13:06:00Z"
timing:
  duration_ms: 65
messages:
  - level: ok
    text: Context updated
```
agents actor context list
agents actor context list [<REGEX>]

Purpose List files stored in an actor context.

Arguments

  • [REGEX]: Optional regex filter for context names.

Examples

=== "Rich"

<div class="highlight"><pre><code>
<span style="color: #66cc66; font-weight: 600;">$</span> <span style="color: cyan; font-weight: 600;">agents</span> actor context list docs

╭─ Context Files ──────────────────────────────╮
│ <span style="color: cyan; font-weight: 600;">Name</span>              <span style="color: cyan; font-weight: 600;">Type</span>  <span style="color: cyan; font-weight: 600;">Size</span>     <span style="color: cyan; font-weight: 600;">Added</span>       │
│ <span style="opacity: 0.7;">────────────────  ────  ───────  ──────────</span>  │
│ README.md         file  4.2 KB   02-08 12:10 │
│ docs/overview.md  file  12.8 KB  02-08 12:10 │
│ docs/cli.md       file  9.5 KB   02-08 12:10 │
╰──────────────────────────────────────────────╯

╭─ Stats ───────────────────────╮
│ <span style="color: yellow; font-weight: 600;">Total Files:</span> 3                │
│ <span style="color: #66cc66; font-weight: 600;">Total Size:</span> 26.5 KB           │
│ <span style="color: #5599ff; font-weight: 600;">Estimated Tokens:</span> ~6,600      │
│ <span style="color: #5599ff; font-weight: 600;">Languages:</span> Markdown           │
╰───────────────────────────────╯

<span style="color: #66cc66; font-weight: 600;">✓ OK</span> 3 files listed
</code></pre></div>

=== "Plain"

```
$ agents actor context list docs

Context Files
  Name              Type  Size     Added
  ----------------  ----  -------  ----------
  README.md         file  4.2 KB   02-08 12:10
  docs/overview.md  file  12.8 KB  02-08 12:10
  docs/cli.md       file  9.5 KB   02-08 12:10

Stats
  Total Files: 3
  Total Size: 26.5 KB
  Estimated Tokens: ~6,600
  Languages: Markdown

[OK] 3 files listed
```

=== "JSON"

```json
{
  "command": "agents actor context list docs",
  "status": "ok",
  "exit_code": 0,
  "data": {
    "context_files": [
      { "name": "README.md", "type": "file", "size_kb": 4.2, "added": "2026-02-08T12:10:00Z" },
      { "name": "docs/overview.md", "type": "file", "size_kb": 12.8, "added": "2026-02-08T12:10:00Z" },
      { "name": "docs/cli.md", "type": "file", "size_kb": 9.5, "added": "2026-02-08T12:10:00Z" }
    ],
    "stats": {
      "total_files": 3,
      "total_size_kb": 26.5,
      "estimated_tokens": 6600,
      "languages": ["Markdown"]
    }
  },
  "timing": { "duration_ms": 35 },
  "messages": [{ "level": "ok", "text": "3 files listed" }]
}
```

=== "YAML"

```yaml
command: agents actor context list docs
status: ok
exit_code: 0
data:
  context_files:
    - name: README.md
      type: file
      size_kb: 4.2
      added: "2026-02-08T12:10:00Z"
    - name: docs/overview.md
      type: file
      size_kb: 12.8
      added: "2026-02-08T12:10:00Z"
    - name: docs/cli.md
      type: file
      size_kb: 9.5
      added: "2026-02-08T12:10:00Z"
  stats:
    total_files: 3
    total_size_kb: 26.5
    estimated_tokens: 6600
    languages:
      - Markdown
timing:
  duration_ms: 35
messages:
  - level: ok
    text: 3 files listed
```
agents actor context show
agents actor context show <NAME>

Purpose Show content of a file in an actor context.

Arguments

  • <NAME>: Context name (positional argument).

Examples

=== "Rich"

<div class="highlight"><pre><code>
<span style="color: #66cc66; font-weight: 600;">$</span> <span style="color: cyan; font-weight: 600;">agents</span> actor context show docs

╭─ Context Summary ───────────╮
│ <span style="color: cyan; font-weight: 600;">Context:</span> docs               │
│ <span style="color: #66cc66; font-weight: 600;">Files:</span> 3                    │
│ <span style="color: yellow; font-weight: 600;">Total Size:</span> 26.5 KB         │
│ <span style="color: #5599ff; font-weight: 600;">Estimated Tokens:</span> ~6,600    │
│ <span style="color: #5599ff; font-weight: 600;">Created:</span> 2026-02-08 12:10   │
╰─────────────────────────────╯

<span style="color: #66cc66; font-weight: 600;">✓ OK</span> Context displayed
</code></pre></div>

=== "Plain"

```
$ agents actor context show docs

Context Summary
  Context: docs
  Files: 3
  Total Size: 26.5 KB
  Estimated Tokens: ~6,600
  Created: 2026-02-08 12:10

[OK] Context displayed
```

=== "JSON"

```json
{
  "command": "agents actor context show docs",
  "status": "ok",
  "exit_code": 0,
  "data": {
    "context_summary": {
      "context": "docs",
      "files": 3,
      "total_size_kb": 26.5,
      "estimated_tokens": 6600,
      "created": "2026-02-08T12:10:00Z"
    }
  },
  "timing": { "duration_ms": 45 },
  "messages": [{ "level": "ok", "text": "Context displayed" }]
}
```

=== "YAML"

```yaml
command: agents actor context show docs
status: ok
exit_code: 0
data:
  context_summary:
    context: docs
    files: 3
    total_size_kb: 26.5
    estimated_tokens: 6600
    created: "2026-02-08T12:10:00Z"
timing:
  duration_ms: 45
messages:
  - level: ok
    text: Context displayed
```
agents actor context export
agents actor context export (--output|-o) <FILE> <NAME>

Purpose Export a context as JSON.

Arguments

  • <NAME>: Context name (positional argument).
  • --output/-o FILE: Output file path (required).

Examples

=== "Rich"

<div class="highlight"><pre><code>
<span style="color: #66cc66; font-weight: 600;">$</span> <span style="color: cyan; font-weight: 600;">agents</span> actor context export <span style="color: cyan;">--output</span> /tmp/docs-context.json docs

╭─ Context Export ───────────────╮
│ <span style="color: cyan; font-weight: 600;">Context:</span> docs                  │
│ <span style="color: #5599ff; font-weight: 600;">Output:</span> /tmp/docs-context.json │
│ <span style="color: yellow; font-weight: 600;">Items:</span> 12                      │
│ <span style="color: #66cc66; font-weight: 600;">Size:</span> 48 KB                    │
╰────────────────────────────────╯

╭─ Integrity ──────────────────╮
│ <span style="color: magenta; font-weight: 600;">Checksum:</span> sha256:19b2...a7d0 │
│ <span style="color: #5599ff; font-weight: 600;">Compressed:</span> no               │
╰──────────────────────────────╯

<span style="color: #66cc66; font-weight: 600;">✓ OK</span> Export completed
</code></pre></div>

=== "Plain"

```
$ agents actor context export --output /tmp/docs-context.json docs

Context Export
  Context: docs
  Output: /tmp/docs-context.json
  Items: 12
  Size: 48 KB

Integrity
  Checksum: sha256:19b2...a7d0
  Compressed: no

[OK] Export completed
```

=== "JSON"

```json
{
  "command": "agents actor context export --output /tmp/docs-context.json docs",
  "status": "ok",
  "exit_code": 0,
  "data": {
    "context_export": {
      "context": "docs",
      "output": "/tmp/docs-context.json",
      "items": 12,
      "size_kb": 48
    },
    "integrity": {
      "checksum": "sha256:19b2...a7d0",
      "compressed": false
    }
  },
  "timing": { "duration_ms": 180 },
  "messages": [{ "level": "ok", "text": "Export completed" }]
}
```

=== "YAML"

```yaml
command: agents actor context export --output /tmp/docs-context.json docs
status: ok
exit_code: 0
data:
  context_export:
    context: docs
    output: /tmp/docs-context.json
    items: 12
    size_kb: 48
  integrity:
    checksum: "sha256:19b2...a7d0"
    compressed: false
timing:
  duration_ms: 180
messages:
  - level: ok
    text: Export completed
```
agents actor context import
agents actor context import [--update] (--input|-i) <FILE> [<NAME>]

Purpose Import a context from JSON.

Arguments

  • [NAME]: Context name (optional, inferred from file if omitted).
  • --input/-i FILE: Input JSON file (required).
  • --update: Replace an existing context with the same name.

Examples

=== "Rich"

<div class="highlight"><pre><code>
<span style="color: #66cc66; font-weight: 600;">$</span> <span style="color: cyan; font-weight: 600;">agents</span> actor context import <span style="color: cyan;">--input</span> /tmp/docs-context.json docs

╭─ Context Import ──────────────╮
│ <span style="color: cyan; font-weight: 600;">Context:</span> docs                 │
│ <span style="color: #5599ff; font-weight: 600;">Input:</span> /tmp/docs-context.json │
│ <span style="color: yellow; font-weight: 600;">Items:</span> 12                     │
╰───────────────────────────────╯

╭─ Merge ───────────╮
│ <span style="color: #5599ff; font-weight: 600;">Strategy:</span> replace │
│ <span style="color: yellow; font-weight: 600;">Conflicts:</span> 0      │
╰───────────────────╯

<span style="color: #66cc66; font-weight: 600;">✓ OK</span> Import completed
</code></pre></div>

=== "Plain"

```
$ agents actor context import --input /tmp/docs-context.json docs

Context Import
  Context: docs
  Input: /tmp/docs-context.json
  Items: 12

Merge
  Strategy: replace
  Conflicts: 0

[OK] Import completed
```

=== "JSON"

```json
{
  "command": "agents actor context import --input /tmp/docs-context.json docs",
  "status": "ok",
  "exit_code": 0,
  "data": {
    "context_import": {
      "context": "docs",
      "input": "/tmp/docs-context.json",
      "items": 12
    },
    "merge": {
      "strategy": "replace",
      "conflicts": 0
    }
  },
  "timing": { "duration_ms": 210 },
  "messages": [{ "level": "ok", "text": "Import completed" }]
}
```

=== "YAML"

```yaml
command: agents actor context import --input /tmp/docs-context.json docs
status: ok
exit_code: 0
data:
  context_import:
    context: docs
    input: /tmp/docs-context.json
    items: 12
  merge:
    strategy: replace
    conflicts: 0
timing:
  duration_ms: 210
messages:
  - level: ok
    text: Import completed
```
agents actor context clear
agents actor context clear [--yes|-y] (--all|-a|<NAME>)

Purpose Clear all files from a context but keep the context itself.

Arguments

  • <NAME>: Context name (positional argument). Use --all/-a to target all contexts.
  • --all, -a: Target all contexts instead of a named one.
  • --yes, -y: Skip confirmation.

Examples

=== "Rich"

<div class="highlight"><pre><code>
<span style="color: #66cc66; font-weight: 600;">$</span> <span style="color: cyan; font-weight: 600;">agents</span> actor context clear docs

Clear context docs? [y/N]: y

╭─ Context Cleared ────╮
│ <span style="color: cyan; font-weight: 600;">Context:</span> docs        │
│ <span style="color: yellow; font-weight: 600;">Items:</span> 12 removed    │
│ <span style="color: #5599ff; font-weight: 600;">Storage:</span> 48 KB freed │
╰──────────────────────╯

╭─ Retention ────────╮
│ <span style="color: #66cc66; font-weight: 600;">Context:</span> preserved │
│ <span style="color: yellow; font-weight: 600;">Files:</span> removed     │
╰────────────────────╯

<span style="color: #66cc66; font-weight: 600;">✓ OK</span> Context cleared
</code></pre></div>

=== "Plain"

```
$ agents actor context clear docs

Clear context docs? [y/N]: y

Context Cleared
  Context: docs
  Items: 12 removed
  Storage: 48 KB freed

Retention
  Context: preserved
  Files: removed

[OK] Context cleared
```

=== "JSON"

```json
{
  "command": "agents actor context clear docs",
  "status": "ok",
  "exit_code": 0,
  "data": {
    "context_cleared": {
      "context": "docs",
      "items": "12 removed",
      "storage": "48 KB freed"
    },
    "retention": {
      "context": "preserved",
      "files": "removed"
    }
  },
  "timing": { "duration_ms": 85 },
  "messages": [{ "level": "ok", "text": "Context cleared" }]
}
```

=== "YAML"

```yaml
command: agents actor context clear docs
status: ok
exit_code: 0
data:
  context_cleared:
    context: docs
    items: 12 removed
    storage: 48 KB freed
  retention:
    context: preserved
    files: removed
timing:
  duration_ms: 85
messages:
  - level: ok
    text: Context cleared
```

agents skill

!!! info "Purpose"

Manage skills — reusable, namespaced collections of tools. Skills are defined in their own YAML configuration files and registered in the system through these commands. Once registered, skills can be referenced by actors.
agents skill add
agents skill add --config|-c <FILE> [--update]

Purpose Register a new skill. The skill is fully defined by the YAML configuration file specified with --config. If a skill with the same name already exists, the command fails unless the --update flag is provided, which allows overwriting the existing registration with the new configuration.

Arguments

  • --config/-c FILE: Path to the skill YAML configuration file (required). The file must fully define the skill, including the name field which determines the skill's registered name.
  • --update: Allow overwriting an existing skill registration. Without this flag, attempting to add a skill whose name is already registered will fail.

Examples

Registering a new skill:

=== "Rich"

<div class="highlight"><pre><code>
<span style="color: #66cc66; font-weight: 600;">$</span> <span style="color: cyan; font-weight: 600;">agents</span> skill add <span style="color: cyan;">--config</span> ./skills/devops-toolkit.yaml

╭─ Skill Registered ────────────────────────╮
│ <span style="color: cyan; font-weight: 600;">Name:</span> local/devops-toolkit                │
│ <span style="color: #5599ff; font-weight: 600;">Description:</span> Full-stack development tools │
│ <span style="color: #66cc66; font-weight: 600;">Config:</span> ./skills/devops-toolkit.yaml      │
│ <span style="color: #66cc66; font-weight: 600;">Created:</span> 2026-02-08 13:10                 │
╰───────────────────────────────────────────╯

╭─ Includes ────────────────────╮
│ <span style="color: #5599ff; font-weight: 600;">local/file-ops</span> (registered)   │
│ <span style="color: #5599ff; font-weight: 600;">local/git-ops</span> (registered)    │
│ <span style="color: #5599ff; font-weight: 600;">local/github</span> (registered)     │
╰───────────────────────────────╯

╭─ Tool Sources ────────────────────────────────╮
│ <span style="color: cyan; font-weight: 600;">Source</span>         <span style="color: cyan; font-weight: 600;">Count</span>  <span style="color: cyan; font-weight: 600;">Details</span>                 │
│ <span style="opacity: 0.7;">─────────────  ─────  ───────────────────</span>     │
│ builtin        14     file, dir, git, shell   │
│ mcp            6      github (4), linear (2)  │
│ agent_skill    2      deploy, code-review     │
│ custom         1      run_migrations          │
│ <span style="opacity: 0.7;">─────────────  ─────  ───────────────────</span>     │
│ <span style="color: yellow; font-weight: 600;">Total:</span>         23                             │
╰───────────────────────────────────────────────╯

╭─ MCP Servers ────────────────────╮
│ <span style="color: #66cc66; font-weight: 600;">linear:</span> validated (2 tools)      │
╰──────────────────────────────────╯

<span style="color: #66cc66; font-weight: 600;">✓ OK</span> Skill registered with 23 tools
</code></pre></div>

=== "Plain"

```
$ agents skill add --config ./skills/devops-toolkit.yaml

Skill Registered
  Name: local/devops-toolkit
  Description: Full-stack development tools
  Config: ./skills/devops-toolkit.yaml
  Created: 2026-02-08 13:10

Includes
  local/file-ops (registered)
  local/git-ops (registered)
  local/github (registered)

Tool Sources
  Source         Count  Details
  -------------  -----  -------------------
  builtin        14     file, dir, git, shell
  mcp            6      github (4), linear (2)
  agent_skill    2      deploy, code-review
  custom         1      run_migrations
  -------------  -----  -------------------
  Total:         23

MCP Servers
  linear: validated (2 tools)

[OK] Skill registered with 23 tools
```

=== "JSON"

```json
{
  "command": "skill add --config ./skills/devops-toolkit.yaml",
  "status": "ok",
  "exit_code": 0,
  "data": {
    "skill": {
      "name": "local/devops-toolkit",
      "description": "Full-stack development tools",
      "config": "./skills/devops-toolkit.yaml",
      "created": "2026-02-08T13:10:00Z"
    },
    "includes": [
      { "name": "local/file-ops", "status": "registered" },
      { "name": "local/git-ops", "status": "registered" },
      { "name": "local/github", "status": "registered" }
    ],
    "tool_sources": [
      { "source": "builtin", "count": 14, "details": "file, dir, git, shell" },
      { "source": "mcp", "count": 6, "details": "github (4), linear (2)" },
      { "source": "agent_skill", "count": 2, "details": "deploy, code-review" },
      { "source": "custom", "count": 1, "details": "run_migrations" }
    ],
    "total_tools": 23,
    "mcp_servers": [
      { "name": "linear", "status": "validated", "tools": 2 }
    ]
  },
  "timing": {
    "duration_ms": 150
  },
  "messages": [
    { "level": "ok", "text": "Skill registered with 23 tools" }
  ]
}
```

=== "YAML"

```yaml
command: skill add --config ./skills/devops-toolkit.yaml
status: ok
exit_code: 0
data:
  skill:
    name: local/devops-toolkit
    description: Full-stack development tools
    config: ./skills/devops-toolkit.yaml
    created: "2026-02-08T13:10:00Z"
  includes:
    - name: local/file-ops
      status: registered
    - name: local/git-ops
      status: registered
    - name: local/github
      status: registered
  tool_sources:
    - source: builtin
      count: 14
      details: file, dir, git, shell
    - source: mcp
      count: 6
      details: github (4), linear (2)
    - source: agent_skill
      count: 2
      details: deploy, code-review
    - source: custom
      count: 1
      details: run_migrations
  total_tools: 23
  mcp_servers:
    - name: linear
      status: validated
      tools: 2
timing:
  duration_ms: 150
messages:
  - level: ok
    text: Skill registered with 23 tools
```

Attempting to add a skill that already exists (without --update):

=== "Rich"

<div class="highlight"><pre><code>
<span style="color: #66cc66; font-weight: 600;">$</span> <span style="color: cyan; font-weight: 600;">agents</span> skill add <span style="color: cyan;">--config</span> ./skills/devops-toolkit-v2.yaml

<span style="color: #ff6666; font-weight: 600;">✗ Error:</span> Skill 'local/devops-toolkit' is already registered.

To overwrite the existing configuration, re-run with <span style="color: cyan;">--update</span>:

  agents skill add <span style="color: cyan;">--config</span> ./skills/devops-toolkit-v2.yaml <span style="color: cyan;">--update</span>
</code></pre></div>

=== "Plain"

```
$ agents skill add --config ./skills/devops-toolkit-v2.yaml

Error: Skill 'local/devops-toolkit' is already registered.

To overwrite the existing configuration, re-run with --update:

  agents skill add --config ./skills/devops-toolkit-v2.yaml --update
```

=== "JSON"

```json
{
  "command": "skill add --config ./skills/devops-toolkit-v2.yaml",
  "status": "error",
  "exit_code": 1,
  "data": {
    "skill_name": "local/devops-toolkit",
    "reason": "already registered"
  },
  "timing": {
    "duration_ms": 30
  },
  "messages": [
    { "level": "error", "text": "Skill 'local/devops-toolkit' is already registered." },
    { "level": "info", "text": "To overwrite the existing configuration, re-run with --update" }
  ]
}
```

=== "YAML"

```yaml
command: skill add --config ./skills/devops-toolkit-v2.yaml
status: error
exit_code: 1
data:
  skill_name: local/devops-toolkit
  reason: already registered
timing:
  duration_ms: 30
messages:
  - level: error
    text: "Skill 'local/devops-toolkit' is already registered."
  - level: info
    text: To overwrite the existing configuration, re-run with --update
```

Updating an existing skill with --update:

=== "Rich"

<div class="highlight"><pre><code>
<span style="color: #66cc66; font-weight: 600;">$</span> <span style="color: cyan; font-weight: 600;">agents</span> skill add <span style="color: cyan;">--config</span> ./skills/devops-toolkit-v2.yaml <span style="color: cyan;">--update</span>

╭─ Skill Updated ───────────────────────────╮
│ <span style="color: cyan; font-weight: 600;">Name:</span> local/devops-toolkit                │
│ <span style="color: #5599ff; font-weight: 600;">Description:</span> Full-stack development tools │
│ <span style="color: #66cc66; font-weight: 600;">Updated:</span> 2026-02-08 14:22                 │
╰───────────────────────────────────────────╯

╭─ Changes ─────────────────────╮
│ <span style="color: #66cc66; font-weight: 600;">Tools Added:</span> 2                │
│ <span style="color: yellow; font-weight: 600;">Tools Removed:</span> 0              │
│ <span style="color: #5599ff; font-weight: 600;">Tools Modified:</span> 1             │
│ <span style="color: #5599ff; font-weight: 600;">Includes Changed:</span> no          │
│ <span style="color: #5599ff; font-weight: 600;">MCP Servers Changed:</span> no       │
╰───────────────────────────────╯

╭─ Affected Actors ─────────────────────────────╮
│ <span style="color: yellow; font-weight: 600;">Warning:</span> 2 actors reference this skill:       │
│ - local/code-assistant                        │
│ - local/full-stack-assistant                  │
│ These actors will pick up changes on next use │
╰───────────────────────────────────────────────╯

<span style="color: #66cc66; font-weight: 600;">✓ OK</span> Skill updated (23 → 25 tools)
</code></pre></div>

=== "Plain"

```
$ agents skill add --config ./skills/devops-toolkit-v2.yaml --update

Skill Updated
  Name: local/devops-toolkit
  Description: Full-stack development tools
  Updated: 2026-02-08 14:22

Changes
  Tools Added: 2
  Tools Removed: 0
  Tools Modified: 1
  Includes Changed: no
  MCP Servers Changed: no

Affected Actors
  Warning: 2 actors reference this skill:
  - local/code-assistant
  - local/full-stack-assistant
  These actors will pick up changes on next use

[OK] Skill updated (23 -> 25 tools)
```

=== "JSON"

```json
{
  "command": "skill add --config ./skills/devops-toolkit-v2.yaml --update",
  "status": "ok",
  "exit_code": 0,
  "data": {
    "skill": {
      "name": "local/devops-toolkit",
      "description": "Full-stack development tools",
      "updated": "2026-02-08T14:22:00Z"
    },
    "changes": {
      "tools_added": 2,
      "tools_removed": 0,
      "tools_modified": 1,
      "includes_changed": false,
      "mcp_servers_changed": false
    },
    "affected_actors": [
      "local/code-assistant",
      "local/full-stack-assistant"
    ],
    "tool_count": {
      "before": 23,
      "after": 25
    }
  },
  "timing": {
    "duration_ms": 180
  },
  "messages": [
    { "level": "ok", "text": "Skill updated (23 -> 25 tools)" },
    { "level": "warn", "text": "2 actors reference this skill and will pick up changes on next use" }
  ]
}
```

=== "YAML"

```yaml
command: skill add --config ./skills/devops-toolkit-v2.yaml --update
status: ok
exit_code: 0
data:
  skill:
    name: local/devops-toolkit
    description: Full-stack development tools
    updated: "2026-02-08T14:22:00Z"
  changes:
    tools_added: 2
    tools_removed: 0
    tools_modified: 1
    includes_changed: false
    mcp_servers_changed: false
  affected_actors:
    - local/code-assistant
    - local/full-stack-assistant
  tool_count:
    before: 23
    after: 25
timing:
  duration_ms: 180
messages:
  - level: ok
    text: "Skill updated (23 -> 25 tools)"
  - level: warn
    text: 2 actors reference this skill and will pick up changes on next use
```
agents skill remove
agents skill remove [--yes|-y] <NAME>

!!! warning "Cascading Impact" Removing a skill ==unregisters all tools== provided by that skill and closes any associated MCP server connections. Other skills that include this skill, and actors that reference it, will lose access to those tools.

Arguments

  • <NAME>: Skill name to remove.
  • --yes: Skip confirmation prompt.

Examples

=== "Rich"

<div class="highlight"><pre><code>
<span style="color: #66cc66; font-weight: 600;">$</span> <span style="color: cyan; font-weight: 600;">agents</span> skill remove local/devops-toolkit

Remove skill local/devops-toolkit? [y/N]: y

╭─ Skill Removed ──────────────────────╮
│ <span style="color: cyan; font-weight: 600;">Name:</span> local/devops-toolkit           │
│ <span style="color: yellow; font-weight: 600;">Tools:</span> 23 removed from registry      │
│ <span style="color: #5599ff; font-weight: 600;">MCP Servers:</span> 1 connection closed     │
╰──────────────────────────────────────╯

╭─ Dependency Check ─────────────────────────────────╮
│ <span style="color: yellow; font-weight: 600;">Warning:</span> 1 skill includes this skill:              │
│ - local/full-stack-dev (will lose devops tools)    │
│ <span style="color: yellow; font-weight: 600;">Warning:</span> 2 actors reference this skill:            │
│ - local/code-assistant                             │
│ - local/full-stack-assistant                       │
╰────────────────────────────────────────────────────╯

<span style="color: #66cc66; font-weight: 600;">✓ OK</span> Skill removed
</code></pre></div>

=== "Plain"

```
$ agents skill remove local/devops-toolkit

Remove skill local/devops-toolkit? [y/N]: y

Skill Removed
  Name: local/devops-toolkit
  Tools: 23 removed from registry
  MCP Servers: 1 connection closed

Dependency Check
  Warning: 1 skill includes this skill:
  - local/full-stack-dev (will lose devops tools)
  Warning: 2 actors reference this skill:
  - local/code-assistant
  - local/full-stack-assistant

[OK] Skill removed
```

=== "JSON"

```json
{
  "command": "skill remove local/devops-toolkit",
  "status": "ok",
  "exit_code": 0,
  "data": {
    "skill": {
      "name": "local/devops-toolkit",
      "tools_removed": 23,
      "mcp_servers_closed": 1
    },
    "dependency_check": {
      "including_skills": [
        { "name": "local/full-stack-dev", "impact": "will lose devops tools" }
      ],
      "referencing_actors": [
        "local/code-assistant",
        "local/full-stack-assistant"
      ]
    }
  },
  "timing": {
    "duration_ms": 95
  },
  "messages": [
    { "level": "ok", "text": "Skill removed" },
    { "level": "warn", "text": "1 skill includes this skill" },
    { "level": "warn", "text": "2 actors reference this skill" }
  ]
}
```

=== "YAML"

```yaml
command: skill remove local/devops-toolkit
status: ok
exit_code: 0
data:
  skill:
    name: local/devops-toolkit
    tools_removed: 23
    mcp_servers_closed: 1
  dependency_check:
    including_skills:
      - name: local/full-stack-dev
        impact: will lose devops tools
    referencing_actors:
      - local/code-assistant
      - local/full-stack-assistant
timing:
  duration_ms: 95
messages:
  - level: ok
    text: Skill removed
  - level: warn
    text: 1 skill includes this skill
  - level: warn
    text: 2 actors reference this skill
```
agents skill list
agents skill list [(--namespace|-n) <NS>] [--source <SOURCE>]

Purpose List registered skills with optional filters.

Arguments

  • --namespace/-n NS: Filter by namespace.
  • --source SOURCE: Filter by tool source type (mcp, agent_skill, builtin, custom).

Examples

=== "Rich"

<div class="highlight"><pre><code>
<span style="color: #66cc66; font-weight: 600;">$</span> <span style="color: cyan; font-weight: 600;">agents</span> skill list

╭─ Skills ────────────────────────────────────────────────────────────╮
│ <span style="color: cyan; font-weight: 600;">Name</span>                    <span style="color: cyan; font-weight: 600;">Tools</span>  <span style="color: cyan; font-weight: 600;">Includes</span>  <span style="color: cyan; font-weight: 600;">Sources</span>                    │
│ <span style="opacity: 0.7;">──────────────────────  ─────  ────────  ──────────────────────</span>     │
│ local/file-ops          9      0         builtin                    │
│ local/git-ops           4      0         builtin                    │
│ local/github            4      0         mcp                        │
│ local/devops-toolkit    23     3         builtin, mcp, custom       │
│ local/full-stack-dev    25     4         builtin, mcp, custom       │
╰─────────────────────────────────────────────────────────────────────╯

╭─ Summary ─────────╮
│ <span style="color: yellow; font-weight: 600;">Total:</span> 5          │
│ <span style="color: #5599ff; font-weight: 600;">Local:</span> 5          │
│ <span style="color: #5599ff; font-weight: 600;">Server:</span> 0         │
│ <span style="color: #5599ff; font-weight: 600;">Total Tools:</span> 28   │
╰───────────────────╯

<span style="color: #66cc66; font-weight: 600;">✓ OK</span> 5 skills listed
</code></pre></div>

=== "Plain"

```
$ agents skill list

Skills
  Name                    Tools  Includes  Sources
  ----------------------  -----  --------  ----------------------
  local/file-ops          9      0         builtin
  local/git-ops           4      0         builtin
  local/github            4      0         mcp
  local/devops-toolkit    23     3         builtin, mcp, custom
  local/full-stack-dev    25     4         builtin, mcp, custom

Summary
  Total: 5
  Local: 5
  Server: 0
  Total Tools: 28

[OK] 5 skills listed
```

=== "JSON"

```json
{
  "command": "skill list",
  "status": "ok",
  "exit_code": 0,
  "data": {
    "skills": [
      { "name": "local/file-ops", "tools": 9, "includes": 0, "sources": ["builtin"] },
      { "name": "local/git-ops", "tools": 4, "includes": 0, "sources": ["builtin"] },
      { "name": "local/github", "tools": 4, "includes": 0, "sources": ["mcp"] },
      { "name": "local/devops-toolkit", "tools": 23, "includes": 3, "sources": ["builtin", "mcp", "custom"] },
      { "name": "local/full-stack-dev", "tools": 25, "includes": 4, "sources": ["builtin", "mcp", "custom"] }
    ],
    "summary": {
      "total": 5,
      "local": 5,
      "server": 0,
      "total_tools": 28
    }
  },
  "timing": {
    "duration_ms": 45
  },
  "messages": [
    { "level": "ok", "text": "5 skills listed" }
  ]
}
```

=== "YAML"

```yaml
command: skill list
status: ok
exit_code: 0
data:
  skills:
    - name: local/file-ops
      tools: 9
      includes: 0
      sources:
        - builtin
    - name: local/git-ops
      tools: 4
      includes: 0
      sources:
        - builtin
    - name: local/github
      tools: 4
      includes: 0
      sources:
        - mcp
    - name: local/devops-toolkit
      tools: 23
      includes: 3
      sources:
        - builtin
        - mcp
        - custom
    - name: local/full-stack-dev
      tools: 25
      includes: 4
      sources:
        - builtin
        - mcp
        - custom
  summary:
    total: 5
    local: 5
    server: 0
    total_tools: 28
timing:
  duration_ms: 45
messages:
  - level: ok
    text: 5 skills listed
```
agents skill show
agents skill show  <NAME>

Purpose Show full details for a registered skill, including its includes, tool sources, and capability summary.

Arguments

  • <NAME>: Skill name.

Examples

=== "Rich"

<div class="highlight"><pre><code>
<span style="color: #66cc66; font-weight: 600;">$</span> <span style="color: cyan; font-weight: 600;">agents</span> skill show local/devops-toolkit

╭─ Skill Details ──────────────────────────────╮
│ <span style="color: cyan; font-weight: 600;">Name:</span> local/devops-toolkit                   │
│ <span style="color: #5599ff; font-weight: 600;">Description:</span> Full-stack development tools    │
│ <span style="color: #66cc66; font-weight: 600;">Config:</span> ./skills/devops-toolkit.yaml         │
│ <span style="color: #66cc66; font-weight: 600;">Created:</span> 2026-02-08 13:10                    │
│ <span style="color: #66cc66; font-weight: 600;">Updated:</span> 2026-02-08 14:22                    │
╰──────────────────────────────────────────────╯

╭─ Includes (3) ────────────────────────────╮
│ <span style="color: #5599ff; font-weight: 600;">local/file-ops</span>  → 9 tools (builtin)       │
│ <span style="color: #5599ff; font-weight: 600;">local/git-ops</span>   → 4 tools (builtin)       │
│ <span style="color: #5599ff; font-weight: 600;">local/github</span>    → 4 tools (mcp)           │
╰───────────────────────────────────────────╯

╭─ Direct Tools (6) ────────────────────────────────────────╮
│ <span style="color: cyan; font-weight: 600;">Name</span>              <span style="color: cyan; font-weight: 600;">Source</span>       <span style="color: cyan; font-weight: 600;">Writes</span>  <span style="color: cyan; font-weight: 600;">Checkpoint</span>         │
│ <span style="opacity: 0.7;">────────────────  ───────────  ──────  ──────────</span>         │
│ create_issue      mcp:linear   yes     no                 │
│ list_issues       mcp:linear   no      —                  │
│ deploy-to-staging agent_skill  yes     composite          │
│ code-review       agent_skill  no      —                  │
│ run_migrations    custom       yes     transaction        │
│ shell_execute     builtin      yes     snapshot           │
╰───────────────────────────────────────────────────────────╯

╭─ MCP Servers (1) ───────────────────╮
│ <span style="color: #66cc66; font-weight: 600;">linear:</span> stdio, 2 tools, connected   │
╰─────────────────────────────────────╯

╭─ Capability Summary ──────────╮
│ <span style="color: #5599ff; font-weight: 600;">Total Tools:</span> 23               │
│ <span style="color: #66cc66; font-weight: 600;">Read-Only:</span> 10                 │
│ <span style="color: yellow; font-weight: 600;">Writes:</span> 13                    │
│ <span style="color: #66cc66; font-weight: 600;">Checkpointable:</span> 10            │
│ <span style="color: yellow; font-weight: 600;">Has Side Effects:</span> 3           │
│ <span style="color: #5599ff; font-weight: 600;">Requires Approval:</span> 1          │
╰───────────────────────────────╯

╭─ Referenced By ───────────────────╮
│ <span style="color: #5599ff; font-weight: 600;">Actors:</span> local/code-assistant      │
│ <span style="color: #5599ff; font-weight: 600;">Skills:</span> local/full-stack-dev      │
╰───────────────────────────────────╯

<span style="color: #66cc66; font-weight: 600;">✓ OK</span> Skill loaded
</code></pre></div>

=== "Plain"

```
$ agents skill show local/devops-toolkit

Skill Details
  Name: local/devops-toolkit
  Description: Full-stack development tools
  Config: ./skills/devops-toolkit.yaml
  Created: 2026-02-08 13:10
  Updated: 2026-02-08 14:22

Includes (3)
  local/file-ops  -> 9 tools (builtin)
  local/git-ops   -> 4 tools (builtin)
  local/github    -> 4 tools (mcp)

Direct Tools (6)
  Name              Source       Writes  Checkpoint
  ----------------  -----------  ------  ----------
  create_issue      mcp:linear   yes     no
  list_issues       mcp:linear   no      -
  deploy-to-staging agent_skill  yes     composite
  code-review       agent_skill  no      -
  run_migrations    custom       yes     transaction
  shell_execute     builtin      yes     snapshot

MCP Servers (1)
  linear: stdio, 2 tools, connected

Capability Summary
  Total Tools: 23
  Read-Only: 10
  Writes: 13
  Checkpointable: 10
  Has Side Effects: 3
  Requires Approval: 1

Referenced By
  Actors: local/code-assistant
  Skills: local/full-stack-dev

[OK] Skill loaded
```

=== "JSON"

```json
{
  "command": "skill show local/devops-toolkit",
  "status": "ok",
  "exit_code": 0,
  "data": {
    "skill": {
      "name": "local/devops-toolkit",
      "description": "Full-stack development tools",
      "config": "./skills/devops-toolkit.yaml",
      "created": "2026-02-08T13:10:00Z",
      "updated": "2026-02-08T14:22:00Z"
    },
    "includes": [
      { "name": "local/file-ops", "tools": 9, "source": "builtin" },
      { "name": "local/git-ops", "tools": 4, "source": "builtin" },
      { "name": "local/github", "tools": 4, "source": "mcp" }
    ],
    "direct_tools": [
      { "name": "create_issue", "source": "mcp:linear", "writes": true, "checkpoint": "no" },
      { "name": "list_issues", "source": "mcp:linear", "writes": false, "checkpoint": null },
      { "name": "deploy-to-staging", "source": "agent_skill", "writes": true, "checkpoint": "composite" },
      { "name": "code-review", "source": "agent_skill", "writes": false, "checkpoint": null },
      { "name": "run_migrations", "source": "custom", "writes": true, "checkpoint": "transaction" },
      { "name": "shell_execute", "source": "builtin", "writes": true, "checkpoint": "snapshot" }
    ],
    "mcp_servers": [
      { "name": "linear", "transport": "stdio", "tools": 2, "status": "connected" }
    ],
    "capability_summary": {
      "total_tools": 23,
      "read_only": 10,
      "writes": 13,
      "checkpointable": 10,
      "has_side_effects": 3,
      "requires_approval": 1
    },
    "referenced_by": {
      "actors": ["local/code-assistant"],
      "skills": ["local/full-stack-dev"]
    }
  },
  "timing": {
    "duration_ms": 110
  },
  "messages": [
    { "level": "ok", "text": "Skill loaded" }
  ]
}
```

=== "YAML"

```yaml
command: skill show local/devops-toolkit
status: ok
exit_code: 0
data:
  skill:
    name: local/devops-toolkit
    description: Full-stack development tools
    config: ./skills/devops-toolkit.yaml
    created: "2026-02-08T13:10:00Z"
    updated: "2026-02-08T14:22:00Z"
  includes:
    - name: local/file-ops
      tools: 9
      source: builtin
    - name: local/git-ops
      tools: 4
      source: builtin
    - name: local/github
      tools: 4
      source: mcp
  direct_tools:
    - name: create_issue
      source: "mcp:linear"
      writes: true
      checkpoint: "no"
    - name: list_issues
      source: "mcp:linear"
      writes: false
      checkpoint: null
    - name: deploy-to-staging
      source: agent_skill
      writes: true
      checkpoint: composite
    - name: code-review
      source: agent_skill
      writes: false
      checkpoint: null
    - name: run_migrations
      source: custom
      writes: true
      checkpoint: transaction
    - name: shell_execute
      source: builtin
      writes: true
      checkpoint: snapshot
  mcp_servers:
    - name: linear
      transport: stdio
      tools: 2
      status: connected
  capability_summary:
    total_tools: 23
    read_only: 10
    writes: 13
    checkpointable: 10
    has_side_effects: 3
    requires_approval: 1
  referenced_by:
    actors:
      - local/code-assistant
    skills:
      - local/full-stack-dev
timing:
  duration_ms: 110
messages:
  - level: ok
    text: Skill loaded
```
agents skill tools
agents skill tools <NAME>

Purpose List all tools provided by a skill, including those inherited from included skills (the flattened tool set).

Arguments

  • <NAME>: Skill name.

Examples

=== "Rich"

<div class="highlight"><pre><code>
<span style="color: #66cc66; font-weight: 600;">$</span> <span style="color: cyan; font-weight: 600;">agents</span> skill tools local/devops-toolkit

╭─ Tools for local/devops-toolkit ─────────────────────────────────────────────────────────╮
│ <span style="color: cyan; font-weight: 600;">Tool</span>               <span style="color: cyan; font-weight: 600;">Source</span>       <span style="color: cyan; font-weight: 600;">From Skill</span>      <span style="color: cyan; font-weight: 600;">Read-Only</span>  <span style="color: cyan; font-weight: 600;">Writes</span>  <span style="color: cyan; font-weight: 600;">Checkpoint</span>            │
│ <span style="opacity: 0.7;">─────────────────  ───────────  ──────────────  ─────────  ──────  ──────────</span>            │
│ read_file          builtin      local/file-ops  ✓          —       —                     │
│ write_file         builtin      local/file-ops  —          ✓       file                  │
│ edit_file          builtin      local/file-ops  —          ✓       file                  │
│ delete_file        builtin      local/file-ops  —          ✓       file                  │
│ move_file          builtin      local/file-ops  —          ✓       file                  │
│ copy_file          builtin      local/file-ops  —          ✓       file                  │
│ create_directory   builtin      local/file-ops  —          ✓       file                  │
│ list_directory     builtin      local/file-ops  ✓          —       —                     │
│ delete_directory   builtin      local/file-ops  —          ✓       file                  │
│ git_status         builtin      local/git-ops   ✓          —       —                     │
│ git_diff           builtin      local/git-ops   ✓          —       —                     │
│ git_log            builtin      local/git-ops   ✓          —       —                     │
│ git_blame          builtin      local/git-ops   ✓          —       —                     │
│ create_issue       mcp:github   local/github    —          ✓       no                    │
│ create_pr          mcp:github   local/github    —          ✓       no                    │
│ list_repos         mcp:github   local/github    ✓          —       —                     │
│ get_file_contents  mcp:github   local/github    ✓          —       —                     │
│ create_issue       mcp:linear   (direct)        —          ✓       no                    │
│ list_issues        mcp:linear   (direct)        ✓          —       —                     │
│ run_migrations     custom       (direct)        —          ✓       transaction           │
│ deploy-to-staging  agent_skill  (direct)        —          ✓       composite             │
│ code-review        agent_skill  (direct)        ✓          —       —                     │
│ shell_execute      builtin      (direct)        —          ✓       snapshot              │
╰──────────────────────────────────────────────────────────────────────────────────────────╯

╭─ Summary ──────────────────╮
│ <span style="color: #5599ff; font-weight: 600;">Total:</span> 23                  │
│ <span style="color: #66cc66; font-weight: 600;">From Includes:</span> 17          │
│ <span style="color: #5599ff; font-weight: 600;">Direct:</span> 6                  │
│ <span style="color: #66cc66; font-weight: 600;">Read-Only:</span> 10              │
│ <span style="color: yellow; font-weight: 600;">Writes:</span> 13                 │
│ <span style="color: #66cc66; font-weight: 600;">Checkpointable:</span> 10         │
╰────────────────────────────╯

<span style="color: #66cc66; font-weight: 600;">✓ OK</span> 23 tools listed
</code></pre></div>

=== "Plain"

```
$ agents skill tools local/devops-toolkit

Tools for local/devops-toolkit
  Tool               Source       From Skill      Read-Only  Writes  Checkpoint
  -----------------  -----------  --------------  ---------  ------  ----------
  read_file          builtin      local/file-ops  yes        -       -
  write_file         builtin      local/file-ops  -          yes     file
  edit_file          builtin      local/file-ops  -          yes     file
  delete_file        builtin      local/file-ops  -          yes     file
  move_file          builtin      local/file-ops  -          yes     file
  copy_file          builtin      local/file-ops  -          yes     file
  create_directory   builtin      local/file-ops  -          yes     file
  list_directory     builtin      local/file-ops  yes        -       -
  delete_directory   builtin      local/file-ops  -          yes     file
  git_status         builtin      local/git-ops   yes        -       -
  git_diff           builtin      local/git-ops   yes        -       -
  git_log            builtin      local/git-ops   yes        -       -
  git_blame          builtin      local/git-ops   yes        -       -
  create_issue       mcp:github   local/github    -          yes     no
  create_pr          mcp:github   local/github    -          yes     no
  list_repos         mcp:github   local/github    yes        -       -
  get_file_contents  mcp:github   local/github    yes        -       -
  create_issue       mcp:linear   (direct)        -          yes     no
  list_issues        mcp:linear   (direct)        yes        -       -
  run_migrations     custom       (direct)        -          yes     transaction
  deploy-to-staging  agent_skill  (direct)        -          yes     composite
  code-review        agent_skill  (direct)        yes        -       -
  shell_execute      builtin      (direct)        -          yes     snapshot

Summary
  Total: 23
  From Includes: 17
  Direct: 6
  Read-Only: 10
  Writes: 13
  Checkpointable: 10

[OK] 23 tools listed
```

=== "JSON"

```json
{
  "command": "skill tools local/devops-toolkit",
  "status": "ok",
  "exit_code": 0,
  "data": {
    "tools": [
      { "name": "read_file", "source": "builtin", "from_skill": "local/file-ops", "read_only": true, "writes": false, "checkpoint": null },
      { "name": "write_file", "source": "builtin", "from_skill": "local/file-ops", "read_only": false, "writes": true, "checkpoint": "file" },
      { "name": "edit_file", "source": "builtin", "from_skill": "local/file-ops", "read_only": false, "writes": true, "checkpoint": "file" },
      { "name": "delete_file", "source": "builtin", "from_skill": "local/file-ops", "read_only": false, "writes": true, "checkpoint": "file" },
      { "name": "move_file", "source": "builtin", "from_skill": "local/file-ops", "read_only": false, "writes": true, "checkpoint": "file" },
      { "name": "copy_file", "source": "builtin", "from_skill": "local/file-ops", "read_only": false, "writes": true, "checkpoint": "file" },
      { "name": "create_directory", "source": "builtin", "from_skill": "local/file-ops", "read_only": false, "writes": true, "checkpoint": "file" },
      { "name": "list_directory", "source": "builtin", "from_skill": "local/file-ops", "read_only": true, "writes": false, "checkpoint": null },
      { "name": "delete_directory", "source": "builtin", "from_skill": "local/file-ops", "read_only": false, "writes": true, "checkpoint": "file" },
      { "name": "git_status", "source": "builtin", "from_skill": "local/git-ops", "read_only": true, "writes": false, "checkpoint": null },
      { "name": "git_diff", "source": "builtin", "from_skill": "local/git-ops", "read_only": true, "writes": false, "checkpoint": null },
      { "name": "git_log", "source": "builtin", "from_skill": "local/git-ops", "read_only": true, "writes": false, "checkpoint": null },
      { "name": "git_blame", "source": "builtin", "from_skill": "local/git-ops", "read_only": true, "writes": false, "checkpoint": null },
      { "name": "create_issue", "source": "mcp:github", "from_skill": "local/github", "read_only": false, "writes": true, "checkpoint": "no" },
      { "name": "create_pr", "source": "mcp:github", "from_skill": "local/github", "read_only": false, "writes": true, "checkpoint": "no" },
      { "name": "list_repos", "source": "mcp:github", "from_skill": "local/github", "read_only": true, "writes": false, "checkpoint": null },
      { "name": "get_file_contents", "source": "mcp:github", "from_skill": "local/github", "read_only": true, "writes": false, "checkpoint": null },
      { "name": "create_issue", "source": "mcp:linear", "from_skill": null, "read_only": false, "writes": true, "checkpoint": "no" },
      { "name": "list_issues", "source": "mcp:linear", "from_skill": null, "read_only": true, "writes": false, "checkpoint": null },
      { "name": "run_migrations", "source": "custom", "from_skill": null, "read_only": false, "writes": true, "checkpoint": "transaction" },
      { "name": "deploy-to-staging", "source": "agent_skill", "from_skill": null, "read_only": false, "writes": true, "checkpoint": "composite" },
      { "name": "code-review", "source": "agent_skill", "from_skill": null, "read_only": true, "writes": false, "checkpoint": null },
      { "name": "shell_execute", "source": "builtin", "from_skill": null, "read_only": false, "writes": true, "checkpoint": "snapshot" }
    ],
    "summary": {
      "total": 23,
      "from_includes": 17,
      "direct": 6,
      "read_only": 10,
      "writes": 13,
      "checkpointable": 10
    }
  },
  "timing": {
    "duration_ms": 85
  },
  "messages": [
    { "level": "ok", "text": "23 tools listed" }
  ]
}
```

=== "YAML"

```yaml
command: skill tools local/devops-toolkit
status: ok
exit_code: 0
data:
  tools:
    - name: read_file
      source: builtin
      from_skill: local/file-ops
      read_only: true
      writes: false
      checkpoint: null
    - name: write_file
      source: builtin
      from_skill: local/file-ops
      read_only: false
      writes: true
      checkpoint: file
    - name: edit_file
      source: builtin
      from_skill: local/file-ops
      read_only: false
      writes: true
      checkpoint: file
    - name: delete_file
      source: builtin
      from_skill: local/file-ops
      read_only: false
      writes: true
      checkpoint: file
    - name: move_file
      source: builtin
      from_skill: local/file-ops
      read_only: false
      writes: true
      checkpoint: file
    - name: copy_file
      source: builtin
      from_skill: local/file-ops
      read_only: false
      writes: true
      checkpoint: file
    - name: create_directory
      source: builtin
      from_skill: local/file-ops
      read_only: false
      writes: true
      checkpoint: file
    - name: list_directory
      source: builtin
      from_skill: local/file-ops
      read_only: true
      writes: false
      checkpoint: null
    - name: delete_directory
      source: builtin
      from_skill: local/file-ops
      read_only: false
      writes: true
      checkpoint: file
    - name: git_status
      source: builtin
      from_skill: local/git-ops
      read_only: true
      writes: false
      checkpoint: null
    - name: git_diff
      source: builtin
      from_skill: local/git-ops
      read_only: true
      writes: false
      checkpoint: null
    - name: git_log
      source: builtin
      from_skill: local/git-ops
      read_only: true
      writes: false
      checkpoint: null
    - name: git_blame
      source: builtin
      from_skill: local/git-ops
      read_only: true
      writes: false
      checkpoint: null
    - name: create_issue
      source: "mcp:github"
      from_skill: local/github
      read_only: false
      writes: true
      checkpoint: "no"
    - name: create_pr
      source: "mcp:github"
      from_skill: local/github
      read_only: false
      writes: true
      checkpoint: "no"
    - name: list_repos
      source: "mcp:github"
      from_skill: local/github
      read_only: true
      writes: false
      checkpoint: null
    - name: get_file_contents
      source: "mcp:github"
      from_skill: local/github
      read_only: true
      writes: false
      checkpoint: null
    - name: create_issue
      source: "mcp:linear"
      from_skill: null
      read_only: false
      writes: true
      checkpoint: "no"
    - name: list_issues
      source: "mcp:linear"
      from_skill: null
      read_only: true
      writes: false
      checkpoint: null
    - name: run_migrations
      source: custom
      from_skill: null
      read_only: false
      writes: true
      checkpoint: transaction
    - name: deploy-to-staging
      source: agent_skill
      from_skill: null
      read_only: false
      writes: true
      checkpoint: composite
    - name: code-review
      source: agent_skill
      from_skill: null
      read_only: true
      writes: false
      checkpoint: null
    - name: shell_execute
      source: builtin
      from_skill: null
      read_only: false
      writes: true
      checkpoint: snapshot
  summary:
    total: 23
    from_includes: 17
    direct: 6
    read_only: 10
    writes: 13
    checkpointable: 10
timing:
  duration_ms: 85
messages:
  - level: ok
    text: 23 tools listed
```

agents tool

!!! info "Purpose" Manage ==tools== — namespaced, independently registered, callable operations. Tools are defined in their own YAML configuration files and registered in the system through these commands. Once registered, tools can be referenced by name in skills (as part of a tool collection) and in actor graphs (as tool nodes).

??? tip "Tool Lifecycle Overview" | Stage | Command | Description | | :---- | :------ | :---------- | | Register | agents tool add | Register from YAML config | | Inspect | agents tool show | View tool details and capabilities | | List | agents tool list | Browse registered tools | | Update | agents tool add --update | Re-register with updated config | | Remove | agents tool remove | Unregister a tool |

agents tool add
agents tool add --config|-c <FILE> [--update]

Purpose Register a new tool. The tool is fully defined by the YAML configuration file specified with --config. If a tool with the same name already exists, the command fails unless the --update flag is provided, which allows overwriting the existing registration with the new configuration.

Arguments

  • --config/-c FILE: Path to the tool YAML configuration file (required). The file must fully define the tool, including the name field which determines the tool's registered name.
  • --update: Allow overwriting an existing tool registration. Without this flag, attempting to add a tool whose name is already registered will fail.

Examples

Registering a new tool:

=== "Rich"

<div class="highlight"><pre><code>
<span style="color: #66cc66; font-weight: 600;">$</span> <span style="color: cyan; font-weight: 600;">agents</span> tool add <span style="color: cyan;">--config</span> ./tools/run-migrations.yaml

╭─ Tool Registered ────────────────────────────╮
│ <span style="color: cyan; font-weight: 600;">Name:</span> local/run-migrations                   │
│ <span style="color: #5599ff; font-weight: 600;">Description:</span> Run database migrations         │
│ <span style="color: #5599ff; font-weight: 600;">Source:</span> custom                               │
│ <span style="color: #66cc66; font-weight: 600;">Config:</span> ./tools/run-migrations.yaml          │
│ <span style="color: #66cc66; font-weight: 600;">Created:</span> 2026-02-09 10:15                    │
╰──────────────────────────────────────────────╯

╭─ Capability ─────────────────────╮
│ <span style="color: yellow; font-weight: 600;">Writes:</span> true                     │
│ <span style="color: #5599ff; font-weight: 600;">Write Scope:</span> database:migrations │
│ <span style="color: #66cc66; font-weight: 600;">Checkpointable:</span> true             │
│ <span style="color: #5599ff; font-weight: 600;">Checkpoint Scope:</span> transaction    │
│ <span style="color: yellow; font-weight: 600;">Side Effects:</span> schema_mutation    │
╰──────────────────────────────────╯

<span style="color: #66cc66; font-weight: 600;">✓ OK</span> Tool registered
</code></pre></div>

=== "Plain"

```
$ agents tool add --config ./tools/run-migrations.yaml

Tool Registered
  Name: local/run-migrations
  Description: Run database migrations
  Source: custom
  Config: ./tools/run-migrations.yaml
  Created: 2026-02-09 10:15

Capability
  Writes: true
  Write Scope: database:migrations
  Checkpointable: true
  Checkpoint Scope: transaction
  Side Effects: schema_mutation

[OK] Tool registered
```

=== "JSON"

```json
{
  "command": "tool add",
  "status": "ok",
  "exit_code": 0,
  "data": {
    "tool_registered": {
      "name": "local/run-migrations",
      "description": "Run database migrations",
      "source": "custom",
      "config": "./tools/run-migrations.yaml",
      "created": "2026-02-09T10:15:00Z"
    },
    "capability": {
      "writes": true,
      "write_scope": "database:migrations",
      "checkpointable": true,
      "checkpoint_scope": "transaction",
      "side_effects": ["schema_mutation"]
    }
  },
  "timing": { "duration_ms": 58 },
  "messages": ["Tool registered"]
}
```

=== "YAML"

```yaml
command: tool add
status: ok
exit_code: 0
data:
  tool_registered:
    name: local/run-migrations
    description: Run database migrations
    source: custom
    config: ./tools/run-migrations.yaml
    created: "2026-02-09T10:15:00Z"
  capability:
    writes: true
    write_scope: "database:migrations"
    checkpointable: true
    checkpoint_scope: transaction
    side_effects:
      - schema_mutation
timing:
  duration_ms: 58
messages:
  - Tool registered
```

Attempting to add a tool that already exists (without --update):

=== "Rich"

<div class="highlight"><pre><code>
<span style="color: #66cc66; font-weight: 600;">$</span> <span style="color: cyan; font-weight: 600;">agents</span> tool add <span style="color: cyan;">--config</span> ./tools/run-migrations-v2.yaml

<span style="color: #ff6666; font-weight: 600;">✗ Error:</span> Tool 'local/run-migrations' is already registered.

To overwrite the existing configuration, re-run with <span style="color: cyan;">--update</span>:

  agents tool add <span style="color: cyan;">--config</span> ./tools/run-migrations-v2.yaml <span style="color: cyan;">--update</span>
</code></pre></div>

=== "Plain"

```
$ agents tool add --config ./tools/run-migrations-v2.yaml

[ERROR] Tool 'local/run-migrations' is already registered.

To overwrite the existing configuration, re-run with --update:

  agents tool add --config ./tools/run-migrations-v2.yaml --update
```

=== "JSON"

```json
{
  "command": "tool add",
  "status": "error",
  "exit_code": 1,
  "data": {
    "error": "Tool 'local/run-migrations' is already registered.",
    "hint": "To overwrite the existing configuration, re-run with --update",
    "suggested_command": "agents tool add --config ./tools/run-migrations-v2.yaml --update"
  },
  "timing": { "duration_ms": 12 },
  "messages": ["Tool 'local/run-migrations' is already registered."]
}
```

=== "YAML"

```yaml
command: tool add
status: error
exit_code: 1
data:
  error: "Tool 'local/run-migrations' is already registered."
  hint: "To overwrite the existing configuration, re-run with --update"
  suggested_command: "agents tool add --config ./tools/run-migrations-v2.yaml --update"
timing:
  duration_ms: 12
messages:
  - "Tool 'local/run-migrations' is already registered."
```

Updating an existing tool with --update:

=== "Rich"

<div class="highlight"><pre><code>
<span style="color: #66cc66; font-weight: 600;">$</span> <span style="color: cyan; font-weight: 600;">agents</span> tool add <span style="color: cyan;">--config</span> ./tools/db-migrate.yaml <span style="color: cyan;">--update</span>

╭─ Tool Updated ─────────────────────────────╮
│ <span style="color: cyan; font-weight: 600;">Name:</span> local/db-migrate                     │
│ <span style="color: #5599ff; font-weight: 600;">Source:</span> custom (Python)                    │
│ <span style="color: yellow; font-weight: 600;">Status:</span> updated (was version 1 → now 2)    │
╰────────────────────────────────────────────╯

╭─ Changes ─────────────────────────────────────╮
│ <span style="color: #5599ff; font-weight: 600;">Input Schema:</span> modified (added batch_size)     │
│ <span style="color: #5599ff; font-weight: 600;">Capabilities:</span> unchanged (writes, checkpoint)  │
│ <span style="color: #5599ff; font-weight: 600;">Description:</span> updated                          │
╰───────────────────────────────────────────────╯

╭─ References ──────────────────╮
│ <span style="color: #5599ff; font-weight: 600;">Skills:</span> 1 (local/db-tools)    │
│ <span style="color: #5599ff; font-weight: 600;">Actors:</span> 0                     │
│ <span style="opacity: 0.7;">Referencing skills will use</span>   │
│ <span style="opacity: 0.7;">the updated definition.</span>       │
╰───────────────────────────────╯

<span style="color: #66cc66; font-weight: 600;">✓ OK</span> Tool updated
</code></pre></div>

=== "Plain"

```
$ agents tool add --config ./tools/db-migrate.yaml --update

Tool Updated
  Name: local/db-migrate
  Source: custom (Python)
  Status: updated (was version 1 -> now 2)

Changes
  Input Schema: modified (added batch_size)
  Capabilities: unchanged (writes, checkpoint)
  Description: updated

References
  Skills: 1 (local/db-tools)
  Actors: 0
  Referencing skills will use the updated definition.

[OK] Tool updated
```

=== "JSON"

```json
{
  "command": "tool add",
  "status": "ok",
  "exit_code": 0,
  "data": {
    "tool_updated": {
      "name": "local/db-migrate",
      "source": "custom",
      "source_language": "Python",
      "status": "updated",
      "previous_version": 1,
      "current_version": 2
    },
    "changes": {
      "input_schema": "modified (added batch_size)",
      "capabilities": "unchanged (writes, checkpoint)",
      "description": "updated"
    },
    "references": {
      "skills": ["local/db-tools"],
      "actors": []
    }
  },
  "timing": { "duration_ms": 45 },
  "messages": ["Tool updated"]
}
```

=== "YAML"

```yaml
command: tool add
status: ok
exit_code: 0
data:
  tool_updated:
    name: local/db-migrate
    source: custom
    source_language: Python
    status: updated
    previous_version: 1
    current_version: 2
  changes:
    input_schema: "modified (added batch_size)"
    capabilities: "unchanged (writes, checkpoint)"
    description: updated
  references:
    skills:
      - local/db-tools
    actors: []
timing:
  duration_ms: 45
messages:
  - Tool updated
```
agents tool remove
agents tool remove [--yes|-y] <NAME>

!!! danger "Destructive Operation" Remove a registered tool. Because Validations are a subtype of Tool and share the same registry, this command ==also removes validations==. When removing a Validation, all attachments are automatically detached before the entry is removed from the registry.

References from skills and actor graphs will **break** until resolved.

Arguments

  • <NAME>: Tool or validation name to remove.
  • --yes: Skip confirmation prompt.

Examples

=== "Rich"

<div class="highlight"><pre><code>
<span style="color: #66cc66; font-weight: 600;">$</span> <span style="color: cyan; font-weight: 600;">agents</span> tool remove local/run-migrations

Remove tool local/run-migrations? [y/N]: y

╭─ Tool Removed ────────────────────╮
│ <span style="color: cyan; font-weight: 600;">Name:</span> local/run-migrations        │
│ <span style="color: #5599ff; font-weight: 600;">Source:</span> custom                    │
╰───────────────────────────────────╯

╭─ References ──────────────────────────────────────╮
│ <span style="color: yellow; font-weight: 600;">Warning:</span> This tool is referenced by:              │
│ - Skill: local/devops-toolkit                     │
│ - Actor graph node: code_executor.run_db_migrate  │
│ These references will break until resolved.       │
╰───────────────────────────────────────────────────╯

<span style="color: #66cc66; font-weight: 600;">✓ OK</span> Tool removed
</code></pre></div>

=== "Plain"

```
$ agents tool remove local/run-migrations

Remove tool local/run-migrations? [y/N]: y

Tool Removed
  Name: local/run-migrations
  Source: custom

References
  Warning: This tool is referenced by:
  - Skill: local/devops-toolkit
  - Actor graph node: code_executor.run_db_migrate
  These references will break until resolved.

[OK] Tool removed
```

=== "JSON"

```json
{
  "command": "tool remove",
  "status": "ok",
  "exit_code": 0,
  "data": {
    "tool_removed": {
      "name": "local/run-migrations",
      "source": "custom"
    },
    "broken_references": [
      { "type": "skill", "name": "local/devops-toolkit" },
      { "type": "actor_graph_node", "name": "code_executor.run_db_migrate" }
    ]
  },
  "timing": { "duration_ms": 34 },
  "messages": [
    "Tool removed",
    "Warning: This tool is referenced by 1 skill and 1 actor graph node. These references will break until resolved."
  ]
}
```

=== "YAML"

```yaml
command: tool remove
status: ok
exit_code: 0
data:
  tool_removed:
    name: local/run-migrations
    source: custom
  broken_references:
    - type: skill
      name: local/devops-toolkit
    - type: actor_graph_node
      name: code_executor.run_db_migrate
timing:
  duration_ms: 34
messages:
  - Tool removed
  - "Warning: This tool is referenced by 1 skill and 1 actor graph node. These references will break until resolved."
```

When removing a validation, attachments are cleaned up automatically:

=== "Rich"

<div class="highlight"><pre><code>
<span style="color: #66cc66; font-weight: 600;">$</span> <span style="color: cyan; font-weight: 600;">agents</span> tool remove local/check-bundle-size

Remove validation local/check-bundle-size? [y/N]: y

╭─ Validation Removed ─────────────────────────────╮
│ <span style="color: cyan; font-weight: 600;">Name:</span> local/check-bundle-size                    │
│ <span style="color: #5599ff; font-weight: 600;">Description:</span> Check bundle size (advisory)        │
╰──────────────────────────────────────────────────╯

╭─ Detached From ────────────────────────────────────────────╮
│ <span style="color: yellow; font-weight: 600;">Warning:</span> This validation had 2 attachments:                │
│ - local/api-repo (scope: project local/api-service)        │
│ - local/api-repo (direct)                                  │
│ These attachments have been removed.                       │
╰────────────────────────────────────────────────────────────╯

<span style="color: #66cc66; font-weight: 600;">✓ OK</span> Validation removed─
</code></pre></div>

=== "Plain"

```
$ agents tool remove local/check-bundle-size

Remove validation local/check-bundle-size? [y/N]: y

Validation Removed
  Name: local/check-bundle-size
  Description: Check bundle size (advisory)

Detached From
  Warning: This validation had 2 attachments:
  - local/api-repo (scope: project local/api-service)
  - local/api-repo (direct)
  These attachments have been removed.

[OK] Validation removed
```

=== "JSON"

```json
{
  "command": "tool remove",
  "status": "ok",
  "exit_code": 0,
  "data": {
    "validation_removed": {
      "name": "local/check-bundle-size",
      "description": "Check bundle size (advisory)"
    },
    "detached_attachments": [
      { "resource": "local/api-repo", "scope": "project", "project": "local/api-service" },
      { "resource": "local/api-repo", "scope": "direct" }
    ]
  },
  "timing": { "duration_ms": 41 },
  "messages": [
    "Validation removed",
    "Warning: This validation had 2 attachments which have been removed."
  ]
}
```

=== "YAML"

```yaml
command: tool remove
status: ok
exit_code: 0
data:
  validation_removed:
    name: local/check-bundle-size
    description: "Check bundle size (advisory)"
  detached_attachments:
    - resource: local/api-repo
      scope: project
      project: local/api-service
    - resource: local/api-repo
      scope: direct
timing:
  duration_ms: 41
messages:
  - Validation removed
  - "Warning: This validation had 2 attachments which have been removed."
```
agents tool list
agents tool list [(--namespace|-n) <NS>] [--source <SOURCE>] [--type (tool|validation)] [<REGEX>]

Purpose List registered tools with optional filters. Because Validations are a subtype of Tool and share the same registry, this command lists both plain tools and validations. Use --type validation to show only validations, or --type tool to show only plain tools. When --type is omitted, both are listed with a Type column distinguishing them.

Arguments

  • --namespace/-n NS: Filter by namespace.
  • --source SOURCE: Filter by tool source type (mcp, agent_skill, builtin, custom).
  • --type TYPE: Filter by entry type: tool (plain tools only) or validation (validations only). When omitted, both are listed.

Examples

=== "Rich"

<div class="highlight"><pre><code>
<span style="color: #66cc66; font-weight: 600;">$</span> <span style="color: cyan; font-weight: 600;">agents</span> tool list <span style="color: cyan;">--namespace</span> local

╭─ Tools ─────────────────────────────────────────────────────────────────╮
│ <span style="color: cyan; font-weight: 600;">Name</span>                       <span style="color: cyan; font-weight: 600;">Type</span>        <span style="color: cyan; font-weight: 600;">Source</span>   <span style="color: cyan; font-weight: 600;">Read-Only</span>  <span style="color: cyan; font-weight: 600;">Writes</span>       │
│ <span style="opacity: 0.7;">─────────────────────────  ──────────  ───────  ─────────  ──────</span>       │
│ local/run-migrations       tool        custom   —          ✓            │
│ local/validate-api-compat  tool        custom   —          ✓            │
│ local/create-subplan       tool        custom   —          ✓            │
│ local/deploy-staging       tool        agent    —          ✓            │
│ local/run-tests            validation  custom   ✓          —            │
│ local/lint-check           validation  custom   ✓          —            │
│ local/check-bundle-size    validation  custom   ✓          —            │
│ local/type-check           validation  custom   ✓          —            │
╰─────────────────────────────────────────────────────────────────────────╯

╭─ Summary ────────────────╮
│ <span style="color: #5599ff; font-weight: 600;">Total:</span> 8                 │
│ <span style="color: #5599ff; font-weight: 600;">Tools:</span> 4                 │
│ <span style="color: #5599ff; font-weight: 600;">Validations:</span> 4           │
│ <span style="color: #66cc66; font-weight: 600;">Read-Only:</span> 4             │
│ <span style="color: yellow; font-weight: 600;">Writes:</span> 4                │
╰──────────────────────────╯

<span style="color: #66cc66; font-weight: 600;">✓ OK</span> 8 tools listed
</code></pre></div>

=== "Plain"

```
$ agents tool list --namespace local

Tools
  Name                       Type        Source   Read-Only  Writes
  -------------------------  ----------  -------  ---------  ------
  local/run-migrations       tool        custom   -          yes
  local/validate-api-compat  tool        custom   -          yes
  local/create-subplan       tool        custom   -          yes
  local/deploy-staging       tool        agent    -          yes
  local/run-tests            validation  custom   yes        -
  local/lint-check           validation  custom   yes        -
  local/check-bundle-size    validation  custom   yes        -
  local/type-check           validation  custom   yes        -

Summary
  Total: 8
  Tools: 4
  Validations: 4
  Read-Only: 4
  Writes: 4

[OK] 8 tools listed
```

=== "JSON"

```json
{
  "command": "tool list",
  "status": "ok",
  "exit_code": 0,
  "data": {
    "tools": [
      { "name": "local/run-migrations", "type": "tool", "source": "custom", "read_only": false, "writes": true },
      { "name": "local/validate-api-compat", "type": "tool", "source": "custom", "read_only": false, "writes": true },
      { "name": "local/create-subplan", "type": "tool", "source": "custom", "read_only": false, "writes": true },
      { "name": "local/deploy-staging", "type": "tool", "source": "agent", "read_only": false, "writes": true },
      { "name": "local/run-tests", "type": "validation", "source": "custom", "read_only": true, "writes": false },
      { "name": "local/lint-check", "type": "validation", "source": "custom", "read_only": true, "writes": false },
      { "name": "local/check-bundle-size", "type": "validation", "source": "custom", "read_only": true, "writes": false },
      { "name": "local/type-check", "type": "validation", "source": "custom", "read_only": true, "writes": false }
    ],
    "summary": {
      "total": 8,
      "tools": 4,
      "validations": 4,
      "read_only": 4,
      "writes": 4
    }
  },
  "timing": { "duration_ms": 22 },
  "messages": ["8 tools listed"]
}
```

=== "YAML"

```yaml
command: tool list
status: ok
exit_code: 0
data:
  tools:
    - name: local/run-migrations
      type: tool
      source: custom
      read_only: false
      writes: true
    - name: local/validate-api-compat
      type: tool
      source: custom
      read_only: false
      writes: true
    - name: local/create-subplan
      type: tool
      source: custom
      read_only: false
      writes: true
    - name: local/deploy-staging
      type: tool
      source: agent
      read_only: false
      writes: true
    - name: local/run-tests
      type: validation
      source: custom
      read_only: true
      writes: false
    - name: local/lint-check
      type: validation
      source: custom
      read_only: true
      writes: false
    - name: local/check-bundle-size
      type: validation
      source: custom
      read_only: true
      writes: false
    - name: local/type-check
      type: validation
      source: custom
      read_only: true
      writes: false
  summary:
    total: 8
    tools: 4
    validations: 4
    read_only: 4
    writes: 4
timing:
  duration_ms: 22
messages:
  - 8 tools listed
```

=== "Rich"

<div class="highlight"><pre><code>
<span style="color: #66cc66; font-weight: 600;">$</span> <span style="color: cyan; font-weight: 600;">agents</span> tool list <span style="color: cyan;">--type</span> validation <span style="color: cyan;">--namespace</span> local

╭─ Validations ─────────────────────────────────────────────────────────────────╮
│ <span style="color: cyan; font-weight: 600;">Name</span>                       <span style="color: cyan; font-weight: 600;">Source</span>   <span style="color: cyan; font-weight: 600;">Mode</span>          <span style="color: cyan; font-weight: 600;">Attachments</span>                 │
│ <span style="opacity: 0.7;">─────────────────────────  ───────  ────────────  ────────────────────────</span>    │
│ local/run-tests            custom   required      2 (1 direct, 1 project)     │
│ local/lint-check           custom   required      1 (1 direct)                │
│ local/check-bundle-size    custom   informational 1 (1 project)               │
│ local/type-check           custom   required      2 (2 project)               │
╰───────────────────────────────────────────────────────────────────────────────╯

╭─ Summary ──────────────╮
│ <span style="color: #5599ff; font-weight: 600;">Total:</span> 4               │
│ <span style="color: #66cc66; font-weight: 600;">Required:</span> 3            │
│ <span style="color: yellow; font-weight: 600;">Informational:</span> 1       │
╰────────────────────────╯

<span style="color: #66cc66; font-weight: 600;">✓ OK</span> 4 validations listed
</code></pre></div>

=== "Plain"

```
$ agents tool list --type validation --namespace local

Validations
  Name                       Source   Mode          Attachments
  -------------------------  -------  ------------  ------------------------
  local/run-tests            custom   required      2 (1 direct, 1 project)
  local/lint-check           custom   required      1 (1 direct)
  local/check-bundle-size    custom   informational 1 (1 project)
  local/type-check           custom   required      2 (2 project)

Summary
  Total: 4
  Required: 3
  Informational: 1

[OK] 4 validations listed
```

=== "JSON"

```json
{
  "command": "tool list",
  "status": "ok",
  "exit_code": 0,
  "data": {
    "validations": [
      { "name": "local/run-tests", "source": "custom", "mode": "required", "attachments": { "total": 2, "direct": 1, "project": 1 } },
      { "name": "local/lint-check", "source": "custom", "mode": "required", "attachments": { "total": 1, "direct": 1 } },
      { "name": "local/check-bundle-size", "source": "custom", "mode": "informational", "attachments": { "total": 1, "project": 1 } },
      { "name": "local/type-check", "source": "custom", "mode": "required", "attachments": { "total": 2, "project": 2 } }
    ],
    "summary": {
      "total": 4,
      "required": 3,
      "informational": 1
    }
  },
  "timing": { "duration_ms": 18 },
  "messages": ["4 validations listed"]
}
```

=== "YAML"

```yaml
command: tool list
status: ok
exit_code: 0
data:
  validations:
    - name: local/run-tests
      source: custom
      mode: required
      attachments:
        total: 2
        direct: 1
        project: 1
    - name: local/lint-check
      source: custom
      mode: required
      attachments:
        total: 1
        direct: 1
    - name: local/check-bundle-size
      source: custom
      mode: informational
      attachments:
        total: 1
        project: 1
    - name: local/type-check
      source: custom
      mode: required
      attachments:
        total: 2
        project: 2
  summary:
    total: 4
    required: 3
    informational: 1
timing:
  duration_ms: 18
messages:
  - 4 validations listed
```
agents tool show
agents tool show <NAME>

Purpose Show full details for a registered tool, including its schema, capability metadata, and where it is referenced. Because Validations are a subtype of Tool and share the same registry, this command also shows validation details. When showing a Validation, additional validation-specific fields are displayed: Mode (required/informational) and Attached To (listing each resource attachment with its scope — direct, project-scoped, or plan-scoped).

Arguments

  • <NAME>: Tool or validation name.

Examples

=== "Rich"

<div class="highlight"><pre><code>
<span style="color: #66cc66; font-weight: 600;">$</span> <span style="color: cyan; font-weight: 600;">agents</span> tool show local/run-migrations

╭─ Tool Details ───────────────────────────────╮
│ <span style="color: cyan; font-weight: 600;">Name:</span> local/run-migrations                   │
│ <span style="color: #5599ff; font-weight: 600;">Description:</span> Run database migrations         │
│ <span style="color: #5599ff; font-weight: 600;">Source:</span> custom                               │
│ <span style="color: #66cc66; font-weight: 600;">Config:</span> ./tools/run-migrations.yaml          │
│ <span style="color: #66cc66; font-weight: 600;">Registered:</span> 2026-02-09 10:15                 │
╰──────────────────────────────────────────────╯

╭─ Input Schema ─────────────────────────────────╮
│ <span style="color: #5599ff; font-weight: 600;">direction</span>: string (required) enum: [up, down]  │
│ <span style="color: #5599ff; font-weight: 600;">count</span>: integer (default: 1)                    │
╰────────────────────────────────────────────────╯

╭─ Capability ──────────────────────╮
│ <span style="color: #5599ff; font-weight: 600;">Read-Only:</span> false                  │
│ <span style="color: yellow; font-weight: 600;">Writes:</span> true                      │
│ <span style="color: #5599ff; font-weight: 600;">Write Scope:</span> database:migrations  │
│ <span style="color: #66cc66; font-weight: 600;">Checkpointable:</span> true              │
│ <span style="color: #5599ff; font-weight: 600;">Checkpoint Scope:</span> transaction     │
│ <span style="color: yellow; font-weight: 600;">Side Effects:</span> schema_mutation     │
│ <span style="color: #5599ff; font-weight: 600;">Idempotent:</span> false                 │
╰───────────────────────────────────╯

╭─ Referenced By ───────────────────────╮
│ <span style="color: #5599ff; font-weight: 600;">Skills:</span>                               │
│   - local/devops-toolkit              │
│ <span style="color: #5599ff; font-weight: 600;">Actor Graph Nodes:</span>                    │
│   - code_executor.run_db_migrate      │
╰───────────────────────────────────────╯

<span style="color: #66cc66; font-weight: 600;">✓ OK</span> Tool details loaded
</code></pre></div>

=== "Plain"

```
$ agents tool show local/run-migrations

Tool Details
  Name: local/run-migrations
  Description: Run database migrations
  Source: custom
  Config: ./tools/run-migrations.yaml
  Registered: 2026-02-09 10:15

Input Schema
  direction: string (required) enum: [up, down]
  count: integer (default: 1)

Capability
  Read-Only: false
  Writes: true
  Write Scope: database:migrations
  Checkpointable: true
  Checkpoint Scope: transaction
  Side Effects: schema_mutation
  Idempotent: false

Referenced By
  Skills:
    - local/devops-toolkit
  Actor Graph Nodes:
    - code_executor.run_db_migrate

[OK] Tool details loaded
```

=== "JSON"

```json
{
  "command": "tool show",
  "status": "ok",
  "exit_code": 0,
  "data": {
    "tool_details": {
      "name": "local/run-migrations",
      "description": "Run database migrations",
      "source": "custom",
      "config": "./tools/run-migrations.yaml",
      "registered": "2026-02-09T10:15:00Z"
    },
    "input_schema": {
      "direction": { "type": "string", "required": true, "enum": ["up", "down"] },
      "count": { "type": "integer", "default": 1 }
    },
    "capability": {
      "read_only": false,
      "writes": true,
      "write_scope": "database:migrations",
      "checkpointable": true,
      "checkpoint_scope": "transaction",
      "side_effects": ["schema_mutation"],
      "idempotent": false
    },
    "referenced_by": {
      "skills": ["local/devops-toolkit"],
      "actor_graph_nodes": ["code_executor.run_db_migrate"]
    }
  },
  "timing": { "duration_ms": 15 },
  "messages": ["Tool details loaded"]
}
```

=== "YAML"

```yaml
command: tool show
status: ok
exit_code: 0
data:
  tool_details:
    name: local/run-migrations
    description: Run database migrations
    source: custom
    config: ./tools/run-migrations.yaml
    registered: "2026-02-09T10:15:00Z"
  input_schema:
    direction:
      type: string
      required: true
      enum:
        - up
        - down
    count:
      type: integer
      default: 1
  capability:
    read_only: false
    writes: true
    write_scope: "database:migrations"
    checkpointable: true
    checkpoint_scope: transaction
    side_effects:
      - schema_mutation
    idempotent: false
  referenced_by:
    skills:
      - local/devops-toolkit
    actor_graph_nodes:
      - code_executor.run_db_migrate
timing:
  duration_ms: 15
messages:
  - Tool details loaded
```

When showing a Validation, the output includes validation-specific sections:

=== "Rich"

<div class="highlight"><pre><code>
<span style="color: #66cc66; font-weight: 600;">$</span> <span style="color: cyan; font-weight: 600;">agents</span> tool show local/run-tests

╭─ Validation Details ──────────────────────────────╮
│ <span style="color: cyan; font-weight: 600;">Name:</span> local/run-tests                             │
│ <span style="color: #5599ff; font-weight: 600;">Type:</span> validation                                  │
│ <span style="color: #5599ff; font-weight: 600;">Description:</span> Run unit tests with coverage         │
│ <span style="color: #5599ff; font-weight: 600;">Source:</span> custom                                    │
│ <span style="color: #66cc66; font-weight: 600;">Mode:</span> required                                    │
│ <span style="color: #66cc66; font-weight: 600;">Config:</span> ./validations/run-tests.yaml              │
│ <span style="color: #66cc66; font-weight: 600;">Registered:</span> 2026-02-09 10:15                      │
╰───────────────────────────────────────────────────╯

╭─ Input Schema ─────────────────────────────────────╮
│ <span style="color: #5599ff; font-weight: 600;">coverage_threshold</span>: integer (default: 80)          │
╰────────────────────────────────────────────────────╯

╭─ Output Schema (Validation Return Format) ─────────╮
│ <span style="color: #66cc66; font-weight: 600;">passed</span>: boolean (required)                         │
│ <span style="color: #5599ff; font-weight: 600;">message</span>: string (optional)                         │
│ <span style="color: #5599ff; font-weight: 600;">data</span>: object (optional, arbitrary structure)       │
╰────────────────────────────────────────────────────╯

╭─ Capability ──────────────────────╮
│ <span style="color: #5599ff; font-weight: 600;">Read-Only:</span> true (enforced)        │
│ <span style="color: #66cc66; font-weight: 600;">Checkpointable:</span> false (enforced)  │
│ <span style="color: yellow; font-weight: 600;">Timeout:</span> 600s                     │
╰───────────────────────────────────╯

╭─ Attached To ─────────────────────────────────────────────────────╮
│ <span style="color: #5599ff; font-weight: 600;">local/api-repo</span> (direct, always active)                            │
│   <span style="opacity: 0.7;">(attachment: 01HXM5B2C3D4E5F6G7H8J9K0L1)</span>                        │
│ <span style="color: #5599ff; font-weight: 600;">local/api-repo</span> (scope: project local/api-service)                 │
│   <span style="opacity: 0.7;">(attachment: 01HXM5A1B2C3D4E5F6G7H8J9K0)</span>                        │
╰───────────────────────────────────────────────────────────────────╯

<span style="color: #66cc66; font-weight: 600;">✓ OK</span> Validation details loaded
</code></pre></div>

=== "Plain"

```
$ agents tool show local/run-tests

Validation Details
  Name: local/run-tests
  Type: validation
  Description: Run unit tests with coverage
  Source: custom
  Mode: required
  Config: ./validations/run-tests.yaml
  Registered: 2026-02-09 10:15

Input Schema
  coverage_threshold: integer (default: 80)

Output Schema (Validation Return Format)
  passed: boolean (required)
  message: string (optional)
  data: object (optional, arbitrary structure)

Capability
  Read-Only: true (enforced)
  Checkpointable: false (enforced)
  Timeout: 600s

Attached To
  local/api-repo (direct, always active)
    (attachment: 01HXM5B2C3D4E5F6G7H8J9K0L1)
  local/api-repo (scope: project local/api-service)
    (attachment: 01HXM5A1B2C3D4E5F6G7H8J9K0)

[OK] Validation details loaded
```

=== "JSON"

```json
{
  "command": "tool show",
  "status": "ok",
  "exit_code": 0,
  "data": {
    "validation_details": {
      "name": "local/run-tests",
      "type": "validation",
      "description": "Run unit tests with coverage",
      "source": "custom",
      "mode": "required",
      "config": "./validations/run-tests.yaml",
      "registered": "2026-02-09T10:15:00Z"
    },
    "input_schema": {
      "coverage_threshold": { "type": "integer", "default": 80 }
    },
    "output_schema": {
      "passed": { "type": "boolean", "required": true },
      "message": { "type": "string", "required": false },
      "data": { "type": "object", "required": false, "description": "arbitrary structure" }
    },
    "capability": {
      "read_only": true,
      "read_only_enforced": true,
      "checkpointable": false,
      "checkpointable_enforced": true,
      "timeout_seconds": 600
    },
    "attached_to": [
      { "resource": "local/api-repo", "scope": "direct", "attachment_id": "01HXM5B2C3D4E5F6G7H8J9K0L1" },
      { "resource": "local/api-repo", "scope": "project", "project": "local/api-service", "attachment_id": "01HXM5A1B2C3D4E5F6G7H8J9K0" }
    ]
  },
  "timing": { "duration_ms": 19 },
  "messages": ["Validation details loaded"]
}
```

=== "YAML"

```yaml
command: tool show
status: ok
exit_code: 0
data:
  validation_details:
    name: local/run-tests
    type: validation
    description: Run unit tests with coverage
    source: custom
    mode: required
    config: ./validations/run-tests.yaml
    registered: "2026-02-09T10:15:00Z"
  input_schema:
    coverage_threshold:
      type: integer
      default: 80
  output_schema:
    passed:
      type: boolean
      required: true
    message:
      type: string
      required: false
    data:
      type: object
      required: false
      description: arbitrary structure
  capability:
    read_only: true
    read_only_enforced: true
    checkpointable: false
    checkpointable_enforced: true
    timeout_seconds: 600
  attached_to:
    - resource: local/api-repo
      scope: direct
      attachment_id: 01HXM5B2C3D4E5F6G7H8J9K0L1
    - resource: local/api-repo
      scope: project
      project: local/api-service
      attachment_id: 01HXM5A1B2C3D4E5F6G7H8J9K0
timing:
  duration_ms: 19
messages:
  - Validation details loaded
```

agents lsp

!!! info "Purpose" Manage the LSP Registry — the global registry of Language Server Protocol servers that provide language intelligence (diagnostics, type information, completions, references, definitions, symbols, formatting, code actions, renaming) to actors. LSP servers are Infrastructure-layer components attached to actors via the lsp: configuration field; they have nothing to do with IDE integration. Each registered LSP server has a namespaced name ([[server:]namespace/]name), a launch command, supported languages, initialization options, and capability declarations. The agents lsp commands mirror the structure of agents tool commands.

agents lsp add
agents lsp add [--update|-u] (--config|-c) <FILE>

Purpose Register an LSP server from a YAML configuration file. The config file defines the server's namespaced name, launch command, supported languages, initialization options, and capability declarations. If the server already exists and --update is not provided, the command fails with an error and a hint to use --update.

Arguments

  • --config/-c FILE: Path to LSP server YAML configuration file (required).
  • --update/-u: Overwrite if the server already exists in the registry.

Examples

Registering a new LSP server:

=== "Rich"

<div class="highlight"><pre><code>
<span style="color: #66cc66; font-weight: 600;">$</span> <span style="color: cyan; font-weight: 600;">agents</span> lsp add <span style="color: cyan;">--config</span> lsp/pyright.yaml

╭─ LSP Server Registered ──────────────────────────────────╮
│ <span style="color: cyan; font-weight: 600;">Name:</span> local/pyright                                      │
│ <span style="color: #5599ff; font-weight: 600;">Languages:</span> python                                        │
│ <span style="color: #5599ff; font-weight: 600;">Command:</span> pyright-langserver --stdio                      │
│ <span style="color: #5599ff; font-weight: 600;">Capabilities:</span> diagnostics, hover, completions,           │
│               references, definitions, symbols,          │
│               rename, code_actions                       │
╰──────────────────────────────────────────────────────────╯

<span style="color: #66cc66; font-weight: 600;">✓ OK</span> LSP server registered
</code></pre></div>

=== "Plain"

```
$ agents lsp add --config lsp/pyright.yaml

LSP Server Registered
  Name: local/pyright
  Languages: python
  Command: pyright-langserver --stdio
  Capabilities: diagnostics, hover, completions, references, definitions, symbols, rename, code_actions

[OK] LSP server registered
```

=== "JSON"

```json
{
  "command": "lsp add",
  "status": "ok",
  "exit_code": 0,
  "data": {
    "lsp_server": {
      "name": "local/pyright",
      "languages": ["python"],
      "command": "pyright-langserver --stdio",
      "capabilities": [
        "diagnostics", "hover", "completions", "references",
        "definitions", "symbols", "rename", "code_actions"
      ]
    }
  },
  "timing": { "duration_ms": 12 },
  "messages": ["LSP server registered"]
}
```

=== "YAML"

```yaml
command: lsp add
status: ok
exit_code: 0
data:
  lsp_server:
    name: local/pyright
    languages:
      - python
    command: pyright-langserver --stdio
    capabilities:
      - diagnostics
      - hover
      - completions
      - references
      - definitions
      - symbols
      - rename
      - code_actions
timing:
  duration_ms: 12
messages:
  - LSP server registered
```

Attempting to add a server that already exists (without --update):

=== "Rich"

<div class="highlight"><pre><code>
<span style="color: #66cc66; font-weight: 600;">$</span> <span style="color: cyan; font-weight: 600;">agents</span> lsp add <span style="color: cyan;">--config</span> lsp/pyright.yaml

<span style="color: red; font-weight: 600;">✗ ERROR</span> LSP server 'local/pyright' already exists

<span style="color: yellow;">Hint:</span> Use <span style="color: cyan;">--update</span> to overwrite: agents lsp add --update --config lsp/pyright.yaml
</code></pre></div>

=== "Plain"

```
$ agents lsp add --config lsp/pyright.yaml

[ERROR] LSP server 'local/pyright' already exists
Hint: Use --update to overwrite: agents lsp add --update --config lsp/pyright.yaml
```

=== "JSON"

```json
{
  "command": "lsp add",
  "status": "error",
  "exit_code": 1,
  "data": {
    "error": "LSP server 'local/pyright' already exists"
  },
  "hint": "Use --update to overwrite",
  "suggested_command": "agents lsp add --update --config lsp/pyright.yaml",
  "timing": { "duration_ms": 5 },
  "messages": ["LSP server 'local/pyright' already exists"]
}
```

=== "YAML"

```yaml
command: lsp add
status: error
exit_code: 1
data:
  error: "LSP server 'local/pyright' already exists"
hint: Use --update to overwrite
suggested_command: agents lsp add --update --config lsp/pyright.yaml
timing:
  duration_ms: 5
messages:
  - "LSP server 'local/pyright' already exists"
```
agents lsp remove
agents lsp remove [--yes|-y] <NAME>

!!! danger "Destructive Operation" Remove a registered LSP server from the LSP Registry. Running LSP server processes bound to the removed entry are terminated. Actor configurations referencing this server by name will fail at activation until resolved.

Arguments

  • <NAME>: Namespaced LSP server name to remove.
  • --yes/-y: Skip confirmation prompt.

Examples

=== "Rich"

<div class="highlight"><pre><code>
<span style="color: #66cc66; font-weight: 600;">$</span> <span style="color: cyan; font-weight: 600;">agents</span> lsp remove local/pyright

Remove LSP server local/pyright? [y/N]: y

╭─ LSP Server Removed ─────────────────────╮
│ <span style="color: cyan; font-weight: 600;">Name:</span> local/pyright                      │
│ <span style="color: #5599ff; font-weight: 600;">Languages:</span> python                        │
╰──────────────────────────────────────────╯

╭─ References ──────────────────────────────────────────────────╮
│ <span style="color: yellow; font-weight: 600;">Warning:</span> This LSP server is referenced by:                    │
│ - Actor: local/code-reviewer (lsp: [local/pyright])           │
│ - Actor: local/refactor-agent (lsp: [local/pyright])          │
│ These actor LSP bindings will fail until resolved.            │
╰───────────────────────────────────────────────────────────────╯

<span style="color: #66cc66; font-weight: 600;">✓ OK</span> LSP server removed
</code></pre></div>

=== "Plain"

```
$ agents lsp remove local/pyright

Remove LSP server local/pyright? [y/N]: y

LSP Server Removed
  Name: local/pyright
  Languages: python

References
  Warning: This LSP server is referenced by:
  - Actor: local/code-reviewer (lsp: [local/pyright])
  - Actor: local/refactor-agent (lsp: [local/pyright])
  These actor LSP bindings will fail until resolved.

[OK] LSP server removed
```

=== "JSON"

```json
{
  "command": "lsp remove",
  "status": "ok",
  "exit_code": 0,
  "data": {
    "lsp_server_removed": {
      "name": "local/pyright",
      "languages": ["python"]
    },
    "broken_references": [
      { "type": "actor", "name": "local/code-reviewer" },
      { "type": "actor", "name": "local/refactor-agent" }
    ]
  },
  "timing": { "duration_ms": 28 },
  "messages": [
    "LSP server removed",
    "Warning: This LSP server is referenced by 2 actors. These actor LSP bindings will fail until resolved."
  ]
}
```

=== "YAML"

```yaml
command: lsp remove
status: ok
exit_code: 0
data:
  lsp_server_removed:
    name: local/pyright
    languages:
      - python
  broken_references:
    - type: actor
      name: local/code-reviewer
    - type: actor
      name: local/refactor-agent
timing:
  duration_ms: 28
messages:
  - LSP server removed
  - "Warning: This LSP server is referenced by 2 actors. These actor LSP bindings will fail until resolved."
```
agents lsp list
agents lsp list [(--namespace|-n) <NS>] [--language <LANG>] [<REGEX>]

Purpose List registered LSP servers with optional filters. Shows each server's name, supported languages, command, and the number of actors currently bound to it.

Arguments

  • --namespace/-n NS: Filter by namespace.
  • --language LANG: Filter by supported language (e.g., python, typescript).
  • <REGEX>: Optional name filter pattern.

Examples

=== "Rich"

<div class="highlight"><pre><code>
<span style="color: #66cc66; font-weight: 600;">$</span> <span style="color: cyan; font-weight: 600;">agents</span> lsp list

╭─ LSP Registry (3 servers) ───────────────────────────────────────────────────────────╮
│ <span style="color: cyan; font-weight: 600;">Name</span>                    │ <span style="color: cyan; font-weight: 600;">Languages</span>            │ <span style="color: cyan; font-weight: 600;">Command</span>                     │ <span style="color: cyan; font-weight: 600;">Bound</span> │
│─────────────────────────│──────────────────────│─────────────────────────────│───────│
│ local/pyright           │ python               │ pyright-langserver --stdio  │   3   │
│ local/ts-server         │ typescript, jsx, tsx │ typescript-language-server  │   2   │
│ local/gopls             │ go                   │ gopls serve                 │   1   │
╰──────────────────────────────────────────────────────────────────────────────────────╯
</code></pre></div>

=== "Plain"

```
$ agents lsp list

LSP Registry (3 servers)
  Name                      Languages              Command                       Bound
  local/pyright             python                 pyright-langserver --stdio       3
  local/ts-server           typescript, jsx, tsx   typescript-language-server        2
  local/gopls               go                     gopls serve                      1
```

=== "JSON"

```json
{
  "command": "lsp list",
  "status": "ok",
  "exit_code": 0,
  "data": {
    "lsp_servers": [
      {
        "name": "local/pyright",
        "languages": ["python"],
        "command": "pyright-langserver --stdio",
        "bound_actors": 3
      },
      {
        "name": "local/ts-server",
        "languages": ["typescript", "jsx", "tsx"],
        "command": "typescript-language-server",
        "bound_actors": 2
      },
      {
        "name": "local/gopls",
        "languages": ["go"],
        "command": "gopls serve",
        "bound_actors": 1
      }
    ],
    "total": 3
  },
  "timing": { "duration_ms": 8 },
  "messages": ["3 LSP servers listed"]
}
```

=== "YAML"

```yaml
command: lsp list
status: ok
exit_code: 0
data:
  lsp_servers:
    - name: local/pyright
      languages:
        - python
      command: pyright-langserver --stdio
      bound_actors: 3
    - name: local/ts-server
      languages:
        - typescript
        - jsx
        - tsx
      command: typescript-language-server
      bound_actors: 2
    - name: local/gopls
      languages:
        - go
      command: gopls serve
      bound_actors: 1
  total: 3
timing:
  duration_ms: 8
messages:
  - 3 LSP servers listed
```

Filtering by language:

=== "Rich"

<div class="highlight"><pre><code>
<span style="color: #66cc66; font-weight: 600;">$</span> <span style="color: cyan; font-weight: 600;">agents</span> lsp list <span style="color: cyan;">--language</span> python

╭─ LSP Registry (1 server, filtered: language=python) ─────────────────────────────╮
│ <span style="color: cyan; font-weight: 600;">Name</span>                    │ <span style="color: cyan; font-weight: 600;">Languages</span>  │ <span style="color: cyan; font-weight: 600;">Command</span>                           │ <span style="color: cyan; font-weight: 600;">Bound</span> │
│─────────────────────────│────────────│───────────────────────────────────│───────│
│ local/pyright           │ python     │ pyright-langserver --stdio        │   3   │
╰──────────────────────────────────────────────────────────────────────────────────╯
</code></pre></div>

=== "Plain"

```
$ agents lsp list --language python

LSP Registry (1 server, filtered: language=python)
  Name                      Languages    Command                       Bound
  local/pyright             python       pyright-langserver --stdio       3
```

=== "JSON"

```json
{
  "command": "lsp list",
  "status": "ok",
  "exit_code": 0,
  "data": {
    "lsp_servers": [
      {
        "name": "local/pyright",
        "languages": ["python"],
        "command": "pyright-langserver --stdio",
        "bound_actors": 3
      }
    ],
    "total": 1,
    "filter": { "language": "python" }
  },
  "timing": { "duration_ms": 6 },
  "messages": ["1 LSP server listed (filtered: language=python)"]
}
```

=== "YAML"

```yaml
command: lsp list
status: ok
exit_code: 0
data:
  lsp_servers:
    - name: local/pyright
      languages:
        - python
      command: pyright-langserver --stdio
      bound_actors: 3
  total: 1
  filter:
    language: python
timing:
  duration_ms: 6
messages:
  - "1 LSP server listed (filtered: language=python)"
```
agents lsp show
agents lsp show <NAME>

Purpose Show full details for a registered LSP server, including its configuration, supported languages, capabilities, initialization options, and which actors are bound to it.

Arguments

  • <NAME>: Namespaced LSP server name.

Examples

=== "Rich"

<div class="highlight"><pre><code>
<span style="color: #66cc66; font-weight: 600;">$</span> <span style="color: cyan; font-weight: 600;">agents</span> lsp show local/pyright

╭─ LSP Server Details ──────────────────────────────────────────╮
│ <span style="color: cyan; font-weight: 600;">Name:</span> local/pyright                                           │
│ <span style="color: #5599ff; font-weight: 600;">Languages:</span> python                                             │
│ <span style="color: #5599ff; font-weight: 600;">Command:</span> pyright-langserver --stdio                           │
│ <span style="color: #5599ff; font-weight: 600;">Root path:</span> {{ project.root }}                                 │
│ <span style="color: #5599ff; font-weight: 600;">Init options:</span>                                                 │
│   python.analysis.typeCheckingMode: standard                  │
│   python.analysis.autoSearchPaths: true                       │
╰───────────────────────────────────────────────────────────────╯

╭─ Capabilities ────────────────────────────────────────────────╮
│ diagnostics, hover, completions, references, definitions,     │
│ symbols, rename, code_actions                                 │
╰───────────────────────────────────────────────────────────────╯

╭─ Bound Actors ────────────────────────────────────────────────╮
│ <span style="color: cyan; font-weight: 600;">local/code-reviewer</span>      (explicit binding)                   │
│ <span style="color: cyan; font-weight: 600;">local/refactor-agent</span>     (language-based: python)             │
│ <span style="color: cyan; font-weight: 600;">local/polyglot-planner</span>   (auto-discovery)                     │
╰───────────────────────────────────────────────────────────────╯
</code></pre></div>

=== "Plain"

```
$ agents lsp show local/pyright

LSP Server Details
  Name: local/pyright
  Languages: python
  Command: pyright-langserver --stdio
  Root path: {{ project.root }}
  Init options:
    python.analysis.typeCheckingMode: standard
    python.analysis.autoSearchPaths: true

Capabilities
  diagnostics, hover, completions, references, definitions, symbols, rename, code_actions

Bound Actors
  local/code-reviewer        (explicit binding)
  local/refactor-agent       (language-based: python)
  local/polyglot-planner     (auto-discovery)
```

=== "JSON"

```json
{
  "command": "lsp show",
  "status": "ok",
  "exit_code": 0,
  "data": {
    "lsp_server": {
      "name": "local/pyright",
      "languages": ["python"],
      "command": "pyright-langserver --stdio",
      "root_path": "{{ project.root }}",
      "init_options": {
        "python.analysis.typeCheckingMode": "standard",
        "python.analysis.autoSearchPaths": true
      },
      "capabilities": [
        "diagnostics", "hover", "completions", "references",
        "definitions", "symbols", "rename", "code_actions"
      ],
      "bound_actors": [
        { "name": "local/code-reviewer", "binding_mode": "explicit" },
        { "name": "local/refactor-agent", "binding_mode": "language_based", "language": "python" },
        { "name": "local/polyglot-planner", "binding_mode": "auto_discovery" }
      ]
    }
  },
  "timing": { "duration_ms": 14 },
  "messages": ["LSP server details loaded"]
}
```

=== "YAML"

```yaml
command: lsp show
status: ok
exit_code: 0
data:
  lsp_server:
    name: local/pyright
    languages:
      - python
    command: pyright-langserver --stdio
    root_path: "{{ project.root }}"
    init_options:
      python.analysis.typeCheckingMode: standard
      python.analysis.autoSearchPaths: true
    capabilities:
      - diagnostics
      - hover
      - completions
      - references
      - definitions
      - symbols
      - rename
      - code_actions
    bound_actors:
      - name: local/code-reviewer
        binding_mode: explicit
      - name: local/refactor-agent
        binding_mode: language_based
        language: python
      - name: local/polyglot-planner
        binding_mode: auto_discovery
timing:
  duration_ms: 14
messages:
  - LSP server details loaded
```
agents lsp serve
agents lsp serve [--log-level <LEVEL>]

Purpose Launch the stub LSP server over stdin/stdout using the JSON-RPC base protocol with Content-Length header framing. The server supports the initialize, shutdown, and exit lifecycle methods. Requests received before initialize are rejected with ServerNotInitialized (-32002) per the LSP specification. After initialization, unsupported LSP methods return a MethodNotFound (-32601) error. After shutdown is received, only exit is accepted; other requests are rejected with InvalidRequest (-32600). Sending initialize a second time is also rejected with InvalidRequest.

This command is intended for development and testing of LSP transport integration. In a future milestone the stub handlers will be replaced by real LSP server proxying through the A2A facade.

Arguments

  • --log-level LEVEL: Logging level for the server process. Valid values: debug, info (default), warning, error. Invalid values cause immediate exit with a descriptive error message.

Transport Limits

Limit Value Description
Max Content-Length 10 MB Messages exceeding this are rejected with a warning log (DoS guard)
Max header lines 32 Headers with more lines are rejected with a warning log (DoS guard)

Examples

=== "Rich"

<div class="highlight"><pre><code>
<span style="color: #66cc66; font-weight: 600;">$</span> <span style="color: cyan; font-weight: 600;">agents</span> lsp serve

<span style="color: cyan; font-weight: 600;">CleverAgents LSP Server</span> (stub) starting — PID 7412
  Log level: info
  Transport: stdin/stdout (JSON-RPC, Content-Length framing)
  Supported methods: initialize, shutdown, exit
  All other methods → MethodNotFound (-32601)
</code></pre></div>

=== "Plain"

```
$ agents lsp serve

CleverAgents LSP Server (stub) starting — PID 7412
  Log level: info
  Transport: stdin/stdout (JSON-RPC, Content-Length framing)
  Supported methods: initialize, shutdown, exit
  All other methods → MethodNotFound (-32601)
```

agents validation

!!! info "Purpose" Register and attach ==validations== — a specialized subtype of Tool that extends the Tool class with pass/fail semantics and attachment scoping. A Validation inherits all base Tool properties (name, source, input_schema, capability, resource bindings, lifecycle hooks, etc.) and adds: a mode field (required or informational), and a structured JSON return format with a mandatory passed boolean, an optional message string, and an optional data object for arbitrary informational output. Since Validation extends Tool, it shares the same backing sources (custom, MCP, agent_skill, builtin) plus the additional wrapped source type for Validations that wrap an existing Tool via the wraps field. It uses the same YAML configuration format (with an additional validation block and optional wraps/transform fields), and the same registries and resolution mechanisms. Validations are always read-only (writes is always false, checkpointable is always false).

Because Validations are a subtype of Tool and share the same Tool Registry and naming namespace, only validation-specific operations have dedicated subcommands here: add (registration with validation-specific fields like mode), attach, and detach. General tool operations — listing, inspection, and removal — are handled through the agents tool commands:

  • List validations: agents tool list --type validation
  • Show validation details: agents tool show <NAME> (displays validation-specific fields when the entry is a Validation)
  • Remove a validation: agents tool remove <NAME> (automatically removes all attachments)

A validation is always attached to a resource. The optional --project or --plan flag controls the scope of the attachment:

  • Direct attachment (no scope flag): The validation is always active for that resource, regardless of which plan or project accesses it. Use agents validation attach <RESOURCE> <VALIDATION> [args...].
  • Project-scoped attachment (--project): The validation only runs for that resource when it is being interacted with through the specified project. Use agents validation attach --project <PROJECT> <RESOURCE> <VALIDATION> [args...].
  • Plan-scoped attachment (--plan): The validation only runs for that resource when it is being interacted with through the specified plan. Use agents validation attach --plan <PLAN_ID> <RESOURCE> <VALIDATION> [args...].

The same validation can be attached to the same resource multiple times — directly, through different projects, and through different plans — and since validations accept arguments, attaching the same validation to the same resource multiple times with different arguments is a common and useful configuration pattern (e.g., running the same test suite with different coverage thresholds for different projects).

Each attach invocation returns a system-assigned attachment ULID uniquely identifying that attachment. Use this ULID with agents validation detach <ATTACHMENT_ID> to remove a specific attachment.

When multiple attachment scopes apply, the union of all applicable validations is used. A resource with a directly-attached validation will always have that validation run, plus any additional validations attached through the active project or plan.

agents validation add
agents validation add --config|-c <FILE> [--required | --informational] [--update]

Purpose Register a new validation. The validation is fully defined by the YAML configuration file specified with --config. The validation is registered in the shared Tool Registry under the name specified in the config file; the name must not conflict with any existing tool or validation. If a validation with the same name already exists, the command fails unless --update is provided. The validation YAML may use wraps to reference an existing Tool, reusing its implementation and interpreting its output through a transform function (see Tool Wrapping under Core Concepts > Validation). When wraps is used, the wrapped Tool must already be registered.

Arguments

  • --config/-c FILE: Path to the validation YAML configuration file (required). The file must fully define the validation, including the name field which determines the validation's registered name, the validation.mode field (required or informational), and either inline implementation (source + code) or wrapping (wraps + transform).
  • --required: Override the mode in the YAML config to required. Mutually exclusive with --informational.
  • --informational: Override the mode in the YAML config to informational. Mutually exclusive with --required.
  • --update: Allow overwriting an existing validation registration.

Examples

=== "Rich"

<div class="highlight"><pre><code>
<span style="color: #66cc66; font-weight: 600;">$</span> <span style="color: cyan; font-weight: 600;">agents</span> validation add <span style="color: cyan;">--config</span> ./validations/run-tests.yaml

╭─ Validation Registered ────────────────────────────────╮
│ <span style="color: cyan; font-weight: 600;">Name:</span> local/run-tests                                  │
│ <span style="color: #5599ff; font-weight: 600;">Description:</span> Run unit tests with coverage              │
│ <span style="color: #5599ff; font-weight: 600;">Source:</span> custom                                         │
│ <span style="color: #66cc66; font-weight: 600;">Mode:</span> required                                         │
│ <span style="color: #66cc66; font-weight: 600;">Config:</span> ./validations/run-tests.yaml                   │
│ <span style="color: #66cc66; font-weight: 600;">Created:</span> 2026-02-09 10:15                              │
╰────────────────────────────────────────────────────────╯

╭─ Capability ────────────────────────╮
│ <span style="color: #5599ff; font-weight: 600;">Read-Only:</span> true (enforced)          │
│ <span style="color: #66cc66; font-weight: 600;">Checkpointable:</span> false (enforced)    │
│ <span style="color: yellow; font-weight: 600;">Timeout:</span> 600s                       │
╰─────────────────────────────────────╯

<span style="color: #66cc66; font-weight: 600;">✓ OK</span> Validation registered
</code></pre></div>

=== "Plain"

```
$ agents validation add --config ./validations/run-tests.yaml

Validation Registered
  Name: local/run-tests
  Description: Run unit tests with coverage
  Source: custom
  Mode: required
  Config: ./validations/run-tests.yaml
  Created: 2026-02-09 10:15

Capability
  Read-Only: true (enforced)
  Checkpointable: false (enforced)
  Timeout: 600s

[OK] Validation registered
```

=== "JSON"

```json
{
  "command": "validation add",
  "status": "ok",
  "exit_code": 0,
  "data": {
    "validation_registered": {
      "name": "local/run-tests",
      "description": "Run unit tests with coverage",
      "source": "custom",
      "mode": "required",
      "config": "./validations/run-tests.yaml",
      "created": "2026-02-09 10:15"
    },
    "capability": {
      "read_only": "true (enforced)",
      "checkpointable": "false (enforced)",
      "timeout": "600s"
    }
  },
  "timing": { "started": "2026-02-09T10:15:00Z", "duration_ms": 120 },
  "messages": ["Validation registered"]
}
```

=== "YAML"

```yaml
command: validation add
status: ok
exit_code: 0
data:
  validation_registered:
    name: local/run-tests
    description: Run unit tests with coverage
    source: custom
    mode: required
    config: ./validations/run-tests.yaml
    created: "2026-02-09 10:15"
  capability:
    read_only: "true (enforced)"
    checkpointable: "false (enforced)"
    timeout: 600s
timing:
  started: "2026-02-09T10:15:00Z"
  duration_ms: 120
messages:
  - "Validation registered"
```

=== "Rich"

<div class="highlight"><pre><code>
<span style="color: #66cc66; font-weight: 600;">$</span> <span style="color: cyan; font-weight: 600;">agents</span> validation add <span style="color: cyan;">--config</span> ./validations/lint-check.yaml

╭─ Validation Registered ──────────────╮
│ <span style="color: cyan; font-weight: 600;">Name:</span> local/lint-check               │
│ <span style="color: #5599ff; font-weight: 600;">Description:</span> Lint check              │
│ <span style="color: #5599ff; font-weight: 600;">Source:</span> custom                       │
│ <span style="color: #66cc66; font-weight: 600;">Mode:</span> required                       │
│ <span style="color: yellow; font-weight: 600;">Timeout:</span> 300s                        │
╰──────────────────────────────────────╯

<span style="color: #66cc66; font-weight: 600;">✓ OK</span> Validation registered
</code></pre></div>

=== "Plain"

```
$ agents validation add --config ./validations/lint-check.yaml

Validation Registered
  Name: local/lint-check
  Description: Lint check
  Source: custom
  Mode: required
  Timeout: 300s

[OK] Validation registered
```

=== "JSON"

```json
{
  "command": "validation add",
  "status": "ok",
  "exit_code": 0,
  "data": {
    "validation_registered": {
      "name": "local/lint-check",
      "description": "Lint check",
      "source": "custom",
      "mode": "required",
      "timeout": "300s"
    }
  },
  "timing": { "started": "2026-02-09T10:15:00Z", "duration_ms": 95 },
  "messages": ["Validation registered"]
}
```

=== "YAML"

```yaml
command: validation add
status: ok
exit_code: 0
data:
  validation_registered:
    name: local/lint-check
    description: Lint check
    source: custom
    mode: required
    timeout: 300s
timing:
  started: "2026-02-09T10:15:00Z"
  duration_ms: 95
messages:
  - "Validation registered"
```

=== "Rich"

<div class="highlight"><pre><code>
<span style="color: #66cc66; font-weight: 600;">$</span> <span style="color: cyan; font-weight: 600;">agents</span> validation add <span style="color: cyan;">--config</span> ./validations/check-bundle-size.yaml

╭─ Validation Registered ───────────────────────────╮
│ <span style="color: cyan; font-weight: 600;">Name:</span> local/check-bundle-size                     │
│ <span style="color: #5599ff; font-weight: 600;">Description:</span> Check bundle size (advisory)         │
│ <span style="color: #5599ff; font-weight: 600;">Source:</span> custom                                    │
│ <span style="color: yellow; font-weight: 600;">Mode:</span> informational                               │
│ <span style="color: yellow; font-weight: 600;">Timeout:</span> 300s                                     │
╰───────────────────────────────────────────────────╯

<span style="color: #66cc66; font-weight: 600;">✓ OK</span> Validation registered
</code></pre></div>

=== "Plain"

```
$ agents validation add --config ./validations/check-bundle-size.yaml

Validation Registered
  Name: local/check-bundle-size
  Description: Check bundle size (advisory)
  Source: custom
  Mode: informational
  Timeout: 300s

[OK] Validation registered
```

=== "JSON"

```json
{
  "command": "validation add",
  "status": "ok",
  "exit_code": 0,
  "data": {
    "validation_registered": {
      "name": "local/check-bundle-size",
      "description": "Check bundle size (advisory)",
      "source": "custom",
      "mode": "informational",
      "timeout": "300s"
    }
  },
  "timing": { "started": "2026-02-09T10:16:00Z", "duration_ms": 88 },
  "messages": ["Validation registered"]
}
```

=== "YAML"

```yaml
command: validation add
status: ok
exit_code: 0
data:
  validation_registered:
    name: local/check-bundle-size
    description: "Check bundle size (advisory)"
    source: custom
    mode: informational
    timeout: 300s
timing:
  started: "2026-02-09T10:16:00Z"
  duration_ms: 88
messages:
  - "Validation registered"
```
agents validation attach
agents validation attach [--project <PROJECT>|--plan <PLAN_ID>]
                         <RESOURCE> <VALIDATION> [<ARGS>...]

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.

The command returns a system-assigned attachment ULID that uniquely identifies this specific attachment association. This ULID is used to detach the validation later via agents validation detach <ATTACHMENT_ID>. The same validation can be attached to the same resource multiple times — directly, through different projects, through different plans, and even to the same resource and scope multiple times with different arguments.

Arguments

  • <RESOURCE>: Resource name or ULID to attach the validation to (positional argument, required).
  • <VALIDATION>: 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.
  • <ARGS>...: Optional validation-specific arguments (passed through to the validation tool's input_schema at execution time).

Examples

=== "Rich"

<div class="highlight"><pre><code>
<span style="color: #66cc66; font-weight: 600;">$</span> <span style="color: cyan; font-weight: 600;">agents</span> validation attach <span style="color: cyan;">--project</span> local/api-service local/api-repo local/run-tests

╭─ Validation Attached ──────────────────────────────────────╮
│ <span style="color: cyan; font-weight: 600;">Attachment ID:</span> 01HXM5A1B2C3D4E5F6G7H8J9K0                  │
│ <span style="color: cyan; font-weight: 600;">Validation:</span> local/run-tests                                │
│ <span style="color: #5599ff; font-weight: 600;">Mode:</span> required                                             │
│ <span style="color: #66cc66; font-weight: 600;">Resource:</span> local/api-repo                                   │
│ <span style="color: #66cc66; font-weight: 600;">Scope:</span> project local/api-service                           │
╰────────────────────────────────────────────────────────────╯

<span style="color: #66cc66; font-weight: 600;">✓ OK</span> Validation attached
</code></pre></div>

=== "Plain"

```
$ agents validation attach --project local/api-service local/api-repo local/run-tests

Validation Attached
  Attachment ID: 01HXM5A1B2C3D4E5F6G7H8J9K0
  Validation: local/run-tests
  Mode: required
  Resource: local/api-repo
  Scope: project local/api-service

[OK] Validation attached
```

=== "JSON"

```json
{
  "command": "validation attach",
  "status": "ok",
  "exit_code": 0,
  "data": {
    "validation_attached": {
      "attachment_id": "01HXM5A1B2C3D4E5F6G7H8J9K0",
      "validation": "local/run-tests",
      "mode": "required",
      "resource": "local/api-repo",
      "scope": "project local/api-service"
    }
  },
  "timing": { "started": "2026-02-09T10:20:00Z", "duration_ms": 45 },
  "messages": ["Validation attached"]
}
```

=== "YAML"

```yaml
command: validation attach
status: ok
exit_code: 0
data:
  validation_attached:
    attachment_id: 01HXM5A1B2C3D4E5F6G7H8J9K0
    validation: local/run-tests
    mode: required
    resource: local/api-repo
    scope: project local/api-service
timing:
  started: "2026-02-09T10:20:00Z"
  duration_ms: 45
messages:
  - "Validation attached"
```

=== "Rich"

<div class="highlight"><pre><code>
<span style="color: #66cc66; font-weight: 600;">$</span> <span style="color: cyan; font-weight: 600;">agents</span> validation attach local/api-repo local/lint-check

╭─ Validation Attached ──────────────────────────────────────╮
│ <span style="color: cyan; font-weight: 600;">Attachment ID:</span> 01HXM5B2C3D4E5F6G7H8J9K0L1                  │
│ <span style="color: cyan; font-weight: 600;">Validation:</span> local/lint-check                               │
│ <span style="color: #5599ff; font-weight: 600;">Mode:</span> required                                             │
│ <span style="color: #66cc66; font-weight: 600;">Resource:</span> local/api-repo                                   │
│ <span style="color: #66cc66; font-weight: 600;">Scope:</span> direct (always active)                              │
│ <span style="opacity: 0.7;">This validation will run for ALL plans/projects</span>            │
│ <span style="opacity: 0.7;">that access this resource.</span>                                 │
╰────────────────────────────────────────────────────────────╯

<span style="color: #66cc66; font-weight: 600;">✓ OK</span> Validation attached
</code></pre></div>

=== "Plain"

```
$ agents validation attach local/api-repo local/lint-check

Validation Attached
  Attachment ID: 01HXM5B2C3D4E5F6G7H8J9K0L1
  Validation: local/lint-check
  Mode: required
  Resource: local/api-repo
  Scope: direct (always active)
  This validation will run for ALL plans/projects
  that access this resource.

[OK] Validation attached
```

=== "JSON"

```json
{
  "command": "validation attach",
  "status": "ok",
  "exit_code": 0,
  "data": {
    "validation_attached": {
      "attachment_id": "01HXM5B2C3D4E5F6G7H8J9K0L1",
      "validation": "local/lint-check",
      "mode": "required",
      "resource": "local/api-repo",
      "scope": "direct (always active)",
      "note": "This validation will run for ALL plans/projects that access this resource."
    }
  },
  "timing": { "started": "2026-02-09T10:21:00Z", "duration_ms": 42 },
  "messages": ["Validation attached"]
}
```

=== "YAML"

```yaml
command: validation attach
status: ok
exit_code: 0
data:
  validation_attached:
    attachment_id: 01HXM5B2C3D4E5F6G7H8J9K0L1
    validation: local/lint-check
    mode: required
    resource: local/api-repo
    scope: "direct (always active)"
    note: "This validation will run for ALL plans/projects that access this resource."
timing:
  started: "2026-02-09T10:21:00Z"
  duration_ms: 42
messages:
  - "Validation attached"
```

Attaching with validation-specific arguments:

=== "Rich"

<div class="highlight"><pre><code>
<span style="color: #66cc66; font-weight: 600;">$</span> <span style="color: cyan; font-weight: 600;">agents</span> validation attach <span style="color: cyan;">--project</span> local/api-service local/api-repo local/run-tests <span style="color: cyan;">--coverage-threshold</span> 90

╭─ Validation Attached ──────────────────────────────────────╮
│ <span style="color: cyan; font-weight: 600;">Attachment ID:</span> 01HXM5C3D4E5F6G7H8J9K0L1M2                  │
│ <span style="color: cyan; font-weight: 600;">Validation:</span> local/run-tests                                │
│ <span style="color: #5599ff; font-weight: 600;">Mode:</span> required                                             │
│ <span style="color: #66cc66; font-weight: 600;">Resource:</span> local/api-repo                                   │
│ <span style="color: #66cc66; font-weight: 600;">Scope:</span> project local/api-service                           │
│ <span style="color: #5599ff; font-weight: 600;">Args:</span> coverage_threshold=90                                │
╰────────────────────────────────────────────────────────────╯

<span style="color: #66cc66; font-weight: 600;">✓ OK</span> Validation attached
</code></pre></div>

=== "Plain"

```
$ agents validation attach --project local/api-service local/api-repo local/run-tests --coverage-threshold 90

Validation Attached
  Attachment ID: 01HXM5C3D4E5F6G7H8J9K0L1M2
  Validation: local/run-tests
  Mode: required
  Resource: local/api-repo
  Scope: project local/api-service
  Args: coverage_threshold=90

[OK] Validation attached
```

=== "JSON"

```json
{
  "command": "validation attach",
  "status": "ok",
  "exit_code": 0,
  "data": {
    "validation_attached": {
      "attachment_id": "01HXM5C3D4E5F6G7H8J9K0L1M2",
      "validation": "local/run-tests",
      "mode": "required",
      "resource": "local/api-repo",
      "scope": "project local/api-service",
      "args": {
        "coverage_threshold": 90
      }
    }
  },
  "timing": { "started": "2026-02-09T10:22:00Z", "duration_ms": 48 },
  "messages": ["Validation attached"]
}
```

=== "YAML"

```yaml
command: validation attach
status: ok
exit_code: 0
data:
  validation_attached:
    attachment_id: 01HXM5C3D4E5F6G7H8J9K0L1M2
    validation: local/run-tests
    mode: required
    resource: local/api-repo
    scope: project local/api-service
    args:
      coverage_threshold: 90
timing:
  started: "2026-02-09T10:22:00Z"
  duration_ms: 48
messages:
  - "Validation attached"
```
agents validation detach
agents validation detach [--yes|-y] <ATTACHMENT_ID>

Purpose Detach a validation by its attachment ULID. The attachment ULID is returned by agents validation attach when the attachment is created and is also displayed by agents tool show <VALIDATION> in the "Attached To" section. Because a validation may be attached to the same resource multiple times — directly, through different projects, through different plans, or even to the same resource and scope with different arguments — the ULID uniquely identifies the specific attachment to remove. The validation itself remains registered in the Tool Registry; only the attachment association is removed.

Arguments

  • <ATTACHMENT_ID>: The ULID of the attachment to remove (returned by agents validation attach).
  • --yes, -y: Skip confirmation prompt.

Examples

=== "Rich"

<div class="highlight"><pre><code>
<span style="color: #66cc66; font-weight: 600;">$</span> <span style="color: cyan; font-weight: 600;">agents</span> validation detach 01HXM5A1B2C3D4E5F6G7H8J9K0

Detach validation local/run-tests from resource local/api-repo (scope: project local/api-service)? [y/N]: y

╭─ Validation Detached ───────────────────────────────────────╮
│ <span style="color: cyan; font-weight: 600;">Attachment ID:</span> 01HXM5A1B2C3D4E5F6G7H8J9K0                   │
│ <span style="color: cyan; font-weight: 600;">Validation:</span> local/run-tests                                 │
│ <span style="color: #5599ff; font-weight: 600;">Resource:</span> local/api-repo                                    │
│ <span style="color: #5599ff; font-weight: 600;">Scope:</span> project local/api-service                            │
╰─────────────────────────────────────────────────────────────╯

<span style="color: #66cc66; font-weight: 600;">✓ OK</span> Validation detached
</code></pre></div>

=== "Plain"

```
$ agents validation detach 01HXM5A1B2C3D4E5F6G7H8J9K0

Detach validation local/run-tests from resource local/api-repo (scope: project local/api-service)? [y/N]: y

Validation Detached
  Attachment ID: 01HXM5A1B2C3D4E5F6G7H8J9K0
  Validation: local/run-tests
  Resource: local/api-repo
  Scope: project local/api-service

[OK] Validation detached
```

=== "JSON"

```json
{
  "command": "validation detach",
  "status": "ok",
  "exit_code": 0,
  "data": {
    "validation_detached": {
      "attachment_id": "01HXM5A1B2C3D4E5F6G7H8J9K0",
      "validation": "local/run-tests",
      "resource": "local/api-repo",
      "scope": "project local/api-service"
    },
    "confirmation": "Detach validation local/run-tests from resource local/api-repo (scope: project local/api-service)? [y/N]: y"
  },
  "timing": { "started": "2026-02-09T10:25:00Z", "duration_ms": 1200 },
  "messages": ["Validation detached"]
}
```

=== "YAML"

```yaml
command: validation detach
status: ok
exit_code: 0
data:
  validation_detached:
    attachment_id: 01HXM5A1B2C3D4E5F6G7H8J9K0
    validation: local/run-tests
    resource: local/api-repo
    scope: project local/api-service
  confirmation: "Detach validation local/run-tests from resource local/api-repo (scope: project local/api-service)? [y/N]: y"
timing:
  started: "2026-02-09T10:25:00Z"
  duration_ms: 1200
messages:
  - "Validation detached"
```

agents resource type

Purpose Manage resource type definitions. Resource types are schema-level definitions that constrain categories of resources — they define what CLI arguments agents resource add <type> accepts, whether instances are physical or virtual, allowed parent/child types, auto-discovery behavior, sandbox strategy, and handler implementation. Built-in types (git-checkout, git, fs-mount, fs-directory, and their child types) are hardcoded. Custom types are defined in YAML configuration files and extend the system with new agents resource add subcommands.

agents resource type add
agents resource type add --config|-c <FILE> [--update]

Purpose Register a new custom resource type. The resource type is fully defined by the YAML configuration file specified with --config.

Arguments

  • --config/-c FILE: Path to the resource type YAML configuration file (required). The file must fully define the resource type, including the name field which determines the type's registered name.
  • --update: If the type name already exists, overwrite it. Without this flag, adding a duplicate name fails with an error.

Examples

=== "Rich"

<div class="highlight"><pre><code>
<span style="color: #66cc66; font-weight: 600;">$</span> <span style="color: cyan; font-weight: 600;">agents</span> resource type add <span style="color: cyan;">--config</span> ./resource-types/svn.yaml

╭─ Resource Type ────────────────────╮
│ <span style="color: cyan; font-weight: 600;">Name:</span> local/svn                    │
│ <span style="color: #5599ff; font-weight: 600;">Physical/Virtual:</span> physical         │
│ <span style="color: #5599ff; font-weight: 600;">User Addable:</span> yes                  │
│ <span style="color: #66cc66; font-weight: 600;">Registered:</span> 2026-02-09 10:15       │
╰────────────────────────────────────╯

╭─ CLI Arguments ────────────────────────╮
│ <span style="color: cyan; font-weight: 600;">Argument</span>      <span style="color: cyan; font-weight: 600;">Required</span>  <span style="color: cyan; font-weight: 600;">Description</span>    │
│ <span style="opacity: 0.7;">────────────  ────────  ─────────────</span>  │
│ <span style="color: cyan;">--url</span>         yes       Repository URL │
│ <span style="color: cyan;">--checkout</span>    no        Local checkout │
╰────────────────────────────────────────╯

╭─ Child Types ──────────────────────────╮
│ <span style="color: #5599ff; font-weight: 600;">Auto-discover:</span> svn-revision, svn-file  │
│ <span style="color: #5599ff; font-weight: 600;">Manual link:</span> fs-mount                  │
╰────────────────────────────────────────╯

╭─ Sandbox ──────────────────────╮
│ <span style="color: #5599ff; font-weight: 600;">Strategy:</span> copy_on_write        │
│ <span style="color: #5599ff; font-weight: 600;">Handler:</span> SVNHandler (custom)   │
╰────────────────────────────────╯

<span style="color: #66cc66; font-weight: 600;">✓ OK</span> Resource type registered
<span style="color: cyan; font-weight: 600;"></span> New subcommand available: agents resource add local/svn
</code></pre></div>

=== "Plain"

```
$ agents resource type add --config ./resource-types/svn.yaml

Resource Type
  Name: local/svn
  Physical/Virtual: physical
  User Addable: yes
  Registered: 2026-02-09 10:15

CLI Arguments
Argument      Required  Description
------------  --------  -------------
--url         yes       Repository URL
--checkout    no        Local checkout

Child Types
  Auto-discover: svn-revision, svn-file
  Manual link: fs-mount

Sandbox
  Strategy: copy_on_write
  Handler: SVNHandler (custom)

[OK] Resource type registered
Info: New subcommand available: agents resource add local/svn
```

=== "JSON"

```json
{
  "command": "resource type add",
  "status": "ok",
  "exit_code": 0,
  "data": {
    "name": "local/svn",
    "physical_virtual": "physical",
    "user_addable": true,
    "registered": "2026-02-09T10:15:00Z",
    "cli_arguments": [
      { "argument": "--url", "required": true, "description": "Repository URL" },
      { "argument": "--checkout", "required": false, "description": "Local checkout" }
    ],
    "child_types": {
      "auto_discover": ["svn-revision", "svn-file"],
      "manual_link": ["fs-mount"]
    },
    "sandbox": {
      "strategy": "copy_on_write",
      "handler": "SVNHandler (custom)"
    }
  },
  "timing": { "started": "2026-02-09T10:15:00Z", "duration_ms": 85 },
  "messages": ["Resource type registered", "New subcommand available: agents resource add local/svn"]
}
```

=== "YAML"

```yaml
command: resource type add
status: ok
exit_code: 0
data:
  name: local/svn
  physical_virtual: physical
  user_addable: true
  registered: "2026-02-09T10:15:00Z"
  cli_arguments:
    - argument: "--url"
      required: true
      description: Repository URL
    - argument: "--checkout"
      required: false
      description: Local checkout
  child_types:
    auto_discover:
      - svn-revision
      - svn-file
    manual_link:
      - fs-mount
  sandbox:
    strategy: copy_on_write
    handler: "SVNHandler (custom)"
timing:
  started: "2026-02-09T10:15:00Z"
  duration_ms: 85
messages:
  - "Resource type registered"
  - "New subcommand available: agents resource add local/svn"
```
agents resource type remove
agents resource type remove [--yes|-y] <NAME>

!!! warning "Destructive Operation" Remove a custom resource type. ==Built-in types cannot be removed.== Fails if any registered resources currently use this type — remove those resources first.

Arguments

  • <NAME>: Resource type name.
  • --yes: Skip confirmation prompt.

Examples

=== "Rich"

<div class="highlight"><pre><code>
<span style="color: #66cc66; font-weight: 600;">$</span> <span style="color: cyan; font-weight: 600;">agents</span> resource type remove local/svn

Remove resource type local/svn? [y/N]: y

╭─ Resource Type Removed ───────╮
│ <span style="color: cyan; font-weight: 600;">Name:</span> local/svn               │
│ <span style="color: yellow; font-weight: 600;">Resources Using:</span> 0            │
│ <span style="color: #5599ff; font-weight: 600;">Subcommand Removed:</span> yes       │
╰───────────────────────────────╯

<span style="color: #66cc66; font-weight: 600;">✓ OK</span> Resource type removed
</code></pre></div>

=== "Plain"

```
$ agents resource type remove local/svn

Remove resource type local/svn? [y/N]: y

Resource Type Removed
  Name: local/svn
  Resources Using: 0
  Subcommand Removed: yes

[OK] Resource type removed
```

=== "JSON"

```json
{
  "command": "resource type remove",
  "status": "ok",
  "exit_code": 0,
  "data": {
    "name": "local/svn",
    "resources_using": 0,
    "subcommand_removed": true
  },
  "timing": { "started": "2026-02-09T10:16:00Z", "duration_ms": 60 },
  "messages": ["Resource type removed"]
}
```

=== "YAML"

```yaml
command: resource type remove
status: ok
exit_code: 0
data:
  name: local/svn
  resources_using: 0
  subcommand_removed: true
timing:
  started: "2026-02-09T10:16:00Z"
  duration_ms: 60
messages:
  - "Resource type removed"
```
agents resource type list
agents resource type list [<REGEX>]

Purpose List all registered resource types (built-in and custom).

Arguments

None (use the global --format option to control output format).

Examples

=== "Rich"

<div class="highlight"><pre><code>
<span style="color: #66cc66; font-weight: 600;">$</span> <span style="color: cyan; font-weight: 600;">agents</span> resource type list

╭─ Resource Types ──────────────────────────────────────────────────────────────────────────────────╮
│ <span style="color: cyan; font-weight: 600;">Name</span>                 <span style="color: cyan; font-weight: 600;">Source</span>    <span style="color: cyan; font-weight: 600;">Phys/Virt</span>  <span style="color: cyan; font-weight: 600;">Addable</span>  <span style="color: cyan; font-weight: 600;">Auto-children</span>                                  │
│ <span style="opacity: 0.7;">───────────────────  ────────  ─────────  ───────  ──────────────────────────────────────</span>         │
│ git-checkout        built-in  physical   yes      git, fs-directory                               │
│ git                 built-in  physical   yes      git-remote, git-branch, git-tag,                │
│                                                    git-commit, git-stash, git-submodule           │
│ git-remote          built-in  physical   no       (none)                                          │
│ git-branch          built-in  physical   no       git-commit                                      │
│ git-tag             built-in  physical   no       (none)                                          │
│ git-commit          built-in  physical   no       git-tree                                        │
│ git-tree            built-in  physical   no       git-tree-entry                                  │
│ git-tree-entry      built-in  physical   no       (none)                                          │
│ git-stash           built-in  physical   no       (none)                                          │
│ git-submodule       built-in  physical   no       (none)                                          │
│ fs-mount            built-in  physical   yes      fs-directory                                    │
│ fs-directory        built-in  physical   yes      fs-file, fs-directory,                          │
│                                                    fs-symlink, fs-hardlink                        │
│ fs-file             built-in  physical   no       (none)                                          │
│ fs-symlink          built-in  physical   no       (none)                                          │
│ fs-hardlink         built-in  physical   no       (none)                                          │
│ file                built-in  virtual    no       (none)                                          │
│ directory           built-in  virtual    no       (none)                                          │
│ symlink             built-in  virtual    no       (none)                                          │
│ commit              built-in  virtual    no       (none)                                          │
│ branch              built-in  virtual    no       (none)                                          │
│ tag                 built-in  virtual    no       (none)                                          │
│ remote              built-in  virtual    no       (none)                                          │
│ submodule           built-in  virtual    no       (none)                                          │
│ tree                built-in  virtual    no       (none)                                          │
│ local/svn           custom    physical   yes      svn-revision, svn-file                          │
╰───────────────────────────────────────────────────────────────────────────────────────────────────╯

╭─ Summary ─────────────────╮
│ <span style="color: #5599ff; font-weight: 600;">Built-in:</span> 24              │
│ <span style="color: #5599ff; font-weight: 600;">Custom:</span> 1                 │
│ <span style="color: #66cc66; font-weight: 600;">User Addable:</span> 4           │
╰───────────────────────────╯

<span style="color: #66cc66; font-weight: 600;">✓ OK</span> 25 resource types listed
</code></pre></div>

=== "Plain"

```
$ agents resource type list

Resource Types
-------------------
Name                 Source    Phys/Virt  Addable  Auto-children
-------------------  --------  ---------  -------  --------------------------------------
git-checkout         built-in  physical   yes      git, fs-directory
git                  built-in  physical   yes      git-remote, git-branch, git-tag, git-commit, git-stash, git-submodule
git-remote           built-in  physical   no       (none)
git-branch           built-in  physical   no       git-commit
git-tag              built-in  physical   no       (none)
git-commit           built-in  physical   no       git-tree
git-tree             built-in  physical   no       git-tree-entry
git-tree-entry       built-in  physical   no       (none)
git-stash            built-in  physical   no       (none)
git-submodule        built-in  physical   no       (none)
fs-mount             built-in  physical   yes      fs-directory
fs-directory         built-in  physical   yes      fs-file, fs-directory, fs-symlink, fs-hardlink
fs-file              built-in  physical   no       (none)
fs-symlink           built-in  physical   no       (none)
fs-hardlink          built-in  physical   no       (none)
file                 built-in  virtual    no       (none)
directory            built-in  virtual    no       (none)
symlink              built-in  virtual    no       (none)
commit               built-in  virtual    no       (none)
branch               built-in  virtual    no       (none)
tag                  built-in  virtual    no       (none)
remote               built-in  virtual    no       (none)
submodule            built-in  virtual    no       (none)
tree                 built-in  virtual    no       (none)
local/svn            custom    physical   yes      svn-revision, svn-file

Summary
  Built-in: 24
  Custom: 1
  User Addable: 4

[OK] 25 resource types listed
```

=== "JSON"

```json
{
  "command": "resource type list",
  "status": "ok",
  "exit_code": 0,
  "data": {
    "resource_types": [
      { "name": "git-checkout", "source": "built-in", "physical_virtual": "physical", "addable": true, "auto_children": ["git", "fs-directory"] },
      { "name": "git", "source": "built-in", "physical_virtual": "physical", "addable": true, "auto_children": ["git-remote", "git-branch", "git-tag", "git-commit", "git-stash", "git-submodule"] },
      { "name": "git-remote", "source": "built-in", "physical_virtual": "physical", "addable": false, "auto_children": [] },
      { "name": "git-branch", "source": "built-in", "physical_virtual": "physical", "addable": false, "auto_children": ["git-commit"] },
      { "name": "git-tag", "source": "built-in", "physical_virtual": "physical", "addable": false, "auto_children": [] },
      { "name": "git-commit", "source": "built-in", "physical_virtual": "physical", "addable": false, "auto_children": ["git-tree"] },
      { "name": "git-tree", "source": "built-in", "physical_virtual": "physical", "addable": false, "auto_children": ["git-tree-entry"] },
      { "name": "git-tree-entry", "source": "built-in", "physical_virtual": "physical", "addable": false, "auto_children": [] },
      { "name": "git-stash", "source": "built-in", "physical_virtual": "physical", "addable": false, "auto_children": [] },
      { "name": "git-submodule", "source": "built-in", "physical_virtual": "physical", "addable": false, "auto_children": [] },
      { "name": "fs-mount", "source": "built-in", "physical_virtual": "physical", "addable": true, "auto_children": ["fs-directory"] },
      { "name": "fs-directory", "source": "built-in", "physical_virtual": "physical", "addable": true, "auto_children": ["fs-file", "fs-directory", "fs-symlink", "fs-hardlink"] },
      { "name": "fs-file", "source": "built-in", "physical_virtual": "physical", "addable": false, "auto_children": [] },
      { "name": "fs-symlink", "source": "built-in", "physical_virtual": "physical", "addable": false, "auto_children": [] },
      { "name": "fs-hardlink", "source": "built-in", "physical_virtual": "physical", "addable": false, "auto_children": [] },
      { "name": "file", "source": "built-in", "physical_virtual": "virtual", "addable": false, "auto_children": [] },
      { "name": "directory", "source": "built-in", "physical_virtual": "virtual", "addable": false, "auto_children": [] },
      { "name": "symlink", "source": "built-in", "physical_virtual": "virtual", "addable": false, "auto_children": [] },
      { "name": "commit", "source": "built-in", "physical_virtual": "virtual", "addable": false, "auto_children": [] },
      { "name": "branch", "source": "built-in", "physical_virtual": "virtual", "addable": false, "auto_children": [] },
      { "name": "tag", "source": "built-in", "physical_virtual": "virtual", "addable": false, "auto_children": [] },
      { "name": "remote", "source": "built-in", "physical_virtual": "virtual", "addable": false, "auto_children": [] },
      { "name": "submodule", "source": "built-in", "physical_virtual": "virtual", "addable": false, "auto_children": [] },
      { "name": "tree", "source": "built-in", "physical_virtual": "virtual", "addable": false, "auto_children": [] },
      { "name": "local/svn", "source": "custom", "physical_virtual": "physical", "addable": true, "auto_children": ["svn-revision", "svn-file"] }
    ],
    "summary": {
      "built_in": 24,
      "custom": 1,
      "user_addable": 4
    }
  },
  "timing": { "started": "2026-02-09T10:16:00Z", "duration_ms": 45 },
  "messages": ["25 resource types listed"]
}
```

=== "YAML"

```yaml
command: resource type list
status: ok
exit_code: 0
data:
  resource_types:
    - name: git-checkout
      source: built-in
      physical_virtual: physical
      addable: true
      auto_children: [git, fs-directory]
    - name: git
      source: built-in
      physical_virtual: physical
      addable: true
      auto_children: [git-remote, git-branch, git-tag, git-commit, git-stash, git-submodule]
    - name: git-remote
      source: built-in
      physical_virtual: physical
      addable: false
      auto_children: []
    - name: git-branch
      source: built-in
      physical_virtual: physical
      addable: false
      auto_children: [git-commit]
    - name: git-tag
      source: built-in
      physical_virtual: physical
      addable: false
      auto_children: []
    - name: git-commit
      source: built-in
      physical_virtual: physical
      addable: false
      auto_children: [git-tree]
    - name: git-tree
      source: built-in
      physical_virtual: physical
      addable: false
      auto_children: [git-tree-entry]
    - name: git-tree-entry
      source: built-in
      physical_virtual: physical
      addable: false
      auto_children: []
    - name: git-stash
      source: built-in
      physical_virtual: physical
      addable: false
      auto_children: []
    - name: git-submodule
      source: built-in
      physical_virtual: physical
      addable: false
      auto_children: []
    - name: fs-mount
      source: built-in
      physical_virtual: physical
      addable: true
      auto_children: [fs-directory]
    - name: fs-directory
      source: built-in
      physical_virtual: physical
      addable: true
      auto_children: [fs-file, fs-directory, fs-symlink, fs-hardlink]
    - name: fs-file
      source: built-in
      physical_virtual: physical
      addable: false
      auto_children: []
    - name: fs-symlink
      source: built-in
      physical_virtual: physical
      addable: false
      auto_children: []
    - name: fs-hardlink
      source: built-in
      physical_virtual: physical
      addable: false
      auto_children: []
    - name: file
      source: built-in
      physical_virtual: virtual
      addable: false
      auto_children: []
    - name: directory
      source: built-in
      physical_virtual: virtual
      addable: false
      auto_children: []
    - name: symlink
      source: built-in
      physical_virtual: virtual
      addable: false
      auto_children: []
    - name: commit
      source: built-in
      physical_virtual: virtual
      addable: false
      auto_children: []
    - name: branch
      source: built-in
      physical_virtual: virtual
      addable: false
      auto_children: []
    - name: tag
      source: built-in
      physical_virtual: virtual
      addable: false
      auto_children: []
    - name: remote
      source: built-in
      physical_virtual: virtual
      addable: false
      auto_children: []
    - name: submodule
      source: built-in
      physical_virtual: virtual
      addable: false
      auto_children: []
    - name: tree
      source: built-in
      physical_virtual: virtual
      addable: false
      auto_children: []
    - name: local/svn
      source: custom
      physical_virtual: physical
      addable: true
      auto_children: [svn-revision, svn-file]
  summary:
    built_in: 24
    custom: 1
    user_addable: 4
timing:
  started: "2026-02-09T10:16:00Z"
  duration_ms: 45
messages:
  - "25 resource types listed"
```
agents resource type show
agents resource type show <NAME>

Purpose Show detailed information about a resource type, including its full schema.

Arguments

  • <NAME>: Resource type name.

Examples

=== "Rich"

<div class="highlight"><pre><code>
<span style="color: #66cc66; font-weight: 600;">$</span> <span style="color: cyan; font-weight: 600;">agents</span> resource type show git-checkout

╭─ Resource Type ──────────────────────────────────────────────────────╮
│ <span style="color: cyan; font-weight: 600;">Name:</span> git-checkout                                                   │
│ <span style="color: #5599ff; font-weight: 600;">Source:</span> built-in                                                     │
│ <span style="color: #5599ff; font-weight: 600;">Description:</span> A locally checked-out git repository with worktree      │
│ <span style="color: #5599ff; font-weight: 600;">Physical/Virtual:</span> physical                                           │
│ <span style="color: #66cc66; font-weight: 600;">User Addable:</span> yes                                                    │
╰──────────────────────────────────────────────────────────────────────╯

╭─ CLI Arguments (agents resource add git-checkout) ─────────────────╮
│ <span style="color: cyan; font-weight: 600;">Argument</span>    <span style="color: cyan; font-weight: 600;">Required</span>  <span style="color: cyan; font-weight: 600;">Type</span>    <span style="color: cyan; font-weight: 600;">Description</span>                          │
│ <span style="opacity: 0.7;">──────────  ────────  ──────  ──────────────────────────────</span>       │
│ <span style="color: cyan;">--path</span>      yes       path    Local checkout directory             │
│ <span style="color: cyan;">--branch</span>    no        string  Default branch (default: main)       │
╰────────────────────────────────────────────────────────────────────╯

╭─ Parent Types ───────────────╮
│ Allowed: (any, top-level OK) │
╰──────────────────────────────╯

╭─ Child Types ────────────────────────────────────────────────────╮
│ <span style="color: cyan; font-weight: 600;">Type</span>           <span style="color: cyan; font-weight: 600;">Auto</span>  <span style="color: cyan; font-weight: 600;">Manual Link</span>  <span style="color: cyan; font-weight: 600;">Description</span>                    │
│ <span style="opacity: 0.7;">─────────────  ────  ───────────  ──────────────────────────</span>     │
│ git            yes   no           Repo object DB and history     │
│ fs-directory   yes   no           Worktree root directory        │
╰──────────────────────────────────────────────────────────────────╯

╭─ Sandbox ──────────────────────╮
│ <span style="color: #5599ff; font-weight: 600;">Strategy:</span> git_worktree         │
│ <span style="color: #5599ff; font-weight: 600;">Checkpointable:</span> yes            │
│ <span style="color: #5599ff; font-weight: 600;">Handler:</span> GitCheckoutHandler    │
╰────────────────────────────────╯

<span style="color: #66cc66; font-weight: 600;">✓ OK</span> Resource type details loaded
</code></pre></div>

=== "Plain"

```
$ agents resource type show git-checkout

Resource Type
  Name: git-checkout
  Source: built-in
  Description: A locally checked-out git repository with worktree
  Physical/Virtual: physical
  User Addable: yes

CLI Arguments (agents resource add git-checkout)
  Argument    Required  Type    Description
  ----------  --------  ------  ------------------------------
  --path      yes       path    Local checkout directory
  --branch    no        string  Default branch (default: main)

Parent Types
  Allowed: (any, top-level OK)

Child Types
  Type           Auto  Manual Link  Description
  -------------  ----  -----------  --------------------------
  git            yes   no           Repo object DB and history
  fs-directory   yes   no           Worktree root directory

Sandbox
  Strategy: git_worktree
  Checkpointable: yes
  Handler: GitCheckoutHandler

[OK] Resource type details loaded
```

=== "JSON"

```json
{
  "command": "resource type show",
  "status": "ok",
  "exit_code": 0,
  "data": {
    "name": "git-checkout",
    "source": "built-in",
    "description": "A locally checked-out git repository with worktree",
    "physical_virtual": "physical",
    "user_addable": true,
    "cli_arguments": [
      { "argument": "--path", "required": true, "type": "path", "description": "Local checkout directory" },
      { "argument": "--branch", "required": false, "type": "string", "description": "Default branch (default: main)" }
    ],
    "parent_types": {
      "allowed": "any",
      "top_level": true
    },
    "child_types": [
      { "type": "git", "auto": true, "manual_link": false, "description": "Repo object DB and history" },
      { "type": "fs-directory", "auto": true, "manual_link": false, "description": "Worktree root directory" }
    ],
    "sandbox": {
      "strategy": "git_worktree",
      "checkpointable": true,
      "handler": "GitCheckoutHandler"
    }
  },
  "timing": { "started": "2026-02-09T10:15:00Z", "duration_ms": 42 },
  "messages": ["Resource type details loaded"]
}
```

=== "YAML"

```yaml
command: resource type show
status: ok
exit_code: 0
data:
  name: git-checkout
  source: built-in
  description: "A locally checked-out git repository with worktree"
  physical_virtual: physical
  user_addable: true
  cli_arguments:
    - argument: "--path"
      required: true
      type: path
      description: "Local checkout directory"
    - argument: "--branch"
      required: false
      type: string
      description: "Default branch (default: main)"
  parent_types:
    allowed: any
    top_level: true
  child_types:
    - type: git
      auto: true
      manual_link: false
      description: "Repo object DB and history"
    - type: fs-directory
      auto: true
      manual_link: false
      description: "Worktree root directory"
  sandbox:
    strategy: git_worktree
    checkpointable: true
    handler: GitCheckoutHandler
timing:
  started: "2026-02-09T10:15:00Z"
  duration_ms: 42
messages:
  - "Resource type details loaded"
```

agents resource

!!! info "Purpose" Manage independently registered ==resources==. Resources represent anything that can be read, written, or queried — git repositories, filesystems, databases, APIs, and more. Resources are registered independently of projects and linked to one or more projects via agents project link-resource. Resources form a ==directed acyclic graph (DAG)== with parent/child relationships, and child resources are often auto-discovered when a parent resource is registered.

??? example "Supported Resource Types" | Type | Physical | Description | | :--- | :------: | :---------- | | git-checkout | Yes | Local git working directory | | git | Yes | Bare git repository | | fs-mount | Yes | Filesystem mount point | | fs-directory | Virtual | Directory within a mount | | fs-file | Virtual | Individual file reference | | container-instance | Yes | Container instance (ADR-039) | | devcontainer-instance | Yes | Devcontainer instance (ADR-043) | | Custom types | Varies | Registered via agents resource type add |

agents resource add
agents resource add [(--description|-d) <DESC>] [--update] <TYPE> <NAME> [type-specific-flags...]

Purpose Register a new resource. Every resource receives a system-assigned ULID. User-added resources also require a namespaced name ([[server:]namespace/]name) specified at creation time. The add command uses type-specific subcommands — each registered resource type (built-in or custom) provides its own subcommand with type-appropriate arguments. On success, the command returns both the name and the ULID assigned to the newly created resource.

Arguments

  • <TYPE>: Resource type name (e.g., git-checkout, git, fs-mount, fs-directory, local/svn). Determines type-specific flags.
  • <NAME>: Namespaced resource name (positional, required). Must be globally unique following the standard [[server:]namespace/]name format.
  • --description/-d TEXT: Optional resource description.

Type-specific flags depend on the resource type. See agents resource type show <TYPE> for available arguments.

Container-specific flags (for container-instance and devcontainer-instance types):

  • --image IMAGE_REF: Container image reference (e.g., ubuntu:latest, python:3.12-slim). Required for container-instance when --container-id is not provided. Not required for devcontainer-instance (image is derived from devcontainer.json).
  • --mount RESOURCE_OR_PATH:CONTAINER_PATH: Mount a resource or host path into the container (repeatable). Accepts either a resource reference (local/api-repo:/workspace) or a raw host path (/home/user/projects/api:/workspace). See ADR-043 §3.2.
  • --clone-into REPO_URL:CONTAINER_PATH: Clone a remote repository into the container at the specified path. The clone happens lazily on first container start.

The command returns the name and ULID of the newly created resource. Auto-discovered child resources are created automatically with ULIDs only (no names).

Examples

=== "Rich"

<div class="highlight"><pre><code>
<span style="color: #66cc66; font-weight: 600;">$</span> <span style="color: cyan; font-weight: 600;">agents</span> resource add git-checkout local/api-repo <span style="color: cyan;">--path</span> /home/user/projects/api-service <span style="color: cyan;">--branch</span> main

╭─ Resource ──────────────────────────────────────╮
│ <span style="color: cyan; font-weight: 600;">Name:</span> local/api-repo                            │
│ <span style="color: cyan; font-weight: 600;">ID:</span> 01HXR1A1B2C3D4E5F6G7H8J9K0                  │
│ <span style="color: #5599ff; font-weight: 600;">Type:</span> git-checkout                              │
│ <span style="color: #5599ff; font-weight: 600;">Physical/Virtual:</span> physical                      │
│ <span style="color: #66cc66; font-weight: 600;">Path:</span> /home/user/projects/api-service           │
│ <span style="color: #5599ff; font-weight: 600;">Branch:</span> main                                    │
│ <span style="color: #66cc66; font-weight: 600;">Created:</span> 2026-02-09 10:20                       │
╰─────────────────────────────────────────────────╯

╭─ Auto-discovered Children ─────────────────────────────────────────╮
│ <span style="color: cyan; font-weight: 600;">ID</span>               <span style="color: cyan; font-weight: 600;">Type</span>            <span style="color: cyan; font-weight: 600;">Status</span>                            │
│ <span style="opacity: 0.7;">───────────────  ──────────────  ─────────────────</span>                 │
│ 01HXR1A1B2C3…   git             <span style="color: #66cc66; font-weight: 600;">created</span>                            │
│ 01HXR1A1B2C4…   git-remote      <span style="color: #66cc66; font-weight: 600;">created</span>                            │
│ 01HXR1A1B2C5…   git-branch      <span style="color: #66cc66; font-weight: 600;">created</span>                            │
│ 01HXR1A1B2C6…   git-branch      <span style="color: #66cc66; font-weight: 600;">created</span>                            │
│ 01HXR1A1B2C7…   fs-directory    <span style="color: #66cc66; font-weight: 600;">created</span>                            │
│   + 47 git-commit resources                                        │
│   + 312 git-tree-entry resources                                   │
│   + 3 fs-directory + 28 fs-file                                    │
╰────────────────────────────────────────────────────────────────────╯

╭─ Capabilities ─────────────────╮
│ <span style="color: #66cc66; font-weight: 600;">Readable:</span> yes                  │
│ <span style="color: yellow; font-weight: 600;">Writable:</span> yes                  │
│ <span style="color: #66cc66; font-weight: 600;">Sandboxable:</span> yes               │
│ <span style="color: #66cc66; font-weight: 600;">Checkpointable:</span> yes            │
│ <span style="color: #5599ff; font-weight: 600;">Sandbox Strategy:</span> git_worktree │
╰────────────────────────────────╯

<span style="color: #66cc66; font-weight: 600;">✓ OK</span> Resource registered (395 child resources discovered)
</code></pre></div>

<div class="highlight"><pre><code>
<span style="color: #66cc66; font-weight: 600;">$</span> <span style="color: cyan; font-weight: 600;">agents</span> resource add fs-mount local/docs <span style="color: cyan;">--mount-path</span> /docs/api-reference

╭─ Resource ──────────────────────────────────────╮
│ <span style="color: cyan; font-weight: 600;">Name:</span> local/docs                                │
│ <span style="color: cyan; font-weight: 600;">ID:</span> 01HXR2B3C4D5E6F7G8H9J0K1L2                  │
│ <span style="color: #5599ff; font-weight: 600;">Type:</span> fs-mount                                  │
│ <span style="color: #5599ff; font-weight: 600;">Physical/Virtual:</span> physical                      │
│ <span style="color: #66cc66; font-weight: 600;">Mount Path:</span> /docs/api-reference                 │
│ <span style="color: #66cc66; font-weight: 600;">Created:</span> 2026-02-09 10:22                       │
╰─────────────────────────────────────────────────╯

╭─ Auto-discovered Children ────────────────────╮
│   + 1 fs-directory (root)                     │
│   + 3 fs-directory resources                  │
│   + 28 fs-file resources                      │
╰───────────────────────────────────────────────╯

╭─ Capabilities ──────────────────────╮
│ <span style="color: #66cc66; font-weight: 600;">Readable:</span> yes                       │
│ <span style="color: yellow; font-weight: 600;">Writable:</span> yes                       │
│ <span style="color: #66cc66; font-weight: 600;">Sandboxable:</span> yes                    │
│ <span style="color: #66cc66; font-weight: 600;">Checkpointable:</span> yes                 │
│ <span style="color: #5599ff; font-weight: 600;">Sandbox Strategy:</span> copy_on_write     │
╰─────────────────────────────────────╯

<span style="color: #66cc66; font-weight: 600;">✓ OK</span> Resource registered (31 child resources discovered)
</code></pre></div>

<div class="highlight"><pre><code>
<span style="color: #66cc66; font-weight: 600;">$</span> <span style="color: cyan; font-weight: 600;">agents</span> resource add container-instance local/api-dev <span style="color: cyan;">--mount</span> local/api-repo:/workspace <span style="color: cyan;">--mount</span> /home/user/.config/nvim:/home/dev/.config/nvim

╭─ Resource ──────────────────────────────────────╮
│ <span style="color: cyan; font-weight: 600;">Name:</span> local/api-dev                             │
│ <span style="color: cyan; font-weight: 600;">ID:</span> 01HXR3C4D5E6F7G8H9J0K1L2M3                  │
│ <span style="color: #5599ff; font-weight: 600;">Type:</span> container-instance                        │
│ <span style="color: #5599ff; font-weight: 600;">Physical/Virtual:</span> physical                      │
│ <span style="color: #66cc66; font-weight: 600;">State:</span> created (not started)                    │
│ <span style="color: #66cc66; font-weight: 600;">Created:</span> 2026-02-09 10:25                       │
╰─────────────────────────────────────────────────╯

╭─ Mounts ──────────────────────────────────────────────────────────╮
│ <span style="color: cyan; font-weight: 600;">Source</span>                            <span style="color: cyan; font-weight: 600;">Container Path</span>   <span style="color: cyan; font-weight: 600;">Kind</span>           │
│ <span style="opacity: 0.7;">────────────────────────────────  ───────────────  ───────────</span>    │
│ local/api-repo                    /workspace       resource-ref   │
│ /home/user/.config/nvim           /home/dev/…      host-path      │
╰───────────────────────────────────────────────────────────────────╯

<span style="color: #66cc66; font-weight: 600;">✓ OK</span> Container resource registered (container will start on first access)
</code></pre></div>

<div class="highlight"><pre><code>
<span style="color: #66cc66; font-weight: 600;">$</span> <span style="color: cyan; font-weight: 600;">agents</span> resource add container-instance cloud/ci-runner <span style="color: cyan;">--clone-into</span> https://github.com/acme/api.git:/workspace

╭─ Resource ──────────────────────────────────────╮
│ <span style="color: cyan; font-weight: 600;">Name:</span> cloud/ci-runner                           │
│ <span style="color: cyan; font-weight: 600;">ID:</span> 01HXR4D5E6F7G8H9J0K1L2M3N4                  │
│ <span style="color: #5599ff; font-weight: 600;">Type:</span> container-instance                        │
│ <span style="color: #5599ff; font-weight: 600;">Physical/Virtual:</span> physical                      │
│ <span style="color: #66cc66; font-weight: 600;">State:</span> created (not started)                    │
│ <span style="color: #66cc66; font-weight: 600;">Clone:</span> https://github.com/acme/api.git          │
│ <span style="color: #66cc66; font-weight: 600;">Clone Target:</span> /workspace                        │
│ <span style="color: #66cc66; font-weight: 600;">Created:</span> 2026-02-09 10:27                       │
╰─────────────────────────────────────────────────╯

<span style="color: #66cc66; font-weight: 600;">✓ OK</span> Container resource registered (repo will be cloned on first start)
</code></pre></div>

<div class="highlight"><pre><code>
<span style="color: #66cc66; font-weight: 600;">$</span> <span style="color: cyan; font-weight: 600;">agents</span> resource add git-checkout local/webapp <span style="color: cyan;">--path</span> /home/user/projects/webapp

╭─ Resource ──────────────────────────────────────────╮
│ <span style="color: cyan; font-weight: 600;">Name:</span> local/webapp                                  │
│ <span style="color: cyan; font-weight: 600;">ID:</span> 01HXR5E6F7G8H9J0K1L2M3N4O5                      │
│ <span style="color: #5599ff; font-weight: 600;">Type:</span> git-checkout                                  │
│ <span style="color: #5599ff; font-weight: 600;">Physical/Virtual:</span> physical                          │
│ <span style="color: #66cc66; font-weight: 600;">Path:</span> /home/user/projects/webapp                    │
│ <span style="color: #66cc66; font-weight: 600;">Branch:</span> main                                        │
│ <span style="color: #66cc66; font-weight: 600;">Created:</span> 2026-02-09 10:30                           │
╰─────────────────────────────────────────────────────╯

╭─ Auto-discovered Children ─────────────────────────────────────────╮
│ <span style="color: cyan; font-weight: 600;">ID</span>               <span style="color: cyan; font-weight: 600;">Type</span>                    <span style="color: cyan; font-weight: 600;">Status</span>                    │
│ <span style="opacity: 0.7;">───────────────  ──────────────────────  ───────────────────</span>       │
│ 01HXR5E6F7G9…   git                     <span style="color: #66cc66; font-weight: 600;">created</span>                    │
│ 01HXR5E6F7GA…   <span style="color: yellow; font-weight: 600;">devcontainer-instance</span>   <span style="color: yellow; font-weight: 600;">detected (not built)</span>       │
│ 01HXR5E6F7GB…   fs-directory            <span style="color: #66cc66; font-weight: 600;">created</span>                    │
│   + 52 git-commit resources                                        │
│   + 189 git-tree-entry resources                                   │
╰────────────────────────────────────────────────────────────────────╯

<span style="color: yellow; font-weight: 600;">⚠ Devcontainer detected</span> at .devcontainer/devcontainer.json
  Container will be built lazily on first access.
  Use <span style="color: cyan;">agents resource show</span> 01HXR5E6F7GA… to inspect.

<span style="color: #66cc66; font-weight: 600;">✓ OK</span> Resource registered (245 child resources discovered)
</code></pre></div>

=== "Plain"

```
$ agents resource add git-checkout local/api-repo --path /home/user/projects/api-service --branch main

Resource
  Name: local/api-repo
  ID: 01HXR1A1B2C3D4E5F6G7H8J9K0
  Type: git-checkout
  Physical/Virtual: physical
  Path: /home/user/projects/api-service
  Branch: main
  Created: 2026-02-09 10:20

Auto-discovered Children
ID               Type            Status
---------------  --------------  -----------------
01HXR1A1B2C3..   git             created
01HXR1A1B2C4..   git-remote      created
01HXR1A1B2C5..   git-branch      created
01HXR1A1B2C6..   git-branch      created
01HXR1A1B2C7..   fs-directory    created
  + 47 git-commit resources
  + 312 git-tree-entry resources
  + 3 fs-directory + 28 fs-file

Capabilities
  Readable: yes
  Writable: yes
  Sandboxable: yes
  Checkpointable: yes
  Sandbox Strategy: git_worktree

[OK] Resource registered (395 child resources discovered)

$ agents resource add fs-mount local/docs --mount-path /docs/api-reference

Resource
  Name: local/docs
  ID: 01HXR2B3C4D5E6F7G8H9J0K1L2
  Type: fs-mount
  Physical/Virtual: physical
  Mount Path: /docs/api-reference
  Created: 2026-02-09 10:22

Auto-discovered Children
  + 1 fs-directory (root)
  + 3 fs-directory resources
  + 28 fs-file resources

Capabilities
  Readable: yes
  Writable: yes
  Sandboxable: yes
  Checkpointable: yes
  Sandbox Strategy: copy_on_write

[OK] Resource registered (31 child resources discovered)

$ agents resource add container-instance local/api-dev --mount local/api-repo:/workspace --mount /home/user/.config/nvim:/home/dev/.config/nvim

Resource
  Name: local/api-dev
  ID: 01HXR3C4D5E6F7G8H9J0K1L2M3
  Type: container-instance
  Physical/Virtual: physical
  State: created (not started)
  Created: 2026-02-09 10:25

Mounts
Source                            Container Path   Kind
--------------------------------  ---------------  ---------
local/api-repo                    /workspace       resource-ref
/home/user/.config/nvim           /home/dev/...    host-path

[OK] Container resource registered (container will start on first access)

$ agents resource add container-instance cloud/ci-runner --clone-into https://github.com/acme/api.git:/workspace

Resource
  Name: cloud/ci-runner
  ID: 01HXR4D5E6F7G8H9J0K1L2M3N4
  Type: container-instance
  Physical/Virtual: physical
  State: created (not started)
  Clone: https://github.com/acme/api.git
  Clone Target: /workspace
  Created: 2026-02-09 10:27

[OK] Container resource registered (repo will be cloned on first start)

$ agents resource add git-checkout local/webapp --path /home/user/projects/webapp

Resource
  Name: local/webapp
  ID: 01HXR5E6F7G8H9J0K1L2M3N4O5
  Type: git-checkout
  Physical/Virtual: physical
  Path: /home/user/projects/webapp
  Branch: main
  Created: 2026-02-09 10:30

Auto-discovered Children
ID               Type                    Status
---------------  ----------------------  -----------------
01HXR5E6F7G9..   git                     created
01HXR5E6F7GA..   devcontainer-instance   detected (not built)
01HXR5E6F7GB..   fs-directory            created
  + 52 git-commit resources
  + 189 git-tree-entry resources

[WARN] Devcontainer detected at .devcontainer/devcontainer.json
  Container will be built lazily on first access.
  Use agents resource show 01HXR5E6F7GA… to inspect.

[OK] Resource registered (245 child resources discovered)
```

=== "JSON"

```json
{
  "command": "resource add",
  "status": "ok",
  "exit_code": 0,
  "data": {
    "name": "local/api-repo",
    "id": "01HXR1A1B2C3D4E5F6G7H8J9K0",
    "type": "git-checkout",
    "physical_virtual": "physical",
    "path": "/home/user/projects/api-service",
    "branch": "main",
    "created": "2026-02-09T10:20:00Z",
    "auto_discovered_children": [
      { "id": "01HXR1A1B2C3…", "type": "git", "status": "created" },
      { "id": "01HXR1A1B2C4…", "type": "git-remote", "status": "created" },
      { "id": "01HXR1A1B2C5…", "type": "git-branch", "status": "created" },
      { "id": "01HXR1A1B2C6…", "type": "git-branch", "status": "created" },
      { "id": "01HXR1A1B2C7…", "type": "fs-directory", "status": "created" }
    ],
    "children_summary": {
      "git_commit": 47,
      "git_tree_entry": 312,
      "fs_directory": 3,
      "fs_file": 28
    },
    "total_children": 395,
    "capabilities": {
      "readable": true,
      "writable": true,
      "sandboxable": true,
      "checkpointable": true,
      "sandbox_strategy": "git_worktree"
    }
  },
  "timing": { "started": "2026-02-09T10:20:00Z", "duration_ms": 250 },
  "messages": ["Resource registered (395 child resources discovered)"]
}
```

=== "YAML"

```yaml
command: resource add
status: ok
exit_code: 0
data:
  name: local/api-repo
  id: 01HXR1A1B2C3D4E5F6G7H8J9K0
  type: git-checkout
  physical_virtual: physical
  path: /home/user/projects/api-service
  branch: main
  created: "2026-02-09T10:20:00Z"
  auto_discovered_children:
    - id: "01HXR1A1B2C3\u2026"
      type: git
      status: created
    - id: "01HXR1A1B2C4\u2026"
      type: git-remote
      status: created
    - id: "01HXR1A1B2C5\u2026"
      type: git-branch
      status: created
    - id: "01HXR1A1B2C6\u2026"
      type: git-branch
      status: created
    - id: "01HXR1A1B2C7\u2026"
      type: fs-directory
      status: created
  children_summary:
    git_commit: 47
    git_tree_entry: 312
    fs_directory: 3
    fs_file: 28
  total_children: 395
  capabilities:
    readable: true
    writable: true
    sandboxable: true
    checkpointable: true
    sandbox_strategy: git_worktree
timing:
  started: "2026-02-09T10:20:00Z"
  duration_ms: 250
messages:
  - "Resource registered (395 child resources discovered)"
```
agents resource remove
agents resource remove [--yes|-y] <NAME>

!!! danger "Cascading Deletion" Removes a registered resource ==and all its auto-discovered child resources==. A git-checkout resource can have hundreds of child resources (files, directories). This operation fails if the resource is linked to any project — use agents project unlink-resource first.

Arguments

  • <NAME>: Resource name (only user-added resources can be removed).
  • --yes: Skip confirmation prompt.

Examples

=== "Rich"

<div class="highlight"><pre><code>
<span style="color: #66cc66; font-weight: 600;">$</span> <span style="color: cyan; font-weight: 600;">agents</span> resource remove local/api-repo

Remove resource local/api-repo and 395 child resources? [y/N]: y

╭─ Resource Removed ──────────────────────────────────╮
│ <span style="color: cyan; font-weight: 600;">Name:</span> local/api-repo                                │
│ <span style="color: #5599ff; font-weight: 600;">Type:</span> git-checkout                                  │
│ <span style="color: yellow; font-weight: 600;">Children Removed:</span> 395                               │
│ <span style="color: #5599ff; font-weight: 600;">Projects Unlinked:</span> 0                                │
╰─────────────────────────────────────────────────────╯

<span style="color: #66cc66; font-weight: 600;">✓ OK</span> Resource removed
</code></pre></div>

=== "Plain"

```
$ agents resource remove local/api-repo

Remove resource local/api-repo and 395 child resources? [y/N]: y

Resource Removed
  Name: local/api-repo
  Type: git-checkout
  Children Removed: 395
  Projects Unlinked: 0

[OK] Resource removed
```

=== "JSON"

```json
{
  "command": "resource remove",
  "status": "ok",
  "exit_code": 0,
  "data": {
    "name": "local/api-repo",
    "type": "git-checkout",
    "children_removed": 395,
    "projects_unlinked": 0
  },
  "timing": { "started": "2026-02-09T10:24:00Z", "duration_ms": 120 },
  "messages": ["Resource removed"]
}
```

=== "YAML"

```yaml
command: resource remove
status: ok
exit_code: 0
data:
  name: local/api-repo
  type: git-checkout
  children_removed: 395
  projects_unlinked: 0
timing:
  started: "2026-02-09T10:24:00Z"
  duration_ms: 120
messages:
  - "Resource removed"
```
agents resource list
agents resource list [--all] [(--type|-t) <TYPE>] 

Purpose List registered resources with optional filters. By default, lists only user-added (named) resources. Use --all to include auto-discovered child resources.

Arguments

  • --type/-t TYPE: Filter by resource type (accepts a regex).
  • --all: Include auto-discovered child resources (by default only user-added resources are shown).

Examples

=== "Rich"

<div class="highlight"><pre><code>
<span style="color: #66cc66; font-weight: 600;">$</span> <span style="color: cyan; font-weight: 600;">agents</span> resource list

╭─ Resources ──────────────────────────────────────────────────────────────────────────────────────────────╮
│ <span style="color: cyan; font-weight: 600;">Name</span>               <span style="color: cyan; font-weight: 600;">ID</span>                            <span style="color: cyan; font-weight: 600;">Type</span>         <span style="color: cyan; font-weight: 600;">Phys/Virt</span>  <span style="color: cyan; font-weight: 600;">Children</span>  <span style="color: cyan; font-weight: 600;">Projects</span>              │
│ <span style="opacity: 0.7;">─────────────────  ────────────────────────────  ───────────  ─────────  ────────  ────────────────</span>      │
│ local/api-repo     01HXR1A1B2C3D4E5F6G7H8J9K0    git-checkout physical   395       local/api-service     │
│ local/docs         01HXR2B2C3D4E5F6G7H8J9K0L1    fs-mount     physical   32        local/api-service     │
│ local/staging-db   01HXR3C3D4E5F6G7H8J9K0L1M2    database     physical   12        local/api-service,    │
│                                                                                    local/staging         │
╰──────────────────────────────────────────────────────────────────────────────────────────────────────────╯

╭─ Summary ───────────╮
│ <span style="color: #5599ff; font-weight: 600;">Total:</span> 3            │
│ <span style="color: #5599ff; font-weight: 600;">Physical:</span> 3         │
│ <span style="color: #5599ff; font-weight: 600;">Virtual:</span> 0          │
│ <span style="color: #5599ff; font-weight: 600;">Total Children:</span> 405 │
╰─────────────────────╯

<span style="color: #66cc66; font-weight: 600;">✓ OK</span> 3 resources listed
</code></pre></div>

=== "Plain"

```
$ agents resource list

Resources
---------
Name               ID                            Type          Phys/Virt  Children  Projects
-----------------  ----------------------------  -----------  ---------  --------  ----------------
local/api-repo     01HXR1A1B2C3D4E5F6G7H8J9K0   git-checkout  physical   395       local/api-service
local/docs         01HXR2B2C3D4E5F6G7H8J9K0L1   fs-mount      physical   32        local/api-service
local/staging-db   01HXR3C3D4E5F6G7H8J9K0L1M2   database      physical   12        local/api-service, local/staging

Summary
  Total: 3
  Physical: 3
  Virtual: 0
  Total Children: 405

[OK] 3 resources listed
```

=== "JSON"

```json
{
  "command": "resource list",
  "status": "ok",
  "exit_code": 0,
  "data": {
    "resources": [
      {
        "name": "local/api-repo",
        "id": "01HXR1A1B2C3D4E5F6G7H8J9K0",
        "type": "git-checkout",
        "physical_virtual": "physical",
        "children": 395,
        "projects": ["local/api-service"]
      },
      {
        "name": "local/docs",
        "id": "01HXR2B2C3D4E5F6G7H8J9K0L1",
        "type": "fs-mount",
        "physical_virtual": "physical",
        "children": 32,
        "projects": ["local/api-service"]
      },
      {
        "name": "local/staging-db",
        "id": "01HXR3C3D4E5F6G7H8J9K0L1M2",
        "type": "database",
        "physical_virtual": "physical",
        "children": 12,
        "projects": ["local/api-service", "local/staging"]
      }
    ],
    "summary": {
      "total": 3,
      "physical": 3,
      "virtual": 0,
      "total_children": 405
    }
  },
  "timing": { "started": "2026-02-09T10:25:00Z", "duration_ms": 90 },
  "messages": ["3 resources listed"]
}
```

=== "YAML"

```yaml
command: resource list
status: ok
exit_code: 0
data:
  resources:
    - name: local/api-repo
      id: 01HXR1A1B2C3D4E5F6G7H8J9K0
      type: git-checkout
      physical_virtual: physical
      children: 395
      projects:
        - local/api-service
    - name: local/docs
      id: 01HXR2B2C3D4E5F6G7H8J9K0L1
      type: fs-mount
      physical_virtual: physical
      children: 32
      projects:
        - local/api-service
    - name: local/staging-db
      id: 01HXR3C3D4E5F6G7H8J9K0L1M2
      type: database
      physical_virtual: physical
      children: 12
      projects:
        - local/api-service
        - local/staging
  summary:
    total: 3
    physical: 3
    virtual: 0
    total_children: 405
timing:
  started: "2026-02-09T10:25:00Z"
  duration_ms: 90
messages:
  - "3 resources listed"
```
agents resource show
agents resource show <RESOURCE>

Purpose Show detailed information about a registered resource including its type, capabilities, parent/child relationships, and linked projects.

Arguments

  • <RESOURCE>: Resource name or ULID.

Examples

=== "Rich"

<div class="highlight"><pre><code>
<span style="color: #66cc66; font-weight: 600;">$</span> <span style="color: cyan; font-weight: 600;">agents</span> resource show local/api-repo

╭─ Resource ─────────────────────────────────────────╮
│ <span style="color: cyan; font-weight: 600;">Name:</span> local/api-repo                               │
│ <span style="color: cyan; font-weight: 600;">ID:</span> 01HXR1A1B2C3D4E5F6G7H8J9K0                     │
│ <span style="color: #5599ff; font-weight: 600;">Type:</span> git-checkout                                 │
│ <span style="color: #5599ff; font-weight: 600;">Physical/Virtual:</span> physical                         │
│ <span style="color: #66cc66; font-weight: 600;">Path:</span> /home/user/projects/api-service              │
│ <span style="color: #5599ff; font-weight: 600;">Branch:</span> main                                       │
│ <span style="color: #66cc66; font-weight: 600;">Created:</span> 2026-02-09 10:20                          │
╰────────────────────────────────────────────────────╯

╭─ Capabilities ────────────────────╮
│ <span style="color: #66cc66; font-weight: 600;">Readable:</span> yes                     │
│ <span style="color: yellow; font-weight: 600;">Writable:</span> yes                     │
│ <span style="color: #66cc66; font-weight: 600;">Sandboxable:</span> yes                  │
│ <span style="color: #66cc66; font-weight: 600;">Checkpointable:</span> yes               │
│ <span style="color: #5599ff; font-weight: 600;">Sandbox Strategy:</span> git_worktree    │
╰───────────────────────────────────╯

╭─ Parents ────╮
│ (top-level)  │
╰──────────────╯

╭─ Direct Children ──────────────────────────────────────────────────────╮
│ <span style="color: cyan; font-weight: 600;">ID</span>                            <span style="color: cyan; font-weight: 600;">Type</span>          <span style="color: cyan; font-weight: 600;">Auto</span>  <span style="color: cyan; font-weight: 600;">Children</span>             │
│ <span style="opacity: 0.7;">────────────────────────────  ────────────  ────  ────────</span>             │
│ 01HXR4D4E5F6G7H8J9K0L1M2N3    git           yes   363                  │
│ 01HXR5E5F6G7H8J9K0L1M2N3P4    fs-directory  yes   32                   │
╰────────────────────────────────────────────────────────────────────────╯

╭─ Linked Projects ─────────────╮
│ - local/api-service (r/w)     │
╰───────────────────────────────╯

╭─ Tool Bindings ────────────────────────────────╮
│ <span style="color: cyan; font-weight: 600;">Tool</span>                      <span style="color: cyan; font-weight: 600;">Slot</span>   <span style="color: cyan; font-weight: 600;">Access</span>        │
│ <span style="opacity: 0.7;">────────────────────────  ─────  ───────────</span>   │
│ (builtin) git_status      repo   read_only     │
│ (builtin) git_diff        repo   read_only     │
│ (builtin) git_log         repo   read_only     │
│ local/run-migrations      db     (not bound)   │
╰────────────────────────────────────────────────╯

<span style="color: #66cc66; font-weight: 600;">✓ OK</span> Resource details loaded
</code></pre></div>

=== "Plain"

```
$ agents resource show local/api-repo

Resource
  Name: local/api-repo
  ID: 01HXR1A1B2C3D4E5F6G7H8J9K0
  Type: git-checkout
  Physical/Virtual: physical
  Path: /home/user/projects/api-service
  Branch: main
  Created: 2026-02-09 10:20

Capabilities
  Readable: yes
  Writable: yes
  Sandboxable: yes
  Checkpointable: yes
  Sandbox Strategy: git_worktree

Parents
  (top-level)

Direct Children
  ID                            Type          Auto  Children
  ----------------------------  ------------  ----  --------
  01HXR4D4E5F6G7H8J9K0L1M2N3  git           yes   363
  01HXR5E5F6G7H8J9K0L1M2N3P4  fs-directory  yes   32

Linked Projects
  - local/api-service (r/w)

Tool Bindings
  Tool                      Slot   Access
  ------------------------  -----  -----------
  (builtin) git_status     repo   read_only
  (builtin) git_diff       repo   read_only
  (builtin) git_log        repo   read_only
  local/run-migrations     db     (not bound)

[OK] Resource details loaded
```

=== "JSON"

```json
{
  "command": "resource show",
  "status": "ok",
  "exit_code": 0,
  "data": {
    "name": "local/api-repo",
    "id": "01HXR1A1B2C3D4E5F6G7H8J9K0",
    "type": "git-checkout",
    "physical_virtual": "physical",
    "path": "/home/user/projects/api-service",
    "branch": "main",
    "created": "2026-02-09T10:20:00Z",
    "capabilities": {
      "readable": true,
      "writable": true,
      "sandboxable": true,
      "checkpointable": true,
      "sandbox_strategy": "git_worktree"
    },
    "parents": ["(top-level)"],
    "direct_children": [
      { "id": "01HXR4D4E5F6G7H8J9K0L1M2N3", "type": "git", "auto": true, "children": 363 },
      { "id": "01HXR5E5F6G7H8J9K0L1M2N3P4", "type": "fs-directory", "auto": true, "children": 32 }
    ],
    "linked_projects": [
      { "name": "local/api-service", "access": "r/w" }
    ],
    "tool_bindings": [
      { "tool": "(builtin) git_status", "slot": "repo", "access": "read_only" },
      { "tool": "(builtin) git_diff", "slot": "repo", "access": "read_only" },
      { "tool": "(builtin) git_log", "slot": "repo", "access": "read_only" },
      { "tool": "local/run-migrations", "slot": "db", "access": "(not bound)" }
    ]
  },
  "timing": { "started": "2026-02-09T10:15:00Z", "duration_ms": 65 },
  "messages": ["Resource details loaded"]
}
```

=== "YAML"

```yaml
command: resource show
status: ok
exit_code: 0
data:
  name: local/api-repo
  id: 01HXR1A1B2C3D4E5F6G7H8J9K0
  type: git-checkout
  physical_virtual: physical
  path: /home/user/projects/api-service
  branch: main
  created: "2026-02-09T10:20:00Z"
  capabilities:
    readable: true
    writable: true
    sandboxable: true
    checkpointable: true
    sandbox_strategy: git_worktree
  parents:
    - (top-level)
  direct_children:
    - id: 01HXR4D4E5F6G7H8J9K0L1M2N3
      type: git
      auto: true
      children: 363
    - id: 01HXR5E5F6G7H8J9K0L1M2N3P4
      type: fs-directory
      auto: true
      children: 32
  linked_projects:
    - name: local/api-service
      access: r/w
  tool_bindings:
    - tool: (builtin) git_status
      slot: repo
      access: read_only
    - tool: (builtin) git_diff
      slot: repo
      access: read_only
    - tool: (builtin) git_log
      slot: repo
      access: read_only
    - tool: local/run-migrations
      slot: db
      access: (not bound)
timing:
  started: "2026-02-09T10:15:00Z"
  duration_ms: 65
messages:
  - Resource details loaded
```
agents resource inspect
agents resource inspect [--tree] [--file <PATH>] <RESOURCE>

Purpose Inspect a resource's contents. For file-based resources (git checkouts, filesystem mounts, directories), displays the file tree or the contents of a specific file. Works with any resource — user-added or auto-discovered — via name or ULID.

Arguments

  • <RESOURCE>: Resource name or ULID.
  • --tree: Show the resource's file/content tree.
  • --file PATH: Show contents of a specific file within the resource.

Examples

=== "Rich"

<div class="highlight"><pre><code>
<span style="color: #66cc66; font-weight: 600;">$</span> <span style="color: cyan; font-weight: 600;">agents</span> resource inspect local/api-repo <span style="color: cyan;">--tree</span>

╭─ Resource Tree: local/api-repo ──────────────────────────────────────╮
│                                                                      │
│ <span style="color: cyan; font-weight: 600;">/home/user/projects/api-service</span>                                      │
│ ├── <span style="color: #5599ff; font-weight: 600;">src/</span>                                                             │
│ │   ├── <span style="color: #5599ff; font-weight: 600;">api/</span>                                                         │
│ │   │   ├── __init__.py                                                  │
│ │   │   ├── <span style="color: #5599ff; font-weight: 600;">auth/</span>                                                    │
│ │   │   │   ├── auth_middleware.py                                   │
│ │   │   │   ├── token_service.py                                     │
│ │   │   │   └── rbac.py                                              │
│ │   │   ├── <span style="color: #5599ff; font-weight: 600;">routes/</span>                                                  │
│ │   │   └── <span style="color: #5599ff; font-weight: 600;">models/</span>                                                  │
│ │   └── <span style="color: #5599ff; font-weight: 600;">utils/</span>                                                       │
│ ├── <span style="color: #5599ff; font-weight: 600;">tests/</span>                                                           │
│ ├── README.md                                                        │
│ └── pyproject.toml                                                   │
│                                                                      │
╰──────────────────────────────────────────────────────────────────────╯

╭─ Summary ──────────────╮
│ <span style="color: #5599ff; font-weight: 600;">Directories:</span> 8         │
│ <span style="color: #5599ff; font-weight: 600;">Files:</span> 41              │
│ <span style="color: #5599ff; font-weight: 600;">Total Size:</span> 284 KB     │
╰────────────────────────╯

<span style="color: #66cc66; font-weight: 600;">✓ OK</span> Resource tree displayed
</code></pre></div>

=== "Plain"

```
$ agents resource inspect local/api-repo --tree

Resource Tree: local/api-repo

/home/user/projects/api-service
+-- src/
|   +-- api/
|   |   +-- __init__.py
|   |   +-- auth/
|   |   |   +-- auth_middleware.py
|   |   |   +-- token_service.py
|   |   |   +-- rbac.py
|   |   +-- routes/
|   |   +-- models/
|   +-- utils/
+-- tests/
+-- README.md
+-- pyproject.toml

Summary
  Directories: 8
  Files: 41
  Total Size: 284 KB

[OK] Resource tree displayed
```

=== "JSON"

```json
{
  "command": "resource inspect",
  "status": "ok",
  "exit_code": 0,
  "data": {
    "resource": "local/api-repo",
    "mode": "tree",
    "root": "/home/user/projects/api-service",
    "tree": [
      {
        "name": "src/",
        "type": "directory",
        "children": [
          {
            "name": "api/",
            "type": "directory",
            "children": [
              { "name": "__init__.py", "type": "file" },
              {
                "name": "auth/",
                "type": "directory",
                "children": [
                  { "name": "auth_middleware.py", "type": "file" },
                  { "name": "token_service.py", "type": "file" },
                  { "name": "rbac.py", "type": "file" }
                ]
              },
              { "name": "routes/", "type": "directory" },
              { "name": "models/", "type": "directory" }
            ]
          },
          { "name": "utils/", "type": "directory" }
        ]
      },
      { "name": "tests/", "type": "directory" },
      { "name": "README.md", "type": "file" },
      { "name": "pyproject.toml", "type": "file" }
    ],
    "summary": {
      "directories": 8,
      "files": 41,
      "total_size": "284 KB"
    }
  },
  "timing": { "started": "2026-02-09T10:15:00Z", "duration_ms": 150 },
  "messages": ["Resource tree displayed"]
}
```

=== "YAML"

```yaml
command: resource inspect
status: ok
exit_code: 0
data:
  resource: local/api-repo
  mode: tree
  root: /home/user/projects/api-service
  tree:
    - name: src/
      type: directory
      children:
        - name: api/
          type: directory
          children:
            - name: __init__.py
              type: file
            - name: auth/
              type: directory
              children:
                - name: auth_middleware.py
                  type: file
                - name: token_service.py
                  type: file
                - name: rbac.py
                  type: file
            - name: routes/
              type: directory
            - name: models/
              type: directory
        - name: utils/
          type: directory
    - name: tests/
      type: directory
    - name: README.md
      type: file
    - name: pyproject.toml
      type: file
  summary:
    directories: 8
    files: 41
    total_size: 284 KB
timing:
  started: "2026-02-09T10:15:00Z"
  duration_ms: 150
messages:
  - Resource tree displayed
```

=== "Rich"

<div class="highlight"><pre><code>
<span style="color: #66cc66; font-weight: 600;">$</span> <span style="color: cyan; font-weight: 600;">agents</span> resource inspect local/api-repo <span style="color: cyan;">--file</span> src/api/auth/auth_middleware.py

╭─ File: src/api/auth/auth_middleware.py ──────────────────╮
│ <span style="color: cyan; font-weight: 600;">Resource:</span> local/api-repo                                 │
│ <span style="color: #5599ff; font-weight: 600;">Size:</span> 2.4 KB                                             │
│ <span style="color: #5599ff; font-weight: 600;">Language:</span> Python                                         │
│ <span style="color: #5599ff; font-weight: 600;">Last Modified:</span> 2026-02-10 14:32                          │
╰──────────────────────────────────────────────────────────╯

<span style="opacity: 0.7;">  1</span> │ <span style="color: cyan;">from</span> fastapi <span style="color: cyan;">import</span> Request, HTTPException
<span style="opacity: 0.7;">  2</span> │ <span style="color: cyan;">from</span> .token_service <span style="color: cyan;">import</span> validate_token
<span style="opacity: 0.7;">  3</span> │
<span style="opacity: 0.7;">  4</span> │ <span style="color: cyan;">async def</span> <span style="color: #66cc66;">auth_middleware</span>(request: Request):
<span style="opacity: 0.7;">  5</span> │     token = request.headers.get(<span style="color: #66cc66;">"Authorization"</span>)
<span style="opacity: 0.7;">  6</span> │     <span style="color: cyan;">if not</span> token:
<span style="opacity: 0.7;">  7</span> │         <span style="color: cyan;">raise</span> HTTPException(status_code=<span style="color: yellow;">401</span>)
<span style="opacity: 0.7;">  8</span> │     user = <span style="color: cyan;">await</span> validate_token(token)
<span style="opacity: 0.7;">  9</span> │     request.state.user = user
<span style="opacity: 0.7;"> 10</span> │     <span style="color: cyan;">return</span> user
<span style="opacity: 0.7;">   </span> │ <span style="opacity: 0.5;">... (24 more lines)</span>

<span style="color: #66cc66; font-weight: 600;">✓ OK</span> File displayed
</code></pre></div>

=== "Plain"

```
$ agents resource inspect local/api-repo --file src/api/auth/auth_middleware.py

File: src/api/auth/auth_middleware.py
  Resource: local/api-repo
  Size: 2.4 KB
  Language: Python
  Last Modified: 2026-02-10 14:32

  1 | from fastapi import Request, HTTPException
  2 | from .token_service import validate_token
  3 |
  4 | async def auth_middleware(request: Request):
  5 |     token = request.headers.get("Authorization")
  6 |     if not token:
  7 |         raise HTTPException(status_code=401)
  8 |     user = await validate_token(token)
  9 |     request.state.user = user
 10 |     return user
    | ... (24 more lines)

[OK] File displayed
```

=== "JSON"

```json
{
  "command": "resource inspect",
  "status": "ok",
  "exit_code": 0,
  "data": {
    "resource": "local/api-repo",
    "mode": "file",
    "file": {
      "path": "src/api/auth/auth_middleware.py",
      "size": "2.4 KB",
      "language": "Python",
      "last_modified": "2026-02-10T14:32:00Z",
      "lines_shown": 10,
      "total_lines": 34,
      "content": "from fastapi import Request, HTTPException\nfrom .token_service import validate_token\n\nasync def auth_middleware(request: Request):\n    token = request.headers.get(\"Authorization\")\n    if not token:\n        raise HTTPException(status_code=401)\n    user = await validate_token(token)\n    request.state.user = user\n    return user\n"
    }
  },
  "timing": { "started": "2026-02-09T10:15:00Z", "duration_ms": 30 },
  "messages": ["File displayed"]
}
```

=== "YAML"

```yaml
command: resource inspect
status: ok
exit_code: 0
data:
  resource: local/api-repo
  mode: file
  file:
    path: src/api/auth/auth_middleware.py
    size: 2.4 KB
    language: Python
    last_modified: "2026-02-10T14:32:00Z"
    lines_shown: 10
    total_lines: 34
    content: |
      from fastapi import Request, HTTPException
      from .token_service import validate_token

      async def auth_middleware(request: Request):
          token = request.headers.get("Authorization")
          if not token:
              raise HTTPException(status_code=401)
          user = await validate_token(token)
          request.state.user = user
          return user
timing:
  started: "2026-02-09T10:15:00Z"
  duration_ms: 30
messages:
  - File displayed
```
agents resource tree
agents resource tree [(--depth|-d) <N>] [(--type|-t) <TYPE>] <RESOURCE>

Purpose Display the resource DAG as a tree starting from a given resource, showing parent/child relationships.

Arguments

  • <RESOURCE>: Resource name or ULID.
  • --depth/-d N: Maximum depth to display (default: 3).
  • --type/-t TYPE: Filter to only show children of a specific type.

Examples

=== "Rich"

<div class="highlight"><pre><code>
<span style="color: #66cc66; font-weight: 600;">$</span> <span style="color: cyan; font-weight: 600;">agents</span> resource tree local/api-repo <span style="color: cyan;">--depth</span> 2

╭─ Resource Tree: local/api-repo ─────────────────────────────────────╮
│                                                                     │
│ <span style="color: cyan; font-weight: 600;">git-checkout 01HXR1A1..J9K0</span> (physical)                              │
│ ├── <span style="color: #5599ff; font-weight: 600;">git 01HXR4D4..M2N3</span> (physical)                                   │
│ │   ├── <span style="color: #5599ff; font-weight: 600;">git-remote 01HXR6F6..P4Q5</span>                                   │
│ │   ├── <span style="color: magenta; font-weight: 600;">git-branch 01HXR7G7..Q5R6</span>                                   │
│ │   │   ├── <span style="color: #66cc66; font-weight: 600;">git-commit 01HXR8H8..R6S7</span>                               │
│ │   │   └── <span style="opacity: 0.7;">... 22 more git-commit resources</span>                        │
│ │   └── <span style="color: magenta; font-weight: 600;">git-branch 01HXR9J9..S7T8</span>                                   │
│ │       └── <span style="opacity: 0.7;">... 26 git-commit resources</span>                             │
│ └── <span style="color: #5599ff; font-weight: 600;">fs-directory 01HXR5E5..N3P4</span> (physical)                          │
│     ├── <span style="color: #66cc66; font-weight: 600;">fs-directory 01HXRAK1..T8U9</span>                                 │
│     ├── <span style="color: #66cc66; font-weight: 600;">fs-directory 01HXRBL2..U9V0</span>                                 │
│     └── <span style="opacity: 0.7;">... 28 more fs-file resources</span>                               │
│                                                                     │
╰─────────────────────────────────────────────────────────────────────╯

╭─ Summary ──────────────╮
│ <span style="color: #5599ff; font-weight: 600;">Total shown:</span> 14        │
│ <span style="color: #5599ff; font-weight: 600;">Total in subtree:</span> 395  │
│ <span style="color: #5599ff; font-weight: 600;">Max depth:</span> 2           │
╰────────────────────────╯

<span style="color: #66cc66; font-weight: 600;">✓ OK</span> Resource tree displayed
</code></pre></div>

=== "Plain"

```
$ agents resource tree local/api-repo --depth 2

Resource Tree: local/api-repo

git-checkout 01HXR1A1..J9K0 (physical)
+-- git 01HXR4D4..M2N3 (physical)
|   +-- git-remote 01HXR6F6..P4Q5
|   +-- git-branch 01HXR7G7..Q5R6
|   |   +-- git-commit 01HXR8H8..R6S7
|   |   +-- ... 22 more git-commit resources
|   +-- git-branch 01HXR9J9..S7T8
|       +-- ... 26 git-commit resources
+-- fs-directory 01HXR5E5..N3P4 (physical)
    +-- fs-directory 01HXRAK1..T8U9
    +-- fs-directory 01HXRBL2..U9V0
    +-- ... 28 more fs-file resources

Summary
  Total shown: 14
  Total in subtree: 395
  Max depth: 2

[OK] Resource tree displayed
```

=== "JSON"

```json
{
  "command": "resource tree",
  "status": "ok",
  "exit_code": 0,
  "data": {
    "resource": "local/api-repo",
    "max_depth": 2,
    "tree": {
      "type": "git-checkout",
      "id": "01HXR1A1..J9K0",
      "physical": true,
      "children": [
        {
          "type": "git",
          "id": "01HXR4D4..M2N3",
          "physical": true,
          "children": [
            { "type": "git-remote", "id": "01HXR6F6..P4Q5" },
            {
              "type": "git-branch",
              "id": "01HXR7G7..Q5R6",
              "children": [
                { "type": "git-commit", "id": "01HXR8H8..R6S7" },
                "... 22 more git-commit resources"
              ]
            },
            {
              "type": "git-branch",
              "id": "01HXR9J9..S7T8",
              "children": ["... 26 git-commit resources"]
            }
          ]
        },
        {
          "type": "fs-directory",
          "id": "01HXR5E5..N3P4",
          "physical": true,
          "children": [
            { "type": "fs-directory", "id": "01HXRAK1..T8U9" },
            { "type": "fs-directory", "id": "01HXRBL2..U9V0" },
            "... 28 more fs-file resources"
          ]
        }
      ]
    },
    "summary": {
      "total_shown": 14,
      "total_in_subtree": 395,
      "max_depth": 2
    }
  },
  "timing": { "started": "2026-02-09T10:15:00Z", "duration_ms": 85 },
  "messages": ["Resource tree displayed"]
}
```

=== "YAML"

```yaml
command: resource tree
status: ok
exit_code: 0
data:
  resource: local/api-repo
  max_depth: 2
  tree:
    type: git-checkout
    id: 01HXR1A1..J9K0
    physical: true
    children:
      - type: git
        id: 01HXR4D4..M2N3
        physical: true
        children:
          - type: git-remote
            id: 01HXR6F6..P4Q5
          - type: git-branch
            id: 01HXR7G7..Q5R6
            children:
              - type: git-commit
                id: 01HXR8H8..R6S7
              - "... 22 more git-commit resources"
          - type: git-branch
            id: 01HXR9J9..S7T8
            children:
              - "... 26 git-commit resources"
      - type: fs-directory
        id: 01HXR5E5..N3P4
        physical: true
        children:
          - type: fs-directory
            id: 01HXRAK1..T8U9
          - type: fs-directory
            id: 01HXRBL2..U9V0
          - "... 28 more fs-file resources"
  summary:
    total_shown: 14
    total_in_subtree: 395
    max_depth: 2
timing:
  started: "2026-02-09T10:15:00Z"
  duration_ms: 85
messages:
  - Resource tree displayed
```
agents resource link-child <PARENT> <CHILD>

Purpose Manually link one resource as a child of another. The child resource must already be registered. The resource types must be compatible (the parent type must allow the child type). A resource can have multiple parents.

Arguments

  • <PARENT>: Parent resource name or ULID.
  • <CHILD>: Child resource name or ULID.

Examples

=== "Rich"

<div class="highlight"><pre><code>
<span style="color: #66cc66; font-weight: 600;">$</span> <span style="color: cyan; font-weight: 600;">agents</span> resource link-child local/api-repo 01HXR2B2C3D4E5F6G7H8J9K0L1

╭─ Child Linked ──────────────────────────────────────╮
│ <span style="color: cyan; font-weight: 600;">Parent:</span> local/api-repo                              │
│ <span style="color: #5599ff; font-weight: 600;">Child:</span> 01HXR2B2C3D4E5F6G7H8J9K0L1                   │
│ <span style="color: #5599ff; font-weight: 600;">Child Type:</span> fs-mount                                │
│ <span style="color: #66cc66; font-weight: 600;">Status:</span> linked                                      │
╰─────────────────────────────────────────────────────╯

<span style="color: #66cc66; font-weight: 600;">✓ OK</span> Child resource linked
</code></pre></div>

=== "Plain"

```
$ agents resource link-child local/api-repo 01HXR2B2C3D4E5F6G7H8J9K0L1

Child Linked
  Parent: local/api-repo
  Child: 01HXR2B2C3D4E5F6G7H8J9K0L1
  Child Type: fs-mount
  Status: linked

[OK] Child resource linked
```

=== "JSON"

```json
{
  "command": "resource link-child",
  "status": "ok",
  "exit_code": 0,
  "data": {
    "parent": "local/api-repo",
    "child": "01HXR2B2C3D4E5F6G7H8J9K0L1",
    "child_type": "fs-mount",
    "status": "linked"
  },
  "timing": { "started": "2026-02-09T10:15:00Z", "duration_ms": 38 },
  "messages": ["Child resource linked"]
}
```

=== "YAML"

```yaml
command: resource link-child
status: ok
exit_code: 0
data:
  parent: local/api-repo
  child: 01HXR2B2C3D4E5F6G7H8J9K0L1
  child_type: fs-mount
  status: linked
timing:
  started: "2026-02-09T10:15:00Z"
  duration_ms: 38
messages:
  - Child resource linked
```
agents resource unlink-child [--yes|-y] <PARENT> <CHILD>

Purpose Remove a manual parent/child link between two resources. Auto-discovered links cannot be manually unlinked.

Arguments

  • <PARENT>: Parent resource name or ULID.
  • <CHILD>: Child resource name or ULID.
  • --yes: Skip confirmation prompt.

Examples

=== "Rich"

<div class="highlight"><pre><code>
<span style="color: #66cc66; font-weight: 600;">$</span> <span style="color: cyan; font-weight: 600;">agents</span> resource unlink-child local/api-repo 01HXR2B2C3D4E5F6G7H8J9K0L1

Unlink 01HXR2B2C3D4E5F6G7H8J9K0L1 from parent local/api-repo? [y/N]: y

╭─ Child Unlinked ────────────────────────────────────╮
│ <span style="color: cyan; font-weight: 600;">Parent:</span> local/api-repo                              │
│ <span style="color: #5599ff; font-weight: 600;">Child:</span> 01HXR2B2C3D4E5F6G7H8J9K0L1                   │
│ <span style="color: #66cc66; font-weight: 600;">Status:</span> unlinked                                    │
╰─────────────────────────────────────────────────────╯

<span style="color: #66cc66; font-weight: 600;">✓ OK</span> Child resource unlinked
</code></pre></div>

=== "Plain"

```
$ agents resource unlink-child local/api-repo 01HXR2B2C3D4E5F6G7H8J9K0L1

Unlink 01HXR2B2C3D4E5F6G7H8J9K0L1 from parent local/api-repo? [y/N]: y

Child Unlinked
  Parent: local/api-repo
  Child: 01HXR2B2C3D4E5F6G7H8J9K0L1
  Status: unlinked

[OK] Child resource unlinked
```

=== "JSON"

```json
{
  "command": "resource unlink-child",
  "status": "ok",
  "exit_code": 0,
  "data": {
    "parent": "local/api-repo",
    "child": "01HXR2B2C3D4E5F6G7H8J9K0L1",
    "status": "unlinked"
  },
  "timing": { "started": "2026-02-09T10:15:00Z", "duration_ms": 45 },
  "messages": ["Child resource unlinked"]
}
```

=== "YAML"

```yaml
command: resource unlink-child
status: ok
exit_code: 0
data:
  parent: local/api-repo
  child: 01HXR2B2C3D4E5F6G7H8J9K0L1
  status: unlinked
timing:
  started: "2026-02-09T10:15:00Z"
  duration_ms: 45
messages:
  - Child resource unlinked
```
agents resource stop
agents resource stop <NAME> [--yes | -y]

Purpose Stop an active devcontainer-instance or container-instance resource. Transitions the container through running → stopping → stopped. Only container-typed resources may be stopped; attempting to stop other resource types produces an error.

Arguments

  • <NAME>: Resource name or ULID of the devcontainer to stop.

Options

  • --yes / -y: Skip the confirmation prompt (useful in scripts and automation).

Examples

=== "Rich"

<div class="highlight"><pre><code>
<span style="color: #66cc66; font-weight: 600;">$</span> <span style="color: cyan; font-weight: 600;">agents</span> resource stop local/my-dc

Stopping container local/my-dc (state: running)...
<span style="color: #66cc66; font-weight: 600;">Stopped:</span> local/my-dc
</code></pre></div>

=== "Plain"

```
$ agents resource stop local/my-dc

Stopping container local/my-dc (state: running)...
Stopped: local/my-dc
```
agents resource rebuild
agents resource rebuild <NAME> [--yes | -y]

Purpose Rebuild a stopped or failed devcontainer-instance resource. Transitions the container through stopped/failed → building → running by invoking devcontainer up with the resource's workspace location. Only devcontainer-instance resources may be rebuilt since the rebuild process invokes the devcontainer CLI; generic container-instance resources do not support rebuild.

Arguments

  • <NAME>: Resource name or ULID of the devcontainer to rebuild.

Options

  • --yes / -y: Skip the confirmation prompt (useful in scripts and automation).

Examples

=== "Rich"

<div class="highlight"><pre><code>
<span style="color: #66cc66; font-weight: 600;">$</span> <span style="color: cyan; font-weight: 600;">agents</span> resource rebuild local/my-dc

Rebuilding local/my-dc...
<span style="color: #66cc66; font-weight: 600;">Rebuilt:</span> local/my-dc
</code></pre></div>

=== "Plain"

```
$ agents resource rebuild local/my-dc

Rebuilding local/my-dc...
Rebuilt: local/my-dc
```

agents plan

!!! info "Purpose" Manage ==plans== through the Strategize → Execute → Apply lifecycle (instantiated from Action templates).

??? abstract "Plan Lifecycle at a Glance" Action ──use──▶ Strategize ──execute──▶ Execute ──apply──▶ Apply ▲ │ │ └──────────────────────┘ │ ▲ (reversion) │ └────────────────────────────────────────┘

- [x] **Action** — Reusable template defining work, actors, and constraints
- [x] **Strategize** — Strategy actor produces a decision tree
- [x] **Execute** — Execution actor implements the strategy in a sandbox
- [x] **Apply** — Changeset is merged into real project resources

!!! note "Phase Reversion"
    Both Execute and Apply can revert to Strategize when constraints are too restrictive. See `delete_content` and `access_network` automation profile flags.
agents plan list
agents plan list [--phase <PHASE>] [--state <STATE>] [--project <PROJECT>]
                [--action <ACTION>] [<REGEX>]

Purpose List plans with optional filtering.

Arguments

  • --phase PHASE: Filter by phase.
  • --state STATE: Filter by processing state.
  • --project PROJECT: Filter by project.
  • --action ACTION: Filter by action name.

Examples

=== "Rich"

<div class="highlight"><pre><code>
<span style="color: #66cc66; font-weight: 600;">$</span> <span style="color: cyan; font-weight: 600;">agents</span> <span style="color: cyan;">--format</span> table plan list <span style="color: cyan;">--phase</span> execute

╭─ Plans ──────────────────────────────────────────────────────────────────────────────╮
│ <span style="color: cyan; font-weight: 600;">ID</span>        <span style="color: cyan; font-weight: 600;">Phase</span>    <span style="color: cyan; font-weight: 600;">State</span>       <span style="color: cyan; font-weight: 600;">Action</span>               <span style="color: cyan; font-weight: 600;">Project</span>            <span style="color: cyan; font-weight: 600;">Elapsed</span>       │
│ <span style="opacity: 0.7;">────────  ───────  ──────────  ───────────────────  ─────────────────  ─────────</span>     │
│ 01HXM7A9  execute  processing  local/code-coverage  local/api-service  00:01:12      │
╰──────────────────────────────────────────────────────────────────────────────────────╯

╭─ Filters ──────╮
│ <span style="color: yellow; font-weight: 600;">Phase:</span> execute │
│ <span style="color: magenta; font-weight: 600;">State:</span> (any)   │
│ <span style="color: #5599ff; font-weight: 600;">Project:</span> (any) │
│ <span style="color: #5599ff; font-weight: 600;">Action:</span> (any)  │
╰────────────────╯

╭─ Summary ─────────╮
│ <span style="color: yellow; font-weight: 600;">Total:</span> 1          │
│ <span style="color: cyan; font-weight: 600;">Processing:</span> 1     │
│ <span style="color: #66cc66; font-weight: 600;">Completed:</span> 0      │
│ <span style="color: #ff6666; font-weight: 600;">Errored:</span> 0        │
╰───────────────────╯

<span style="color: #66cc66; font-weight: 600;">✓ OK</span> 1 plan listed
</code></pre></div>

=== "Plain"

```
$ agents --format table plan list --phase execute

Plans
  ID        Phase    State       Action               Project            Elapsed
  --------  -------  ----------  -------------------  -----------------  ---------
  01HXM7A9  execute  processing  local/code-coverage  local/api-service  00:01:12

Filters
  Phase: execute
  State: (any)
  Project: (any)
  Action: (any)

Summary
  Total: 1
  Processing: 1
  Completed: 0
  Errored: 0

[OK] 1 plan listed
```

=== "JSON"

```json
{
  "command": "plan list",
  "status": "ok",
  "exit_code": 0,
  "data": {
    "plans": [
      {
        "id": "01HXM7A9",
        "phase": "execute",
        "state": "processing",
        "action": "local/code-coverage",
        "project": "local/api-service",
        "elapsed": "00:01:12"
      }
    ],
    "filters": {
      "phase": "execute",
      "state": null,
      "project": null,
      "action": null
    },
    "summary": {
      "total": 1,
      "processing": 1,
      "completed": 0,
      "errored": 0
    }
  },
  "timing": { "started": "2026-02-09T14:30:00Z", "duration_ms": 95 },
  "messages": ["1 plan listed"]
}
```

=== "YAML"

```yaml
command: plan list
status: ok
exit_code: 0
data:
  plans:
    - id: "01HXM7A9"
      phase: execute
      state: processing
      action: "local/code-coverage"
      project: "local/api-service"
      elapsed: "00:01:12"
  filters:
    phase: execute
    state: null
    project: null
    action: null
  summary:
    total: 1
    processing: 1
    completed: 0
    errored: 0
timing:
  started: "2026-02-09T14:30:00Z"
  duration_ms: 95
messages:
  - "1 plan listed"
```

Listing all plans without filters shows plans across all phases and states:

=== "Rich"

<div class="highlight"><pre><code>
<span style="color: #66cc66; font-weight: 600;">$</span> <span style="color: cyan; font-weight: 600;">agents</span> plan list

╭─ Plans ──────────────────────────────────────────────────────────────────────────────────────╮
│ <span style="color: cyan; font-weight: 600;">ID</span>        <span style="color: cyan; font-weight: 600;">Phase</span>       <span style="color: cyan; font-weight: 600;">State</span>       <span style="color: cyan; font-weight: 600;">Action</span>               <span style="color: cyan; font-weight: 600;">Project</span>            <span style="color: cyan; font-weight: 600;">Elapsed</span>            │
│ <span style="opacity: 0.7;">────────  ──────────  ──────────  ───────────────────  ─────────────────  ─────────</span>          │
│ 01HXM7A9  execute     processing  local/code-coverage  local/api-service  00:01:12           │
│ 01HXM6R3  <span style="color: #66cc66; font-weight: 600;">apply  </span>     <span style="color: #66cc66; font-weight: 600;">applied </span>    local/add-auth       local/api-service  00:07:14           │
│ 01HXM5K2  <span style="color: #ff6666; font-weight: 600;">execute</span>     <span style="color: #ff6666; font-weight: 600;">errored</span>     local/migrate-db     local/api-service  00:04:33           │
│ 01HXM4J1  strategize  processing  local/refactor-api   local/web-app      00:00:45           │
│ 01HXM3H8  <span style="opacity: 0.5;">cancelled</span>   <span style="opacity: 0.5;">cancelled</span>   local/add-logging    local/api-service  00:02:10           │
╰──────────────────────────────────────────────────────────────────────────────────────────────╯

╭─ Summary ─────────────╮
│ <span style="color: yellow;">Total:</span> 5              │
│ <span style="color: cyan; font-weight: 600;">Processing:</span> 2         │
│ <span style="color: #66cc66; font-weight: 600;">Completed:</span> 1          │
│ <span style="color: #ff6666; font-weight: 600;">Errored:</span> 1            │
│ <span style="opacity: 0.5;">Cancelled:</span> 1          │
╰───────────────────────╯

<span style="color: #66cc66; font-weight: 600;">✓ OK</span> 5 plans listed
</code></pre></div>

=== "Plain"

```
$ agents plan list

Plans
  ID        Phase       State       Action               Project            Elapsed
  --------  ----------  ----------  -------------------  -----------------  ---------
  01HXM7A9  execute     processing  local/code-coverage  local/api-service  00:01:12
  01HXM6R3  apply       applied     local/add-auth       local/api-service  00:07:14
  01HXM5K2  execute     errored     local/migrate-db     local/api-service  00:04:33
  01HXM4J1  strategize  processing  local/refactor-api   local/web-app      00:00:45
  01HXM3H8  cancelled   cancelled   local/add-logging    local/api-service  00:02:10

Summary
  Total: 5
  Processing: 2
  Completed: 1
  Errored: 1
  Cancelled: 1

[OK] 5 plans listed
```

=== "JSON"

```json
{
  "command": "plan list",
  "status": "ok",
  "exit_code": 0,
  "data": {
    "plans": [
      { "id": "01HXM7A9", "phase": "execute", "state": "processing", "action": "local/code-coverage", "project": "local/api-service", "elapsed": "00:01:12" },
      { "id": "01HXM6R3", "phase": "apply", "state": "applied", "action": "local/add-auth", "project": "local/api-service", "elapsed": "00:07:14" },
      { "id": "01HXM5K2", "phase": "execute", "state": "errored", "action": "local/migrate-db", "project": "local/api-service", "elapsed": "00:04:33" },
      { "id": "01HXM4J1", "phase": "strategize", "state": "processing", "action": "local/refactor-api", "project": "local/web-app", "elapsed": "00:00:45" },
      { "id": "01HXM3H8", "phase": "cancelled", "state": "cancelled", "action": "local/add-logging", "project": "local/api-service", "elapsed": "00:02:10" }
    ],
    "summary": {
      "total": 5,
      "processing": 2,
      "completed": 1,
      "errored": 1,
      "cancelled": 1
    }
  },
  "timing": { "started": "2026-02-09T14:30:00Z", "duration_ms": 95 },
  "messages": ["5 plans listed"]
}
```

=== "YAML"

```yaml
command: plan list
status: ok
exit_code: 0
data:
  plans:
    - id: "01HXM7A9"
      phase: execute
      state: processing
      action: "local/code-coverage"
      project: "local/api-service"
      elapsed: "00:01:12"
    - id: "01HXM6R3"
      phase: apply
      state: applied
      action: "local/add-auth"
      project: "local/api-service"
      elapsed: "00:07:14"
    - id: "01HXM5K2"
      phase: execute
      state: errored
      action: "local/migrate-db"
      project: "local/api-service"
      elapsed: "00:04:33"
    - id: "01HXM4J1"
      phase: strategize
      state: processing
      action: "local/refactor-api"
      project: "local/web-app"
      elapsed: "00:00:45"
    - id: "01HXM3H8"
      phase: cancelled
      state: cancelled
      action: "local/add-logging"
      project: "local/api-service"
      elapsed: "00:02:10"
  summary:
    total: 5
    processing: 2
    completed: 1
    errored: 1
    cancelled: 1
timing:
  started: "2026-02-09T14:30:00Z"
  duration_ms: 95
messages:
  - "5 plans listed"
```

Filtering by project with --project:

=== "Rich"

<div class="highlight"><pre><code>
<span style="color: #66cc66; font-weight: 600;">$</span> <span style="color: cyan; font-weight: 600;">agents</span> plan list <span style="color: cyan;">--project</span> local/web-app

╭─ Plans ──────────────────────────────────────────────────────────────────────────────╮
│ <span style="color: cyan; font-weight: 600;">ID</span>        <span style="color: cyan; font-weight: 600;">Phase</span>       <span style="color: cyan; font-weight: 600;">State</span>       <span style="color: cyan; font-weight: 600;">Action</span>               <span style="color: cyan; font-weight: 600;">Project</span>        <span style="color: cyan; font-weight: 600;">Elapsed</span>        │
│ <span style="opacity: 0.7;">────────  ──────────  ──────────  ───────────────────  ─────────────  ─────────</span>      │
│ 01HXM4J1  strategize  processing  local/refactor-api   local/web-app  00:00:45       │
╰──────────────────────────────────────────────────────────────────────────────────────╯

╭─ Filters ─────────────────╮
│ <span style="color: yellow;">Phase:</span> (any)              │
│ <span style="color: magenta; font-weight: 600;">State:</span> (any)              │
│ <span style="color: #5599ff; font-weight: 600;">Project:</span> local/web-app    │
│ <span style="color: #5599ff; font-weight: 600;">Action:</span> (any)             │
╰───────────────────────────╯

<span style="color: #66cc66; font-weight: 600;">✓ OK</span> 1 plan listed
</code></pre></div>

=== "Plain"

```
$ agents plan list --project local/web-app

Plans
  ID        Phase       State       Action               Project        Elapsed
  --------  ----------  ----------  -------------------  -------------  ---------
  01HXM4J1  strategize  processing  local/refactor-api   local/web-app  00:00:45

Filters
  Phase: (any)
  State: (any)
  Project: local/web-app
  Action: (any)

[OK] 1 plan listed
```

=== "JSON"

```json
{
  "command": "plan list",
  "status": "ok",
  "exit_code": 0,
  "data": {
    "plans": [
      {
        "id": "01HXM4J1",
        "phase": "strategize",
        "state": "processing",
        "action": "local/refactor-api",
        "project": "local/web-app",
        "elapsed": "00:00:45"
      }
    ],
    "filters": {
      "phase": null,
      "state": null,
      "project": "local/web-app",
      "action": null
    },
    "total": 1
  },
  "timing": { "started": "2026-02-09T14:30:00Z", "duration_ms": 85 },
  "messages": ["1 plan listed"]
}
```

=== "YAML"

```yaml
command: plan list
status: ok
exit_code: 0
data:
  plans:
    - id: "01HXM4J1"
      phase: strategize
      state: processing
      action: "local/refactor-api"
      project: "local/web-app"
      elapsed: "00:00:45"
  filters:
    phase: null
    state: null
    project: "local/web-app"
    action: null
  total: 1
timing:
  started: "2026-02-09T14:30:00Z"
  duration_ms: 85
messages:
  - "1 plan listed"
```
agents plan use
agents plan use [--automation-profile <PROFILE>]
                [--invariant <INVARIANT>]...
                [--strategy-actor <STRATEGY_ACTOR>]
                [--execution-actor <EXEC_ACTOR>]
                [--estimation-actor <EST_ACTOR>]
                [--invariant-actor <INV_ACTOR>]
                [--execution-environment <RESOURCE_NAME>]
                [--execution-env-priority (fallback|override)]
                [--arg/-a name=value]...
                <ACTION> <PROJECT>...

Purpose Apply an action to one or more projects and start the Strategize phase.

Arguments

  • <ACTION_NAME>: Action name.
  • <PROJECT>: One or more project names (positional arguments, repeatable).
  • --arg/-a name=value: Action argument (repeatable).
  • --automation-profile PROFILE: Automation profile name (e.g., trusted, auto, local/careful-auto). Overrides the profile inherited from the action, project, or global config.
  • --strategy-actor ACTOR: Override the action's strategy actor for this plan.
  • --execution-actor ACTOR: Override the action's execution actor for this plan.
  • --estimation-actor ACTOR: Override the action's estimation actor for this plan.
  • --invariant-actor ACTOR: Override the action's Invariant Reconciliation Actor for this plan.
  • --invariant TEXT: Invariant to attach to the created plan (repeatable). These are added as plan-level invariants in addition to any invariants inherited from the action, project, or global scope.
  • --execution-environment RESOURCE_NAME: Name of a container-instance or devcontainer-instance resource to use as the execution environment for this plan. Overrides or supplements the project-level execution_environment setting depending on --execution-env-priority. See ADR-043 §3.3.
  • --execution-env-priority fallback|override: Priority semantics for the plan-level execution environment. fallback (default): defers to auto-detected devcontainers or project-level overrides. override: always uses the specified environment, bypassing all other resolution. See §Execution Environment Routing.

All actor arguments (--strategy-actor, --execution-actor, --estimation-actor, --invariant-actor) are optional overrides. When provided, they replace whatever was set when creating the action. When omitted, the action's configured actors are used.

Examples

=== "Rich"

<div class="highlight"><pre><code>
<span style="color: #66cc66; font-weight: 600;">$</span> <span style="color: cyan; font-weight: 600;">agents</span> plan use local/code-coverage local/api-service \
  <span style="color: cyan;">--arg</span> target_coverage_percent=85 <span style="color: cyan;">--automation-profile</span> trusted

╭─ Plan Created ──────────────────────╮
│ <span style="color: cyan; font-weight: 600;">Plan ID:</span> 01HXM8C2ZK4Q7C2B3F2R4VYV6J │
│ <span style="color: yellow; font-weight: 600;">Phase:</span> strategize                   │
│ <span style="color: magenta; font-weight: 600;">Action:</span> local/code-coverage         │
│ <span style="color: #5599ff; font-weight: 600;">Project:</span> local/api-service          │
│ <span style="color: #66cc66; font-weight: 600;">Automation:</span> trusted                 │
│ <span style="color: #5599ff; font-weight: 600;">Attempt:</span> 1                          │
╰─────────────────────────────────────╯

╭─ Inputs ──────────────────────╮
│ - target_coverage_percent=85  │
│ - automation_profile=trusted  │
╰───────────────────────────────╯

╭─ Actors ────────────────────────╮
│ <span style="color: magenta; font-weight: 600;">Strategy:</span> local/strategist      │
│ <span style="color: magenta; font-weight: 600;">Execution:</span> local/executor       │
│ <span style="color: #5599ff; font-weight: 600;">Estimation:</span> (none)              │
╰─────────────────────────────────╯

╭─ Automation ─────────────────────────╮
│ <span style="color: magenta; font-weight: 600;">Profile:</span> trusted                     │
│ <span style="color: #5599ff; font-weight: 600;">Source:</span> CLI flag                     │
│ <span style="color: #5599ff; font-weight: 600;">Read-Only:</span> no                        │
╰──────────────────────────────────────╯

╭─ Context ───────────────────────╮
│ <span style="color: #5599ff; font-weight: 600;">Resources:</span> 2 (repo, db)         │
│ <span style="color: #5599ff; font-weight: 600;">Indexed Files:</span> 347              │
│ <span style="color: #5599ff; font-weight: 600;">View:</span> strategize                │
│ <span style="color: #5599ff; font-weight: 600;">Hot Token Budget:</span> 12,000        │
╰─────────────────────────────────╯

╭─ Next Steps ─────────────────────────────────────╮
│ - agents plan execute 01HXM8C2ZK4Q7C2B3F2R4VYV6J │
│ - agents plan status 01HXM8C2ZK4Q7C2B3F2R4VYV6J  │
│ - agents plan tree 01HXM8C2ZK4Q7C2B3F2R4VYV6J    │
╰──────────────────────────────────────────────────╯

<span style="color: #66cc66; font-weight: 600;">✓ OK</span> Plan created
</code></pre></div>

=== "Plain"

```
$ agents plan use local/code-coverage local/api-service \
  --arg target_coverage_percent=85 --automation-profile trusted

Plan Created
  Plan ID: 01HXM8C2ZK4Q7C2B3F2R4VYV6J
  Phase: strategize
  Action: local/code-coverage
  Project: local/api-service
  Automation: trusted
  Attempt: 1

Inputs
  - target_coverage_percent=85
  - automation_profile=trusted

Actors
  Strategy: local/strategist
  Execution: local/executor
  Estimation: (none)

Automation
  Profile: trusted
  Source: CLI flag
  Read-Only: no

Context
  Resources: 2 (repo, db)
  Indexed Files: 347
  View: strategize
  Hot Token Budget: 12,000

Next Steps
  - agents plan execute 01HXM8C2ZK4Q7C2B3F2R4VYV6J
  - agents plan status 01HXM8C2ZK4Q7C2B3F2R4VYV6J
  - agents plan tree 01HXM8C2ZK4Q7C2B3F2R4VYV6J

[OK] Plan created
```

=== "JSON"

```json
{
  "command": "plan use",
  "status": "ok",
  "exit_code": 0,
  "data": {
    "plan_id": "01HXM8C2ZK4Q7C2B3F2R4VYV6J",
    "phase": "strategize",
    "action": "local/code-coverage",
    "project": "local/api-service",
    "automation_profile": "trusted",
    "attempt": 1,
    "inputs": {
      "target_coverage_percent": 85,
      "automation_profile": "trusted"
    },
    "actors": {
      "strategy": "local/strategist",
      "execution": "local/executor",
      "estimation": null
    },
    "automation": {
      "profile": "trusted",
      "source": "CLI flag",
      "read_only": false
    },
    "context": {
      "resources": 2,
      "indexed_files": 347,
      "view": "strategize",
      "hot_token_budget": 12000
    },
    "next_steps": [
      "agents plan execute 01HXM8C2ZK4Q7C2B3F2R4VYV6J",
      "agents plan status 01HXM8C2ZK4Q7C2B3F2R4VYV6J",
      "agents plan tree 01HXM8C2ZK4Q7C2B3F2R4VYV6J"
    ]
  },
  "timing": { "started": "2026-02-09T14:30:00Z", "duration_ms": 95 },
  "messages": ["Plan created"]
}
```

=== "YAML"

```yaml
command: plan use
status: ok
exit_code: 0
data:
  plan_id: "01HXM8C2ZK4Q7C2B3F2R4VYV6J"
  phase: strategize
  action: "local/code-coverage"
  project: "local/api-service"
  automation_profile: trusted
  attempt: 1
  inputs:
    target_coverage_percent: 85
    automation_profile: trusted
  actors:
    strategy: "local/strategist"
    execution: "local/executor"
    estimation: null
  automation:
    profile: trusted
    source: "CLI flag"
    read_only: false
  context:
    resources: 2
    indexed_files: 347
    view: strategize
    hot_token_budget: 12000
  next_steps:
    - "agents plan execute 01HXM8C2ZK4Q7C2B3F2R4VYV6J"
    - "agents plan status 01HXM8C2ZK4Q7C2B3F2R4VYV6J"
    - "agents plan tree 01HXM8C2ZK4Q7C2B3F2R4VYV6J"
timing:
  started: "2026-02-09T14:30:00Z"
  duration_ms: 95
messages:
  - "Plan created"
```

Applying an action to multiple projects simultaneously, with plan-level invariants:

=== "Rich"

<div class="highlight"><pre><code>
<span style="color: #66cc66; font-weight: 600;">$</span> <span style="color: cyan; font-weight: 600;">agents</span> plan use local/security-audit local/api-service local/web-app \
  <span style="color: cyan;">--invariant</span> <span style="color: #66cc66;">&quot;Never modify production database schemas&quot;</span> \
  <span style="color: cyan;">--invariant</span> <span style="color: #66cc66;">&quot;All changes must include test coverage&quot;</span> \
  <span style="color: cyan;">--automation-profile</span> supervised

╭─ Plan Created ──────────────────────────────────────╮
│ <span style="color: cyan; font-weight: 600;">Plan:</span> 01HXM9D2ZK4Q7C2B3F2R4VYV6J                    │
│ <span style="color: #5599ff; font-weight: 600;">Action:</span> local/security-audit                        │
│ <span style="color: yellow;">Phase:</span> strategize (running)                         │
│ <span style="color: magenta; font-weight: 600;">Automation:</span> supervised                              │
╰─────────────────────────────────────────────────────╯

╭─ Target Projects ───────────────────╮
│ <span style="color: #66cc66; font-weight: 600;">1.</span> local/api-service (3 resources)  │
│ <span style="color: #66cc66; font-weight: 600;">2.</span> local/web-app (2 resources)      │
╰─────────────────────────────────────╯

╭─ Plan Invariants ──────────────────────────────────────────╮
│ <span style="color: magenta; font-weight: 600;">Scope</span>     <span style="color: magenta; font-weight: 600;">Source</span>   <span style="color: magenta; font-weight: 600;">Invariant</span>                               │
│ <span style="opacity: 0.7;">────────  ───────  ──────────────────────────────────</span>      │
│ plan      CLI      Never modify production DB schemas      │
│ plan      CLI      All changes must include test coverage  │
│ project   config   API responses must be backward-compat   │
│ global    config   Follow Python PEP 8 style guide         │
╰────────────────────────────────────────────────────────────╯

<span style="color: #66cc66; font-weight: 600;">✓ OK</span> Plan created — strategize in progress
</code></pre></div>

=== "Plain"

```
$ agents plan use local/security-audit local/api-service local/web-app \
  --invariant "Never modify production database schemas" \
  --invariant "All changes must include test coverage" \
  --automation-profile supervised

Plan Created
  Plan: 01HXM9D2ZK4Q7C2B3F2R4VYV6J
  Action: local/security-audit
  Phase: strategize (running)
  Automation: supervised

Target Projects
  1. local/api-service (3 resources)
  2. local/web-app (2 resources)

Plan Invariants
  Scope     Source   Invariant
  --------  -------  ----------------------------------
  plan      CLI      Never modify production DB schemas
  plan      CLI      All changes must include test coverage
  project   config   API responses must be backward-compat
  global    config   Follow Python PEP 8 style guide

[OK] Plan created -- strategize in progress
```

=== "JSON"

```json
{
  "command": "plan use",
  "status": "ok",
  "exit_code": 0,
  "data": {
    "plan_id": "01HXM9D2ZK4Q7C2B3F2R4VYV6J",
    "action": "local/security-audit",
    "phase": "strategize",
    "phase_state": "running",
    "automation_profile": "supervised",
    "target_projects": [
      { "name": "local/api-service", "resources": 3 },
      { "name": "local/web-app", "resources": 2 }
    ],
    "invariants": [
      { "scope": "plan", "source": "CLI", "text": "Never modify production DB schemas" },
      { "scope": "plan", "source": "CLI", "text": "All changes must include test coverage" },
      { "scope": "project", "source": "config", "text": "API responses must be backward-compat" },
      { "scope": "global", "source": "config", "text": "Follow Python PEP 8 style guide" }
    ]
  },
  "timing": { "started": "2026-02-09T14:30:00Z", "duration_ms": 130 },
  "messages": ["Plan created -- strategize in progress"]
}
```

=== "YAML"

```yaml
command: plan use
status: ok
exit_code: 0
data:
  plan_id: "01HXM9D2ZK4Q7C2B3F2R4VYV6J"
  action: "local/security-audit"
  phase: strategize
  phase_state: running
  automation_profile: supervised
  target_projects:
    - name: "local/api-service"
      resources: 3
    - name: "local/web-app"
      resources: 2
  invariants:
    - scope: plan
      source: CLI
      text: "Never modify production DB schemas"
    - scope: plan
      source: CLI
      text: "All changes must include test coverage"
    - scope: project
      source: config
      text: "API responses must be backward-compat"
    - scope: global
      source: config
      text: "Follow Python PEP 8 style guide"
timing:
  started: "2026-02-09T14:30:00Z"
  duration_ms: 130
messages:
  - "Plan created -- strategize in progress"
```

Using an action with custom actor overrides:

=== "Rich"

<div class="highlight"><pre><code>
<span style="color: #66cc66; font-weight: 600;">$</span> <span style="color: cyan; font-weight: 600;">agents</span> plan use local/code-coverage local/api-service \
  <span style="color: cyan;">--strategy-actor</span> local/senior-planner \
  <span style="color: cyan;">--execution-actor</span> local/fast-executor \
  <span style="color: cyan;">--arg</span> target_coverage_percent=95

╭─ Plan Created ──────────────────────────────────────╮
│ <span style="color: cyan; font-weight: 600;">Plan:</span> 01HXM9E3ZK4Q7C2B3F2R4VYV6J                    │
│ <span style="color: #5599ff; font-weight: 600;">Action:</span> local/code-coverage                         │
│ <span style="color: yellow;">Phase:</span> strategize (running)                         │
│ <span style="color: magenta; font-weight: 600;">Automation:</span> trusted                                 │
╰─────────────────────────────────────────────────────╯

╭─ Actor Overrides ────────────────────╮
│ <span style="color: #5599ff; font-weight: 600;">Strategy:</span> local/senior-planner       │
│ <span style="color: #5599ff; font-weight: 600;">Execution:</span> local/fast-executor       │
│ <span style="opacity: 0.7;">(Estimation: action default)</span>         │
╰──────────────────────────────────────╯

╭─ Arguments ─────────────────╮
│ <span style="color: cyan; font-weight: 600;">target_coverage_percent:</span> 95 │
╰─────────────────────────────╯

<span style="color: #66cc66; font-weight: 600;">✓ OK</span> Plan created — strategize in progress
</code></pre></div>

=== "Plain"

```
$ agents plan use local/code-coverage local/api-service \
  --strategy-actor local/senior-planner \
  --execution-actor local/fast-executor \
  --arg target_coverage_percent=95

Plan Created
  Plan: 01HXM9E3ZK4Q7C2B3F2R4VYV6J
  Action: local/code-coverage
  Phase: strategize (running)
  Automation: trusted

Actor Overrides
  Strategy: local/senior-planner
  Execution: local/fast-executor
  (Estimation: action default)

Arguments
  target_coverage_percent: 95

[OK] Plan created -- strategize in progress
```

=== "JSON"

```json
{
  "command": "plan use",
  "status": "ok",
  "exit_code": 0,
  "data": {
    "plan_id": "01HXM9E3ZK4Q7C2B3F2R4VYV6J",
    "action": "local/code-coverage",
    "phase": "strategize",
    "phase_state": "running",
    "automation_profile": "trusted",
    "actor_overrides": {
      "strategy": "local/senior-planner",
      "execution": "local/fast-executor",
      "estimation": null
    },
    "arguments": {
      "target_coverage_percent": 95
    }
  },
  "timing": { "started": "2026-02-09T14:30:00Z", "duration_ms": 110 },
  "messages": ["Plan created -- strategize in progress"]
}
```

=== "YAML"

```yaml
command: plan use
status: ok
exit_code: 0
data:
  plan_id: "01HXM9E3ZK4Q7C2B3F2R4VYV6J"
  action: "local/code-coverage"
  phase: strategize
  phase_state: running
  automation_profile: trusted
  actor_overrides:
    strategy: "local/senior-planner"
    execution: "local/fast-executor"
    estimation: null
  arguments:
    target_coverage_percent: 95
timing:
  started: "2026-02-09T14:30:00Z"
  duration_ms: 110
messages:
  - "Plan created -- strategize in progress"
```
agents plan execute
agents plan execute <PLAN_ID>

Purpose Start or resume execution for a plan.

Arguments

  • <PLAN_ID>: Plan ID (required).

Examples

=== "Rich"

<div class="highlight"><pre><code>
<span style="color: #66cc66; font-weight: 600;">$</span> <span style="color: cyan; font-weight: 600;">agents</span> plan execute 01HXM8C2ZK4Q7C2B3F2R4VYV6J

╭─ Execution ──────────────────────╮
│ <span style="color: cyan; font-weight: 600;">Plan:</span> 01HXM8C2ZK4Q7C2B3F2R4VYV6J │
│ <span style="color: yellow; font-weight: 600;">Phase:</span> execute                   │
│ <span style="color: magenta; font-weight: 600;">Sandbox:</span> git_worktree            │
│ <span style="color: #5599ff; font-weight: 600;">Worker:</span> local/executor           │
│ <span style="color: #66cc66; font-weight: 600;">Started:</span> 12:58:10                │
│ <span style="color: #5599ff; font-weight: 600;">Attempt:</span> 1                       │
╰──────────────────────────────────╯

╭─ Sandbox ──────────────────────────────────╮
│ <span style="color: #5599ff; font-weight: 600;">Strategy:</span> git_worktree                     │
│ <span style="color: #5599ff; font-weight: 600;">Path:</span> /repos/api/.worktrees/plan-01HXM8    │
│ <span style="color: #5599ff; font-weight: 600;">Branch:</span> cleveragents/plan-01HXM8C2         │
│ <span style="color: #66cc66; font-weight: 600;">Status:</span> active                             │
╰────────────────────────────────────────────╯

╭─ Strategy Summary ─────────────────────╮
│ <span style="color: #5599ff; font-weight: 600;">Decisions:</span> 8                           │
│ <span style="color: magenta; font-weight: 600;">Invariants:</span> 2                          │
│ <span style="color: #5599ff; font-weight: 600;">Planned Child Plans:</span> 2+                │
│ <span style="color: #5599ff; font-weight: 600;">Estimated Files:</span> ~12                   │
│ <span style="color: #5599ff; font-weight: 600;">Risk:</span> low                              │
╰────────────────────────────────────────╯

╭─ Progress ─────────╮
│ <span style="color: cyan; font-weight: 600;">⏳</span> Collect context │
│ <span style="color: yellow; font-weight: 600;">•</span> Run tools        │
│ <span style="color: yellow; font-weight: 600;">•</span> Build changeset  │
│ <span style="color: yellow; font-weight: 600;">•</span> Validate         │
╰────────────────────╯

<span style="color: #66cc66; font-weight: 600;">✓ OK</span> Execution started
</code></pre></div>

=== "Plain"

```
$ agents plan execute 01HXM8C2ZK4Q7C2B3F2R4VYV6J

Execution
  Plan: 01HXM8C2ZK4Q7C2B3F2R4VYV6J
  Phase: execute
  Sandbox: git_worktree
  Worker: local/executor
  Started: 12:58:10
  Attempt: 1

Sandbox
  Strategy: git_worktree
  Path: /repos/api/.worktrees/plan-01HXM8
  Branch: cleveragents/plan-01HXM8C2
  Status: active

Strategy Summary
  Decisions: 8
  Invariants: 2
  Planned Child Plans: 2+
  Estimated Files: ~12
  Risk: low

Progress
  ... Collect context
  . Run tools
  . Build changeset
  . Validate

[OK] Execution started
```

=== "JSON"

```json
{
  "command": "plan execute",
  "status": "ok",
  "exit_code": 0,
  "data": {
    "plan_id": "01HXM8C2ZK4Q7C2B3F2R4VYV6J",
    "phase": "execute",
    "sandbox": {
      "strategy": "git_worktree",
      "path": "/repos/api/.worktrees/plan-01HXM8",
      "branch": "cleveragents/plan-01HXM8C2",
      "status": "active"
    },
    "worker": "local/executor",
    "started": "12:58:10",
    "attempt": 1,
    "strategy_summary": {
      "decisions": 8,
      "invariants": 2,
      "planned_child_plans": "2+",
      "estimated_files": 12,
      "risk": "low"
    },
    "progress": [
      { "label": "Collect context", "status": "running" },
      { "label": "Run tools", "status": "pending" },
      { "label": "Build changeset", "status": "pending" },
      { "label": "Validate", "status": "pending" }
    ]
  },
  "timing": { "started": "2026-02-09T14:30:00Z", "duration_ms": 150 },
  "messages": ["Execution started"]
}
```

=== "YAML"

```yaml
command: plan execute
status: ok
exit_code: 0
data:
  plan_id: "01HXM8C2ZK4Q7C2B3F2R4VYV6J"
  phase: execute
  sandbox:
    strategy: git_worktree
    path: "/repos/api/.worktrees/plan-01HXM8"
    branch: "cleveragents/plan-01HXM8C2"
    status: active
  worker: "local/executor"
  started: "12:58:10"
  attempt: 1
  strategy_summary:
    decisions: 8
    invariants: 2
    planned_child_plans: "2+"
    estimated_files: 12
    risk: low
  progress:
    - label: "Collect context"
      status: running
    - label: "Run tools"
      status: pending
    - label: "Build changeset"
      status: pending
    - label: "Validate"
      status: pending
timing:
  started: "2026-02-09T14:30:00Z"
  duration_ms: 150
messages:
  - "Execution started"
```

Resuming execution of a plan that was previously paused or errored:

=== "Rich"

<div class="highlight"><pre><code>
<span style="color: #66cc66; font-weight: 600;">$</span> <span style="color: cyan; font-weight: 600;">agents</span> plan execute 01HXM7K2ZK4Q7C2B3F2R4VYV6J

╭─ Execution Resumed ─────────────────╮
│ <span style="color: cyan; font-weight: 600;">Plan:</span> 01HXM7K2ZK4Q7C2B3F2R4VYV6J    │
│ <span style="color: yellow; font-weight: 600;">Phase:</span> execute (resumed)            │
│ <span style="color: magenta; font-weight: 600;">Sandbox:</span> git_worktree               │
│ <span style="color: #5599ff; font-weight: 600;">Worker:</span> local/executor              │
│ <span style="color: #5599ff; font-weight: 600;">Checkpoint:</span> cp_01HXM8C2 (loaded)    │
│ <span style="color: #5599ff; font-weight: 600;">Resumed From:</span> step 4 of 6           │
╰─────────────────────────────────────╯

╭─ Previous Progress ──────────────────────╮
│ <span style="color: #66cc66; font-weight: 600;">✓</span> Step 1: Collect context (0.8s)         │
│ <span style="color: #66cc66; font-weight: 600;">✓</span> Step 2: Analyze codebase (4.2s)        │
│ <span style="color: #66cc66; font-weight: 600;">✓</span> Step 3: Generate migrations (6.1s)     │
│ <span style="color: #ff6666; font-weight: 600;">✗</span> Step 4: Apply migrations (errored)     │
│ <span style="opacity: 0.5;">○</span> Step 5: Update models (pending)        │
│ <span style="opacity: 0.5;">○</span> Step 6: Run validations (pending)      │
╰──────────────────────────────────────────╯

╭─ Guidance Applied ──────────────────────────────────────────────╮
│ <span style="opacity: 0.7;"><span style="color: #66cc66;">"Use smaller batch sizes for the migration to avoid timeouts"</span></span>   │
╰─────────────────────────────────────────────────────────────────╯

<span style="color: #66cc66; font-weight: 600;">✓ OK</span> Execution resumed from checkpoint cp_01HXM8C2
</code></pre></div>

=== "Plain"

```
$ agents plan execute 01HXM7K2ZK4Q7C2B3F2R4VYV6J

Execution Resumed
  Plan: 01HXM7K2ZK4Q7C2B3F2R4VYV6J
  Phase: execute (resumed)
  Sandbox: git_worktree
  Worker: local/executor
  Checkpoint: cp_01HXM8C2 (loaded)
  Resumed From: step 4 of 6

Previous Progress
  v Step 1: Collect context (0.8s)
  v Step 2: Analyze codebase (4.2s)
  v Step 3: Generate migrations (6.1s)
  x Step 4: Apply migrations (errored)
  o Step 5: Update models (pending)
  o Step 6: Run validations (pending)

Guidance Applied
  "Use smaller batch sizes for the migration to avoid timeouts"

[OK] Execution resumed from checkpoint cp_01HXM8C2
```

=== "JSON"

```json
{
  "command": "plan execute",
  "status": "ok",
  "exit_code": 0,
  "data": {
    "plan_id": "01HXM7K2ZK4Q7C2B3F2R4VYV6J",
    "phase": "execute",
    "resumed": true,
    "sandbox": "git_worktree",
    "worker": "local/executor",
    "checkpoint": "cp_01HXM8C2",
    "resumed_from": "step 4 of 6",
    "progress": [
      { "step": 1, "label": "Collect context", "status": "done", "duration_s": 0.8 },
      { "step": 2, "label": "Analyze codebase", "status": "done", "duration_s": 4.2 },
      { "step": 3, "label": "Generate migrations", "status": "done", "duration_s": 6.1 },
      { "step": 4, "label": "Apply migrations", "status": "errored" },
      { "step": 5, "label": "Update models", "status": "pending" },
      { "step": 6, "label": "Run validations", "status": "pending" }
    ],
    "guidance": "Use smaller batch sizes for the migration to avoid timeouts"
  },
  "timing": { "started": "2026-02-09T14:30:00Z", "duration_ms": 180 },
  "messages": ["Execution resumed from checkpoint cp_01HXM8C2"]
}
```

=== "YAML"

```yaml
command: plan execute
status: ok
exit_code: 0
data:
  plan_id: "01HXM7K2ZK4Q7C2B3F2R4VYV6J"
  phase: execute
  resumed: true
  sandbox: git_worktree
  worker: "local/executor"
  checkpoint: "cp_01HXM8C2"
  resumed_from: "step 4 of 6"
  progress:
    - step: 1
      label: "Collect context"
      status: done
      duration_s: 0.8
    - step: 2
      label: "Analyze codebase"
      status: done
      duration_s: 4.2
    - step: 3
      label: "Generate migrations"
      status: done
      duration_s: 6.1
    - step: 4
      label: "Apply migrations"
      status: errored
    - step: 5
      label: "Update models"
      status: pending
    - step: 6
      label: "Run validations"
      status: pending
  guidance: "Use smaller batch sizes for the migration to avoid timeouts"
timing:
  started: "2026-02-09T14:30:00Z"
  duration_ms: 180
messages:
  - "Execution resumed from checkpoint cp_01HXM8C2"
```
agents plan apply
agents plan apply [--yes|-y] <PLAN_ID>

Purpose Apply sandboxed changes to real resources.

Arguments

  • <PLAN_ID>: Plan ID.
  • --yes: Skip confirmation.

Examples

=== "Rich"

<div class="highlight"><pre><code>
<span style="color: #66cc66; font-weight: 600;">$</span> <span style="color: cyan; font-weight: 600;">agents</span> plan apply 01HXM8C2ZK4Q7C2B3F2R4VYV6J

Apply changes for plan 01HXM8C2ZK4Q7C2B3F2R4VYV6J? [y/N]: y

╭─ Apply Summary ─────────────────────╮
│ <span style="color: cyan; font-weight: 600;">Plan:</span> 01HXM8C2ZK4Q7C2B3F2R4VYV6J    │
│ <span style="color: #5599ff; font-weight: 600;">Artifacts:</span> 6 files updated          │
│ <span style="color: yellow; font-weight: 600;">Changes:</span> 42 insertions, 9 deletions │
│ <span style="color: #5599ff; font-weight: 600;">Project:</span> local/api-service          │
│ <span style="color: #66cc66; font-weight: 600;">Applied At:</span> 2026-02-08 13:04        │
╰─────────────────────────────────────╯

╭─ Validation (from Execute) ────╮
│ <span style="color: #66cc66; font-weight: 600;">Tests:</span> passed (24/24)          │
│ <span style="color: #66cc66; font-weight: 600;">Lint:</span> passed (0 warnings)      │
│ <span style="color: #66cc66; font-weight: 600;">Type Check:</span> passed (0 errors)  │
│ <span style="color: #66cc66; font-weight: 600;">Duration:</span> 12.4s                │
╰────────────────────────────────╯

╭─ Sandbox Cleanup ─────────╮
│ <span style="color: #66cc66; font-weight: 600;">Worktree:</span> removed         │
│ <span style="color: #66cc66; font-weight: 600;">Branch:</span> merged to main    │
│ <span style="color: #5599ff; font-weight: 600;">Checkpoint:</span> archived      │
╰───────────────────────────╯

╭─ Plan Lifecycle ────────────────────────╮
│ <span style="color: #66cc66; font-weight: 600;">Phase:</span> apply                            │
│ <span style="color: #66cc66; font-weight: 600;">State:</span> applied                          │
│ <span style="color: #5599ff; font-weight: 600;">Total Duration:</span> 00:06:14                │
│ <span style="color: yellow; font-weight: 600;">Total Cost:</span> $0.0847                     │
│ <span style="color: #5599ff; font-weight: 600;">Decisions Made:</span> 8                       │
│ <span style="color: #5599ff; font-weight: 600;">Child Plans:</span> 2 (completed)              │
╰─────────────────────────────────────────╯

╭─ Next Steps ──────╮
│ - Review git diff │
│ - Commit changes  │
╰───────────────────╯

<span style="color: #66cc66; font-weight: 600;">✓ OK</span> Changes applied
</code></pre></div>

=== "Plain"

```
$ agents plan apply 01HXM8C2ZK4Q7C2B3F2R4VYV6J

Apply changes for plan 01HXM8C2ZK4Q7C2B3F2R4VYV6J? [y/N]: y

Apply Summary
  Plan: 01HXM8C2ZK4Q7C2B3F2R4VYV6J
  Artifacts: 6 files updated
  Changes: 42 insertions, 9 deletions
  Project: local/api-service
  Applied At: 2026-02-08 13:04

Validation (from Execute)
  Tests: passed (24/24)
  Lint: passed (0 warnings)
  Type Check: passed (0 errors)
  Duration: 12.4s

Sandbox Cleanup
  Worktree: removed
  Branch: merged to main
  Checkpoint: archived

Plan Lifecycle
  Phase: apply
  State: applied
  Total Duration: 00:06:14
  Total Cost: $0.0847
  Decisions Made: 8
  Child Plans: 2 (completed)

Next Steps
  - Review git diff
  - Commit changes

[OK] Changes applied
```

=== "JSON"

```json
{
  "command": "plan apply",
  "status": "ok",
  "exit_code": 0,
  "data": {
    "plan_id": "01HXM8C2ZK4Q7C2B3F2R4VYV6J",
    "artifacts": 6,
    "changes": {
      "insertions": 42,
      "deletions": 9
    },
    "project": "local/api-service",
    "applied_at": "2026-02-08T13:04:00Z",
    "validation": {
      "tests": { "status": "passed", "passed": 24, "total": 24 },
      "lint": { "status": "passed", "warnings": 0 },
      "type_check": { "status": "passed", "errors": 0 },
      "duration_s": 12.4
    },
    "sandbox_cleanup": {
      "worktree": "removed",
      "branch": "merged to main",
      "checkpoint": "archived"
    },
    "lifecycle": {
      "phase": "apply",
      "state": "applied",
      "total_duration": "00:06:14",
      "total_cost": "$0.0847",
      "decisions_made": 8,
      "child_plans": 2
    }
  },
  "timing": { "started": "2026-02-09T14:30:00Z", "duration_ms": 1250 },
  "messages": ["Changes applied"]
}
```

=== "YAML"

```yaml
command: plan apply
status: ok
exit_code: 0
data:
  plan_id: "01HXM8C2ZK4Q7C2B3F2R4VYV6J"
  artifacts: 6
  changes:
    insertions: 42
    deletions: 9
  project: "local/api-service"
  applied_at: "2026-02-08T13:04:00Z"
  validation:
    tests:
      status: passed
      passed: 24
      total: 24
    lint:
      status: passed
      warnings: 0
    type_check:
      status: passed
      errors: 0
    duration_s: 12.4
  sandbox_cleanup:
    worktree: removed
    branch: merged to main
    checkpoint: archived
  lifecycle:
    phase: apply
    state: applied
    total_duration: "00:06:14"
    total_cost: "$0.0847"
    decisions_made: 8
    child_plans: 2
timing:
  started: "2026-02-09T14:30:00Z"
  duration_ms: 1250
messages:
  - "Changes applied"
```

When Execute-phase validations failed and were not resolved (e.g., the execution actor exhausted its retry limit or failed outright), the plan cannot be applied. Apply checks the validation record and refuses to proceed:

=== "Rich"

<div class="highlight"><pre><code>
<span style="color: #66cc66; font-weight: 600;">$</span> <span style="color: cyan; font-weight: 600;">agents</span> plan apply <span style="color: cyan;">--yes</span> 01HXM8C2ZK4Q7C2B3F2R4VYV6J

╭─ Apply Summary ─────────────────────╮
│ <span style="color: cyan; font-weight: 600;">Plan:</span> 01HXM8C2ZK4Q7C2B3F2R4VYV6J    │
│ <span style="color: #5599ff; font-weight: 600;">Artifacts:</span> 6 files updated          │
│ <span style="color: yellow;">Changes:</span> 42 insertions, 9 deletions │
│ <span style="color: #5599ff; font-weight: 600;">Project:</span> local/api-service          │
╰─────────────────────────────────────╯

╭─ Validation ───────────────────────────────────────────────╮
│ <span style="color: #ff6666; font-weight: 600;">✗</span> Tests: <span style="color: #ff6666; font-weight: 600;">FAILED</span> (22/24 passed, 2 failed)                   │
│   <span style="opacity: 0.7;">FAIL test_auth.py::test_session_refresh — AssertionError</span> │
│   <span style="opacity: 0.7;">FAIL test_auth.py::test_token_expiry — TimeoutError</span>      │
│ <span style="color: #66cc66; font-weight: 600;">✓</span> Lint: passed (0 warnings)                                │
│ <span style="color: #66cc66; font-weight: 600;">✓</span> Type Check: passed (0 errors)                            │
│ <span style="color: #5599ff; font-weight: 600;">Duration:</span> 14.8s                                            │
╰────────────────────────────────────────────────────────────╯

╭─ Sandbox Status ─────────────────────────────────────────────╮
│ <span style="color: yellow; font-weight: 600;">Worktree:</span> preserved (changes NOT committed)                  │
│ <span style="color: #5599ff; font-weight: 600;">Checkpoint:</span> cp_01HXM8C2 (pre-apply state available)          │
│ <span style="opacity: 0.7;">The sandbox is preserved for correction or manual review.</span>    │
╰──────────────────────────────────────────────────────────────╯

╭─ Recovery Options ──────────────────────────────────────────────────╮
│ - <span style="color: cyan;">agents plan prompt</span> <span style="opacity: 0.7;">— provide guidance to fix test failures</span>        │
│ - <span style="color: cyan;">agents plan correct</span> <span style="opacity: 0.7;">— revert and re-execute with guidance</span>         │
│ - <span style="color: cyan;">agents plan rollback</span> <span style="opacity: 0.7;">— restore to a previous checkpoint</span>           │
│ - <span style="color: cyan;">agents plan cancel</span> <span style="opacity: 0.7;">— abort the plan entirely</span>                      │
╰─────────────────────────────────────────────────────────────────────╯

<span style="color: #ff6666; font-weight: 600;">✗ ERROR</span> Apply refused — 2 required Execute-phase validations did not pass
</code></pre></div>

=== "Plain"

```
$ agents plan apply --yes 01HXM8C2ZK4Q7C2B3F2R4VYV6J

Apply Summary
  Plan: 01HXM8C2ZK4Q7C2B3F2R4VYV6J
  Artifacts: 6 files updated
  Changes: 42 insertions, 9 deletions
  Project: local/api-service

Validation
  x Tests: FAILED (22/24 passed, 2 failed)
    FAIL test_auth.py::test_session_refresh -- AssertionError
    FAIL test_auth.py::test_token_expiry -- TimeoutError
  v Lint: passed (0 warnings)
  v Type Check: passed (0 errors)
  Duration: 14.8s

Sandbox Status
  Worktree: preserved (changes NOT committed)
  Checkpoint: cp_01HXM8C2 (pre-apply state available)
  The sandbox is preserved for correction or manual review.

Recovery Options
  - agents plan prompt -- provide guidance to fix test failures
  - agents plan correct -- revert and re-execute with guidance
  - agents plan rollback -- restore to a previous checkpoint
  - agents plan cancel -- abort the plan entirely

[ERROR] Apply refused -- 2 required Execute-phase validations did not pass
```

=== "JSON"

```json
{
  "command": "plan apply",
  "status": "error",
  "exit_code": 1,
  "data": {
    "plan_id": "01HXM8C2ZK4Q7C2B3F2R4VYV6J",
    "artifacts": 6,
    "changes": {
      "insertions": 42,
      "deletions": 9
    },
    "project": "local/api-service",
    "validation": {
      "tests": {
        "status": "failed",
        "passed": 22,
        "total": 24,
        "failures": [
          "test_auth.py::test_session_refresh -- AssertionError",
          "test_auth.py::test_token_expiry -- TimeoutError"
        ]
      },
      "lint": { "status": "passed", "warnings": 0 },
      "type_check": { "status": "passed", "errors": 0 },
      "duration_s": 14.8
    },
    "sandbox": {
      "worktree": "preserved",
      "checkpoint": "cp_01HXM8C2"
    },
    "recovery_options": [
      "agents plan prompt",
      "agents plan correct",
      "agents plan rollback",
      "agents plan cancel"
    ]
  },
  "timing": { "started": "2026-02-09T14:30:00Z", "duration_ms": 320 },
  "messages": ["Apply refused -- 2 required Execute-phase validations did not pass"]
}
```

=== "YAML"

```yaml
command: plan apply
status: error
exit_code: 1
data:
  plan_id: "01HXM8C2ZK4Q7C2B3F2R4VYV6J"
  artifacts: 6
  changes:
    insertions: 42
    deletions: 9
  project: "local/api-service"
  validation:
    tests:
      status: failed
      passed: 22
      total: 24
      failures:
        - "test_auth.py::test_session_refresh -- AssertionError"
        - "test_auth.py::test_token_expiry -- TimeoutError"
    lint:
      status: passed
      warnings: 0
    type_check:
      status: passed
      errors: 0
    duration_s: 14.8
  sandbox:
    worktree: preserved
    checkpoint: "cp_01HXM8C2"
  recovery_options:
    - "agents plan prompt"
    - "agents plan correct"
    - "agents plan rollback"
    - "agents plan cancel"
timing:
  started: "2026-02-09T14:30:00Z"
  duration_ms: 320
messages:
  - "Apply refused -- 2 required Execute-phase validations did not pass"
```
agents plan status
agents plan status <PLAN_ID>

Purpose Show detailed status for a plan.

Arguments

  • <PLAN_ID>: Plan ID (required).

Examples

=== "Rich"

<div class="highlight"><pre><code>
<span style="color: #66cc66; font-weight: 600;">$</span> <span style="color: cyan; font-weight: 600;">agents</span> plan status 01HXM8C2ZK4Q7C2B3F2R4VYV6J

╭─ Plan Status ────────────────────╮
│ <span style="color: cyan; font-weight: 600;">Plan:</span> 01HXM8C2ZK4Q7C2B3F2R4VYV6J │
│ <span style="color: yellow; font-weight: 600;">Phase:</span> execute                   │
│ <span style="color: magenta; font-weight: 600;">State:</span> processing                │
│ <span style="color: #5599ff; font-weight: 600;">Action:</span> local/code-coverage      │
│ <span style="color: #66cc66; font-weight: 600;">Project:</span> local/api-service       │
│ <span style="color: #5599ff; font-weight: 600;">Automation:</span> review               │
│ <span style="color: #5599ff; font-weight: 600;">Attempt:</span> 1                       │
╰──────────────────────────────────╯

╭─ Progress ───────╮
│ <span style="color: #66cc66; font-weight: 600;">✓</span> Strategize     │
│ <span style="color: cyan; font-weight: 600;">⏳</span> Execute       │
│ <span style="color: yellow; font-weight: 600;">•</span> Apply (queued) │
╰──────────────────╯

╭─ Timing ──────────╮
│ <span style="color: #66cc66; font-weight: 600;">Started:</span> 12:57:01 │
│ <span style="color: yellow; font-weight: 600;">Elapsed:</span> 00:01:12 │
│ <span style="color: #66cc66; font-weight: 600;">ETA:</span> 00:03:45     │
╰───────────────────╯

╭─ Execution Detail ──────────╮
│ <span style="color: #5599ff; font-weight: 600;">Sandbox:</span> git_worktree       │
│ <span style="color: #5599ff; font-weight: 600;">Tool Calls:</span> 8               │
│ <span style="color: #5599ff; font-weight: 600;">Files Modified:</span> 3           │
│ <span style="color: #5599ff; font-weight: 600;">Child Plans:</span> 1/2 complete   │
│ <span style="color: #5599ff; font-weight: 600;">Checkpoints:</span> 2 created      │
╰─────────────────────────────╯

╭─ Cost ───────────────╮
│ <span style="color: #5599ff; font-weight: 600;">Tokens Used:</span> 12,420  │
│ <span style="color: yellow; font-weight: 600;">Cost So Far:</span> $0.041  │
│ <span style="color: #5599ff; font-weight: 600;">Estimated:</span> $0.085    │
╰──────────────────────╯

<span style="color: #66cc66; font-weight: 600;">✓ OK</span> Status refreshed
</code></pre></div>

=== "Plain"

```
$ agents plan status 01HXM8C2ZK4Q7C2B3F2R4VYV6J

Plan Status
  Plan: 01HXM8C2ZK4Q7C2B3F2R4VYV6J
  Phase: execute
  State: processing
  Action: local/code-coverage
  Project: local/api-service
  Automation: review
  Attempt: 1

Progress
  [OK] Strategize
  ... Execute
  . Apply (queued)

Timing
  Started: 12:57:01
  Elapsed: 00:01:12
  ETA: 00:03:45

Execution Detail
  Sandbox: git_worktree
  Tool Calls: 8
  Files Modified: 3
  Child Plans: 1/2 complete
  Checkpoints: 2 created

Cost
  Tokens Used: 12,420
  Cost So Far: $0.041
  Estimated: $0.085

[OK] Status refreshed
```

=== "JSON"

```json
{
  "command": "plan status",
  "status": "ok",
  "exit_code": 0,
  "data": {
    "plan_id": "01HXM8C2ZK4Q7C2B3F2R4VYV6J",
    "phase": "execute",
    "state": "processing",
    "action": "local/code-coverage",
    "project": "local/api-service",
    "automation": "review",
    "attempt": 1,
    "progress": [
      { "step": "Strategize", "status": "done" },
      { "step": "Execute", "status": "running" },
      { "step": "Apply", "status": "queued" }
    ],
    "timing": {
      "started": "12:57:01",
      "elapsed": "00:01:12",
      "eta": "00:03:45"
    },
    "execution": {
      "sandbox": "git_worktree",
      "tool_calls": 8,
      "files_modified": 3,
      "child_plans": "1/2 complete",
      "checkpoints": 2
    },
    "cost": {
      "tokens_used": 12420,
      "cost_so_far": 0.041,
      "estimated": 0.085
    }
  },
  "timing": {
    "started": "2026-02-08T12:57:01Z",
    "duration_ms": 120
  },
  "messages": ["Status refreshed"]
}
```

=== "YAML"

```yaml
command: plan status
status: ok
exit_code: 0
data:
  plan_id: "01HXM8C2ZK4Q7C2B3F2R4VYV6J"
  phase: execute
  state: processing
  action: local/code-coverage
  project: local/api-service
  automation: review
  attempt: 1
  progress:
    - step: Strategize
      status: done
    - step: Execute
      status: running
    - step: Apply
      status: queued
  timing:
    started: "12:57:01"
    elapsed: "00:01:12"
    eta: "00:03:45"
  execution:
    sandbox: git_worktree
    tool_calls: 8
    files_modified: 3
    child_plans: "1/2 complete"
    checkpoints: 2
  cost:
    tokens_used: 12420
    cost_so_far: 0.041
    estimated: 0.085
timing:
  started: "2026-02-08T12:57:01Z"
  duration_ms: 120
messages:
  - "Status refreshed"
```

Status of a plan that has completed successfully:

=== "Rich"

<div class="highlight"><pre><code>
<span style="color: #66cc66; font-weight: 600;">$</span> <span style="color: cyan; font-weight: 600;">agents</span> plan status 01HXM6R3ZK4Q7C2B3F2R4VYV6J

╭─ Plan Status ─────────────────────╮
│ <span style="color: cyan; font-weight: 600;">Plan:</span> 01HXM6R3ZK4Q7C2B3F2R4VYV6J  │
│ <span style="color: #66cc66; font-weight: 600;">Phase:</span> apply                      │
│ <span style="color: #66cc66; font-weight: 600;">State:</span> applied                    │
│ <span style="color: #5599ff; font-weight: 600;">Action:</span> local/add-auth            │
│ <span style="color: #66cc66; font-weight: 600;">Project:</span> local/api-service        │
│ <span style="color: #5599ff; font-weight: 600;">Automation:</span> trusted               │
│ <span style="color: #5599ff; font-weight: 600;">Attempt:</span> 1                        │
╰───────────────────────────────────╯

╭─ Progress ───────────╮
│ <span style="color: #66cc66; font-weight: 600;">✓</span> Strategize         │
│ <span style="color: #66cc66; font-weight: 600;">✓</span> Execute            │
│ <span style="color: #66cc66; font-weight: 600;">✓</span> Apply (committed)  │
╰──────────────────────╯

╭─ Timing ─────────────────────────╮
│ <span style="color: #66cc66; font-weight: 600;">Started:</span> 2026-02-08 12:57:01     │
│ <span style="color: #66cc66; font-weight: 600;">Finished:</span> 2026-02-08 13:04:15    │
│ <span style="color: #5599ff; font-weight: 600;">Total Duration:</span> 00:07:14         │
╰──────────────────────────────────╯

╭─ Result ────────────────────────────╮
│ <span style="color: #5599ff; font-weight: 600;">Decisions Made:</span> 8                   │
│ <span style="color: #5599ff; font-weight: 600;">Child Plans:</span> 2/2 complete           │
│ <span style="color: #66cc66; font-weight: 600;">Artifacts:</span> 6 files updated          │
│ <span style="color: #66cc66; font-weight: 600;">Validations:</span> 3/3 passed             │
│ <span style="color: yellow;">Total Cost:</span> $0.085                  │
╰─────────────────────────────────────╯

<span style="color: #66cc66; font-weight: 600;">✓ OK</span> Plan completed successfully
</code></pre></div>

=== "Plain"

```
$ agents plan status 01HXM6R3ZK4Q7C2B3F2R4VYV6J

Plan Status
  Plan: 01HXM6R3ZK4Q7C2B3F2R4VYV6J
  Phase: apply
  State: applied
  Action: local/add-auth
  Project: local/api-service
  Automation: trusted
  Attempt: 1

Progress
  [OK] Strategize
  [OK] Execute
  [OK] Apply (committed)

Timing
  Started: 2026-02-08 12:57:01
  Finished: 2026-02-08 13:04:15
  Total Duration: 00:07:14

Result
  Decisions Made: 8
  Child Plans: 2/2 complete
  Artifacts: 6 files updated
  Validations: 3/3 passed
  Total Cost: $0.085

[OK] Plan completed successfully
```

=== "JSON"

```json
{
  "command": "plan status",
  "status": "ok",
  "exit_code": 0,
  "data": {
    "plan_id": "01HXM6R3ZK4Q7C2B3F2R4VYV6J",
    "phase": "apply",
    "state": "applied",
    "action": "local/add-auth",
    "project": "local/api-service",
    "automation": "trusted",
    "attempt": 1,
    "progress": [
      { "step": "Strategize", "status": "done" },
      { "step": "Execute", "status": "done" },
      { "step": "Apply", "status": "committed" }
    ],
    "timing": {
      "started": "2026-02-08T12:57:01Z",
      "finished": "2026-02-08T13:04:15Z",
      "total_duration": "00:07:14"
    },
    "result": {
      "decisions_made": 8,
      "child_plans": "2/2 complete",
      "artifacts": "6 files updated",
      "validations": "3/3 passed",
      "total_cost": 0.085
    }
  },
  "timing": {
    "started": "2026-02-08T12:57:01Z",
    "duration_ms": 434000
  },
  "messages": ["Plan completed successfully"]
}
```

=== "YAML"

```yaml
command: plan status
status: ok
exit_code: 0
data:
  plan_id: "01HXM6R3ZK4Q7C2B3F2R4VYV6J"
  phase: apply
  state: applied
  action: local/add-auth
  project: local/api-service
  automation: trusted
  attempt: 1
  progress:
    - step: Strategize
      status: done
    - step: Execute
      status: done
    - step: Apply
      status: committed
  timing:
    started: "2026-02-08T12:57:01Z"
    finished: "2026-02-08T13:04:15Z"
    total_duration: "00:07:14"
  result:
    decisions_made: 8
    child_plans: "2/2 complete"
    artifacts: "6 files updated"
    validations: "3/3 passed"
    total_cost: 0.085
timing:
  started: "2026-02-08T12:57:01Z"
  duration_ms: 434000
messages:
  - "Plan completed successfully"
```

Status of a plan in the strategize phase:

=== "Rich"

<div class="highlight"><pre><code>
<span style="color: #66cc66; font-weight: 600;">$</span> <span style="color: cyan; font-weight: 600;">agents</span> plan status 01HXM9F2ZK4Q7C2B3F2R4VYV6J

╭─ Plan Status ────────────────────╮
│ <span style="color: cyan; font-weight: 600;">Plan:</span> 01HXM9F2ZK4Q7C2B3F2R4VYV6J │
│ <span style="color: yellow;">Phase:</span> strategize                │
│ <span style="color: magenta; font-weight: 600;">State:</span> processing                │
│ <span style="color: #5599ff; font-weight: 600;">Action:</span> local/refactor-auth      │
│ <span style="color: #5599ff; font-weight: 600;">Project:</span> local/api-service       │
│ <span style="color: #5599ff; font-weight: 600;">Automation:</span> supervised           │
╰──────────────────────────────────╯

╭─ Progress ───────────────╮
│ <span style="color: cyan; font-weight: 600;">⏳</span> Strategize (running)  │
│ <span style="opacity: 0.5;">○</span> Execute (waiting)      │
│ <span style="opacity: 0.5;">○</span> Apply (waiting)        │
╰──────────────────────────╯

╭─ Strategy Progress ────────╮
│ <span style="color: #5599ff; font-weight: 600;">Decisions Made:</span> 4          │
│ <span style="color: #5599ff; font-weight: 600;">Invariants Enforced:</span> 2     │
│ <span style="color: #5599ff; font-weight: 600;">Child Plans Planned:</span> 3     │
│ <span style="color: yellow;">Elapsed:</span> 00:00:28          │
╰────────────────────────────╯

<span style="color: #66cc66; font-weight: 600;">✓ OK</span> Strategize in progress
</code></pre></div>

=== "Plain"

```
$ agents plan status 01HXM9F2ZK4Q7C2B3F2R4VYV6J

Plan Status
  Plan: 01HXM9F2ZK4Q7C2B3F2R4VYV6J
  Phase: strategize
  State: processing
  Action: local/refactor-auth
  Project: local/api-service
  Automation: supervised

Progress
  ... Strategize (running)
  o Execute (waiting)
  o Apply (waiting)

Strategy Progress
  Decisions Made: 4
  Invariants Enforced: 2
  Child Plans Planned: 3
  Elapsed: 00:00:28

[OK] Strategize in progress
```

=== "JSON"

```json
{
  "command": "plan status",
  "status": "ok",
  "exit_code": 0,
  "data": {
    "plan_id": "01HXM9F2ZK4Q7C2B3F2R4VYV6J",
    "phase": "strategize",
    "state": "processing",
    "action": "local/refactor-auth",
    "project": "local/api-service",
    "automation": "supervised",
    "progress": [
      { "step": "Strategize", "status": "running" },
      { "step": "Execute", "status": "waiting" },
      { "step": "Apply", "status": "waiting" }
    ],
    "strategy_progress": {
      "decisions_made": 4,
      "invariants_enforced": 2,
      "child_plans_planned": 3,
      "elapsed": "00:00:28"
    }
  },
  "timing": {
    "started": "2026-02-08T12:57:01Z",
    "duration_ms": 28000
  },
  "messages": ["Strategize in progress"]
}
```

=== "YAML"

```yaml
command: plan status
status: ok
exit_code: 0
data:
  plan_id: "01HXM9F2ZK4Q7C2B3F2R4VYV6J"
  phase: strategize
  state: processing
  action: local/refactor-auth
  project: local/api-service
  automation: supervised
  progress:
    - step: Strategize
      status: running
    - step: Execute
      status: waiting
    - step: Apply
      status: waiting
  strategy_progress:
    decisions_made: 4
    invariants_enforced: 2
    child_plans_planned: 3
    elapsed: "00:00:28"
timing:
  started: "2026-02-08T12:57:01Z"
  duration_ms: 28000
messages:
  - "Strategize in progress"
```

Status of a plan that encountered an error:

=== "Rich"

<div class="highlight"><pre><code>
<span style="color: #66cc66; font-weight: 600;">$</span> <span style="color: cyan; font-weight: 600;">agents</span> plan status 01HXM7K2ZK4Q7C2B3F2R4VYV6J

╭─ Plan Status ────────────────────╮
│ <span style="color: cyan; font-weight: 600;">Plan:</span> 01HXM7K2ZK4Q7C2B3F2R4VYV6J │
│ <span style="color: #ff6666; font-weight: 600;">Phase:</span> execute                   │
│ <span style="color: #ff6666; font-weight: 600;">State:</span> errored                   │
│ <span style="color: #5599ff; font-weight: 600;">Action:</span> local/migrate-db         │
│ <span style="color: #5599ff; font-weight: 600;">Project:</span> local/api-service       │
│ <span style="color: #5599ff; font-weight: 600;">Automation:</span> supervised           │
│ <span style="color: #5599ff; font-weight: 600;">Attempt:</span> 1                       │
╰──────────────────────────────────╯

╭─ Progress ───────────╮
│ <span style="color: #66cc66; font-weight: 600;">✓</span> Strategize         │
│ <span style="color: #ff6666; font-weight: 600;">✗</span> Execute            │
│ <span style="opacity: 0.5;">○</span> Apply (skipped)    │
╰──────────────────────╯

╭─ Error Detail ──────────────────────────────────────────────────╮
│ <span style="color: #ff6666; font-weight: 600;">Error:</span> Tool invocation failed: connection refused               │
│ <span style="color: #5599ff; font-weight: 600;">Tool:</span> local/db-migrate                                          │
│ <span style="color: #5599ff; font-weight: 600;">Step:</span> 4 of 6                                                    │
│ <span style="color: #5599ff; font-weight: 600;">Checkpoint:</span> cp_01HXM8C2 (sandbox state preserved)               │
│ <span style="opacity: 0.7;">Recoverable: yes — use <span style="color: #66cc66;">"agents plan prompt"</span> to provide guidance</span> │
╰─────────────────────────────────────────────────────────────────╯

╭─ Cost ───────────────╮
│ <span style="color: #5599ff; font-weight: 600;">Tokens Used:</span> 8,340   │
│ <span style="color: yellow;">Cost So Far:</span> $0.028  │
╰──────────────────────╯

<span style="color: #ff6666; font-weight: 600;">✗ ERROR</span> Plan errored — use `agents plan prompt` to resume or `agents plan cancel` to abort
</code></pre></div>

=== "Plain"

```
$ agents plan status 01HXM7K2ZK4Q7C2B3F2R4VYV6J

Plan Status
  Plan: 01HXM7K2ZK4Q7C2B3F2R4VYV6J
  Phase: execute
  State: errored
  Action: local/migrate-db
  Project: local/api-service
  Automation: supervised
  Attempt: 1

Progress
  [OK] Strategize
  [FAIL] Execute
  o Apply (skipped)

Error Detail
  Error: Tool invocation failed: connection refused
  Tool: local/db-migrate
  Step: 4 of 6
  Checkpoint: cp_01HXM8C2 (sandbox state preserved)
  Recoverable: yes -- use "agents plan prompt" to provide guidance

Cost
  Tokens Used: 8,340
  Cost So Far: $0.028

[ERROR] Plan errored -- use `agents plan prompt` to resume or `agents plan cancel` to abort
```

=== "JSON"

```json
{
  "command": "plan status",
  "status": "error",
  "exit_code": 1,
  "data": {
    "plan_id": "01HXM7K2ZK4Q7C2B3F2R4VYV6J",
    "phase": "execute",
    "state": "errored",
    "action": "local/migrate-db",
    "project": "local/api-service",
    "automation": "supervised",
    "attempt": 1,
    "progress": [
      { "step": "Strategize", "status": "done" },
      { "step": "Execute", "status": "failed" },
      { "step": "Apply", "status": "skipped" }
    ],
    "error": {
      "message": "Tool invocation failed: connection refused",
      "tool": "local/db-migrate",
      "step": "4 of 6",
      "checkpoint": "cp_01HXM8C2",
      "recoverable": true
    },
    "cost": {
      "tokens_used": 8340,
      "cost_so_far": 0.028
    }
  },
  "timing": {
    "started": "2026-02-08T12:57:01Z",
    "duration_ms": 72000
  },
  "messages": ["Plan errored — use `agents plan prompt` to resume or `agents plan cancel` to abort"]
}
```

=== "YAML"

```yaml
command: plan status
status: error
exit_code: 1
data:
  plan_id: "01HXM7K2ZK4Q7C2B3F2R4VYV6J"
  phase: execute
  state: errored
  action: local/migrate-db
  project: local/api-service
  automation: supervised
  attempt: 1
  progress:
    - step: Strategize
      status: done
    - step: Execute
      status: failed
    - step: Apply
      status: skipped
  error:
    message: "Tool invocation failed: connection refused"
    tool: local/db-migrate
    step: "4 of 6"
    checkpoint: cp_01HXM8C2
    recoverable: true
  cost:
    tokens_used: 8340
    cost_so_far: 0.028
timing:
  started: "2026-02-08T12:57:01Z"
  duration_ms: 72000
messages:
  - "Plan errored — use `agents plan prompt` to resume or `agents plan cancel` to abort"
```
agents plan cancel
agents plan cancel [(--reason|-r) <REASON>] <PLAN_ID>

!!! warning "Irreversible State Transition" Cancel a plan that is not in a terminal state. Cancellation transitions the plan to the cancelled state, which is ==terminal==. The sandbox and any child plan artifacts are preserved for inspection but cannot be resumed.

!!! tip "Recovery After Cancel"
    A cancelled plan cannot be resumed, but you can re-execute the same action with `agents plan use` to create a new plan instance.

Arguments

  • <PLAN_ID>: Plan ID.
  • --reason/-r TEXT: Optional reason.

Examples

=== "Rich"

<div class="highlight"><pre><code>
<span style="color: #66cc66; font-weight: 600;">$</span> <span style="color: cyan; font-weight: 600;">agents</span> plan cancel 01HXM8C2ZK4Q7C2B3F2R4VYV6J <span style="color: cyan;">--reason</span> <span style="color: #66cc66;">&quot;blocked on credentials&quot;</span>

╭─ Plan Cancelled ─────────────────╮
│ <span style="color: cyan; font-weight: 600;">Plan:</span> 01HXM8C2ZK4Q7C2B3F2R4VYV6J │
│ <span style="color: yellow; font-weight: 600;">Phase:</span> execute                   │
│ <span style="color: magenta; font-weight: 600;">Reason:</span> blocked on credentials   │
│ <span style="color: #5599ff; font-weight: 600;">State:</span> cancelled                 │
│ <span style="color: #5599ff; font-weight: 600;">Cancelled At:</span> 13:02:15           │
╰──────────────────────────────────╯

╭─ Sandbox ────────────────╮
│ <span style="color: yellow; font-weight: 600;">Status:</span> preserved        │
│ <span style="color: #5599ff; font-weight: 600;">Files Modified:</span> 3        │
│ <span style="color: #5599ff; font-weight: 600;">Checkpoints:</span> 2           │
╰──────────────────────────╯

╭─ Child Plans ─────────────╮
│ <span style="color: #66cc66; font-weight: 600;">Completed:</span> 1              │
│ <span style="color: yellow; font-weight: 600;">Cancelled:</span> 1              │
│ <span style="color: #5599ff; font-weight: 600;">Artifacts Preserved:</span> yes  │
╰───────────────────────────╯

╭─ Recovery ───────────────────────────────────────────╮
│ - Resolve credentials                                │
│ - Run agents plan execute 01HXM8C2ZK4Q7C2B3F2R4VYV6J │
╰──────────────────────────────────────────────────────╯

<span style="color: #66cc66; font-weight: 600;">✓ OK</span> Plan cancelled
</code></pre></div>

=== "Plain"

```
$ agents plan cancel 01HXM8C2ZK4Q7C2B3F2R4VYV6J --reason "blocked on credentials"

Plan Cancelled
  Plan: 01HXM8C2ZK4Q7C2B3F2R4VYV6J
  Phase: execute
  Reason: blocked on credentials
  State: cancelled
  Cancelled At: 13:02:15

Sandbox
  Status: preserved
  Files Modified: 3
  Checkpoints: 2

Child Plans
  Completed: 1
  Cancelled: 1
  Artifacts Preserved: yes

Recovery
  - Resolve credentials
  - Run agents plan execute 01HXM8C2ZK4Q7C2B3F2R4VYV6J

[OK] Plan cancelled
```

=== "JSON"

```json
{
  "command": "plan cancel",
  "status": "ok",
  "exit_code": 0,
  "data": {
    "plan_id": "01HXM8C2ZK4Q7C2B3F2R4VYV6J",
    "phase": "execute",
    "reason": "blocked on credentials",
    "state": "cancelled",
    "cancelled_at": "13:02:15",
    "sandbox": {
      "status": "preserved",
      "files_modified": 3,
      "checkpoints": 2
    },
    "child_plans": {
      "completed": 1,
      "cancelled": 1,
      "artifacts_preserved": true
    },
    "recovery": [
      "Resolve credentials",
      "Run agents plan execute 01HXM8C2ZK4Q7C2B3F2R4VYV6J"
    ]
  },
  "timing": {
    "started": "2026-02-08T13:02:15Z",
    "duration_ms": 340
  },
  "messages": ["Plan cancelled"]
}
```

=== "YAML"

```yaml
command: plan cancel
status: ok
exit_code: 0
data:
  plan_id: "01HXM8C2ZK4Q7C2B3F2R4VYV6J"
  phase: execute
  reason: "blocked on credentials"
  state: cancelled
  cancelled_at: "13:02:15"
  sandbox:
    status: preserved
    files_modified: 3
    checkpoints: 2
  child_plans:
    completed: 1
    cancelled: 1
    artifacts_preserved: true
  recovery:
    - "Resolve credentials"
    - "Run agents plan execute 01HXM8C2ZK4Q7C2B3F2R4VYV6J"
timing:
  started: "2026-02-08T13:02:15Z"
  duration_ms: 340
messages:
  - "Plan cancelled"
```
agents plan tree
agents plan tree [--show-superseded] <PLAN_ID>

Purpose Render the decision tree for a plan.

Arguments

  • <PLAN_ID>: Plan ID (required).
  • --show-superseded: Include superseded decisions.

Examples

=== "Rich"

<div class="highlight"><pre><code>
<span style="color: #66cc66; font-weight: 600;">$</span> <span style="color: cyan; font-weight: 600;">agents</span> plan tree 01HXM8C2ZK4Q7C2B3F2R4VYV6J

╭─ Decision Tree ──────────────────────────────────────────────────────────────────────────╮
│ Plan: 01HXM8C2ZK4Q7C2B3F2R4VYV6J                                                         │
│ ├─ <span style="color: white;">[prompt_definition]</span> <span style="color: #66cc66;">"Increase test coverage to 85%"</span>                                   │
│ ├─ <span style="color: magenta;">[invariant_enforced]</span> <span style="color: #66cc66;">"Prioritize financial transaction and user mgmt code"</span>            │
│ ├─ <span style="color: magenta;">[invariant_enforced]</span> <span style="color: #66cc66;">"All API calls over TCP must be mocked"</span>                          │
│ ├─ <span style="color: #5599ff;">[strategy_choice]</span> <span style="color: #66cc66;">"Prioritize auth and payments"</span> <span style="opacity: 0.7;">(confidence: 0.82)</span>                   │
│ ├─ <span style="color: #66cc66;">[subplan_parallel_spawn]</span> "Implement auth and payment modules, in parallel"            │
│ │  ├─ <span style="color: #66cc66;">[subplan_spawn]</span> "Write auth tests" → Plan: 01HXM9F1A                               │
│ │  └─ <span style="color: #66cc66;">[subplan_spawn]</span> "Write payment tests" → Plan: 01HXM9F2B                            │
│ └─ <span style="color: #66cc66;">[subplan_parallel_spawn]</span> "Write tests for remaining modules"                          │
│    └─ ...                                                                                │
╰──────────────────────────────────────────────────────────────────────────────────────────╯

╭─ Tree Summary ─────────────╮
│ <span style="color: yellow; font-weight: 600;">Nodes:</span> 9                   │
│ <span style="color: #5599ff; font-weight: 600;">Depth:</span> 3                   │
│ <span style="color: #66cc66; font-weight: 600;">Child Plans:</span> 2+            │
│ <span style="color: magenta; font-weight: 600;">Invariants:</span> 2              │
│ <span style="color: #5599ff; font-weight: 600;">Superseded:</span> 0 (hidden)     │
╰────────────────────────────╯

╭─ Child Plans ──────────────────────────────────────╮
│ <span style="color: cyan; font-weight: 600;">ID</span>          <span style="color: cyan; font-weight: 600;">Phase</span>    <span style="color: cyan; font-weight: 600;">State</span>                         │
│ <span style="opacity: 0.7;">──────────  ───────  ─────────</span>                     │
│ 01HXM9F1A   execute  processing                    │
│ 01HXM9F2B   execute  queued                        │
╰────────────────────────────────────────────────────╯

╭─ Decision IDs (for correction) ──────────────────╮
│ <span style="color: #5599ff; font-weight: 600;">Root:</span> 01HXM9A0B1Q2W3R5G8Z0P4Q1X8                 │
│ <span style="color: magenta; font-weight: 600;">Invariant 1:</span> 01HXM9A0C1R3X4S6G9Z1P5Q2Y9          │
│ <span style="color: magenta; font-weight: 600;">Invariant 2:</span> 01HXM9A0D2S4Y5T7H0Z2P6Q3Z0          │
│ <span style="color: #5599ff; font-weight: 600;">Strategy:</span> 01HXM9A1C2Q7W3R5G8Z0P4Q1X9             │
│ <span style="color: #66cc66; font-weight: 600;">Parallel 1:</span> 01HXM9A1D3R8X5S7H1Z2P5Q2Y0           │
│ <span style="color: #66cc66; font-weight: 600;">Spawn Auth:</span> 01HXM9A2D3Q8W4R6H9Z1P5Q2X0           │
│ <span style="color: #66cc66; font-weight: 600;">Spawn Payment:</span> 01HXM9A3E4Q9W5R7I0Z2P6Q3X1        │
│ <span style="color: #66cc66; font-weight: 600;">Parallel 2:</span> 01HXM9A4F5Q0W6R8J1Z3P7Q4X2           │
╰──────────────────────────────────────────────────╯

<span style="color: #66cc66; font-weight: 600;">✓ OK</span> Decision tree rendered
</code></pre></div>

=== "Plain"

```
$ agents plan tree 01HXM8C2ZK4Q7C2B3F2R4VYV6J

Decision Tree
  Plan: 01HXM8C2ZK4Q7C2B3F2R4VYV6J
  +-- [prompt_definition] "Increase test coverage to 85%"
  +-- [invariant_enforced] "Prioritize financial transaction and user mgmt code"
  +-- [invariant_enforced] "All API calls over TCP must be mocked"
  +-- [strategy_choice] "Prioritize auth and payments" (confidence: 0.82)
  +-- [subplan_parallel_spawn] "Implement auth and payment modules, in parallel"
  |   +-- [subplan_spawn] "Write auth tests" -> Plan: 01HXM9F1A
  |   +-- [subplan_spawn] "Write payment tests" -> Plan: 01HXM9F2B
  +-- [subplan_parallel_spawn] "Write tests for remaining modules"
      +-- ...

Tree Summary
  Nodes: 9
  Depth: 3
  Child Plans: 2+
  Invariants: 2
  Superseded: 0 (hidden)

Child Plans
  ID          Phase    State
  ----------  -------  ---------
  01HXM9F1A   execute  processing
  01HXM9F2B   execute  queued

Decision IDs (for correction)
  Root: 01HXM9A0B1Q2W3R5G8Z0P4Q1X8
  Invariant 1: 01HXM9A0C1R3X4S6G9Z1P5Q2Y9
  Invariant 2: 01HXM9A0D2S4Y5T7H0Z2P6Q3Z0
  Strategy: 01HXM9A1C2Q7W3R5G8Z0P4Q1X9
  Parallel 1: 01HXM9A1D3R8X5S7H1Z2P5Q2Y0
  Spawn Auth: 01HXM9A2D3Q8W4R6H9Z1P5Q2X0
  Spawn Payment: 01HXM9A3E4Q9W5R7I0Z2P6Q3X1
  Parallel 2: 01HXM9A4F5Q0W6R8J1Z3P7Q4X2

[OK] Decision tree rendered
```

=== "JSON"

```json
{
  "command": "plan tree",
  "status": "ok",
  "exit_code": 0,
  "data": {
    "plan_id": "01HXM8C2ZK4Q7C2B3F2R4VYV6J",
    "tree": {
      "type": "prompt_definition",
      "description": "Increase test coverage to 85%",
      "children": [
        { "type": "invariant_enforced", "description": "Prioritize financial transaction and user mgmt code" },
        { "type": "invariant_enforced", "description": "All API calls over TCP must be mocked" },
        { "type": "strategy_choice", "description": "Prioritize auth and payments", "confidence": 0.82 },
        {
          "type": "subplan_parallel_spawn",
          "description": "Implement auth and payment modules, in parallel",
          "children": [
            { "type": "subplan_spawn", "description": "Write auth tests", "plan_id": "01HXM9F1A" },
            { "type": "subplan_spawn", "description": "Write payment tests", "plan_id": "01HXM9F2B" }
          ]
        },
        {
          "type": "subplan_parallel_spawn",
          "description": "Write tests for remaining modules",
          "children": ["..."]
        }
      ]
    },
    "summary": {
      "nodes": 9,
      "depth": 3,
      "child_plans": "2+",
      "invariants": 2,
      "superseded": 0
    },
    "child_plans": [
      { "id": "01HXM9F1A", "phase": "execute", "state": "processing" },
      { "id": "01HXM9F2B", "phase": "execute", "state": "queued" }
    ],
    "decision_ids": {
      "root": "01HXM9A0B1Q2W3R5G8Z0P4Q1X8",
      "invariant_1": "01HXM9A0C1R3X4S6G9Z1P5Q2Y9",
      "invariant_2": "01HXM9A0D2S4Y5T7H0Z2P6Q3Z0",
      "strategy": "01HXM9A1C2Q7W3R5G8Z0P4Q1X9",
      "parallel_1": "01HXM9A1D3R8X5S7H1Z2P5Q2Y0",
      "spawn_auth": "01HXM9A2D3Q8W4R6H9Z1P5Q2X0",
      "spawn_payment": "01HXM9A3E4Q9W5R7I0Z2P6Q3X1",
      "parallel_2": "01HXM9A4F5Q0W6R8J1Z3P7Q4X2"
    }
  },
  "timing": {
    "started": "2026-02-08T12:58:00Z",
    "duration_ms": 85
  },
  "messages": ["Decision tree rendered"]
}
```

=== "YAML"

```yaml
command: plan tree
status: ok
exit_code: 0
data:
  plan_id: "01HXM8C2ZK4Q7C2B3F2R4VYV6J"
  tree:
    type: prompt_definition
    description: "Increase test coverage to 85%"
    children:
      - type: invariant_enforced
        description: "Prioritize financial transaction and user mgmt code"
      - type: invariant_enforced
        description: "All API calls over TCP must be mocked"
      - type: strategy_choice
        description: "Prioritize auth and payments"
        confidence: 0.82
      - type: subplan_parallel_spawn
        description: "Implement auth and payment modules, in parallel"
        children:
          - type: subplan_spawn
            description: "Write auth tests"
            plan_id: "01HXM9F1A"
          - type: subplan_spawn
            description: "Write payment tests"
            plan_id: "01HXM9F2B"
      - type: subplan_parallel_spawn
        description: "Write tests for remaining modules"
        children:
          - "..."
  summary:
    nodes: 9
    depth: 3
    child_plans: "2+"
    invariants: 2
    superseded: 0
  child_plans:
    - id: "01HXM9F1A"
      phase: execute
      state: processing
    - id: "01HXM9F2B"
      phase: execute
      state: queued
  decision_ids:
    root: "01HXM9A0B1Q2W3R5G8Z0P4Q1X8"
    invariant_1: "01HXM9A0C1R3X4S6G9Z1P5Q2Y9"
    invariant_2: "01HXM9A0D2S4Y5T7H0Z2P6Q3Z0"
    strategy: "01HXM9A1C2Q7W3R5G8Z0P4Q1X9"
    parallel_1: "01HXM9A1D3R8X5S7H1Z2P5Q2Y0"
    spawn_auth: "01HXM9A2D3Q8W4R6H9Z1P5Q2X0"
    spawn_payment: "01HXM9A3E4Q9W5R7I0Z2P6Q3X1"
    parallel_2: "01HXM9A4F5Q0W6R8J1Z3P7Q4X2"
timing:
  started: "2026-02-08T12:58:00Z"
  duration_ms: 85
messages:
  - "Decision tree rendered"
```
agents plan explain
agents plan explain [--show-context] [--show-reasoning] <DECISION_ID>

Purpose Show a detailed explanation for a decision.

Arguments

  • <DECISION_ID>: Decision ID.
  • --show-context: Include the context snapshot.
  • --show-reasoning: Include raw model reasoning if available.

Examples

=== "Rich"

<div class="highlight"><pre><code>
<span style="color: #66cc66; font-weight: 600;">$</span> <span style="color: cyan; font-weight: 600;">agents</span> plan explain 01HXM9A1C2Q7W3R5G8Z0P4Q1X9 <span style="color: cyan;">--show-context</span>

╭─ Decision ─────────────────────────────────────╮
│ <span style="color: cyan; font-weight: 600;">ID:</span> 01HXM9A1C2Q7W3R5G8Z0P4Q1X9                 │
│ <span style="color: #5599ff; font-weight: 600;">Type:</span> strategy_choice                          │
│ <span style="color: magenta; font-weight: 600;">Question:</span> Which modules should be prioritized? │
│ <span style="color: #66cc66; font-weight: 600;">Chosen:</span> Auth and payments                      │
│ <span style="color: yellow; font-weight: 600;">Confidence:</span> 0.82                               │
│ <span style="color: #5599ff; font-weight: 600;">Plan:</span> 01HXM8C2ZK4Q7C2B3F2R4VYV6J               │
│ <span style="color: #5599ff; font-weight: 600;">Sequence:</span> 2 of 5                               │
│ <span style="color: #66cc66; font-weight: 600;">Created:</span> 2026-02-08 12:58                      │
╰────────────────────────────────────────────────╯

╭─ Alternatives Considered ──────────────────────╮
│ 1. Auth and payments <span style="color: #66cc66; font-weight: 600;">(chosen)</span>                  │
│ 2. User module first (coverage 71%, med risk)  │
│ 3. All modules equally (spread thin)           │
╰────────────────────────────────────────────────╯

╭─ Impact ──────────────────────╮
│ <span style="color: #5599ff; font-weight: 600;">Downstream Decisions:</span> 3       │
│ <span style="color: #5599ff; font-weight: 600;">Downstream Child Plans:</span> 2     │
│ <span style="color: #66cc66; font-weight: 600;">Artifacts Produced:</span> 5         │
│ <span style="color: yellow; font-weight: 600;">Correction Impact:</span> medium     │
╰───────────────────────────────╯

╭─ Context Snapshot ───────────────╮
│ - Coverage < 70% in auth         │
│ - Payments failures last release │
│ - Auth: 12 files, 45% coverage   │
│ - Payments: 8 files, 52% cover.  │
│ <span style="color: #5599ff; font-weight: 600;">Hot Context Hash:</span> sha256:4b2e... │
╰──────────────────────────────────╯

╭─ Rationale ───────────────────────────────────────╮
│ Auth and payment modules have the lowest coverage │
│ and highest business risk. Auth handles security  │
│ tokens, payments handles money. Both had bugs in  │
│ the last release traceable to missing tests.      │
╰───────────────────────────────────────────────────╯

╭─ Correction ──────────────────────────────────────────────╮
│ agents plan correct 01HXM9A1C2Q7W3R5G8Z0P4Q1X9            │
│   <span style="color: cyan;">--mode</span> revert <span style="color: cyan;">--guidance</span> <span style="color: #66cc66;">"Prioritize payments first..."</span> │
╰───────────────────────────────────────────────────────────╯

<span style="color: #66cc66; font-weight: 600;">✓ OK</span> Decision explained
</code></pre></div>

=== "Plain"

```
$ agents plan explain 01HXM9A1C2Q7W3R5G8Z0P4Q1X9 --show-context

Decision
  ID: 01HXM9A1C2Q7W3R5G8Z0P4Q1X9
  Type: strategy_choice
  Question: Which modules should be prioritized?
  Chosen: Auth and payments
  Confidence: 0.82
  Plan: 01HXM8C2ZK4Q7C2B3F2R4VYV6J
  Sequence: 2 of 5
  Created: 2026-02-08 12:58

Alternatives Considered
  1. Auth and payments (chosen)
  2. User module first (coverage 71%, med risk)
  3. All modules equally (spread thin)

Impact
  Downstream Decisions: 3
  Downstream Child Plans: 2
  Artifacts Produced: 5
  Correction Impact: medium

Context Snapshot
  - Coverage < 70% in auth
  - Payments failures last release
  - Auth: 12 files, 45% coverage
  - Payments: 8 files, 52% cover.
  Hot Context Hash: sha256:4b2e...

Rationale
  Auth and payment modules have the lowest coverage
  and highest business risk. Auth handles security
  tokens, payments handles money. Both had bugs in
  the last release traceable to missing tests.

Correction
  agents plan correct 01HXM9A1C2Q7W3R5G8Z0P4Q1X9
    --mode revert --guidance "Prioritize payments first..."

[OK] Decision explained
```

=== "JSON"

```json
{
  "command": "plan explain",
  "status": "ok",
  "exit_code": 0,
  "data": {
    "decision_id": "01HXM9A1C2Q7W3R5G8Z0P4Q1X9",
    "type": "strategy_choice",
    "question": "Which modules should be prioritized?",
    "chosen": "Auth and payments",
    "confidence": 0.82,
    "plan_id": "01HXM8C2ZK4Q7C2B3F2R4VYV6J",
    "sequence": "2 of 5",
    "created": "2026-02-08T12:58:00Z",
    "alternatives": [
      { "index": 1, "description": "Auth and payments", "chosen": true },
      { "index": 2, "description": "User module first (coverage 71%, med risk)", "chosen": false },
      { "index": 3, "description": "All modules equally (spread thin)", "chosen": false }
    ],
    "impact": {
      "downstream_decisions": 3,
      "downstream_child_plans": 2,
      "artifacts_produced": 5,
      "correction_impact": "medium"
    },
    "context_snapshot": {
      "items": [
        "Coverage < 70% in auth",
        "Payments failures last release",
        "Auth: 12 files, 45% coverage",
        "Payments: 8 files, 52% cover."
      ],
      "hot_context_hash": "sha256:4b2e..."
    },
    "rationale": "Auth and payment modules have the lowest coverage and highest business risk. Auth handles security tokens, payments handles money. Both had bugs in the last release traceable to missing tests.",
    "correction_hint": "agents plan correct 01HXM9A1C2Q7W3R5G8Z0P4Q1X9 --mode revert --guidance "Prioritize payments first...""
  },
  "timing": {
    "started": "2026-02-08T12:58:00Z",
    "duration_ms": 110
  },
  "messages": ["Decision explained"]
}
```

=== "YAML"

```yaml
command: plan explain
status: ok
exit_code: 0
data:
  decision_id: "01HXM9A1C2Q7W3R5G8Z0P4Q1X9"
  type: strategy_choice
  question: "Which modules should be prioritized?"
  chosen: "Auth and payments"
  confidence: 0.82
  plan_id: "01HXM8C2ZK4Q7C2B3F2R4VYV6J"
  sequence: "2 of 5"
  created: "2026-02-08T12:58:00Z"
  alternatives:
    - index: 1
      description: "Auth and payments"
      chosen: true
    - index: 2
      description: "User module first (coverage 71%, med risk)"
      chosen: false
    - index: 3
      description: "All modules equally (spread thin)"
      chosen: false
  impact:
    downstream_decisions: 3
    downstream_child_plans: 2
    artifacts_produced: 5
    correction_impact: medium
  context_snapshot:
    items:
      - "Coverage < 70% in auth"
      - "Payments failures last release"
      - "Auth: 12 files, 45% coverage"
      - "Payments: 8 files, 52% cover."
    hot_context_hash: "sha256:4b2e..."
  rationale: >-
    Auth and payment modules have the lowest coverage
    and highest business risk. Auth handles security
    tokens, payments handles money. Both had bugs in
    the last release traceable to missing tests.
  correction_hint: >-
    agents plan correct 01HXM9A1C2Q7W3R5G8Z0P4Q1X9
    --mode revert --guidance "Prioritize payments first..."
timing:
  started: "2026-02-08T12:58:00Z"
  duration_ms: 110
messages:
  - "Decision explained"
```

Including the raw model reasoning with --show-reasoning:

=== "Rich"

<div class="highlight"><pre><code>
<span style="color: #66cc66; font-weight: 600;">$</span> <span style="color: cyan; font-weight: 600;">agents</span> plan explain <span style="color: cyan;">--show-reasoning</span> 01HXM9A1C2Q7W3R5G8Z0P4Q1X9

╭─ Decision ──────────────────────────────╮
│ <span style="color: cyan; font-weight: 600;">ID:</span> 01HXM9A1C2Q7W3R5G8Z0P4Q1X9          │
│ <span style="color: #5599ff; font-weight: 600;">Type:</span> strategy_choice                   │
│ <span style="color: magenta; font-weight: 600;">Choice:</span> Convert to async/await          │
│ <span style="color: #5599ff; font-weight: 600;">Alternatives:</span> 3                         │
╰─────────────────────────────────────────╯

╭─ Alternatives Considered ────────────────────────────────────╮
│ <span style="color: #66cc66; font-weight: 600;">1. Convert to async/await patterns</span> (chosen)                  │
│ <span style="opacity: 0.7;">   2. Keep synchronous with thread pool</span>                      │
│ <span style="opacity: 0.7;">   3. Use callback-based approach</span>                            │
╰──────────────────────────────────────────────────────────────╯

╭─ Rationale ──────────────────────────────────────────────────╮
│ Async/await is the modern Python standard for I/O-bound      │
│ operations. The project already uses asyncio in 3 modules.   │
│ Thread pools would add complexity without native support.    │
╰──────────────────────────────────────────────────────────────╯

╭─ Model Reasoning (raw) ───────────────────────────────────────╮
│ <span style="opacity: 0.7;">I need to decide on the concurrency pattern for the payment</span>   │
│ <span style="opacity: 0.7;">processing module. Let me analyze the current codebase:</span>       │
│ <span style="opacity: 0.7;"></span>                                                              │
│ <span style="opacity: 0.7;">1. src/payments/api.py uses synchronous requests.</span>             │
│ <span style="opacity: 0.7;">2. src/core/scheduler.py already uses asyncio.</span>                │
│ <span style="opacity: 0.7;">3. The database driver (asyncpg) supports async natively.</span>     │
│ <span style="opacity: 0.7;">4. Project invariant says <span style="color: #66cc66;">"prefer modern Python patterns"</span>.</span>    │
│ <span style="opacity: 0.7;"></span>                                                              │
│ <span style="opacity: 0.7;">Given that 3/5 core modules already use asyncio, and the</span>      │
│ <span style="opacity: 0.7;">database driver supports it, converting to async/await is</span>     │
│ <span style="opacity: 0.7;">the most consistent choice. Thread pools would work but</span>       │
│ <span style="opacity: 0.7;">add unnecessary complexity and don't integrate well with</span>      │
│ <span style="opacity: 0.7;">the existing asyncio event loop in scheduler.py.</span>              │
╰───────────────────────────────────────────────────────────────╯

<span style="color: #66cc66; font-weight: 600;">✓ OK</span> Decision explained
</code></pre></div>

=== "Plain"

```
$ agents plan explain --show-reasoning 01HXM9A1C2Q7W3R5G8Z0P4Q1X9

Decision
  ID: 01HXM9A1C2Q7W3R5G8Z0P4Q1X9
  Type: strategy_choice
  Choice: Convert to async/await
  Alternatives: 3

Alternatives Considered
  1. Convert to async/await patterns (chosen)
  2. Keep synchronous with thread pool
  3. Use callback-based approach

Rationale
  Async/await is the modern Python standard for I/O-bound
  operations. The project already uses asyncio in 3 modules.
  Thread pools would add complexity without native support.

Model Reasoning (raw)
  I need to decide on the concurrency pattern for the payment
  processing module. Let me analyze the current codebase:

  1. src/payments/api.py uses synchronous requests.
  2. src/core/scheduler.py already uses asyncio.
  3. The database driver (asyncpg) supports async natively.
  4. Project invariant says "prefer modern Python patterns".

  Given that 3/5 core modules already use asyncio, and the
  database driver supports it, converting to async/await is
  the most consistent choice. Thread pools would work but
  add unnecessary complexity and don't integrate well with
  the existing asyncio event loop in scheduler.py.

[OK] Decision explained
```

=== "JSON"

```json
{
  "command": "plan explain",
  "status": "ok",
  "exit_code": 0,
  "data": {
    "decision_id": "01HXM9A1C2Q7W3R5G8Z0P4Q1X9",
    "type": "strategy_choice",
    "choice": "Convert to async/await",
    "alternatives_count": 3,
    "alternatives": [
      { "index": 1, "description": "Convert to async/await patterns", "chosen": true },
      { "index": 2, "description": "Keep synchronous with thread pool", "chosen": false },
      { "index": 3, "description": "Use callback-based approach", "chosen": false }
    ],
    "rationale": "Async/await is the modern Python standard for I/O-bound operations. The project already uses asyncio in 3 modules. Thread pools would add complexity without native support.",
    "model_reasoning": "I need to decide on the concurrency pattern for the payment processing module. Let me analyze the current codebase:\n\n1. src/payments/api.py uses synchronous requests.\n2. src/core/scheduler.py already uses asyncio.\n3. The database driver (asyncpg) supports async natively.\n4. Project invariant says \"prefer modern Python patterns\".\n\nGiven that 3/5 core modules already use asyncio, and the database driver supports it, converting to async/await is the most consistent choice. Thread pools would work but add unnecessary complexity and don't integrate well with the existing asyncio event loop in scheduler.py."
  },
  "timing": {
    "started": "2026-02-08T12:58:00Z",
    "duration_ms": 95
  },
  "messages": ["Decision explained"]
}
```

=== "YAML"

```yaml
command: plan explain
status: ok
exit_code: 0
data:
  decision_id: "01HXM9A1C2Q7W3R5G8Z0P4Q1X9"
  type: strategy_choice
  choice: "Convert to async/await"
  alternatives_count: 3
  alternatives:
    - index: 1
      description: "Convert to async/await patterns"
      chosen: true
    - index: 2
      description: "Keep synchronous with thread pool"
      chosen: false
    - index: 3
      description: "Use callback-based approach"
      chosen: false
  rationale: >-
    Async/await is the modern Python standard for I/O-bound
    operations. The project already uses asyncio in 3 modules.
    Thread pools would add complexity without native support.
  model_reasoning: >-
    I need to decide on the concurrency pattern for the payment
    processing module. Let me analyze the current codebase:
    1. src/payments/api.py uses synchronous requests.
    2. src/core/scheduler.py already uses asyncio.
    3. The database driver (asyncpg) supports async natively.
    4. Project invariant says "prefer modern Python patterns".
    Given that 3/5 core modules already use asyncio, and the
    database driver supports it, converting to async/await is
    the most consistent choice.
timing:
  started: "2026-02-08T12:58:00Z"
  duration_ms: 95
messages:
  - "Decision explained"
```
agents plan correct
agents plan correct --mode (revert|append) (--guidance|-g) <GUIDANCE>
                   [--dry-run] [--yes|-y] <DECISION_ID>

Purpose Correct a decision either by reverting and re-executing or by appending a fix.

Arguments

  • <DECISION_ID>: Decision ID.
  • --mode revert|append: Correction mode.
  • --guidance/-g TEXT: Guidance text.
  • --dry-run: Show impact without executing.
  • --yes: Skip confirmation for revert mode.

Examples

=== "Rich"

<div class="highlight"><pre><code>
<span style="color: #66cc66; font-weight: 600;">$</span> <span style="color: cyan; font-weight: 600;">agents</span> plan correct 01HXM9A1C2Q7W3R5G8Z0P4Q1X9 <span style="color: cyan;">--mode</span> revert \
  <span style="color: cyan;">--guidance</span> <span style="color: #66cc66;">&quot;Prioritize payments first&quot;</span> <span style="color: cyan;">--yes</span>

╭─ Correction ────────────────────────────────────────╮
│ <span style="color: #5599ff; font-weight: 600;">Mode:</span> revert                                        │
│ <span style="color: yellow; font-weight: 600;">Impact:</span> 3 decisions, 2 child plans, 5 artifacts     │
│ <span style="color: #66cc66; font-weight: 600;">New Decision:</span> 01HXM9B7Z3Q1Q8K2E9H7K3W2M8            │
│ <span style="color: magenta; font-weight: 600;">Corrects:</span> 01HXM9A1C2Q7W3R5G8Z0P4Q1X9                │
│ <span style="color: #5599ff; font-weight: 600;">Attempt:</span> 2                                          │
╰─────────────────────────────────────────────────────╯

╭─ Affected Subtree ──────────────╮
│ <span style="color: yellow; font-weight: 600;">Decisions Invalidated:</span> 3        │
│ <span style="color: yellow; font-weight: 600;">Child Plans Rolled Back:</span> 2      │
│ <span style="color: yellow; font-weight: 600;">Artifacts Archived:</span> 5           │
│ <span style="color: #5599ff; font-weight: 600;">Unaffected Decisions:</span> 2         │
╰─────────────────────────────────╯

╭─ Sandbox Rollback ─────────────╮
│ <span style="color: #66cc66; font-weight: 600;">Checkpoint:</span> cp_01HXM8C2        │
│ <span style="color: #5599ff; font-weight: 600;">Files Reverted:</span> 5              │
│ <span style="color: #66cc66; font-weight: 600;">Status:</span> restored               │
╰────────────────────────────────╯

╭─ Recompute ──────────────╮
│ <span style="color: yellow; font-weight: 600;">Queued:</span> 2 child plans    │
│ <span style="color: #66cc66; font-weight: 600;">ETA:</span> 4m                  │
╰──────────────────────────╯

╭─ History ───────────────────────────────────────────╮
│ - Original decision superseded                      │
│ - Prior artifacts archived for comparison           │
│ - agents plan diff <span style="color: cyan;">--correction</span> 01HXM9B7Z3Q1Q8K2..  │
╰─────────────────────────────────────────────────────╯

<span style="color: #66cc66; font-weight: 600;">✓ OK</span> Correction applied
</code></pre></div>

=== "Plain"

```
$ agents plan correct 01HXM9A1C2Q7W3R5G8Z0P4Q1X9 --mode revert       --guidance "Prioritize payments first" --yes

Correction
  Mode: revert
  Impact: 3 decisions, 2 child plans, 5 artifacts
  New Decision: 01HXM9B7Z3Q1Q8K2E9H7K3W2M8
  Corrects: 01HXM9A1C2Q7W3R5G8Z0P4Q1X9
  Attempt: 2

Affected Subtree
  Decisions Invalidated: 3
  Child Plans Rolled Back: 2
  Artifacts Archived: 5
  Unaffected Decisions: 2

Sandbox Rollback
  Checkpoint: cp_01HXM8C2
  Files Reverted: 5
  Status: restored

Recompute
  Queued: 2 child plans
  ETA: 4m

History
  - Original decision superseded
  - Prior artifacts archived for comparison
  - agents plan diff --correction 01HXM9B7Z3Q1Q8K2..

[OK] Correction applied
```

=== "JSON"

```json
{
  "command": "plan correct",
  "status": "ok",
  "exit_code": 0,
  "data": {
    "correction": {
      "mode": "revert",
      "impact": "3 decisions, 2 child plans, 5 artifacts",
      "new_decision": "01HXM9B7Z3Q1Q8K2E9H7K3W2M8",
      "corrects": "01HXM9A1C2Q7W3R5G8Z0P4Q1X9",
      "attempt": 2
    },
    "affected_subtree": {
      "decisions_invalidated": 3,
      "child_plans_rolled_back": 2,
      "artifacts_archived": 5,
      "unaffected_decisions": 2
    },
    "sandbox_rollback": {
      "checkpoint": "cp_01HXM8C2",
      "files_reverted": 5,
      "status": "restored"
    },
    "recompute": {
      "queued": "2 child plans",
      "eta": "4m"
    },
    "history": [
      "Original decision superseded",
      "Prior artifacts archived for comparison",
      "agents plan diff --correction 01HXM9B7Z3Q1Q8K2.."
    ]
  },
  "timing": { "started": "2025-06-15T10:24:00Z", "duration_ms": 2350 },
  "messages": ["Correction applied"]
}
```

=== "YAML"

```yaml
command: plan correct
status: ok
exit_code: 0
data:
  correction:
    mode: revert
    impact: "3 decisions, 2 child plans, 5 artifacts"
    new_decision: "01HXM9B7Z3Q1Q8K2E9H7K3W2M8"
    corrects: "01HXM9A1C2Q7W3R5G8Z0P4Q1X9"
    attempt: 2
  affected_subtree:
    decisions_invalidated: 3
    child_plans_rolled_back: 2
    artifacts_archived: 5
    unaffected_decisions: 2
  sandbox_rollback:
    checkpoint: cp_01HXM8C2
    files_reverted: 5
    status: restored
  recompute:
    queued: "2 child plans"
    eta: "4m"
  history:
    - "Original decision superseded"
    - "Prior artifacts archived for comparison"
    - "agents plan diff --correction 01HXM9B7Z3Q1Q8K2.."
timing:
  started: "2025-06-15T10:24:00Z"
  duration_ms: 2350
messages:
  - "Correction applied"
```

Using --mode append to add a corrective decision without reverting existing work (useful when the original decision was partially correct):

=== "Rich"

<div class="highlight"><pre><code>
<span style="color: #66cc66; font-weight: 600;">$</span> <span style="color: cyan; font-weight: 600;">agents</span> plan correct 01HXM9A1C2Q7W3R5G8Z0P4Q1X9 <span style="color: cyan;">--mode</span> append \
  <span style="color: cyan;">--guidance</span> <span style="color: #66cc66;">&quot;Also add rate limiting to the auth endpoints&quot;</span>

╭─ Correction ─────────────────────────────────────╮
│ <span style="color: #5599ff; font-weight: 600;">Mode:</span> append                                     │
│ <span style="color: #66cc66; font-weight: 600;">Impact:</span> adds to existing subtree, no rollback    │
│ <span style="color: #66cc66; font-weight: 600;">New Decision:</span> 01HXM9C3Z5T2Q8K2E9H7K3W2M8         │
│ <span style="color: #5599ff; font-weight: 600;">Appended After:</span> 01HXM9A1C2Q7W3R5G8Z0P4Q1X9       │
│ <span style="color: #5599ff; font-weight: 600;">Attempt:</span> 2                                       │
╰──────────────────────────────────────────────────╯

╭─ Append Detail ─────────────────────────────────────────────────╮
│ <span style="color: #5599ff; font-weight: 600;">Original decision preserved:</span> yes                                │
│ <span style="color: #5599ff; font-weight: 600;">Existing artifacts kept:</span> yes                                    │
│ <span style="color: #66cc66; font-weight: 600;">Additional work:</span> appended as new child plan                     │
│ <span style="opacity: 0.7;">The original 5 artifacts remain; a new child plan will add</span>      │
│ <span style="opacity: 0.7;">rate-limiting code on top of the existing auth changes.</span>         │
╰─────────────────────────────────────────────────────────────────╯

╭─ Queued ──────────╮
│ <span style="color: yellow;">New child:</span> 1      │
│ <span style="color: #66cc66; font-weight: 600;">ETA:</span> 2m           │
╰───────────────────╯

<span style="color: #66cc66; font-weight: 600;">✓ OK</span> Append correction queued
</code></pre></div>

=== "Plain"

```
$ agents plan correct 01HXM9A1C2Q7W3R5G8Z0P4Q1X9 --mode append       --guidance "Also add rate limiting to the auth endpoints"

Correction
  Mode: append
  Impact: adds to existing subtree, no rollback
  New Decision: 01HXM9C3Z5T2Q8K2E9H7K3W2M8
  Appended After: 01HXM9A1C2Q7W3R5G8Z0P4Q1X9
  Attempt: 2

Append Detail
  Original decision preserved: yes
  Existing artifacts kept: yes
  Additional work: appended as new child plan
  The original 5 artifacts remain; a new child plan will add
  rate-limiting code on top of the existing auth changes.

Queued
  New child: 1
  ETA: 2m

[OK] Append correction queued
```

=== "JSON"

```json
{
  "command": "plan correct",
  "status": "ok",
  "exit_code": 0,
  "data": {
    "correction": {
      "mode": "append",
      "impact": "adds to existing subtree, no rollback",
      "new_decision": "01HXM9C3Z5T2Q8K2E9H7K3W2M8",
      "appended_after": "01HXM9A1C2Q7W3R5G8Z0P4Q1X9",
      "attempt": 2
    },
    "append_detail": {
      "original_decision_preserved": true,
      "existing_artifacts_kept": true,
      "additional_work": "appended as new child plan",
      "note": "The original 5 artifacts remain; a new child plan will add rate-limiting code on top of the existing auth changes."
    },
    "queued": {
      "new_child": 1,
      "eta": "2m"
    }
  },
  "timing": { "started": "2025-06-15T10:26:00Z", "duration_ms": 890 },
  "messages": ["Append correction queued"]
}
```

=== "YAML"

```yaml
command: plan correct
status: ok
exit_code: 0
data:
  correction:
    mode: append
    impact: "adds to existing subtree, no rollback"
    new_decision: "01HXM9C3Z5T2Q8K2E9H7K3W2M8"
    appended_after: "01HXM9A1C2Q7W3R5G8Z0P4Q1X9"
    attempt: 2
  append_detail:
    original_decision_preserved: true
    existing_artifacts_kept: true
    additional_work: "appended as new child plan"
    note: "The original 5 artifacts remain; a new child plan will add rate-limiting code on top of the existing auth changes."
  queued:
    new_child: 1
    eta: "2m"
timing:
  started: "2025-06-15T10:26:00Z"
  duration_ms: 890
messages:
  - "Append correction queued"
```

Using --dry-run to preview the impact of a correction before committing to it:

=== "Rich"

<div class="highlight"><pre><code>
<span style="color: #66cc66; font-weight: 600;">$</span> <span style="color: cyan; font-weight: 600;">agents</span> plan correct 01HXM9A1C2Q7W3R5G8Z0P4Q1X9 <span style="color: cyan;">--mode</span> revert \
  <span style="color: cyan;">--guidance</span> <span style="color: #66cc66;">&quot;Use async/await pattern instead&quot;</span> <span style="color: cyan;">--dry-run</span>

╭─ Dry Run — Correction Preview ─────────────────────────────────────╮
│ <span style="color: yellow; font-weight: 600;">⚠  This is a preview only. No changes will be made.</span>                │
╰────────────────────────────────────────────────────────────────────╯

╭─ Would Revert ───────────────────────────────────────╮
│ <span style="color: #5599ff; font-weight: 600;">Decisions to invalidate:</span> 3                           │
│   <span style="opacity: 0.7;">01HXM9A1..  strategy_choice    <span style="color: #66cc66;">"sync pattern"</span></span>      │
│   <span style="opacity: 0.7;">01HXM9A2..  implementation_choice  <span style="color: #66cc66;">"requests"</span></span>      │
│   <span style="opacity: 0.7;">01HXM9A3..  tool_invocation    write_file ×4</span>       │
│ <span style="color: #5599ff; font-weight: 600;">Child plans to roll back:</span> 2                          │
│ <span style="color: #5599ff; font-weight: 600;">Artifacts to archive:</span> 5 files                        │
│ <span style="color: #5599ff; font-weight: 600;">Unaffected decisions:</span> 2 (will be kept)               │
╰──────────────────────────────────────────────────────╯

╭─ Estimated Cost ──────────╮
│ <span style="color: #5599ff; font-weight: 600;">Re-strategize:</span> ~$0.012    │
│ <span style="color: #5599ff; font-weight: 600;">Re-execute:</span> ~$0.035       │
│ <span style="color: #5599ff; font-weight: 600;">Total:</span> ~$0.047            │
│ <span style="color: #5599ff; font-weight: 600;">ETA:</span> ~4 minutes           │
╰───────────────────────────╯

<span style="opacity: 0.7;">To execute this correction, remove --dry-run and add --yes</span>
</code></pre></div>

=== "Plain"

```
$ agents plan correct 01HXM9A1C2Q7W3R5G8Z0P4Q1X9 --mode revert       --guidance "Use async/await pattern instead" --dry-run

Dry Run -- Correction Preview
  WARNING: This is a preview only. No changes will be made.

Would Revert
  Decisions to invalidate: 3
    01HXM9A1..  strategy_choice       "sync pattern"
    01HXM9A2..  implementation_choice  "requests"
    01HXM9A3..  tool_invocation       write_file x4
  Child plans to roll back: 2
  Artifacts to archive: 5 files
  Unaffected decisions: 2 (will be kept)

Estimated Cost
  Re-strategize: ~$0.012
  Re-execute: ~$0.035
  Total: ~$0.047
  ETA: ~4 minutes

To execute this correction, remove --dry-run and add --yes
```

=== "JSON"

```json
{
  "command": "plan correct",
  "status": "ok",
  "exit_code": 0,
  "data": {
    "dry_run": true,
    "would_revert": {
      "decisions_to_invalidate": [
        { "id": "01HXM9A1..", "type": "strategy_choice", "label": "sync pattern" },
        { "id": "01HXM9A2..", "type": "implementation_choice", "label": "requests" },
        { "id": "01HXM9A3..", "type": "tool_invocation", "label": "write_file x4" }
      ],
      "child_plans_to_roll_back": 2,
      "artifacts_to_archive": 5,
      "unaffected_decisions": 2
    },
    "estimated_cost": {
      "re_strategize": "$0.012",
      "re_execute": "$0.035",
      "total": "$0.047",
      "eta": "~4 minutes"
    }
  },
  "timing": { "started": "2025-06-15T10:28:00Z", "duration_ms": 320 },
  "messages": ["To execute this correction, remove --dry-run and add --yes"]
}
```

=== "YAML"

```yaml
command: plan correct
status: ok
exit_code: 0
data:
  dry_run: true
  would_revert:
    decisions_to_invalidate:
      - id: "01HXM9A1.."
        type: strategy_choice
        label: "sync pattern"
      - id: "01HXM9A2.."
        type: implementation_choice
        label: "requests"
      - id: "01HXM9A3.."
        type: tool_invocation
        label: "write_file x4"
    child_plans_to_roll_back: 2
    artifacts_to_archive: 5
    unaffected_decisions: 2
  estimated_cost:
    re_strategize: "$0.012"
    re_execute: "$0.035"
    total: "$0.047"
    eta: "~4 minutes"
timing:
  started: "2025-06-15T10:28:00Z"
  duration_ms: 320
messages:
  - "To execute this correction, remove --dry-run and add --yes"
```
agents plan diff
agents plan diff (--correction <CORRECTION_ATTEMPT_ID>|<PLAN_ID>)

Purpose Show diffs for a plan or a correction attempt.

Arguments

  • <PLAN_ID>: Show diff for a plan (positional argument). Mutually exclusive with --correction.
  • --correction CORRECTION_ATTEMPT_ID: Show diff for a correction attempt instead.

Examples

=== "Rich"

<div class="highlight"><pre><code>
<span style="color: #66cc66; font-weight: 600;">$</span> <span style="color: cyan; font-weight: 600;">agents</span> plan diff 01HXM8C2ZK4Q7C2B3F2R4VYV6J

╭─ Diff Summary ─────────────────────────────────╮
│ <span style="color: cyan; font-weight: 600;">Plan:</span> 01HXM8C2ZK4Q7C2B3F2R4VYV6J               │
│ <span style="color: cyan; font-weight: 600;">Project:</span> local/api-service                     │
│ <span style="color: yellow; font-weight: 600;">Files Changed:</span> 2                               │
│ <span style="color: #66cc66; font-weight: 600;">Insertions:</span> 12                                 │
│ <span style="color: #ff6666; font-weight: 600;">Deletions:</span> 4                                   │
│ <span style="color: #5599ff; font-weight: 600;">Net Change:</span> +8 lines                           │
╰────────────────────────────────────────────────╯

╭─ Files ───────────────────────────────╮
│ <span style="color: cyan; font-weight: 600;">Path</span>                 <span style="color: cyan; font-weight: 600;">Change</span>  <span style="color: cyan; font-weight: 600;">Status</span>   │
│ <span style="opacity: 0.7;">───────────────────  ──────  ────────</span> │
│ src/auth/session.py  <span style="color: #66cc66; font-weight: 600;">+8 -2</span>   modified │
│ src/auth/tokens.py   <span style="color: #66cc66; font-weight: 600;">+4 -2</span>   modified │
╰───────────────────────────────────────╯

╭─ Patch Preview ──────────────────────────╮
│ <span style="opacity: 0.7;">--- a/src/auth/session.py</span>                │
│ <span style="opacity: 0.7;">+++ b/src/auth/session.py</span>                │
│ <span style="color: cyan;">@@ -12,4 +12,10 @@</span>                       │
│ <span style="color: #ff6666;">- import jwt</span>                             │
│ <span style="color: #66cc66;">+ import sessionlib</span>                      │
│ <span style="color: #ff6666;">- def validate_token(...)</span>                │
│ <span style="color: #66cc66;">+ def validate_session(...)</span>              │
│ <span style="opacity: 0.7;">--- a/src/auth/tokens.py</span>                 │
│ <span style="opacity: 0.7;">+++ b/src/auth/tokens.py</span>                 │
│ <span style="color: cyan;">@@ -5,3 +5,7 @@</span>                          │
│ <span style="color: #ff6666;">- TOKEN_EXPIRY = 3600</span>                    │
│ <span style="color: #66cc66;">+ TOKEN_EXPIRY = 7200</span>                    │
╰──────────────────────────────────────────╯

╭─ Risk Assessment ────────────────╮
│ <span style="color: #66cc66; font-weight: 600;">API Compatibility:</span> preserved     │
│ <span style="color: #66cc66; font-weight: 600;">Test Coverage:</span> maintained        │
│ <span style="color: yellow; font-weight: 600;">Breaking Changes:</span> none detected  │
╰──────────────────────────────────╯

<span style="color: #66cc66; font-weight: 600;">✓ OK</span> Diff generated
</code></pre></div>

=== "Plain"

```
$ agents plan diff 01HXM8C2ZK4Q7C2B3F2R4VYV6J

Diff Summary
  Plan: 01HXM8C2ZK4Q7C2B3F2R4VYV6J
  Project: local/api-service
  Files Changed: 2
  Insertions: 12
  Deletions: 4
  Net Change: +8 lines

Files
  Path                 Change  Status
  -------------------  ------  --------
  src/auth/session.py  +8 -2   modified
  src/auth/tokens.py   +4 -2   modified

Patch Preview
  --- a/src/auth/session.py
  +++ b/src/auth/session.py
  @@ -12,4 +12,10 @@
  - import jwt
  + import sessionlib
  - def validate_token(...)
  + def validate_session(...)
  --- a/src/auth/tokens.py
  +++ b/src/auth/tokens.py
  @@ -5,3 +5,7 @@
  - TOKEN_EXPIRY = 3600
  + TOKEN_EXPIRY = 7200

Risk Assessment
  API Compatibility: preserved
  Test Coverage: maintained
  Breaking Changes: none detected

[OK] Diff generated
```

=== "JSON"

```json
{
  "command": "plan diff",
  "status": "ok",
  "exit_code": 0,
  "data": {
    "diff_summary": {
      "plan": "01HXM8C2ZK4Q7C2B3F2R4VYV6J",
      "project": "local/api-service",
      "files_changed": 2,
      "insertions": 12,
      "deletions": 4,
      "net_change": "+8 lines"
    },
    "files": [
      { "path": "src/auth/session.py", "change": "+8 -2", "status": "modified" },
      { "path": "src/auth/tokens.py", "change": "+4 -2", "status": "modified" }
    ],
    "patch_preview": [
      {
        "file": "src/auth/session.py",
        "hunks": [
          {
            "range": "@@ -12,4 +12,10 @@",
            "deletions": ["import jwt", "def validate_token(...)"],
            "insertions": ["import sessionlib", "def validate_session(...)"]
          }
        ]
      },
      {
        "file": "src/auth/tokens.py",
        "hunks": [
          {
            "range": "@@ -5,3 +5,7 @@",
            "deletions": ["TOKEN_EXPIRY = 3600"],
            "insertions": ["TOKEN_EXPIRY = 7200"]
          }
        ]
      }
    ],
    "risk_assessment": {
      "api_compatibility": "preserved",
      "test_coverage": "maintained",
      "breaking_changes": "none detected"
    }
  },
  "timing": { "started": "2025-06-15T10:30:00Z", "duration_ms": 750 },
  "messages": ["Diff generated"]
}
```

=== "YAML"

```yaml
command: plan diff
status: ok
exit_code: 0
data:
  diff_summary:
    plan: "01HXM8C2ZK4Q7C2B3F2R4VYV6J"
    project: local/api-service
    files_changed: 2
    insertions: 12
    deletions: 4
    net_change: "+8 lines"
  files:
    - path: src/auth/session.py
      change: "+8 -2"
      status: modified
    - path: src/auth/tokens.py
      change: "+4 -2"
      status: modified
  patch_preview:
    - file: src/auth/session.py
      hunks:
        - range: "@@ -12,4 +12,10 @@"
          deletions:
            - "import jwt"
            - "def validate_token(...)"
          insertions:
            - "import sessionlib"
            - "def validate_session(...)"
    - file: src/auth/tokens.py
      hunks:
        - range: "@@ -5,3 +5,7 @@"
          deletions:
            - "TOKEN_EXPIRY = 3600"
          insertions:
            - "TOKEN_EXPIRY = 7200"
  risk_assessment:
    api_compatibility: preserved
    test_coverage: maintained
    breaking_changes: none detected
timing:
  started: "2025-06-15T10:30:00Z"
  duration_ms: 750
messages:
  - "Diff generated"
```

Showing the diff for a specific correction attempt, comparing what changed between the original and corrected execution:

=== "Rich"

<div class="highlight"><pre><code>
<span style="color: #66cc66; font-weight: 600;">$</span> <span style="color: cyan; font-weight: 600;">agents</span> plan diff <span style="color: cyan;">--correction</span> 01HXM9B7Z3Q1Q8K2E9H7K3W2M8

╭─ Correction Diff ───────────────────────────────╮
│ <span style="color: cyan; font-weight: 600;">Correction:</span> 01HXM9B7Z3Q1Q8K2E9H7K3W2M8          │
│ <span style="color: #5599ff; font-weight: 600;">Original Decision:</span> 01HXM9A1C2Q7W3R5..           │
│ <span style="color: #5599ff; font-weight: 600;">Mode:</span> revert                                    │
│ <span style="color: yellow;">Files Changed:</span> 3                                │
│ <span style="color: #66cc66;">New Insertions:</span> 18                              │
│ <span style="color: #ff6666;">New Deletions:</span> 6                                │
╰─────────────────────────────────────────────────╯

╭─ Comparison ─────────────────────────────────────────────────────╮
│ <span style="color: cyan; font-weight: 600;">File</span>                  <span style="color: cyan; font-weight: 600;">Before (original)</span>  <span style="color: cyan; font-weight: 600;">After (corrected)</span>       │
│ <span style="opacity: 0.7;">────────────────────  ────────────────  ────────────────────</span>     │
│ src/payments/api.py   +12 -4            +18 -6 (expanded)        │
│ src/auth/tokens.py    +8 -2             (unchanged)              │
│ tests/test_payments.py  (new file)      (new file, larger)       │
╰──────────────────────────────────────────────────────────────────╯

╭─ Patch Preview (corrected vs original) ───────────╮
│ <span style="opacity: 0.7;">--- a/src/payments/api.py (original)</span>              │
│ <span style="opacity: 0.7;">+++ b/src/payments/api.py (corrected)</span>             │
│ <span style="color: cyan;">@@ -1,12 +1,18 @@</span>                                 │
│ <span style="color: #ff6666;">- # sync payment processing</span>                       │
│ <span style="color: #66cc66;">+ # async payment processing (corrected)</span>          │
│ <span style="color: #66cc66;">+ import asyncio</span>                                  │
│ <span style="color: #66cc66;">+ from aiohttp import ClientSession</span>               │
│   <span style="opacity: 0.7;">...</span>                                             │
╰───────────────────────────────────────────────────╯

<span style="color: #66cc66; font-weight: 600;">✓ OK</span> Correction diff generated
</code></pre></div>

=== "Plain"

```
$ agents plan diff --correction 01HXM9B7Z3Q1Q8K2E9H7K3W2M8

Correction Diff
  Correction: 01HXM9B7Z3Q1Q8K2E9H7K3W2M8
  Original Decision: 01HXM9A1C2Q7W3R5..
  Mode: revert
  Files Changed: 3
  New Insertions: 18
  New Deletions: 6

Comparison
  File                  Before (original)  After (corrected)
  --------------------  -----------------  --------------------
  src/payments/api.py   +12 -4             +18 -6 (expanded)
  src/auth/tokens.py    +8 -2              (unchanged)
  tests/test_payments.py  (new file)       (new file, larger)

Patch Preview (corrected vs original)
  --- a/src/payments/api.py (original)
  +++ b/src/payments/api.py (corrected)
  @@ -1,12 +1,18 @@
  - # sync payment processing
  + # async payment processing (corrected)
  + import asyncio
  + from aiohttp import ClientSession
    ...

[OK] Correction diff generated
```

=== "JSON"

```json
{
  "command": "plan diff",
  "status": "ok",
  "exit_code": 0,
  "data": {
    "correction_diff": {
      "correction": "01HXM9B7Z3Q1Q8K2E9H7K3W2M8",
      "original_decision": "01HXM9A1C2Q7W3R5..",
      "mode": "revert",
      "files_changed": 3,
      "new_insertions": 18,
      "new_deletions": 6
    },
    "comparison": [
      { "file": "src/payments/api.py", "before": "+12 -4", "after": "+18 -6 (expanded)" },
      { "file": "src/auth/tokens.py", "before": "+8 -2", "after": "(unchanged)" },
      { "file": "tests/test_payments.py", "before": "(new file)", "after": "(new file, larger)" }
    ],
    "patch_preview": [
      {
        "file": "src/payments/api.py",
        "context": "corrected vs original",
        "hunks": [
          {
            "range": "@@ -1,12 +1,18 @@",
            "deletions": ["# sync payment processing"],
            "insertions": ["# async payment processing (corrected)", "import asyncio", "from aiohttp import ClientSession"]
          }
        ]
      }
    ]
  },
  "timing": { "started": "2025-06-15T10:32:00Z", "duration_ms": 920 },
  "messages": ["Correction diff generated"]
}
```

=== "YAML"

```yaml
command: plan diff
status: ok
exit_code: 0
data:
  correction_diff:
    correction: "01HXM9B7Z3Q1Q8K2E9H7K3W2M8"
    original_decision: "01HXM9A1C2Q7W3R5.."
    mode: revert
    files_changed: 3
    new_insertions: 18
    new_deletions: 6
  comparison:
    - file: src/payments/api.py
      before: "+12 -4"
      after: "+18 -6 (expanded)"
    - file: src/auth/tokens.py
      before: "+8 -2"
      after: "(unchanged)"
    - file: tests/test_payments.py
      before: "(new file)"
      after: "(new file, larger)"
  patch_preview:
    - file: src/payments/api.py
      context: "corrected vs original"
      hunks:
        - range: "@@ -1,12 +1,18 @@"
          deletions:
            - "# sync payment processing"
          insertions:
            - "# async payment processing (corrected)"
            - "import asyncio"
            - "from aiohttp import ClientSession"
timing:
  started: "2025-06-15T10:32:00Z"
  duration_ms: 920
messages:
  - "Correction diff generated"
```
agents plan artifacts
agents plan artifacts <PLAN_ID>

Purpose List artifacts produced by a plan.

Arguments

  • <PLAN_ID>: Plan ID.

Examples

=== "Rich"

<div class="highlight"><pre><code>
<span style="color: #66cc66; font-weight: 600;">$</span> <span style="color: cyan; font-weight: 600;">agents</span> plan artifacts 01HXM8C2ZK4Q7C2B3F2R4VYV6J

╭─ Artifacts ─────────────────────────────────────────────────╮
│ <span style="color: cyan; font-weight: 600;">Path</span>                   <span style="color: cyan; font-weight: 600;">Type</span>   <span style="color: cyan; font-weight: 600;">Size</span>    <span style="color: cyan; font-weight: 600;">Change</span>     <span style="color: cyan; font-weight: 600;">Child Plan</span> │
│ <span style="opacity: 0.7;">─────────────────────  ─────  ──────  ─────────  ───────</span>    │
│ src/auth/session.py    write  2.1 KB  <span style="color: #66cc66; font-weight: 600;">+8 -2</span>      root       │
│ tests/test_session.py  write  4.7 KB  <span style="color: #66cc66; font-weight: 600;">+47 -0</span>     root       │
│ src/auth/tokens.py     edit   1.8 KB  <span style="color: #66cc66; font-weight: 600;">+4 -2</span>      root       │
│ tests/test_tokens.py   write  3.2 KB  <span style="color: #66cc66; font-weight: 600;">+32 -0</span>     auth       │
╰─────────────────────────────────────────────────────────────╯

╭─ Summary ───────────╮
│ <span style="color: yellow; font-weight: 600;">Total:</span> 4            │
│ <span style="color: #66cc66; font-weight: 600;">Writes:</span> 2 (new)     │
│ <span style="color: #5599ff; font-weight: 600;">Edits:</span> 2 (modified) │
│ <span style="color: #ff6666; font-weight: 600;">Deletes:</span> 0          │
│ <span style="color: #5599ff; font-weight: 600;">Total Size:</span> 11.8 KB │
╰─────────────────────╯

╭─ By Plan ─────────────────╮
│ <span style="color: #5599ff; font-weight: 600;">Root Plan:</span> 3 artifacts    │
│ <span style="color: #5599ff; font-weight: 600;">auth-tests:</span> 1 artifact    │
│ <span style="color: #5599ff; font-weight: 600;">payment-tests:</span> (pending)  │
╰───────────────────────────╯

<span style="color: #66cc66; font-weight: 600;">✓ OK</span> 4 artifacts listed
</code></pre></div>

=== "Plain"

```
$ agents plan artifacts 01HXM8C2ZK4Q7C2B3F2R4VYV6J

Artifacts
  Path                   Type   Size    Change     Child Plan
  ---------------------  -----  ------  ---------  ----------
  src/auth/session.py    write  2.1 KB  +8 -2      root
  tests/test_session.py  write  4.7 KB  +47 -0     root
  src/auth/tokens.py     edit   1.8 KB  +4 -2      root
  tests/test_tokens.py   write  3.2 KB  +32 -0     auth

Summary
  Total: 4
  Writes: 2 (new)
  Edits: 2 (modified)
  Deletes: 0
  Total Size: 11.8 KB

By Plan
  Root Plan: 3 artifacts
  auth-tests: 1 artifact
  payment-tests: (pending)

[OK] 4 artifacts listed
```

=== "JSON"

```json
{
  "command": "plan artifacts",
  "status": "ok",
  "exit_code": 0,
  "data": {
    "artifacts": [
      { "path": "src/auth/session.py", "type": "write", "size": "2.1 KB", "change": "+8 -2", "child_plan": "root" },
      { "path": "tests/test_session.py", "type": "write", "size": "4.7 KB", "change": "+47 -0", "child_plan": "root" },
      { "path": "src/auth/tokens.py", "type": "edit", "size": "1.8 KB", "change": "+4 -2", "child_plan": "root" },
      { "path": "tests/test_tokens.py", "type": "write", "size": "3.2 KB", "change": "+32 -0", "child_plan": "auth" }
    ],
    "summary": {
      "total": 4,
      "writes": 2,
      "edits": 2,
      "deletes": 0,
      "total_size": "11.8 KB"
    },
    "by_plan": {
      "root": 3,
      "auth-tests": 1,
      "payment-tests": "pending"
    }
  },
  "timing": { "started": "2025-06-15T10:33:00Z", "duration_ms": 480 },
  "messages": ["4 artifacts listed"]
}
```

=== "YAML"

```yaml
command: plan artifacts
status: ok
exit_code: 0
data:
  artifacts:
    - path: src/auth/session.py
      type: write
      size: "2.1 KB"
      change: "+8 -2"
      child_plan: root
    - path: tests/test_session.py
      type: write
      size: "4.7 KB"
      change: "+47 -0"
      child_plan: root
    - path: src/auth/tokens.py
      type: edit
      size: "1.8 KB"
      change: "+4 -2"
      child_plan: root
    - path: tests/test_tokens.py
      type: write
      size: "3.2 KB"
      change: "+32 -0"
      child_plan: auth
  summary:
    total: 4
    writes: 2
    edits: 2
    deletes: 0
    total_size: "11.8 KB"
  by_plan:
    root: 3
    auth-tests: 1
    payment-tests: pending
timing:
  started: "2025-06-15T10:33:00Z"
  duration_ms: 480
messages:
  - "4 artifacts listed"
```
agents plan errors
agents plan errors <PLAN_ID>

Purpose Show error decisions with recovery hints and retry history for a plan.

Arguments

  • <PLAN_ID>: Plan ID (ULID).

Examples

=== "Rich"

<div class="highlight"><pre><code>
<span style="color: #66cc66; font-weight: 600;">$</span> <span style="color: cyan; font-weight: 600;">agents</span> plan errors 01HXM8C2ZK4Q7C2B3F2R4VYV6J

╭─ Plan Errors ──────────────────────────────────────────────────────╮
│ <span style="color: cyan; font-weight: 600;">Plan:</span> 01HXM8C2ZK4Q7C2B3F2R4VYV6J                                  │
│ <span style="color: cyan; font-weight: 600;">Phase:</span> execute                                                    │
│ <span style="color: cyan; font-weight: 600;">State:</span> errored                                                    │
│ <span style="color: red; font-weight: 600;">Error:</span> Tool execution failed: write_file permission denied         │
│                                                                    │
│ <span style="color: cyan; font-weight: 600;">Error Category:</span> tool_execution                                    │
│ <span style="color: cyan; font-weight: 600;">Error Phase:</span> execute                                              │
│ <span style="color: cyan; font-weight: 600;">Retry Count:</span> 2/3                                                  │
│ <span style="color: cyan; font-weight: 600;">Retriable:</span> true                                                   │
│                                                                    │
│ <span style="color: cyan; font-weight: 600;">Recovery Suggestions:</span>                                             │
│   → Check sandbox permissions and retry execution                  │
│     <span style="opacity: 0.7;">$ agents plan execute 01HXM8C2ZK4Q7C2B3F2R4VYV6J</span>              │
│   → Revert to Strategize phase to adjust the plan                  │
│     <span style="opacity: 0.7;">$ agents plan correct --mode revert -g "..." &lt;DECISION_ID&gt;</span>     │
╰────────────────────────────────────────────────────────────────────╯

<span style="color: #66cc66; font-weight: 600;">✓ OK</span>
</code></pre></div>

=== "Plain"

```
$ agents plan errors 01HXM8C2ZK4Q7C2B3F2R4VYV6J

Plan Errors
  Plan: 01HXM8C2ZK4Q7C2B3F2R4VYV6J
  Phase: execute
  State: errored
  Error: Tool execution failed: write_file permission denied
  Error Category: tool_execution
  Error Phase: execute
  Retry Count: 2/3
  Retriable: true

Recovery Suggestions
  → Check sandbox permissions and retry execution
    $ agents plan execute 01HXM8C2ZK4Q7C2B3F2R4VYV6J
  → Revert to Strategize phase to adjust the plan
    $ agents plan correct --mode revert -g "..." <DECISION_ID>

[OK]
```

=== "JSON"

```json
{
  "plan_id": "01HXM8C2ZK4Q7C2B3F2R4VYV6J",
  "phase": "execute",
  "state": "errored",
  "error_message": "Tool execution failed: write_file permission denied",
  "error_category": "tool_execution",
  "error_phase": "execute",
  "retry_count": 2,
  "max_retries": 3,
  "is_retriable": true,
  "recovery_hints": [
    {
      "action": "retry",
      "message": "Check sandbox permissions and retry execution",
      "cli_command": "agents plan execute 01HXM8C2ZK4Q7C2B3F2R4VYV6J"
    },
    {
      "action": "revert",
      "message": "Revert to Strategize phase to adjust the plan",
      "cli_command": "agents plan correct --mode revert -g \"...\" <DECISION_ID>"
    }
  ]
}
```
agents plan prompt
agents plan prompt <PLAN_ID> <GUIDANCE>

Purpose Provide additional guidance to a plan, typically when it is errored or awaiting input.

Arguments

  • <PLAN_ID>: Plan ID.
  • "<GUIDANCE>": Guidance text.

Examples

=== "Rich"

<div class="highlight"><pre><code>
<span style="color: #66cc66; font-weight: 600;">$</span> <span style="color: cyan; font-weight: 600;">agents</span> plan prompt 01HXM8C2ZK4Q7C2B3F2R4VYV6J <span style="color: #66cc66;">&quot;Use mocks for database tests&quot;</span>

╭─ Guidance Added ────────────────────────╮
│ <span style="color: cyan; font-weight: 600;">Plan:</span> 01HXM8C2ZK4Q7C2B3F2R4VYV6J        │
│ <span style="color: magenta; font-weight: 600;">Guidance:</span> Use mocks for database tests  │
│ <span style="color: #5599ff; font-weight: 600;">Scope:</span> next execution step              │
│ <span style="color: #5599ff; font-weight: 600;">Phase:</span> execute                          │
│ <span style="color: #5599ff; font-weight: 600;">State:</span> errored → processing             │
╰─────────────────────────────────────────╯

╭─ Decision Created ────────────────────────╮
│ <span style="color: #5599ff; font-weight: 600;">Type:</span> user_intervention                   │
│ <span style="color: #5599ff; font-weight: 600;">ID:</span> 01HXM9C5G7R2X8S3K4Z5Q8R6Y3            │
│ <span style="color: #5599ff; font-weight: 600;">Parent:</span> 01HXM9A1C2Q7W3R5G8Z0P4Q1X9        │
╰───────────────────────────────────────────╯

╭─ Queue ────╮
│ <span style="color: yellow; font-weight: 600;">Pending:</span> 1 │
│ <span style="color: #66cc66; font-weight: 600;">Applied:</span> 0 │
╰────────────╯

<span style="color: #66cc66; font-weight: 600;">✓ OK</span> Guidance queued
</code></pre></div>

=== "Plain"

```
$ agents plan prompt 01HXM8C2ZK4Q7C2B3F2R4VYV6J "Use mocks for database tests"

Guidance Added
  Plan: 01HXM8C2ZK4Q7C2B3F2R4VYV6J
  Guidance: Use mocks for database tests
  Scope: next execution step
  Phase: execute
  State: errored -> processing

Decision Created
  Type: user_intervention
  ID: 01HXM9C5G7R2X8S3K4Z5Q8R6Y3
  Parent: 01HXM9A1C2Q7W3R5G8Z0P4Q1X9

Queue
  Pending: 1
  Applied: 0

[OK] Guidance queued
```

=== "JSON"

```json
{
  "command": "plan prompt",
  "status": "ok",
  "exit_code": 0,
  "data": {
    "guidance_added": {
      "plan": "01HXM8C2ZK4Q7C2B3F2R4VYV6J",
      "guidance": "Use mocks for database tests",
      "scope": "next execution step",
      "phase": "execute",
      "state_transition": "errored -> processing"
    },
    "decision_created": {
      "type": "user_intervention",
      "id": "01HXM9C5G7R2X8S3K4Z5Q8R6Y3",
      "parent": "01HXM9A1C2Q7W3R5G8Z0P4Q1X9"
    },
    "queue": {
      "pending": 1,
      "applied": 0
    }
  },
  "timing": { "started": "2025-06-15T10:35:00Z", "duration_ms": 620 },
  "messages": ["Guidance queued"]
}
```

=== "YAML"

```yaml
command: plan prompt
status: ok
exit_code: 0
data:
  guidance_added:
    plan: "01HXM8C2ZK4Q7C2B3F2R4VYV6J"
    guidance: "Use mocks for database tests"
    scope: next execution step
    phase: execute
    state_transition: "errored -> processing"
  decision_created:
    type: user_intervention
    id: "01HXM9C5G7R2X8S3K4Z5Q8R6Y3"
    parent: "01HXM9A1C2Q7W3R5G8Z0P4Q1X9"
  queue:
    pending: 1
    applied: 0
timing:
  started: "2025-06-15T10:35:00Z"
  duration_ms: 620
messages:
  - "Guidance queued"
```
agents plan rollback
agents plan rollback [--yes|-y] <PLAN_ID> <CHECKPOINT_ID>

!!! danger "Destructive Operation" Rollback a plan sandbox to a previous checkpoint. All changes made ==after== the target checkpoint are reverted: files are restored or removed, decisions are discarded, and tool calls are undone. Child plans spawned after the checkpoint are invalidated.

Arguments

  • <PLAN_ID>: Plan ID.
  • <CHECKPOINT_ID>: Checkpoint ID.
  • --yes: Skip confirmation.

Examples

=== "Rich"

<div class="highlight"><pre><code>
<span style="color: #66cc66; font-weight: 600;">$</span> <span style="color: cyan; font-weight: 600;">agents</span> plan rollback 01HXM8C2ZK4Q7C2B3F2R4VYV6J cp_01HXM8C2

Rollback plan 01HXM8C2ZK4Q7C2B3F2R4VYV6J to cp_01HXM8C2? [y/N]: y

╭─ Rollback Summary ───────────────╮
│ <span style="color: cyan; font-weight: 600;">Plan:</span> 01HXM8C2ZK4Q7C2B3F2R4VYV6J │
│ <span style="color: magenta; font-weight: 600;">Checkpoint:</span> cp_01HXM8C2          │
│ <span style="color: yellow; font-weight: 600;">Label:</span> before auth refactor      │
│ <span style="color: yellow; font-weight: 600;">Files:</span> 6 reverted                │
╰──────────────────────────────────╯

╭─ Changes Reverted ──────────────────╮
│ <span style="color: yellow; font-weight: 600;">File</span>                    <span style="color: yellow; font-weight: 600;">Action</span>      │
│ <span style="opacity: 0.7;">──────────────────────  ──────────</span>  │
│ src/auth/session.py     restored    │
│ src/auth/tokens.py      restored    │
│ tests/test_session.py   removed     │
│ tests/test_tokens.py    removed     │
│ src/auth/fixtures.py    restored    │
│ src/auth/__init__.py    restored        │
╰─────────────────────────────────────╯

╭─ Impact ──────────────────────────────╮
│ <span style="color: yellow; font-weight: 600;">Child Plans Invalidated:</span> 2            │
│ <span style="color: #5599ff; font-weight: 600;">Sandbox:</span> restored to cp_01HXM8C2      │
│ <span style="color: #5599ff; font-weight: 600;">Decisions After CP:</span> 2 discarded       │
│ <span style="color: #5599ff; font-weight: 600;">Tool Calls After CP:</span> 5 undone         │
╰───────────────────────────────────────╯

╭─ Post-Rollback State ──────────╮
│ <span style="color: yellow; font-weight: 600;">Phase:</span> execute                 │
│ <span style="color: #5599ff; font-weight: 600;">State:</span> queued (awaiting input) │
│ <span style="color: #5599ff; font-weight: 600;">Checkpoints Remaining:</span> 2       │
╰────────────────────────────────╯

<span style="color: #66cc66; font-weight: 600;">✓ OK</span> Rollback complete
</code></pre></div>

=== "Plain"

```
$ agents plan rollback 01HXM8C2ZK4Q7C2B3F2R4VYV6J cp_01HXM8C2

Rollback plan 01HXM8C2ZK4Q7C2B3F2R4VYV6J to cp_01HXM8C2? [y/N]: y

Rollback Summary
  Plan: 01HXM8C2ZK4Q7C2B3F2R4VYV6J
  Checkpoint: cp_01HXM8C2
  Label: before auth refactor
  Files: 6 reverted

Changes Reverted
  File                    Action
  ----------------------  ----------
  src/auth/session.py     restored
  src/auth/tokens.py      restored
  tests/test_session.py   removed
  tests/test_tokens.py    removed
  src/auth/fixtures.py    restored
  src/auth/__init__.py    restored

Impact
  Child Plans Invalidated: 2
  Sandbox: restored to cp_01HXM8C2
  Decisions After CP: 2 discarded
  Tool Calls After CP: 5 undone

Post-Rollback State
  Phase: execute
  State: queued (awaiting input)
  Checkpoints Remaining: 2

[OK] Rollback complete
```

=== "JSON"

```json
{
  "command": "plan rollback",
  "status": "ok",
  "exit_code": 0,
  "data": {
    "rollback_summary": {
      "plan": "01HXM8C2ZK4Q7C2B3F2R4VYV6J",
      "checkpoint": "cp_01HXM8C2",
      "label": "before auth refactor",
      "files_reverted": 6
    },
    "changes_reverted": [
      { "file": "src/auth/session.py", "action": "restored" },
      { "file": "src/auth/tokens.py", "action": "restored" },
      { "file": "tests/test_session.py", "action": "removed" },
      { "file": "tests/test_tokens.py", "action": "removed" },
      { "file": "src/auth/fixtures.py", "action": "restored" },
      { "file": "src/auth/__init__.py", "action": "restored" }
    ],
    "impact": {
      "child_plans_invalidated": 2,
      "sandbox": "restored to cp_01HXM8C2",
      "decisions_after_cp": "2 discarded",
      "tool_calls_after_cp": "5 undone"
    },
    "post_rollback_state": {
      "phase": "execute",
      "state": "queued (awaiting input)",
      "checkpoints_remaining": 2
    }
  },
  "timing": { "started": "2025-06-15T10:37:00Z", "duration_ms": 1850 },
  "messages": ["Rollback complete"]
}
```

=== "YAML"

```yaml
command: plan rollback
status: ok
exit_code: 0
data:
  rollback_summary:
    plan: "01HXM8C2ZK4Q7C2B3F2R4VYV6J"
    checkpoint: cp_01HXM8C2
    label: before auth refactor
    files_reverted: 6
  changes_reverted:
    - file: src/auth/session.py
      action: restored
    - file: src/auth/tokens.py
      action: restored
    - file: tests/test_session.py
      action: removed
    - file: tests/test_tokens.py
      action: removed
    - file: src/auth/fixtures.py
      action: restored
    - file: src/auth/__init__.py
      action: restored
  impact:
    child_plans_invalidated: 2
    sandbox: "restored to cp_01HXM8C2"
    decisions_after_cp: "2 discarded"
    tool_calls_after_cp: "5 undone"
  post_rollback_state:
    phase: execute
    state: "queued (awaiting input)"
    checkpoints_remaining: 2
timing:
  started: "2025-06-15T10:37:00Z"
  duration_ms: 1850
messages:
  - "Rollback complete"
```

agents action

!!! info "Purpose" Manage ==reusable actions== — named templates that define a complete unit of work including strategy actors, execution actors, definition of done, arguments, and invariants. Actions serve as the entry point to the plan lifecycle.

??? tip "Action vs Plan" An action is a ==template==; a plan is an ==instance==. When you run agents plan use <ACTION> <PROJECT>, the action template spawns a new plan that progresses through the lifecycle phases. Actions can be reused many times (default) or configured as single-use (reusable: false).

agents action create
agents action create --config|-c <CFG_FILE>

Purpose Create a new action template from a YAML configuration file. The --config file is required and must fully define the action.

Arguments

  • --config/-c FILE: YAML configuration file defining the action (required). The file must fully define the action, including the name field, strategy-actor, execution-actor, and definition-of-done.

Examples

=== "Rich"

<div class="highlight"><pre><code>
<span style="color: #66cc66; font-weight: 600;">$</span> <span style="color: cyan; font-weight: 600;">agents</span> action create <span style="color: cyan;">--config</span> ./actions/code-coverage.yaml

╭─ Action Created ────────────────────────╮
│ <span style="color: cyan; font-weight: 600;">Name:</span> local/code-coverage               │
│ <span style="color: yellow; font-weight: 600;">State:</span> available                        │
│ <span style="color: magenta; font-weight: 600;">Strategy Actor:</span> local/strategist        │
│ <span style="color: magenta; font-weight: 600;">Execution Actor:</span> local/executor         │
│ <span style="color: #66cc66; font-weight: 600;">Reusable:</span> yes                           │
│ <span style="color: #5599ff; font-weight: 600;">Read Only:</span> no                           │
│ <span style="color: #66cc66; font-weight: 600;">Config:</span> ./actions/code-coverage.yaml    │
│ <span style="color: #66cc66; font-weight: 600;">Created:</span> 2026-02-08 12:20               │
╰─────────────────────────────────────────╯

╭─ Definition of Done ─╮
│ Coverage reaches 85% │
╰──────────────────────╯

╭─ Arguments ──────────────────────────────────────────────────────╮
│ <span style="color: cyan; font-weight: 600;">Name</span>                     <span style="color: cyan; font-weight: 600;">Type</span>    <span style="color: cyan; font-weight: 600;">Required</span>  <span style="color: cyan; font-weight: 600;">Description</span>           │
│ <span style="opacity: 0.7;">───────────────────────  ──────  ────────  ─────────────────────</span> │
│ target_coverage_percent  int     yes       Target coverage %     │
│ test_command             string  no        Test framework to use │
╰──────────────────────────────────────────────────────────────────╯

╭─ Automation ──────────────────────────╮
│ <span style="color: magenta; font-weight: 600;">Profile:</span> supervised                   │
│ <span style="color: #5599ff; font-weight: 600;">Source:</span> default                       │
╰───────────────────────────────────────╯

<span style="color: #66cc66; font-weight: 600;">✓ OK</span> Action created
</code></pre></div>

=== "Plain"

```
$ agents action create --config ./actions/code-coverage.yaml

Action Created
  Name: local/code-coverage
  State: available
  Strategy Actor: local/strategist
  Execution Actor: local/executor
  Reusable: yes
  Read Only: no
  Config: ./actions/code-coverage.yaml
  Created: 2026-02-08 12:20

Definition of Done
  Coverage reaches 85%

Arguments
  Name                     Type    Required  Description
  -----------------------  ------  --------  ---------------------
  target_coverage_percent  int     yes       Target coverage %
  test_command             string  no        Test framework to use

Automation
  Profile: supervised
  Source: default

[OK] Action created
```

=== "JSON"

```json
{
  "command": "action create",
  "status": "ok",
  "exit_code": 0,
  "data": {
    "name": "local/code-coverage",
    "state": "available",
    "strategy_actor": "local/strategist",
    "execution_actor": "local/executor",
    "reusable": true,
    "read_only": false,
    "config": "./actions/code-coverage.yaml",
    "created": "2026-02-08T12:20:00Z",
    "definition_of_done": "Coverage reaches 85%",
    "arguments": [
      { "name": "target_coverage_percent", "type": "int", "required": true, "description": "Target coverage %" },
      { "name": "test_command", "type": "string", "required": false, "description": "Test framework to use" }
    ],
    "automation": {
      "profile": "supervised",
      "source": "default"
    }
  },
  "timing": { "started": "2026-02-08T12:20:00Z", "duration_ms": 110 },
  "messages": ["Action created"]
}
```

=== "YAML"

```yaml
command: action create
status: ok
exit_code: 0
data:
  name: local/code-coverage
  state: available
  strategy_actor: local/strategist
  execution_actor: local/executor
  reusable: true
  read_only: false
  config: "./actions/code-coverage.yaml"
  created: "2026-02-08T12:20:00Z"
  definition_of_done: "Coverage reaches 85%"
  arguments:
    - name: target_coverage_percent
      type: int
      required: true
      description: "Target coverage %"
    - name: test_command
      type: string
      required: false
      description: "Test framework to use"
  automation:
    profile: supervised
    source: default
timing:
  started: "2026-02-08T12:20:00Z"
  duration_ms: 110
messages:
  - "Action created"
```
agents action list
agents action list [(--namespace|-n) <NS>] [(--state|-s) <STATE>] [<REGEX>]

Purpose List actions with optional filters.

Arguments

  • --namespace/-n NS: Filter by namespace.
  • --state/-s STATE: Filter by state.

Examples

=== "Rich"

<div class="highlight"><pre><code>
<span style="color: #66cc66; font-weight: 600;">$</span> <span style="color: cyan; font-weight: 600;">agents</span> action list

╭─ Actions ──────────────────────────────────────────────────────────────────────────────────╮
│ <span style="color: cyan; font-weight: 600;">Name</span>                 <span style="color: cyan; font-weight: 600;">State</span>      <span style="color: cyan; font-weight: 600;">Strategy Actor</span>    <span style="color: cyan; font-weight: 600;">Execution Actor</span>  <span style="color: cyan; font-weight: 600;">Reusable</span>  <span style="color: cyan; font-weight: 600;">Plans</span>         │
│ <span style="opacity: 0.7;">───────────────────  ─────────  ────────────────  ───────────────  ────────  ─────</span>         │
│ local/code-coverage  available  local/strategist  local/executor   ✓         3             │
╰────────────────────────────────────────────────────────────────────────────────────────────╯

╭─ Filters ────────╮
│ <span style="color: yellow; font-weight: 600;">State:</span> available │
│ <span style="color: #5599ff; font-weight: 600;">Namespace:</span> (any) │
╰──────────────────╯

╭─ Summary ──────────────╮
│ <span style="color: yellow; font-weight: 600;">Total:</span> 1               │
│ <span style="color: #66cc66; font-weight: 600;">Available:</span> 1           │
│ <span style="color: yellow; font-weight: 600;">Archived:</span> 0            │
│ <span style="color: #5599ff; font-weight: 600;">Total Plans Created:</span> 3 │
╰────────────────────────╯

<span style="color: #66cc66; font-weight: 600;">✓ OK</span> 1 action listed
</code></pre></div>

=== "Plain"

```
$ agents action list

Actions
  Name                 State      Strategy Actor    Execution Actor  Reusable  Plans
  -------------------  ---------  ----------------  ---------------  --------  -----
  local/code-coverage  available  local/strategist  local/executor   yes       3

Filters
  State: available
  Namespace: (any)

Summary
  Total: 1
  Available: 1
  Archived: 0
  Total Plans Created: 3

[OK] 1 action listed
```

=== "JSON"

```json
{
  "command": "action list",
  "status": "ok",
  "exit_code": 0,
  "data": {
    "actions": [
      {
        "name": "local/code-coverage",
        "state": "available",
        "strategy_actor": "local/strategist",
        "execution_actor": "local/executor",
        "reusable": true,
        "plans": 3
      }
    ],
    "filters": { "state": "available", "namespace": null },
    "summary": { "total": 1, "available": 1, "archived": 0, "total_plans_created": 3 }
  },
  "timing": { "started": "2026-02-08T12:21:00Z", "duration_ms": 45 },
  "messages": ["1 action listed"]
}
```

=== "YAML"

```yaml
command: action list
status: ok
exit_code: 0
data:
  actions:
    - name: local/code-coverage
      state: available
      strategy_actor: local/strategist
      execution_actor: local/executor
      reusable: true
      plans: 3
  filters:
    state: available
    namespace: null
  summary:
    total: 1
    available: 1
    archived: 0
    total_plans_created: 3
timing:
  started: "2026-02-08T12:21:00Z"
  duration_ms: 45
messages:
  - "1 action listed"
```
agents action show
agents action show <ACTION_NAME>

Purpose Show details for an action.

Arguments

  • <ACTION_NAME>: Action name.

Examples

=== "Rich"

<div class="highlight"><pre><code>
<span style="color: #66cc66; font-weight: 600;">$</span> <span style="color: cyan; font-weight: 600;">agents</span> action show local/code-coverage

╭─ Action Details ──────────────────────╮
│ <span style="color: cyan; font-weight: 600;">Name:</span> local/code-coverage             │
│ <span style="color: yellow; font-weight: 600;">State:</span> available                      │
│ <span style="color: magenta; font-weight: 600;">Strategy Actor:</span> local/strategist      │
│ <span style="color: magenta; font-weight: 600;">Execution Actor:</span> local/executor       │
│ <span style="color: #66cc66; font-weight: 600;">Reusable:</span> yes                         │
│ <span style="color: #5599ff; font-weight: 600;">Read Only:</span> no                         │
│ <span style="color: #66cc66; font-weight: 600;">Created:</span> 2026-02-08 12:20             │
╰───────────────────────────────────────╯

╭─ Definition of Done ─╮
│ Coverage reaches 85% │
╰──────────────────────╯

╭─ Arguments ──────────────────────────────────────────────────────╮
│ <span style="color: cyan; font-weight: 600;">Name</span>                     <span style="color: cyan; font-weight: 600;">Type</span>    <span style="color: cyan; font-weight: 600;">Required</span>  <span style="color: cyan; font-weight: 600;">Description</span>           │
│ <span style="opacity: 0.7;">───────────────────────  ──────  ────────  ─────────────────────</span> │
│ target_coverage_percent  int     yes       Target coverage %     │
│ test_command             string  no        Test framework to use │
╰──────────────────────────────────────────────────────────────────╯

╭─ Automation ──────────────────────────╮
│ <span style="color: magenta; font-weight: 600;">Profile:</span> supervised                   │
│ <span style="color: #5599ff; font-weight: 600;">Source:</span> default                       │
╰───────────────────────────────────────╯

╭─ History ────────────────────╮
│ <span style="color: #5599ff; font-weight: 600;">Plans Created:</span> 3             │
│ <span style="color: #66cc66; font-weight: 600;">Plans Completed:</span> 2           │
│ <span style="color: #ff6666; font-weight: 600;">Plans Failed:</span> 0              │
│ <span style="color: yellow; font-weight: 600;">Avg Duration:</span> 00:04:30       │
│ <span style="color: #5599ff; font-weight: 600;">Avg Cost:</span> $0.072             │
╰──────────────────────────────╯

╭─ Usage ───────────────────────────────────────────────────────────╮
│ - agents plan use local/code-coverage local/api-service           │
│     <span style="color: cyan;">--arg</span> target_coverage_percent=85                              │
╰───────────────────────────────────────────────────────────────────╯

<span style="color: #66cc66; font-weight: 600;">✓ OK</span> Action loaded
</code></pre></div>

=== "Plain"

```
$ agents action show local/code-coverage

Action Details
  Name: local/code-coverage
  State: available
  Strategy Actor: local/strategist
  Execution Actor: local/executor
  Reusable: yes
  Read Only: no
  Created: 2026-02-08 12:20

Definition of Done
  Coverage reaches 85%

Arguments
  Name                     Type    Required  Description
  -----------------------  ------  --------  ---------------------
  target_coverage_percent  int     yes       Target coverage %
  test_command             string  no        Test framework to use

Automation
  Profile: supervised
  Source: default

History
  Plans Created: 3
  Plans Completed: 2
  Plans Failed: 0
  Avg Duration: 00:04:30
  Avg Cost: $0.072

Usage
  agents plan use local/code-coverage local/api-service
    --arg target_coverage_percent=85

[OK] Action loaded
```

=== "JSON"

```json
{
  "command": "action show",
  "status": "ok",
  "exit_code": 0,
  "data": {
    "name": "local/code-coverage",
    "state": "available",
    "strategy_actor": "local/strategist",
    "execution_actor": "local/executor",
    "reusable": true,
    "read_only": false,
    "created": "2026-02-08T12:20:00Z",
    "definition_of_done": "Coverage reaches 85%",
    "arguments": [
      { "name": "target_coverage_percent", "type": "int", "required": true, "description": "Target coverage %" },
      { "name": "test_command", "type": "string", "required": false, "description": "Test framework to use" }
    ],
    "automation": { "profile": "supervised", "source": "default" },
    "history": {
      "plans_created": 3,
      "plans_completed": 2,
      "plans_failed": 0,
      "avg_duration": "00:04:30",
      "avg_cost": "$0.072"
    }
  },
  "timing": { "started": "2026-02-08T12:21:00Z", "duration_ms": 60 },
  "messages": ["Action loaded"]
}
```

=== "YAML"

```yaml
command: action show
status: ok
exit_code: 0
data:
  name: local/code-coverage
  state: available
  strategy_actor: local/strategist
  execution_actor: local/executor
  reusable: true
  read_only: false
  created: "2026-02-08T12:20:00Z"
  definition_of_done: "Coverage reaches 85%"
  arguments:
    - name: target_coverage_percent
      type: int
      required: true
      description: "Target coverage %"
    - name: test_command
      type: string
      required: false
      description: "Test framework to use"
  automation:
    profile: supervised
    source: default
  history:
    plans_created: 3
    plans_completed: 2
    plans_failed: 0
    avg_duration: "00:04:30"
    avg_cost: "$0.072"
timing:
  started: "2026-02-08T12:21:00Z"
  duration_ms: 60
messages:
  - "Action loaded"
```
agents action archive
agents action archive <ACTION_NAME>

Purpose Archive an action.

Arguments

  • <ACTION_NAME>: Action name.

Examples

=== "Rich"

<div class="highlight"><pre><code>
<span style="color: #66cc66; font-weight: 600;">$</span> <span style="color: cyan; font-weight: 600;">agents</span> action archive local/old-action

╭─ Action Archived ───────────╮
│ <span style="color: cyan; font-weight: 600;">Name:</span> local/old-action      │
│ <span style="color: yellow; font-weight: 600;">State:</span> available → archived │
│ <span style="color: #66cc66; font-weight: 600;">Archived:</span> 2026-02-08 12:22  │
╰─────────────────────────────╯

╭─ Impact ───────────────────────╮
│ <span style="color: yellow; font-weight: 600;">Availability:</span> hidden from list │
│ <span style="color: #5599ff; font-weight: 600;">Existing Plans:</span> unchanged      │
│ <span style="color: #5599ff; font-weight: 600;">Active Plans:</span> 0 affected       │
╰────────────────────────────────╯

╭─ History ─────────────────╮
│ <span style="color: #5599ff; font-weight: 600;">Total Plans:</span> 5            │
│ <span style="color: #66cc66; font-weight: 600;">Completed:</span> 4              │
│ <span style="color: #ff6666; font-weight: 600;">Failed:</span> 1                 │
│ <span style="color: #5599ff; font-weight: 600;">Last Used:</span> 2026-02-06     │
╰───────────────────────────╯

<span style="color: #66cc66; font-weight: 600;">✓ OK</span> Action archived
</code></pre></div>

=== "Plain"

```
$ agents action archive local/old-action

Action Archived
  Name: local/old-action
  State: available -> archived
  Archived: 2026-02-08 12:22

Impact
  Availability: hidden from list
  Existing Plans: unchanged
  Active Plans: 0 affected

History
  Total Plans: 5
  Completed: 4
  Failed: 1
  Last Used: 2026-02-06

[OK] Action archived
```

=== "JSON"

```json
{
  "command": "action archive",
  "status": "ok",
  "exit_code": 0,
  "data": {
    "name": "local/old-action",
    "state_transition": "available -> archived",
    "archived": "2026-02-08T12:22:00Z",
    "impact": {
      "availability": "hidden from list",
      "existing_plans": "unchanged",
      "active_plans_affected": 0
    },
    "history": {
      "total_plans": 5,
      "completed": 4,
      "failed": 1,
      "last_used": "2026-02-06"
    }
  },
  "timing": { "started": "2026-02-08T12:22:00Z", "duration_ms": 55 },
  "messages": ["Action archived"]
}
```

=== "YAML"

```yaml
command: action archive
status: ok
exit_code: 0
data:
  name: local/old-action
  state_transition: "available -> archived"
  archived: "2026-02-08T12:22:00Z"
  impact:
    availability: hidden from list
    existing_plans: unchanged
    active_plans_affected: 0
  history:
    total_plans: 5
    completed: 4
    failed: 1
    last_used: "2026-02-06"
timing:
  started: "2026-02-08T12:22:00Z"
  duration_ms: 55
messages:
  - "Action archived"
```

agents automation-profile

!!! info "Purpose" Manage ==automation profiles== — named collections of confidence thresholds (floating-point values from 0.0 to 1.0) that control which tasks are automated vs. require human approval. Each threshold specifies the minimum confidence level at which the system proceeds automatically; below the threshold, the system drops to manual mode.

!!! tip "Threshold Scale" | Value | Meaning | | :---: | :------ | | 0.0 | ==Always automatic== — no human approval needed | | 0.5 | Automatic when confidence ≥ 50% | | 1.0 | ==Always manual== — always requires human approval |

??? example "Built-in Profiles" The following profiles are always available and cannot be removed:

| Profile | Description |
| :------ | :---------- |
| `manual` | All thresholds at `1.0` — everything requires approval |
| `review` | Automatic strategize and execute, manual apply |
| `supervised` | Automatic strategize, manual execute and apply |
| `cautious` | Most operations automatic, manual for risky decisions |
| `trusted` | Nearly fully automatic, manual only for apply |
| `auto` | Fully automatic except apply |
| `ci` | Optimized for CI/CD pipelines |
| `full-auto` | All thresholds at `0.0` — fully autonomous |

Custom profiles follow the same `<namespace>/<name>` naming convention as other entities.
agents automation-profile add
agents automation-profile add --config|-c <FILE> [--update]

Purpose Register a new custom automation profile from a YAML configuration file. The profile is fully defined by the config file. If a profile with the same name already exists, the command fails unless the --update flag is provided.

Arguments

  • --config/-c FILE: YAML configuration file defining the profile (required). The file must fully define the profile, including the name field which determines the profile's registered name.
  • --update: Replace an existing profile with the same name.

Examples

=== "Rich"

<div class="highlight"><pre><code>
<span style="color: #66cc66; font-weight: 600;">$</span> <span style="color: cyan; font-weight: 600;">agents</span> automation-profile add <span style="color: cyan;">--config</span> ./profiles/careful-auto.yaml

╭─ Profile Registered ─────────────────────────────────────────────╮
│ <span style="color: cyan; font-weight: 600;">Name:</span> local/careful-auto                                         │
│ <span style="color: #5599ff; font-weight: 600;">Description:</span> Autonomous execution with mandatory sandbox         │
│              and manual apply                                    │
│ <span style="color: #66cc66; font-weight: 600;">Created:</span> 2026-02-08 14:30                                        │
╰──────────────────────────────────────────────────────────────────╯

╭─ Confidence Thresholds ────────────────╮
│ <span style="color: #66cc66; font-weight: 600;">decompose_task:</span> 0.0                   │
│ <span style="color: #66cc66; font-weight: 600;">create_tool:</span> 0.0                      │
│ <span style="color: #ff6666; font-weight: 600;">select_tool:</span> 1.0                        │
│ <span style="color: #66cc66; font-weight: 600;">edit_code:</span> 0.0         │
│ <span style="color: #66cc66; font-weight: 600;">execute_command:</span> 0.0            │
│ <span style="color: #66cc66; font-weight: 600;">create_file:</span> 0.0               │
│ <span style="color: #ff6666; font-weight: 600;">delete_content:</span> 1.0            │
│ <span style="color: #ff6666; font-weight: 600;">access_network:</span> 1.0         │
│ <span style="color: #66cc66; font-weight: 600;">install_dependency:</span> 0.0                  │
│ <span style="color: #66cc66; font-weight: 600;">modify_config:</span> 0.0              │
│ <span style="color: #66cc66; font-weight: 600;">approve_plan:</span> 0.0           │
│ <span style="color: #66cc66; font-weight: 600;">require_sandbox:</span> true                  │
│ <span style="color: #66cc66; font-weight: 600;">require_checkpoints:</span> true              │
│ <span style="color: #ff6666; font-weight: 600;">allow_unsafe_tools:</span> false              │
╰────────────────────────────────────────╯

<span style="color: #66cc66; font-weight: 600;">✓ OK</span> Profile registered
</code></pre></div>

=== "Plain"

```
$ agents automation-profile add --config ./profiles/careful-auto.yaml

Profile Registered
  Name: local/careful-auto
  Description: Autonomous execution with mandatory sandbox and manual apply
  Created: 2026-02-08 14:30

Confidence Thresholds
  decompose_task: 0.0
  create_tool: 0.0
  select_tool: 1.0
  edit_code: 0.0
  execute_command: 0.0
  create_file: 0.0
  delete_content: 1.0
  access_network: 1.0
  install_dependency: 0.0
  modify_config: 0.0
  approve_plan: 0.0
  require_sandbox: true
  require_checkpoints: true
  allow_unsafe_tools: false

[OK] Profile registered
```

=== "JSON"

```json
{
  "command": "automation-profile add",
  "status": "ok",
  "exit_code": 0,
  "data": {
    "name": "local/careful-auto",
    "description": "Autonomous execution with mandatory sandbox and manual apply",
    "created": "2026-02-08T14:30:00Z",
    "thresholds": {
      "decompose_task": 0.0,
      "create_tool": 0.0,
      "select_tool": 1.0,
      "edit_code": 0.0,
      "execute_command": 0.0,
      "create_file": 0.0,
      "delete_content": 1.0,
      "access_network": 1.0,
      "install_dependency": 0.0,
      "modify_config": 0.0,
      "approve_plan": 0.0
    },
    "flags": {
      "require_sandbox": true,
      "require_checkpoints": true,
      "allow_unsafe_tools": false
    }
  },
  "timing": { "started": "2026-02-08T14:30:00Z", "duration_ms": 70 },
  "messages": ["Profile registered"]
}
```

=== "YAML"

```yaml
command: automation-profile add
status: ok
exit_code: 0
data:
  name: local/careful-auto
  description: "Autonomous execution with mandatory sandbox and manual apply"
  created: "2026-02-08T14:30:00Z"
  thresholds:
    decompose_task: 0.0
    create_tool: 0.0
    select_tool: 1.0
    edit_code: 0.0
    execute_command: 0.0
    create_file: 0.0
    delete_content: 1.0
    access_network: 1.0
    install_dependency: 0.0
    modify_config: 0.0
    approve_plan: 0.0
  flags:
    require_sandbox: true
    require_checkpoints: true
    allow_unsafe_tools: false
timing:
  started: "2026-02-08T14:30:00Z"
  duration_ms: 70
messages:
  - "Profile registered"
```
agents automation-profile remove
agents automation-profile remove [--yes|-y] <NAME>

!!! warning "Destructive Operation" Remove a custom automation profile. ==Built-in profiles cannot be removed.== Plans and projects currently referencing this profile will fall back to their inherited or global default profile.

Arguments

  • <NAME>: Profile name.
  • --yes, -y: Skip confirmation prompt.

Examples

=== "Rich"

<div class="highlight"><pre><code>
<span style="color: #66cc66; font-weight: 600;">$</span> <span style="color: cyan; font-weight: 600;">agents</span> automation-profile remove local/careful-auto

Remove automation profile local/careful-auto? [y/N]: y

╭─ Profile Removed ──────────╮
│ <span style="color: cyan; font-weight: 600;">Name:</span> local/careful-auto   │
╰────────────────────────────╯

<span style="color: #66cc66; font-weight: 600;">✓ OK</span> Profile removed
</code></pre></div>

=== "Plain"

```
$ agents automation-profile remove local/careful-auto

Remove automation profile local/careful-auto? [y/N]: y

Profile Removed
  Name: local/careful-auto

[OK] Profile removed
```

=== "JSON"

```json
{
  "command": "automation-profile remove",
  "status": "ok",
  "exit_code": 0,
  "data": {
    "name": "local/careful-auto"
  },
  "timing": { "started": "2026-02-08T14:31:00Z", "duration_ms": 40 },
  "messages": ["Profile removed"]
}
```

=== "YAML"

```yaml
command: automation-profile remove
status: ok
exit_code: 0
data:
  name: local/careful-auto
timing:
  started: "2026-02-08T14:31:00Z"
  duration_ms: 40
messages:
  - "Profile removed"
```
agents automation-profile list
agents automation-profile list [<REGEX>]

Purpose List all available automation profiles (built-in and custom).

Arguments

  • [REGEX]: Optional filter pattern.

Examples

=== "Rich"

<div class="highlight"><pre><code>
<span style="color: #66cc66; font-weight: 600;">$</span> <span style="color: cyan; font-weight: 600;">agents</span> automation-profile list

╭─ Automation Profiles ──────────────────────────────────────────────────────────╮
│ <span style="color: cyan; font-weight: 600;">Name</span>                <span style="color: cyan; font-weight: 600;">Source</span>    <span style="color: cyan; font-weight: 600;">Auto-Apply</span>  <span style="color: cyan; font-weight: 600;">Sandbox</span>  <span style="color: cyan; font-weight: 600;">Description</span>                 │
│ <span style="opacity: 0.7;">──────────────────  ────────  ──────────  ───────  ────────────────────────</span>    │
│ manual             built-in  1.0         yes      Human-driven (default)       │
│ review             built-in  1.0         yes      Auto-phase, manual decisions │
│ supervised         built-in  1.0         yes      Auto-plan, manual exec       │
│ cautious           built-in  1.0         yes      Confidence-gated automation  │
│ trusted            built-in  1.0         yes      Auto-exec, manual apply      │
│ auto               built-in  1.0         yes      Full auto except apply       │
│ ci                 built-in  0.0         yes      Full auto, safe CI/CD        │
│ full-auto          built-in  0.0         no       Complete automation          │
│ local/careful-auto custom    0.8         yes      Custom careful profile       │
╰────────────────────────────────────────────────────────────────────────────────╯

╭─ Summary ───────────╮
│ <span style="color: #5599ff; font-weight: 600;">Built-in:</span> 8         │
│ <span style="color: #5599ff; font-weight: 600;">Custom:</span> 1           │
│ <span style="color: #5599ff; font-weight: 600;">Total:</span> 9            │
╰─────────────────────╯

<span style="color: #66cc66; font-weight: 600;">✓ OK</span> 9 profiles listed
</code></pre></div>

=== "Plain"

```
$ agents automation-profile list

Automation Profiles
  Name                Source    Auto-Apply  Sandbox  Description
  ------------------  --------  ----------  -------  ----------------------------
  manual              built-in  1.0         yes      Human-driven (default)
  review              built-in  1.0         yes      Auto-phase, manual decisions
  supervised          built-in  1.0         yes      Auto-plan, manual exec
  cautious            built-in  1.0         yes      Confidence-gated automation
  trusted             built-in  1.0         yes      Auto-exec, manual apply
  auto                built-in  1.0         yes      Full auto except apply
  ci                  built-in  0.0         yes      Full auto, safe CI/CD
  full-auto           built-in  0.0         no       Complete automation
  local/careful-auto  custom    0.8         yes      Custom careful profile

Summary
  Built-in: 8
  Custom: 1
  Total: 9

[OK] 9 profiles listed
```

=== "JSON"

```json
{
  "command": "automation-profile list",
  "status": "ok",
  "exit_code": 0,
  "data": {
    "profiles": [
      { "name": "manual", "source": "built-in", "select_tool": 1.0, "sandbox": true, "description": "Human-driven (default)" },
      { "name": "review", "source": "built-in", "select_tool": 1.0, "sandbox": true, "description": "Auto-phase, manual decisions" },
      { "name": "supervised", "source": "built-in", "select_tool": 1.0, "sandbox": true, "description": "Auto-plan, manual exec" },
      { "name": "cautious", "source": "built-in", "select_tool": 1.0, "sandbox": true, "description": "Confidence-gated automation" },
      { "name": "trusted", "source": "built-in", "select_tool": 1.0, "sandbox": true, "description": "Auto-exec, manual apply" },
      { "name": "auto", "source": "built-in", "select_tool": 1.0, "sandbox": true, "description": "Full auto except apply" },
      { "name": "ci", "source": "built-in", "select_tool": 0.0, "sandbox": true, "description": "Full auto, safe CI/CD" },
      { "name": "full-auto", "source": "built-in", "select_tool": 0.0, "sandbox": false, "description": "Complete automation" },
      { "name": "local/careful-auto", "source": "custom", "select_tool": 0.8, "sandbox": true, "description": "Custom careful profile" }
    ],
    "summary": { "built_in": 8, "custom": 1, "total": 9 }
  },
  "timing": { "started": "2026-02-08T14:31:00Z", "duration_ms": 50 },
  "messages": ["9 profiles listed"]
}
```

=== "YAML"

```yaml
command: automation-profile list
status: ok
exit_code: 0
data:
  profiles:
    - name: manual
      source: built-in
      select_tool: 1.0
      sandbox: true
      description: "Human-driven (default)"
    - name: review
      source: built-in
      select_tool: 1.0
      sandbox: true
      description: "Auto-phase, manual decisions"
    - name: supervised
      source: built-in
      select_tool: 1.0
      sandbox: true
      description: "Auto-plan, manual exec"
    - name: cautious
      source: built-in
      select_tool: 1.0
      sandbox: true
      description: "Confidence-gated automation"
    - name: trusted
      source: built-in
      select_tool: 1.0
      sandbox: true
      description: "Auto-exec, manual apply"
    - name: auto
      source: built-in
      select_tool: 1.0
      sandbox: true
      description: "Full auto except apply"
    - name: ci
      source: built-in
      select_tool: 0.0
      sandbox: true
      description: "Full auto, safe CI/CD"
    - name: full-auto
      source: built-in
      select_tool: 0.0
      sandbox: false
      description: "Complete automation"
    - name: local/careful-auto
      source: custom
      select_tool: 0.8
      sandbox: true
      description: "Custom careful profile"
  summary:
    built_in: 8
    custom: 1
    total: 9
timing:
  started: "2026-02-08T14:31:00Z"
  duration_ms: 50
messages:
  - "9 profiles listed"
```
agents automation-profile show
agents automation-profile show <NAME>

Purpose Show full details for an automation profile, including all flag values.

Arguments

  • <NAME>: Profile name.

Examples

=== "Rich"

<div class="highlight"><pre><code>
<span style="color: #66cc66; font-weight: 600;">$</span> <span style="color: cyan; font-weight: 600;">agents</span> automation-profile show trusted

╭─ Automation Profile ───────────────────────────────────────────╮
│ <span style="color: cyan; font-weight: 600;">Name:</span> trusted                                                  │
│ <span style="color: #5599ff; font-weight: 600;">Source:</span> built-in                                               │
│ <span style="color: #5599ff; font-weight: 600;">Description:</span> Auto-exec, manual apply. Day-to-day development   │
╰────────────────────────────────────────────────────────────────╯

╭─ Phase Transitions (thresholds) ─────╮
│ <span style="color: #66cc66; font-weight: 600;">decompose_task:</span> 0.0                 │
│ <span style="color: #66cc66; font-weight: 600;">create_tool:</span> 0.0                    │
│ <span style="color: #ff6666; font-weight: 600;">select_tool:</span> 1.0                      │
╰──────────────────────────────────────╯

╭─ Decision Automation (thresholds) ───╮
│ <span style="color: #66cc66; font-weight: 600;">edit_code:</span> 0.0       │
│ <span style="color: #66cc66; font-weight: 600;">execute_command:</span> 0.0          │
╰──────────────────────────────────────╯

╭─ Self-Repair (thresholds) ───────────╮
│ <span style="color: #66cc66; font-weight: 600;">create_file:</span> 0.0             │
│ <span style="color: #ff6666; font-weight: 600;">delete_content:</span> 1.0          │
│ <span style="color: #ff6666; font-weight: 600;">access_network:</span> 1.0         │
│ <span style="color: #66cc66; font-weight: 600;">modify_config:</span> 0.0            │
│ <span style="color: #ff6666; font-weight: 600;">approve_plan:</span> 1.0         │
╰──────────────────────────────────────╯

╭─ Execution Controls (thresholds) ────╮
│ <span style="color: #66cc66; font-weight: 600;">install_dependency:</span> 0.0                │
│ <span style="color: #66cc66; font-weight: 600;">require_sandbox:</span> true                │
│ <span style="color: #66cc66; font-weight: 600;">require_checkpoints:</span> true            │
│ <span style="color: #ff6666; font-weight: 600;">allow_unsafe_tools:</span> false            │
╰──────────────────────────────────────╯

<span style="color: #66cc66; font-weight: 600;">✓ OK</span> Profile loaded
</code></pre></div>

=== "Plain"

```
$ agents automation-profile show trusted

Automation Profile
  Name: trusted
  Source: built-in
  Description: Auto-exec, manual apply. Day-to-day development

Phase Transitions (thresholds)
  decompose_task: 0.0
  create_tool: 0.0
  select_tool: 1.0

Decision Automation (thresholds)
  edit_code: 0.0
  execute_command: 0.0

Self-Repair (thresholds)
  create_file: 0.0
  delete_content: 1.0
  access_network: 1.0
  modify_config: 0.0
  approve_plan: 1.0

Execution Controls (thresholds)
  install_dependency: 0.0
  require_sandbox: true
  require_checkpoints: true
  allow_unsafe_tools: false

[OK] Profile loaded
```

=== "JSON"

```json
{
  "command": "automation-profile show",
  "status": "ok",
  "exit_code": 0,
  "data": {
    "name": "trusted",
    "source": "built-in",
    "description": "Auto-exec, manual apply. Day-to-day development",
    "phase_transitions": {
      "decompose_task": 0.0,
      "create_tool": 0.0,
      "select_tool": 1.0
    },
    "decision_automation": {
      "edit_code": 0.0,
      "execute_command": 0.0
    },
    "self_repair": {
      "create_file": 0.0,
      "delete_content": 1.0,
      "access_network": 1.0,
      "modify_config": 0.0,
      "approve_plan": 1.0
    },
    "execution_controls": {
      "install_dependency": 0.0,
      "require_sandbox": true,
      "require_checkpoints": true,
      "allow_unsafe_tools": false
    }
  },
  "timing": { "started": "2026-02-08T14:31:00Z", "duration_ms": 55 },
  "messages": ["Profile loaded"]
}
```

=== "YAML"

```yaml
command: automation-profile show
status: ok
exit_code: 0
data:
  name: trusted
  source: built-in
  description: "Auto-exec, manual apply. Day-to-day development"
  phase_transitions:
    decompose_task: 0.0
    create_tool: 0.0
    select_tool: 1.0
  decision_automation:
    edit_code: 0.0
    execute_command: 0.0
  self_repair:
    create_file: 0.0
    delete_content: 1.0
    access_network: 1.0
    modify_config: 0.0
    approve_plan: 1.0
  execution_controls:
    install_dependency: 0.0
    require_sandbox: true
    require_checkpoints: true
    allow_unsafe_tools: false
timing:
  started: "2026-02-08T14:31:00Z"
  duration_ms: 55
messages:
  - "Profile loaded"
```

agents config

!!! info "Purpose" Manage ==global configuration values== that control system-wide defaults. Configuration keys use hierarchical dot-path notation (e.g., core.automation-profile, core.log.level).

??? tip "Key Configuration Categories" | Category | Example Keys | Description | | :------- | :----------- | :---------- | | Core | core.automation-profile, core.format, core.log.level | System-wide defaults | | Actor | actor.default.invariant | Default actor assignments | | Output | core.format | Default rendering format (rich, json, yaml, etc.) |

agents config set
agents config set <key> <value>

Purpose Set a configuration key.

Arguments

  • <key>: Hierarchical dot-path key name (e.g., core.automation-profile, core.log.level, actor.default.invariant, core.format). See Global Configuration Keys for the complete reference.
  • <value>: Value to set.

The actor.default.invariant key sets the default Invariant Reconciliation Actor used globally. This actor is used to reconcile invariant conflicts when neither the plan nor the project specifies one.

The core.format key sets the default output rendering format used by all commands. Accepted values are rich, color, table, plain, json, yaml. When set, this value is used unless overridden by the --format CLI flag. See Output Rendering Framework for details.

Examples

=== "Rich"

<div class="highlight"><pre><code>
<span style="color: #66cc66; font-weight: 600;">$</span> <span style="color: cyan; font-weight: 600;">agents</span> config set core.automation-profile trusted

╭─ Config Updated ──────────────────────╮
│ <span style="color: cyan; font-weight: 600;">Key:</span> core.automation-profile          │
│ <span style="color: #66cc66; font-weight: 600;">Value:</span> trusted                        │
│ <span style="color: yellow; font-weight: 600;">Previous:</span> manual                      │
│ <span style="color: #5599ff; font-weight: 600;">Source:</span> config                        │
│ <span style="color: yellow; font-weight: 600;">Scope:</span> global                         │
╰───────────────────────────────────────╯

╭─ Effective ────────────────────────╮
│ <span style="color: #5599ff; font-weight: 600;">Sessions:</span> new sessions             │
│ <span style="color: yellow; font-weight: 600;">Plans:</span> future plans (unless set)   │
│ <span style="color: #5599ff; font-weight: 600;">Existing:</span> unchanged                │
╰────────────────────────────────────╯

╭─ Saved To ──────────────────────────╮
│ <span style="color: #5599ff; font-weight: 600;">File:</span> ~/.cleveragents/config.toml   │
│ <span style="color: #5599ff; font-weight: 600;">Line:</span> 8                             │
╰─────────────────────────────────────╯

<span style="color: #66cc66; font-weight: 600;">✓ OK</span> Config updated
</code></pre></div>

=== "Plain"

```
$ agents config set core.automation-profile trusted

Config Updated
  Key: core.automation-profile
  Value: trusted
  Previous: manual
  Source: config
  Scope: global

Effective
  Sessions: new sessions
  Plans: future plans (unless set)
  Existing: unchanged

Saved To
  File: ~/.cleveragents/config.toml
  Line: 8

[OK] Config updated
```

=== "JSON"

```json
{
  "command": "config set",
  "status": "ok",
  "exit_code": 0,
  "data": {
    "key": "core.automation-profile",
    "value": "trusted",
    "previous": "manual",
    "source": "config",
    "scope": "global",
    "effective": {
      "sessions": "new sessions",
      "plans": "future plans (unless set)",
      "existing": "unchanged"
    },
    "saved_to": { "file": "~/.cleveragents/config.toml", "line": 8 }
  },
  "timing": { "started": "2026-02-08T14:32:00Z", "duration_ms": 30 },
  "messages": ["Config updated"]
}
```

=== "YAML"

```yaml
command: config set
status: ok
exit_code: 0
data:
  key: core.automation-profile
  value: trusted
  previous: manual
  source: config
  scope: global
  effective:
    sessions: new sessions
    plans: "future plans (unless set)"
    existing: unchanged
  saved_to:
    file: "~/.cleveragents/config.toml"
    line: 8
timing:
  started: "2026-02-08T14:32:00Z"
  duration_ms: 30
messages:
  - "Config updated"
```

Setting the default output format:

=== "Rich"

<div class="highlight"><pre><code>
<span style="color: #66cc66; font-weight: 600;">$</span> <span style="color: cyan; font-weight: 600;">agents</span> config set core.format table

╭─ Config Updated ─────────────────╮
│ <span style="color: cyan; font-weight: 600;">Key:</span> core.format                 │
│ <span style="color: #5599ff; font-weight: 600;">Previous:</span> rich                   │
│ <span style="color: #66cc66; font-weight: 600;">New Value:</span> table                 │
│ <span style="color: #5599ff; font-weight: 600;">Scope:</span> user (~/.cleveragents)    │
╰──────────────────────────────────╯

<span style="color: #66cc66; font-weight: 600;">✓ OK</span> Set core.format = table
</code></pre></div>

=== "Plain"

```
$ agents config set core.format table

Config Updated
  Key: core.format
  Previous: rich
  New Value: table
  Scope: user (~/.cleveragents)

[OK] Set core.format = table
```

=== "JSON"

```json
{
  "command": "config set",
  "status": "ok",
  "exit_code": 0,
  "data": {
    "key": "core.format",
    "previous": "rich",
    "value": "table",
    "scope": "user (~/.cleveragents)"
  },
  "timing": { "started": "2026-02-08T14:32:00Z", "duration_ms": 25 },
  "messages": ["Set core.format = table"]
}
```

=== "YAML"

```yaml
command: config set
status: ok
exit_code: 0
data:
  key: core.format
  previous: rich
  value: table
  scope: "user (~/.cleveragents)"
timing:
  started: "2026-02-08T14:32:00Z"
  duration_ms: 25
messages:
  - "Set core.format = table"
```

Setting a global invariant actor:

=== "Rich"

<div class="highlight"><pre><code>
<span style="color: #66cc66; font-weight: 600;">$</span> <span style="color: cyan; font-weight: 600;">agents</span> config set actor.default.invariant local/invariant-resolver

╭─ Config Updated ──────────────────────────────────╮
│ <span style="color: cyan; font-weight: 600;">Key:</span> actor.default.invariant                      │
│ <span style="color: #5599ff; font-weight: 600;">Previous:</span> (not set)                               │
│ <span style="color: #66cc66; font-weight: 600;">New Value:</span> local/invariant-resolver               │
│ <span style="color: #5599ff; font-weight: 600;">Scope:</span> user (~/.cleveragents)                     │
╰───────────────────────────────────────────────────╯

<span style="color: #66cc66; font-weight: 600;">✓ OK</span> Set actor.default.invariant = local/invariant-resolver
</code></pre></div>

=== "Plain"

```
$ agents config set actor.default.invariant local/invariant-resolver

Config Updated
  Key: actor.default.invariant
  Previous: (not set)
  New Value: local/invariant-resolver
  Scope: user (~/.cleveragents)

[OK] Set actor.default.invariant = local/invariant-resolver
```

=== "JSON"

```json
{
  "command": "config set",
  "status": "ok",
  "exit_code": 0,
  "data": {
    "key": "actor.default.invariant",
    "previous": null,
    "value": "local/invariant-resolver",
    "scope": "user (~/.cleveragents)"
  },
  "timing": { "started": "2026-02-08T14:32:00Z", "duration_ms": 28 },
  "messages": ["Set actor.default.invariant = local/invariant-resolver"]
}
```

=== "YAML"

```yaml
command: config set
status: ok
exit_code: 0
data:
  key: actor.default.invariant
  previous: null
  value: local/invariant-resolver
  scope: "user (~/.cleveragents)"
timing:
  started: "2026-02-08T14:32:00Z"
  duration_ms: 28
messages:
  - "Set actor.default.invariant = local/invariant-resolver"
```
agents config get
agents config get <key>

Purpose Get a configuration value.

Arguments

  • <key>: Key to read.

Examples

=== "Rich"

<div class="highlight"><pre><code>
<span style="color: #66cc66; font-weight: 600;">$</span> <span style="color: cyan; font-weight: 600;">agents</span> config get core.automation-profile

╭─ Config ──────────────────────────╮
│ <span style="color: cyan; font-weight: 600;">Key:</span> core.automation-profile      │
│ <span style="color: #66cc66; font-weight: 600;">Value:</span> trusted                    │
│ <span style="color: #5599ff; font-weight: 600;">Source:</span> config                    │
│ <span style="color: yellow; font-weight: 600;">Overridden:</span> no                    │
│ <span style="color: #5599ff; font-weight: 600;">Type:</span> string                      │
╰───────────────────────────────────╯

╭─ Origin ──────────────────────────╮
│ <span style="color: #5599ff; font-weight: 600;">File:</span> ~/.cleveragents/config.toml │
│ <span style="color: #66cc66; font-weight: 600;">Line:</span> 8                           │
│ <span style="color: #5599ff; font-weight: 600;">Default:</span> supervised               │
╰───────────────────────────────────╯

╭─ Resolution Chain ──────────────╮
│ 1. CLI flag: (not set)          │
│ 2. Env var: (not set)           │
│ 3. Config file: trusted         │
│ 4. Default: supervised          │
│ <span style="color: #66cc66; font-weight: 600;">Winner:</span> config file (level 3)   │
╰─────────────────────────────────╯

<span style="color: #66cc66; font-weight: 600;">✓ OK</span> Config read
</code></pre></div>

=== "Plain"

```
$ agents config get core.automation-profile

Config
  Key: core.automation-profile
  Value: trusted
  Source: config
  Overridden: no
  Type: string

Origin
  File: ~/.cleveragents/config.toml
  Line: 8
  Default: supervised

Resolution Chain
  1. CLI flag: (not set)
  2. Env var: (not set)
  3. Config file: trusted
  4. Default: supervised
  Winner: config file (level 3)

[OK] Config read
```

=== "JSON"

```json
{
  "command": "config get",
  "status": "ok",
  "exit_code": 0,
  "data": {
    "key": "core.automation-profile",
    "value": "trusted",
    "source": "config",
    "overridden": false,
    "type": "string",
    "origin": {
      "file": "~/.cleveragents/config.toml",
      "line": 8,
      "default": "supervised"
    },
    "resolution_chain": [
      { "level": 1, "source": "CLI flag", "value": null },
      { "level": 2, "source": "Env var", "value": null },
      { "level": 3, "source": "Config file", "value": "trusted" },
      { "level": 4, "source": "Default", "value": "supervised" }
    ],
    "winner": { "source": "config file", "level": 3 }
  },
  "timing": { "started": "2026-02-08T14:33:00Z", "duration_ms": 20 },
  "messages": ["Config read"]
}
```

=== "YAML"

```yaml
command: config get
status: ok
exit_code: 0
data:
  key: core.automation-profile
  value: trusted
  source: config
  overridden: false
  type: string
  origin:
    file: "~/.cleveragents/config.toml"
    line: 8
    default: supervised
  resolution_chain:
    - level: 1
      source: "CLI flag"
      value: null
    - level: 2
      source: "Env var"
      value: null
    - level: 3
      source: "Config file"
      value: trusted
    - level: 4
      source: Default
      value: supervised
  winner:
    source: "config file"
    level: 3
timing:
  started: "2026-02-08T14:33:00Z"
  duration_ms: 20
messages:
  - "Config read"
```
agents config list
agents config list [--filter-values <REGEX>] [<REGEX>]

Purpose List all configuration values.

Arguments

None.

Examples

=== "Rich"

<div class="highlight"><pre><code>
<span style="color: #66cc66; font-weight: 600;">$</span> <span style="color: cyan; font-weight: 600;">agents</span> config list

╭─ Config ───────────────────────────────────────────────────╮
│ <span style="color: cyan; font-weight: 600;">Key</span>               <span style="color: cyan; font-weight: 600;">Value</span>               <span style="color: cyan; font-weight: 600;">Source</span>   <span style="color: cyan; font-weight: 600;">Modified</span>    │
│ <span style="opacity: 0.7;">────────────────  ──────────────────  ───────  ────────</span>    │
│ core.automation-profile  trusted             config   yes  │
│ actor.default.invariant local/reconciler    config   yes   │
│ core.log.level          FATAL               default  no    │
╰────────────────────────────────────────────────────────────╯

╭─ Overrides ─────╮
│ <span style="color: yellow; font-weight: 600;">Env:</span> none       │
│ <span style="color: #5599ff; font-weight: 600;">CLI Flags:</span> none │
╰─────────────────╯

╭─ Config File ───────────────────────╮
│ <span style="color: #5599ff; font-weight: 600;">Path:</span> ~/.cleveragents/config.toml   │
│ <span style="color: #5599ff; font-weight: 600;">Size:</span> 284 bytes                     │
│ <span style="color: #66cc66; font-weight: 600;">Valid:</span> yes                          │
╰─────────────────────────────────────╯

<span style="color: #66cc66; font-weight: 600;">✓ OK</span> 6 settings listed
</code></pre></div>

=== "Plain"

```
$ agents config list

Config
  Key                      Value               Source   Modified
  -----------------------  ------------------  -------  --------
  core.automation-profile  trusted             config   yes
  actor.default.invariant  local/reconciler    config   yes
  core.log.level           FATAL               default  no

Overrides
  Env: none
  CLI Flags: none

Config File
  Path: ~/.cleveragents/config.toml
  Size: 284 bytes
  Valid: yes

[OK] 6 settings listed
```

=== "JSON"

```json
{
  "command": "config list",
  "status": "ok",
  "exit_code": 0,
  "data": {
    "settings": [
      { "key": "core.automation-profile", "value": "trusted", "source": "config", "modified": true },
      { "key": "actor.default.invariant", "value": "local/reconciler", "source": "config", "modified": true },
      { "key": "core.log.level", "value": "FATAL", "source": "default", "modified": false }
    ],
    "overrides": { "env": null, "cli_flags": null },
    "config_file": {
      "path": "~/.cleveragents/config.toml",
      "size_bytes": 284,
      "valid": true
    }
  },
  "timing": { "started": "2026-02-08T14:33:00Z", "duration_ms": 25 },
  "messages": ["6 settings listed"]
}
```

=== "YAML"

```yaml
command: config list
status: ok
exit_code: 0
data:
  settings:
    - key: core.automation-profile
      value: trusted
      source: config
      modified: true
    - key: actor.default.invariant
      value: local/reconciler
      source: config
      modified: true
    - key: core.log.level
      value: FATAL
      source: default
      modified: false
  overrides:
    env: null
    cli_flags: null
  config_file:
    path: "~/.cleveragents/config.toml"
    size_bytes: 284
    valid: true
timing:
  started: "2026-02-08T14:33:00Z"
  duration_ms: 25
messages:
  - "6 settings listed"
```

agents invariant

!!! info "Purpose" Manage ==invariants== — named constraints that guide and constrain plan execution. Invariants can be attached to any scope: global (all plans), project (all plans targeting that project), plan (a specific plan and its child plans), or action (carried forward when the action is used).

!!! warning "Scope Requirements" Exactly ==one scope flag== is required for add and list. The available scopes are:

| Scope | Flag | Applies To |
| :---- | :--- | :--------- |
| **Global** | `--global` | All plans across all projects |
| **Project** | `--project <NAME>` | All plans targeting that project |
| **Plan** | `--plan <ID>` | A specific plan and its child plans |
| **Action** | `--action <NAME>` | Carried forward when the action is used |

When multiple scopes apply to a plan, the ==union== of all applicable invariants is enforced.
agents invariant add
agents invariant add [--global] [(--project|-p) PROJECT] [--plan PLAN_ID]...
                     [--action ACTION]... <INVARIANT_TEXT>

Purpose Add an invariant at the specified scope.

Arguments

  • <INVARIANT_TEXT>: The invariant text (positional argument at end of command).
  • --global: Attach as a global invariant (applies to all plans).
  • --project/-p PROJECT: Attach to a project (applies to all plans targeting this project).
  • --plan PLAN_ID: Attach to a plan (plan-level invariant). Repeatable.
  • --action ACTION: Attach to an action (action-level invariant). Repeatable.

At least one scope flag (--global, --project, --plan, or --action) must be provided. --plan and --action can be repeated to attach the same invariant to multiple plans or actions.

Examples

=== "Rich"

<div class="highlight"><pre><code>
<span style="color: #66cc66; font-weight: 600;">$</span> <span style="color: cyan; font-weight: 600;">agents</span> invariant add <span style="color: cyan;">--global</span> <span style="color: #66cc66;">&quot;All public APIs must maintain backward compatibility&quot;</span>

╭─ Invariant Added ──────────────────────────────────────────────────────╮
│ <span style="color: #66cc66; font-weight: 600;">Invariant:</span> All public APIs must maintain backward compatibility        │
│ <span style="color: #5599ff; font-weight: 600;">Scope:</span> global                                                          │
│ <span style="color: #5599ff; font-weight: 600;">ID:</span> inv_01HXM9A1B                                                      │
╰────────────────────────────────────────────────────────────────────────╯

<span style="color: #66cc66; font-weight: 600;">✓ OK</span> Invariant added

<span style="color: #66cc66; font-weight: 600;">$</span> <span style="color: cyan; font-weight: 600;">agents</span> invariant add <span style="color: cyan;">--project</span> local/api-service <span style="color: #66cc66;">&quot;All endpoints must validate auth tokens&quot;</span>

╭─ Invariant Added ──────────────────────────────────────────────╮
│ <span style="color: cyan; font-weight: 600;">Project:</span> local/api-service                                     │
│ <span style="color: #66cc66; font-weight: 600;">Invariant:</span> All endpoints must validate auth tokens             │
│ <span style="color: #5599ff; font-weight: 600;">Scope:</span> project                                                 │
│ <span style="color: #5599ff; font-weight: 600;">ID:</span> inv_01HXM9A2C                                              │
╰────────────────────────────────────────────────────────────────╯

<span style="color: #66cc66; font-weight: 600;">✓ OK</span> Invariant added

<span style="color: #66cc66; font-weight: 600;">$</span> <span style="color: cyan; font-weight: 600;">agents</span> invariant add <span style="color: cyan;">--plan</span> 01HXM8C2ZK4Q7C2B3F2R4VYV6J <span style="color: #66cc66;">&quot;All database queries must use parameterized statements&quot;</span>

╭─ Invariant Added ─────────────────────────────────────────────────────╮
│ <span style="color: cyan; font-weight: 600;">Plan:</span> 01HXM8C2ZK4Q7C2B3F2R4VYV6J                                      │
│ <span style="color: #66cc66; font-weight: 600;">Invariant:</span> All database queries must use parameterized statements     │
│ <span style="color: #5599ff; font-weight: 600;">Scope:</span> plan                                                           │
│ <span style="color: #5599ff; font-weight: 600;">ID:</span> inv_01HXM9G3A                                                     │
╰───────────────────────────────────────────────────────────────────────╯

<span style="color: #66cc66; font-weight: 600;">✓ OK</span> Invariant added

<span style="color: #66cc66; font-weight: 600;">$</span> <span style="color: cyan; font-weight: 600;">agents</span> invariant add <span style="color: cyan;">--action</span> local/code-coverage <span style="color: #66cc66;">&quot;Test files must not import production secrets&quot;</span>

╭─ Invariant Added ──────────────────────────────────────────────────────╮
│ <span style="color: cyan; font-weight: 600;">Action:</span> local/code-coverage                                            │
│ <span style="color: #66cc66; font-weight: 600;">Invariant:</span> Test files must not import production secrets               │
│ <span style="color: #5599ff; font-weight: 600;">Scope:</span> action                                                          │
│ <span style="color: #5599ff; font-weight: 600;">ID:</span> inv_01HXM9H4B                                                      │
╰────────────────────────────────────────────────────────────────────────╯

<span style="color: #66cc66; font-weight: 600;">✓ OK</span> Invariant added
</code></pre></div>

=== "Plain"

```
$ agents invariant add --global "All public APIs must maintain backward compatibility"

Invariant Added
  Invariant: All public APIs must maintain backward compatibility
  Scope: global
  ID: inv_01HXM9A1B

[OK] Invariant added

$ agents invariant add --project local/api-service "All endpoints must validate auth tokens"

Invariant Added
  Project: local/api-service
  Invariant: All endpoints must validate auth tokens
  Scope: project
  ID: inv_01HXM9A2C

[OK] Invariant added

$ agents invariant add --plan 01HXM8C2ZK4Q7C2B3F2R4VYV6J "All database queries must use parameterized statements"

Invariant Added
  Plan: 01HXM8C2ZK4Q7C2B3F2R4VYV6J
  Invariant: All database queries must use parameterized statements
  Scope: plan
  ID: inv_01HXM9G3A

[OK] Invariant added

$ agents invariant add --action local/code-coverage "Test files must not import production secrets"

Invariant Added
  Action: local/code-coverage
  Invariant: Test files must not import production secrets
  Scope: action
  ID: inv_01HXM9H4B

[OK] Invariant added
```

=== "JSON"

```json
{
  "command": "invariant add",
  "status": "ok",
  "exit_code": 0,
  "data": [
    {
      "invariant": "All public APIs must maintain backward compatibility",
      "scope": "global",
      "id": "inv_01HXM9A1B"
    },
    {
      "project": "local/api-service",
      "invariant": "All endpoints must validate auth tokens",
      "scope": "project",
      "id": "inv_01HXM9A2C"
    },
    {
      "plan": "01HXM8C2ZK4Q7C2B3F2R4VYV6J",
      "invariant": "All database queries must use parameterized statements",
      "scope": "plan",
      "id": "inv_01HXM9G3A"
    },
    {
      "action": "local/code-coverage",
      "invariant": "Test files must not import production secrets",
      "scope": "action",
      "id": "inv_01HXM9H4B"
    }
  ],
  "timing": { "started": "2026-02-08T14:35:00Z", "duration_ms": 45 },
  "messages": ["Invariant added"]
}
```

=== "YAML"

```yaml
command: invariant add
status: ok
exit_code: 0
data:
  - invariant: "All public APIs must maintain backward compatibility"
    scope: global
    id: inv_01HXM9A1B
  - project: local/api-service
    invariant: "All endpoints must validate auth tokens"
    scope: project
    id: inv_01HXM9A2C
  - plan: 01HXM8C2ZK4Q7C2B3F2R4VYV6J
    invariant: "All database queries must use parameterized statements"
    scope: plan
    id: inv_01HXM9G3A
  - action: local/code-coverage
    invariant: "Test files must not import production secrets"
    scope: action
    id: inv_01HXM9H4B
timing:
  started: "2026-02-08T14:35:00Z"
  duration_ms: 45
messages:
  - "Invariant added"
```
agents invariant list
agents invariant list [--global] [(--project|-p) PROJECT] [--plan PLAN_ID] [--action ACTION]
                     [--effective] [<REGEX>]

Purpose List invariants at a given scope. Use --effective with --plan to show the final reconciled view of invariants (after precedence resolution) rather than just the invariants directly attached at that scope.

Arguments

  • --global: List global invariants.
  • --project/-p PROJECT: List invariants attached to a project.
  • --plan PLAN_ID: List invariants attached to a plan.
  • --action ACTION: List invariants attached to an action.
  • --effective: (Only with --plan) Show the reconciled invariant view after precedence resolution across all scopes.

Examples

=== "Rich"

<div class="highlight"><pre><code>
<span style="color: #66cc66; font-weight: 600;">$</span> <span style="color: cyan; font-weight: 600;">agents</span> invariant list <span style="color: cyan;">--global</span>

╭─ Global Invariants ──────────────────────────────────────────────────────╮
│ <span style="color: cyan; font-weight: 600;">ID</span>              <span style="color: cyan; font-weight: 600;">Text</span>                                                     │
│ <span style="opacity: 0.7;">──────────────  ────────────────────────────────────────────────────</span>     │
│ inv_01HXM9A1B  All public APIs must maintain backward compatibility      │
│ inv_01HXM9A1C  Payment processing must be idempotent                     │
╰──────────────────────────────────────────────────────────────────────────╯

<span style="color: #66cc66; font-weight: 600;">✓ OK</span> 2 invariants

<span style="color: #66cc66; font-weight: 600;">$</span> <span style="color: cyan; font-weight: 600;">agents</span> invariant list <span style="color: cyan;">--plan</span> 01HXM8C2ZK4Q7C2B3F2R4VYV6J <span style="color: cyan;">--effective</span>

╭─ Effective Invariants (Plan 01HXM8C2ZK4Q7C2B3F2R4VYV6J) ──────────────────────────────────╮
│ <span style="color: cyan; font-weight: 600;">ID</span>              <span style="color: cyan; font-weight: 600;">Source</span>   <span style="color: cyan; font-weight: 600;">Text</span>                                                             │
│ <span style="opacity: 0.7;">──────────────  ───────  ──────────────────────────────────────────────────────</span>           │
│ inv_01HXM9A1B  global   All public APIs must maintain backward compatibility              │
│ inv_01HXM9A2C  project  All endpoints must validate auth tokens                           │
│ inv_01HXM9G3A  plan     All database queries must use parameterized statements            │
╰───────────────────────────────────────────────────────────────────────────────────────────╯

│ <span style="color: yellow; font-weight: 600;">Conflicts Resolved:</span> 1                                                              │
│ <span style="opacity: 0.7;">  Global <span style="color: #66cc66;">"Use shared DB pool"</span> overridden by plan "All database queries must use    │
│   parameterized statements"</span>                                                        │

<span style="color: #66cc66; font-weight: 600;">✓ OK</span> 3 effective invariants (1 global, 1 project, 1 plan; 1 conflict resolved)
</code></pre></div>

=== "Plain"

```
$ agents invariant list --global

Global Invariants
  ID              Text
  --------------  --------------------------------------------------------
  inv_01HXM9A1B   All public APIs must maintain backward compatibility
  inv_01HXM9A1C   Payment processing must be idempotent

[OK] 2 invariants

$ agents invariant list --plan 01HXM8C2ZK4Q7C2B3F2R4VYV6J --effective

Effective Invariants (Plan 01HXM8C2ZK4Q7C2B3F2R4VYV6J)
  ID              Source   Text
  --------------  -------  --------------------------------------------------
  inv_01HXM9A1B   global   All public APIs must maintain backward compatibility
  inv_01HXM9A2C   project  All endpoints must validate auth tokens
  inv_01HXM9G3A   plan     All database queries must use parameterized statements

Conflicts Resolved: 1
  Global "Use shared DB pool" overridden by plan "All database queries must use
  parameterized statements"

[OK] 3 effective invariants (1 global, 1 project, 1 plan; 1 conflict resolved)
```

=== "JSON"

```json
{
  "command": "invariant list",
  "status": "ok",
  "exit_code": 0,
  "data": {
    "scope": "plan",
    "plan_id": "01HXM8C2ZK4Q7C2B3F2R4VYV6J",
    "effective": true,
    "invariants": [
      { "id": "inv_01HXM9A1B", "source": "global", "text": "All public APIs must maintain backward compatibility" },
      { "id": "inv_01HXM9A2C", "source": "project", "text": "All endpoints must validate auth tokens" },
      { "id": "inv_01HXM9G3A", "source": "plan", "text": "All database queries must use parameterized statements" }
    ],
    "conflicts_resolved": [
      {
        "overridden": { "source": "global", "text": "Use shared DB pool" },
        "by": { "source": "plan", "text": "All database queries must use parameterized statements" }
      }
    ]
  },
  "timing": { "started": "2026-02-08T14:35:00Z", "duration_ms": 35 },
  "messages": ["3 effective invariants (1 global, 1 project, 1 plan; 1 conflict resolved)"]
}
```

=== "YAML"

```yaml
command: invariant list
status: ok
exit_code: 0
data:
  scope: plan
  plan_id: 01HXM8C2ZK4Q7C2B3F2R4VYV6J
  effective: true
  invariants:
    - id: inv_01HXM9A1B
      source: global
      text: "All public APIs must maintain backward compatibility"
    - id: inv_01HXM9A2C
      source: project
      text: "All endpoints must validate auth tokens"
    - id: inv_01HXM9G3A
      source: plan
      text: "All database queries must use parameterized statements"
  conflicts_resolved:
    - overridden:
        source: global
        text: "Use shared DB pool"
      by:
        source: plan
        text: "All database queries must use parameterized statements"
timing:
  started: "2026-02-08T14:35:00Z"
  duration_ms: 35
messages:
  - "3 effective invariants (1 global, 1 project, 1 plan; 1 conflict resolved)"
```
agents invariant remove
agents invariant remove [--yes|-y] <INVARIANT_ID>

!!! warning "Constraint Removal" Remove an invariant by ID. The invariant is removed from whichever scope it was originally attached to. ==Active plans== that have already incorporated this invariant into their decision trees are not retroactively affected, but future plans will no longer enforce this constraint.

Arguments

  • <INVARIANT_ID>: Invariant ID to remove.
  • --yes: Skip confirmation.

Examples

=== "Rich"

<div class="highlight"><pre><code>
<span style="color: #66cc66; font-weight: 600;">$</span> <span style="color: cyan; font-weight: 600;">agents</span> invariant remove inv_01HXM9A1C

Remove invariant inv_01HXM9A1C (<span style="color: #66cc66;">"Payment processing must be idempotent"</span>, scope: global)? [y/N]: y

╭─ Invariant Removed ──────────────────────────────────────────────╮
│ <span style="color: yellow; font-weight: 600;">Removed:</span> Payment processing must be idempotent                   │
│ <span style="color: #5599ff; font-weight: 600;">Scope:</span> global                                                    │
│ <span style="color: #5599ff; font-weight: 600;">ID:</span> inv_01HXM9A1C                                                │
╰──────────────────────────────────────────────────────────────────╯

<span style="color: #66cc66; font-weight: 600;">✓ OK</span> Invariant removed
</code></pre></div>

=== "Plain"

```
$ agents invariant remove inv_01HXM9A1C

Remove invariant inv_01HXM9A1C ("Payment processing must be idempotent", scope: global)? [y/N]: y

Invariant Removed
  Removed: Payment processing must be idempotent
  Scope: global
  ID: inv_01HXM9A1C

[OK] Invariant removed
```

=== "JSON"

```json
{
  "command": "invariant remove",
  "status": "ok",
  "exit_code": 0,
  "data": {
    "removed": "Payment processing must be idempotent",
    "scope": "global",
    "id": "inv_01HXM9A1C"
  },
  "timing": { "started": "2026-02-08T14:36:00Z", "duration_ms": 30 },
  "messages": ["Invariant removed"]
}
```

=== "YAML"

```yaml
command: invariant remove
status: ok
exit_code: 0
data:
  removed: "Payment processing must be idempotent"
  scope: global
  id: inv_01HXM9A1C
timing:
  started: "2026-02-08T14:36:00Z"
  duration_ms: 30
messages:
  - "Invariant removed"
```

Core Concepts

Plan

!!! adr "Architecture Decision" The plan lifecycle, phase transitions, and plan hierarchy are defined in ADR-006: Plan Lifecycle.

A plan is the fundamental unit of orchestration and traceability.

Plan Lifecycle Phases

A plan progresses through four phases:

Action → Strategize → Execute → Apply

In this spec:

  • Action is the first phase. An action serves as a reusable plan template — it defines the work to be done (description, definition of done, actors, arguments, invariants) without being bound to any project. No significant processing occurs during the Action phase. When an action is used (via agents plan use), it spawns a new plan entity that enters the Strategize phase. By default, the original action remains available for reuse; single-use actions (created with reusable: false) are deleted after first use. Although Action is formally a phase in the lifecycle, it is unique in that it functions primarily as a template from which plans are instantiated.
  • Strategize is the second phase and the first where active processing occurs (the output is a strategy).
  • Execute is the third phase (the output is a changeset).
  • Apply is the fourth and final phase. Apply merges the sandbox changeset into real project resources. Upon completion, the plan reaches one of several terminal states: applied (success — changes committed), constrained (cannot complete within the current strategy's constraints — may trigger reversion to Strategize), errored (failed), or cancelled.

The normal phase progression is forward, but both Execute and Apply may revert to Strategize when the current strategy's constraints are too restrictive (see Phase Reversion below).

Phase Transition Verbs (CLI / UX Contract)

Verbs that trigger phase transitions. CleverAgents should standardize these verbs as the public API (CLI, TUI, web):

Current Phase Command Verb Next Phase
(none) create Action
Action use Strategize
Strategize execute Execute
Execute apply Apply

!!! note "Important behavioral rules" * CleverAgents uses automation profiles to control which of these phase transitions happen automatically. The profile determines whether each transition requires explicit user action or proceeds autonomously, but the verbs remain the conceptual contract. * There is no strategize command. The phase transition verbs are used in CLI commands — not the phase names. The use verb (as in agents plan use) is the command that transitions a plan from the Action phase into Strategize. This is by design: ==use describes the user's intent== (using an action template on a project), and the system responds by entering the Strategize phase.

Phase Reversion (→ Strategize)

!!! adr "Architecture Decision" Phase reversion rules and the decision tree model are detailed in ADR-006: Plan Lifecycle and ADR-007: Decision Tree and Correction.

While the normal phase progression is forward (Action → Strategize → Execute → Apply), both the Execute and Apply phases may revert to Strategize under specific conditions. In all cases, the reversion target is always Strategize — there is no reversion to Execute or any other intermediate phase.

Execute → Strategize
  • Trigger: During execution, the execution actor discovers that constraints established during Strategize are too restrictive to complete the work. For example, a strategy decision may have mandated an approach that proves infeasible once actual resources are examined, dependencies are resolved, or implementation details are encountered.
  • Behavior: Rather than overriding or silently violating Strategize-phase decisions, the plan reverts to the Strategize phase so the strategy actor can adjust the decision tree. Execute-phase decisions must always be constrained by Strategize-phase decisions — if those constraints cannot be honored, the correct response is reversion, not violation.
  • After reversion: The strategy actor receives the execution actor's findings (the reason for reversion, the specific constraints that were too restrictive) and produces an updated decision tree. The plan then re-enters the Execute phase with the revised strategy.
  • Automation: Controlled by the delete_content automation profile flag.
Apply → Strategize
  • Trigger: During the Apply phase, the system determines that the changeset cannot be successfully applied within the current strategy's constraints. Examples include: merge conflicts that violate invariants, validation failures at apply time that cannot be resolved within the strategy's scope, or resource state drift that contradicts strategy assumptions.
  • Behavior: The plan enters the constrained terminal state within the Apply phase. From this state, the plan may revert to Strategize either automatically (if the access_network automation profile flag allows it) or after manual user approval.
  • After reversion: The strategy actor receives the Apply phase's findings (the constraint violations, the specific issues encountered) and produces an updated decision tree. The plan then progresses forward again through Execute and Apply with the revised strategy.
  • Automation: Controlled by the access_network automation profile flag (separate from delete_content, which governs Execute → Strategize reversion).

Plan States (Per Phase)

A plan's ==phase== indicates "what step of the lifecycle it is in." Separately, the plan has a ==processing state== indicating "what is happening right now."

=== "Action Phase"

| State | Description |
| :---- | :---------- |
| `available` | Action exists and can be used |
| `archived` | Soft-deleted or hidden (optional) |

=== "Strategize / Execute Phases"

| State | Terminal? | Description |
| :---- | :-------: | :---------- |
| `queued` | No | Waiting for compute/worker |
| `processing` | No | Currently running |
| `errored` | Yes | Failed; includes error metadata |
| `complete` | Yes | Finished successfully |
| `cancelled` | Yes | User/system cancelled; safe terminal |

=== "Apply Phase"

| State | Terminal? | Description |
| :---- | :-------: | :---------- |
| `queued` | No | Waiting for compute/worker |
| `processing` | No | Currently running — diff review, conflict resolution, validation |
| `errored` | Yes | Failed; includes error metadata |
| `applied` | Yes | ==Changes successfully committed== to real resources |
| `constrained` | Yes | Cannot complete within current strategy's constraints; may trigger reversion to Strategize |
| `cancelled` | Yes | User/system cancelled; safe terminal |

Plan Identity and Traceability

Every plan should have:

Field Type Description
plan_id ULID ==Unique, immutable== identifier
parent_plan_id ULID? Nullable; present for child plans
root_plan_id ULID The top-most plan in the tree
attempt Integer Attempt counter (increments on phase re-run)
created_at Timestamp When the plan was created
updated_at Timestamp Last modification time
completed_at Timestamp? When the plan reached a terminal state
created_by Identity User or session identity

Plan Hierarchy and Parallelism

!!! adr "Architecture Decision" Plan hierarchy, child plan spawning, and parallel execution are covered in ADR-006: Plan Lifecycle.

A single plan should usually represent the smallest "complete" unit of work (similar to what would fit in one git commit). However:

  • Plans are hierarchical. A child plan is simply a Plan with a parent — it follows the same lifecycle, has the same data model, and is functionally identical to a root plan except that it has a parent_plan_id.
  • Decisions about child plans are made during Strategize (as subplan_spawn decision types). Multiple child plans that should execute concurrently are grouped under a subplan_parallel_spawn decision.
  • Child plans are actually spawned during Execute (based on those decisions).
  • Child plans can run in parallel (via subplan_parallel_spawn) or sequentially (via individual subplan_spawn decisions). Without a subplan_parallel_spawn wrapper, each subplan_spawn decision results in sequential execution.
  • Applicable invariants are enforced during Strategize by adding invariant_enforced decisions to the tree, which constrain downstream decisions and child plans.
  • The parent plan is responsible for merging results.

This is core to the long-term objective: tackling large tasks while only recomputing parts of the decision tree when corrected.

Hierarchical Decomposition for Scale

??? example "Example: Converting Firefox to Rust" When handling massive tasks, the system uses hierarchical decomposition. Each level spawns child plans (which are themselves full Plans with their own decision trees):

**1. Root Plan** — High-level architectural decisions and invariants

:   - "Convert Firefox Renderer to Rust"
    - Invariant: "Maintain API compatibility with existing C++ callers"
    - Decision: "Start with leaf modules, work inward"
    - Context: Module dependency graph (2,847 modules)

**2. Subsystem-Level Plans** — Major component decisions (`subplan_parallel_spawn`)

:   - "Phase 1: Convert utility libraries (no external deps)"
    - Each subsystem plan gets its own bounded context and inherits parent invariants

**3. Module-Level Plans** — Individual module conversions

:   - "Convert string_utils module"
    - Context: Only the 47 functions and 12 dependent files
    - Decision: "Use Rust's String type"

**4. File-Level Plans** — Specific file changes

:   - Actual code transformations
    - Minimal context needed

!!! tip
    At each level, only the ==relevant context== is loaded. The persistent decision graph means we can always reconstruct why we're converting a particular module and what constraints apply from higher-level decisions.
Child Plan Spawning Mechanism

In the actor definition for the execution actor, tool nodes can directly invoke registered tools to trigger child plans. The local/create-subplan tool is independently registered and referenced by name in the actor graph node. During execution, subplan_spawn decisions are realized as actual child plans, and subplan_parallel_spawn groups trigger concurrent spawning of all enclosed child plans.


# Example: Execution actor with subplan spawning capability.
# The graph uses a tool node referencing a named registered tool.
actors:
  code_executor:
    type: graph
    config:
      actor: anthropic/claude-3-opus
    skills:
      - local/plan-tools          # Skill containing create_subplan for LLM tool-calling
      - local/file-ops
    routes:
      execute_workflow:
        nodes:
          - name: spawn_test_subplan
            type: tool
            tool: local/create-subplan # Named tool from Tool Registry

The local/create-subplan tool is independently registered via its own YAML configuration file:


# File: tools/create-subplan.yaml
cleveragents:
  version: "3.0"

tool: name: local/create-subplan description: "Spawn a subplan for a given action" source: custom

input_schema: type: object properties: action: { type: string } target_files: { type: array, items: { type: string } } required: [action]

capability: writes: true checkpointable: false side_effects: [spawn_subplan]

code: | subplan = ctx.spawn_subplan( action=params["action"], target_files=params.get("target_files", []) ) return {"subplan_id": subplan.id}

The local/plan-tools skill references this tool (and others) by name:


# File: skills/plan-tools.yaml
skill:
  name: local/plan-tools
  description: "Tools for spawning and managing subplans"
  tools:
    - local/create-subplan
Child Plan Execution Modes

=== "Sequential"

Individual `subplan_spawn` decisions without a `subplan_parallel_spawn` wrapper execute one after another. If one fails, subsequent child plans are ==not started==.

=== "Parallel"

Multiple `subplan_spawn` decisions grouped under a `subplan_parallel_spawn` decision execute ==concurrently==. If one fails, others can continue. The `subplan_parallel_spawn` decision acts as a container that signals the system to spawn all enclosed child plans simultaneously.

Parallel execution is bounded by `SubplanConfig.max_parallel` (default: `5`, range: 150). This cap prevents runaway resource consumption when a large number of child plans are spawned simultaneously. The runtime uses a `ThreadPoolExecutor` with `min(max_parallel, len(subplans))` workers. The `SubplanConfig` model also controls `merge_strategy` (default: `git_three_way`), `fail_fast` (default: `false`), `timeout_per_subplan_seconds` (default: `null`), `retry_failed` (default: `true`), and `max_retries` (default: `2`).

=== "Dependency-Ordered"

When child plans have explicit dependencies on each other, the `DEPENDENCY_ORDERED` execution mode respects those dependencies while maximizing concurrency. The dependency graph is provided as a `dependency_graph: dict[str, list[str]]` mapping each `subplan_id` to the list of `subplan_ids` it depends on.

The runtime performs a topological sort to determine execution order, then executes independent subplans (those whose dependencies are all satisfied) concurrently in "waves". Subsequent waves wait for the previous wave to complete. Circular dependencies are detected and raise a `ValueError`.

This mode is useful when child plans produce artifacts that other child plans consume — for example, a code generation plan that must complete before a test plan can run.
Child Plan Failure Handling

!!! note "Failure Semantics" | Execution Mode | On Failure | Behavior | | :------------- | :--------- | :------- | | Parallel | One child fails | Other child plans ==continue== | | Sequential | One child fails | Subsequent child plans ==not started== | | Dependency-Ordered | One child fails | Dependent child plans ==not started==; independent plans ==continue== |

!!! tip
    An "error" only occurs if an exception is thrown by the application (a bug). Plan failures (e.g., tests don't pass) are handled within the plan's logic, not as application errors.
Child Plan Result Merging

The way child plan results are merged depends on the resource type:

  • Git-compatible resources (source code, text files): Git-style merge
  • Databases: Transaction coordination or sequential application
  • Other resources: Pluggable merge strategies based on resource type
  • Non-mergeable resources: May require sequential execution only

The Plan "Decision Tree" and Visualization

!!! adr "Architecture Decision" The decision tree's dual structure (structural tree and influence DAG), versioning model, and historical reconstruction are defined in ADR-034: Decision Tree Versioning and History.

CleverAgents records enough information to render:

  • an ASCII tree in the TUI, and
  • optionally a GUI tree via visualization tools (D3/Cytoscape) once the data exists.

This implies each plan should persist:

  • decisions made,
  • the rationale (or at least the prompt/context snapshot that produced it),
  • dependencies ("this decision influenced these child plans").

This is required for "correcting plans" (see Behavior section).

Dual Structure: Structural Tree and Influence DAG

A plan's decisions form two overlapping structures that serve different purposes:

Structural Tree (parent_decision_id): Defines the hierarchical rendering order. Each decision has at most one parent. The prompt_definition decision is the root. This tree determines how agents plan tree renders the hierarchy.

Influence DAG (decision_dependencies table): Captures which decisions constrained or influenced which other decisions. A single decision may be influenced by multiple upstream decisions. For example, an implementation_choice might be influenced by both a strategy_choice (which set the approach) and a resource_selection (which determined the files to modify).

Structure Storage Purpose Used For
Structural tree parent_decision_id column Rendering, navigation, tree visualization agents plan tree, agents plan explain
Influence DAG decision_dependencies table Dependency tracking, impact analysis agents plan correct (affected subtree computation)

A decision's parent in the structural tree may differ from its dependencies in the influence DAG. The tree answers "what is this decision nested under?"; the DAG answers "what decisions caused this one to exist?"

Current Tree vs. Superseded Branches

The current tree for a plan is the set of active, non-superseded decisions:


SELECT * FROM decisions
WHERE plan_id = :plan_id
  AND superseded_by IS NULL
ORDER BY sequence_number;

When a decision is corrected, the original and all its descendants are marked superseded_by = <new_decision_id>. Superseded decisions are never deleted — they remain in the database as permanent records, forming an append-only history. agents plan tree --show-superseded renders both current and historical branches, with superseded decisions displayed dimmed and annotated.

Structural Invariants

The current tree always satisfies:

  1. Single root: Exactly one prompt_definition decision with parent_decision_id IS NULL.
  2. Reachability: Every non-root decision is reachable from the root via parent_decision_id links.
  3. Acyclicity: No circular references in parent_decision_id chains.
  4. Monotonic ordering: sequence_number is monotonically increasing per plan. New decisions (including corrections) always receive higher sequence numbers than existing ones. Gaps in the current tree's sequence numbers indicate where superseded decisions once existed.

Decision Data Model

!!! adr "Architecture Decision" The decision data model, decision types, and decision tree semantics are defined in ADR-007: Decision Tree and Correction.

Relationship Between Plan Description and Decisions

Each plan has a description field (inherited from the action's description, potentially with argument substitutions). This description acts as the primary component of the prompt fed to the strategy actor during the Strategize phase.

Decisions are choices that are NOT explicitly defined by the plan description. They represent the gaps, ambiguities, or implementation details that must be resolved to execute the plan.

For example:

  • Plan description: "Increase test coverage to 85%"
  • Decisions that emerge:
    • "Which modules should be prioritized?" (not specified in description)
    • "Should we use mocks or integration tests for the database layer?" (not specified)
    • "Should we refactor the auth module to make it more testable, or write tests around it as-is?" (not specified)
Decision Making Based on Autonomy Level

Who makes decisions depends on the plan's automation profile:

Profile Flag Who Makes Decisions
edit_code = 1.0 (always manual) User is prompted for each decision point during Strategize
edit_code = 0.0 (always automatic) Strategy actor makes decisions autonomously, records reasoning
execute_command = 1.0 (always manual) User is prompted for each decision point during Execute
execute_command = 0.0 (always automatic) Execution actor makes decisions autonomously

Intermediate thresholds (e.g., edit_code = 0.7) cause the system to auto-decide only when the Semantic Escalation confidence score meets or exceeds the threshold; otherwise the user is prompted.

When automation allows automatic decisions, the strategy actor uses its best judgment based on context, and records its reasoning in the decision's rationale field.

When user input is required, the system pauses and prompts the user:


Decision required: Which modules should be prioritized for test coverage?

Options identified by the strategy actor:

  1. auth module (currently 45% coverage, high risk)
  2. payment module (currently 52% coverage, high risk)
  3. user module (currently 71% coverage, medium risk)

Your choice (or provide custom guidance): _

The Prompt as the Root Decision

The prompt passed to the strategize actor is itself a decision node in the decision tree—specifically, it's the root decision of type prompt_definition.

This is important because:

  1. Every plan has its own prompt: The root plan's prompt comes from the action description + user arguments. Child plan prompts are created by parent plans during their execution.

  2. Parent plans create child plan prompts: When a parent plan spawns a child plan, it decides what prompt to give that child plan. This is recorded as a prompt_definition decision in the parent's tree, and becomes the root decision of the child plan's tree.

  3. Invariants flow into the decision tree: During Strategize, applicable invariants (from global, project, action, and plan scopes) are reconciled via the Invariant Reconciliation Actor and recorded as invariant_enforced decisions, making them explicit constraints that influence downstream decisions and child plans.

  4. Unified correction mechanism: Since the prompt, invariants, and all other decisions are part of the same tree, correcting any of them uses the same agents plan correct command.


Plan: 01KH29QDEE6DZTXKWNKCV8VP0F
├── [prompt_definition] "Increase test coverage to 85% for the whole project."
├── [invariant_enforced] "Prioritize all functionality related to financial transactions and user management"
├── [invariant_enforced] "All API calls over TCP must be mocked"
├── [strategy_choice] "Prioritize auth and payment modules, implement them in parallel before the rest"
├── [subplan_parallel_spawn] "Implement auth and payment modules, in parallel"
│   ├── [subplan_spawn] "Write tests for auth module"
│   │   └── Plan: 01KH29R8WPKPBHRY7Q0NA9XW86
│   │       ├── [prompt_definition] "Write unit tests for auth module using mocks for the remote API calls"
│   │       ├── [implementation_choice] "Test login flow first"
│   │       └── ...
│   └── [subplan_spawn] "Write tests for payment module"
│       └── Plan: 01KH29RN2YKSXMTBDG82AKRHRA
│           ├── [prompt_definition] "Write unit tests for payment module using"
│           └── ...
└── [subplan_parallel_spawn] "Write tests for all modules except the auth and payment modules"
    └──  ...
Correcting Decisions (Including Prompts)

All corrections use the same unified command:


agents plan correct <decision_id> --mode=<mode> --guidance "<corrected decision text>"

Parameters:

  • <decision_id>: The ULID of the decision to correct
  • --mode: Either revert (rollback and re-run) or append (add fix at end)
  • --guidance: Free-form text specifying what the correct decision should be

Examples:


# Correct a strategy choice
agents plan correct 01ARZ3NDEKTSV4RRFFQ69G5FAV --mode=revert \
  --guidance "Prioritize the payment module first, not auth, due to upcoming deadline"

# Correct the root prompt to be more specific agents plan tree <plan_id> # Shows: [prompt_definition] id=01ARZ3NDEKTSV4RRFFQ69G5FAV "Increase test coverage to 85%"

agents plan correct 01ARZ3NDEKTSV4RRFFQ69G5FAV --mode=revert </span> --guidance "Increase test coverage to 85%, prioritizing auth and payment modules. Use mocks for database tests, not integration tests."

# Correct a subplan's prompt (originally created by parent plan) agents plan correct 01BRZ4PDFLUTW5SSGR70H6GBW --mode=revert </span> --guidance "Write unit tests for auth module, focusing on edge cases for token expiration"

# Append a fix rather than rewriting history agents plan correct 01ARZ3NDEKTSV4RRFFQ69G5FAV --mode=append </span> --guidance "The previous approach missed error handling tests - add comprehensive error path coverage"

# Remove an invariant that shouldn't apply agents plan correct 01CRZ5QEHMVUX6TTHR81I7HCX --mode=revert </span> --guidance "Remove this invariant - TCP mocking is not needed for this module since it has no network calls"

# Add a missing invariant to the plan agents invariant add --plan 01HXM8C2ZK4Q7C2B3F2R4VYV6J "All database queries must use parameterized statements"

Note: CLI commands should not require interactive input. The --guidance parameter provides the correction inline.

When to correct the prompt vs. a specific decision:

Situation Correction Approach
Original request was too vague Correct the prompt_definition decision
Strategy actor made a bad choice on a specific question Correct that specific decision
Parent plan gave a child plan a bad prompt Correct the child plan's prompt_definition
An invariant should not apply to this plan Correct (remove) the invariant_enforced decision
A missing constraint should be added Add a new invariant_enforced decision via agents invariant add --plan or correct the plan's strategy
Sequential child plans should run in parallel Correct the relevant decisions to use a subplan_parallel_spawn grouping

Because the prompt is part of the decision tree, the system automatically knows that correcting it invalidates all downstream decisions in that plan (and its child plans).

Decisions are created during both the Strategize and Execute phases. The Strategize phase produces the initial decision tree — strategy choices, invariant enforcement, resource selections, and child plan blueprints. The Execute phase may create additional decisions constrained by those made during Strategize, reflecting discoveries and adjustments that arise during execution. If Execute finds that Strategize-phase constraints are too restrictive to proceed, the plan reverts to Strategize for adjustment (see Phase Reversion below). The decision tree captures what choices were made and why, enabling correction and replay.

Decision Recording Protocol

!!! adr "Architecture Decision" The decision recording protocol, tool-based recording mechanism, and context snapshot capture are defined in ADR-033: Decision Recording Protocol. Decision-aligned checkpointing during Execute is defined in ADR-035: Decision Tree Rollback and Replay.

Actors record decisions by calling a built-in record_decision tool. This tool is automatically included in every actor's skill set for plan-related invocations. Decision types are partitioned into three creation categories:

Category Decision Types Creator Mechanism
System-created prompt_definition, user_intervention Plan lifecycle engine Created automatically at plan instantiation (prompt) and when user provides guidance (intervention)
Reconciliation-actor-recorded invariant_enforced Invariant Reconciliation Actor The actor evaluates applicable invariants, resolves conflicts using precedence rules, and calls record_decision for each enforced invariant
Actor-recorded strategy_choice, resource_selection, subplan_spawn, subplan_parallel_spawn, implementation_choice, tool_invocation, error_recovery, validation_response Strategy or Execution actor Actor identifies a choice point and calls record_decision

The record_decision tool accepts the decision type, question, chosen option, alternatives considered, confidence score, and rationale. The system automatically captures the context snapshot at the moment of the call — the hot context hash, resource references, and a LangGraph checkpoint of the actor's complete state (actor_state_ref). This snapshot enables replay from the exact decision point during correction.

Strategize-phase recording loop: The strategy actor's system prompt instructs it to identify ambiguities and choice points in the plan description. For each choice point, the actor gathers context, evaluates options, and calls record_decision. Each recorded decision becomes part of the actor's context for subsequent reasoning.


# Pseudocode: Strategy actor decision recording loop
while unresolved_ambiguities_remain(plan, context):
    choice_point = analyze_context_for_ambiguity(context)
    options = generate_and_evaluate_options(choice_point, context, invariants)
<span style="opacity: 0.7;"># Record via tool call — system auto-captures context snapshot</span>
decision_id = record_decision(
    decision_type = choice_point.type,
    question = choice_point.question,
    chosen_option = best_option.description,
    alternatives_considered = [o.description <span style="color: magenta; font-weight: 600;">for</span> o <span style="color: magenta; font-weight: 600;">in</span> rejected_options],
    confidence_score = best_option.confidence,
    rationale = best_option.reasoning
)

context.add_decision(decision_id)  <span style="opacity: 0.7;"># Decision informs subsequent reasoning</span>

Execute-phase recording pattern: The execution actor sees Strategize-phase decisions as read-only constraints. When it encounters a choice point not already resolved by the strategy (implementation detail, error handling, tool selection), it calls record_decision. Execute-phase decisions must be consistent with Strategize-phase constraints — if they cannot be, the plan reverts to Strategize rather than recording a contradictory decision.

!!! warning "Execute-Phase Checkpoint Trigger" In the Execute phase, record_decision is treated as a write operation for checkpoint purposes. The system automatically creates a coordinated checkpoint group — one checkpoint per modified sandbox resource — at every Execute-phase decision point. This creates a 1:1 mapping between decisions and resource states, enabling fine-grained rollback to any Execute-phase decision (see Correcting Plans in the Behavior section).

Automation profile integration: When record_decision is called, the system checks the confidence score against the applicable threshold (edit_code or execute_command). If confidence is below the threshold, execution pauses and the decision is presented to the user for approval or override.

Decision Record Structure

Decision:
  # Identity
  decision_id: ULID # Unique identifier
  plan_id: ULID # Parent plan this decision belongs to
  parent_decision_id: ULID | null # Parent decision (for tree structure)
  sequence_number: int # Order within the plan's decisions

# Classification decision_type: enum - prompt_definition # The prompt/description for this plan (root decision) - invariant_enforced # An invariant (from global, project, action, or plan scope) applicable to this plan, added as a constraint - strategy_choice # High-level approach decision during Strategize - implementation_choice # How to implement a specific task - resource_selection # Which resources to read/modify - subplan_spawn # Decision to create a child plan (spawned later in Execute) - subplan_parallel_spawn # Decision to spawn a group of child plans in parallel (contains subplan_spawn children) - tool_invocation # Which skill/tool to use - error_recovery # How to handle a failure - validation_response # Response to validation failure - user_intervention # User provided guidance/correction

# The Decision Itself question: str # What question was being answered chosen_option: str # What was decided alternatives_considered: list[str] # Other options that were evaluated confidence_score: float | null # 0.0-1.0 if the actor provided confidence

# Context Snapshot (for replay) context_snapshot: hot_context_hash: str # Cryptographic hash of the exact context hot_context_ref: str # Pointer to the full stored snapshot relevant_resources: list[ResourceRef] # Every file/symbol that influenced this decision actor_state_ref: str # Complete LangGraph checkpoint

# When the system decides "refactor the authentication module to use async patterns," # it permanently records: # - Which files were examined to make that decision # - What symbols and dependencies were traced # - The exact code state that was analyzed # - The reasoning chain that led to this choice # - Alternative approaches that were considered but rejected

# Rationale rationale: str # Why this option was chosen actor_reasoning: str | null # Raw LLM reasoning if available

# Downstream Impact (populated during Execute phase) downstream_decision_ids: list[ULID] # Decisions that depend on this one downstream_plan_ids: list[ULID] # Child plans spawned because of this decision artifacts_produced: list[ArtifactRef] # Files/outputs created under this decision

# Timestamps created_at: datetime

# Correction Metadata is_correction: bool # Was this decision a correction of another? corrects_decision_id: ULID | null # If correction, which decision was replaced correction_reason: str | null # Why the correction was made superseded_by: ULID | null # If this decision was later corrected

Decision Timing
Phase Decision Activity
Strategize Initial decisions are created, including invariant_enforced decisions for applicable invariants, strategy_choice decisions, subplan_spawn decisions, and subplan_parallel_spawn decisions grouping parallel child plans. downstream_plan_ids is empty. The system aims to make reasonable, comprehensive decisions at this stage to guide execution.
Execute Child plans are spawned and downstream_plan_ids is populated based on subplan_spawn decisions. Additional decisions may be created during execution — these are constrained by Strategize-phase decisions and arise from discoveries made during execution (e.g., implementation_choice, resource_selection, tool_invocation, error_recovery). If Execute determines that Strategize-phase constraints are too restrictive, the plan reverts to Strategize for adjustment rather than overriding those constraints.
Apply No new decisions. History can be flagged for cleanup after successful apply.
Decision Tree Storage Schema

-- Core decision table
CREATE TABLE decisions (
    decision_id TEXT PRIMARY KEY,      -- ULID
    plan_id TEXT NOT NULL,
    parent_decision_id TEXT,
    sequence_number INTEGER NOT NULL,
    decision_type TEXT NOT NULL,        -- prompt_definition, invariant_enforced, strategy_choice,
                                       -- implementation_choice, resource_selection, subplan_spawn,
                                       -- subplan_parallel_spawn, tool_invocation, error_recovery,
                                       -- validation_response, user_intervention
    question TEXT,
    chosen_option TEXT NOT NULL,
    alternatives_considered TEXT,      -- JSON array
    confidence_score REAL,
    rationale TEXT,
    actor_reasoning TEXT,
    context_snapshot TEXT NOT NULL,    -- JSON blob
    is_correction BOOLEAN DEFAULT FALSE,
    corrects_decision_id TEXT,
    correction_reason TEXT,
    superseded_by TEXT,
    created_at TEXT NOT NULL,
<span style="color: #5599ff; font-weight: 600;">FOREIGN</span> <span style="color: #5599ff; font-weight: 600;">KEY</span> (plan_id) <span style="color: #5599ff; font-weight: 600;">REFERENCES</span> plans(plan_id),
<span style="color: #5599ff; font-weight: 600;">FOREIGN</span> <span style="color: #5599ff; font-weight: 600;">KEY</span> (parent_decision_id) <span style="color: #5599ff; font-weight: 600;">REFERENCES</span> decisions(decision_id),
<span style="color: #5599ff; font-weight: 600;">FOREIGN</span> <span style="color: #5599ff; font-weight: 600;">KEY</span> (corrects_decision_id) <span style="color: #5599ff; font-weight: 600;">REFERENCES</span> decisions(decision_id),
<span style="color: #5599ff; font-weight: 600;">FOREIGN</span> <span style="color: #5599ff; font-weight: 600;">KEY</span> (superseded_by) <span style="color: #5599ff; font-weight: 600;">REFERENCES</span> decisions(decision_id)

);

-- Downstream relationships (many-to-many for DAG) CREATE TABLE decision_dependencies ( upstream_decision_id TEXT NOT NULL, downstream_decision_id TEXT NOT NULL, dependency_type TEXT NOT NULL, -- 'decision', 'plan', 'artifact' downstream_ref TEXT NOT NULL, -- The actual ID of decision/plan/artifact

<span style="color: #5599ff; font-weight: 600;">PRIMARY</span> <span style="color: #5599ff; font-weight: 600;">KEY</span> (upstream_decision_id, downstream_decision_id, downstream_ref),
<span style="color: #5599ff; font-weight: 600;">FOREIGN</span> <span style="color: #5599ff; font-weight: 600;">KEY</span> (upstream_decision_id) <span style="color: #5599ff; font-weight: 600;">REFERENCES</span> decisions(decision_id)

);

-- Correction history CREATE TABLE correction_attempts ( attempt_id TEXT PRIMARY KEY, -- ULID plan_id TEXT NOT NULL, original_decision_id TEXT NOT NULL, new_decision_id TEXT, original_subtree_snapshot TEXT, -- Reference to archived state correction_reason TEXT, status TEXT NOT NULL, -- 'pending', 'executing', 'completed', 'failed' created_at TEXT NOT NULL, completed_at TEXT,

<span style="color: #5599ff; font-weight: 600;">FOREIGN</span> <span style="color: #5599ff; font-weight: 600;">KEY</span> (plan_id) <span style="color: #5599ff; font-weight: 600;">REFERENCES</span> plans(plan_id),
<span style="color: #5599ff; font-weight: 600;">FOREIGN</span> <span style="color: #5599ff; font-weight: 600;">KEY</span> (original_decision_id) <span style="color: #5599ff; font-weight: 600;">REFERENCES</span> decisions(decision_id),
<span style="color: #5599ff; font-weight: 600;">FOREIGN</span> <span style="color: #5599ff; font-weight: 600;">KEY</span> (new_decision_id) <span style="color: #5599ff; font-weight: 600;">REFERENCES</span> decisions(decision_id)

);

Action

!!! adr "Architecture Decision" Actions as reusable plan templates and the action-to-plan transition are defined in ADR-006: Plan Lifecycle.

What an Action Is

An action is a reusable plan template that is not associated with any projects yet.

Actions are created via CLI commands with a required --config YAML file that fully defines the action. Any CLI options supplied alongside the config file act as optional overrides for values in the YAML.

Examples:

  • "Increase test coverage to 80%"
  • "Refactor module X to be async-safe"
  • "Write an RFC for feature Y"
  • "Provision an infra cluster and validate access" (non-code)

Actions are intentionally project-agnostic so they can be reused across projects.

An action is the first stage of a plan — before it is used. Because of this, invariants can be attached to actions. When an action is used (via agents plan use), any invariants attached to the action are carried forward as plan-level invariants on the resulting plan. This allows teams to bake constraints directly into reusable templates. Invariants can also be added to a plan after the action is used, via agents invariant add --plan or --invariant flags on agents plan use.

Action Creation (CLI)

Actions are created using the CLI. The action is fully defined by the YAML configuration file:


agents action create --config ./actions/code-coverage.yaml

The YAML file provides the complete action definition, including the name, strategy-actor, execution-actor, definition-of-done, arguments, invariants, and all other properties.

Required parameters:

  • --config: YAML configuration file that fully defines the action (name, strategy-actor, execution-actor, definition-of-done, etc.)

Arguments defined in the YAML's args section are values that will be:

  • Injected into the description and/or definition of done (via templating)
  • Passed into the context of the actors
  • Required when using the action on projects

Action Data Model (Expanded)

A plan in the Action phase has:

1) name (namespaced)

Format:

  • [server:][namespace/]<name>

Rules:

  • If server is omitted, default server is assumed unless namespace is local.
  • If namespace is omitted, default is local.
  • Names should be stable identifiers (kebab-case recommended).

Examples:

  • local/code-coverage
  • myusername/code-coverage
  • myorgname/code-coverage
  • prod:myorgname/code-coverage (server-qualified)
2) short_description

Optional at creation; auto-filled if blank.

3) long_description

Optional but recommended for reusable actions.

4) definition_of_done (DoD)

Required. Must be explicit and testable.

5) actors

Two actors minimum, two optional:

  • strategy_actor (planner/architect) — required
  • execution_actor (builder/implementer) — required
  • estimation_actor (cost/risk estimator) — optional
  • invariant_actor (Invariant Reconciliation Actor) — optional; resolves invariant conflicts when the plan enters Strategize. Lookup falls back to project, then global config.

Actors can be:

  • an LLM agent (built-in),
  • a graph (custom yaml, or built-in),

Note: graphs are hierarchical allowing them to reference other actors as nodes.

Actor abstraction is central: an actor may be a single agent or an entire graph.

6) reusable (boolean)
  • Default: true.
  • If true: using the action creates a new plan in Strategize while leaving the action available.
  • If false: action self-deletes (or auto-archives) after first use.
7) read_only (boolean)
  • Default: false.
  • If true: the plan must only use read-only tools (tools with read_only: true in their capability metadata) and must never modify resources (even in sandbox).
  • Read-only actions are still useful for "investigation reports," architecture reviews, or dry-run planning.

To make actions genuinely reusable, actions should declare their inputs:

  • required args (e.g., target coverage percent),
  • optional args (e.g., test framework),
  • validation rules (types, bounds).

Example:

  • target_coverage_percent: integer 0100
9) automation_profile

The resolved automation profile name for this plan (e.g., trusted, auto, local/careful-auto). Determined at plan use time using the profile precedence rules (plan > action > project > global). Once set, it is locked to the plan.

Strategy (Strategize Phase)

!!! adr "Architecture Decision" The Strategize phase, strategy actors, and decision generation are defined in ADR-006: Plan Lifecycle.

Using an Action (Transition to Strategize)

The use command transitions an Action into the Strategize phase by applying it to one or more projects:


# Basic usage
agents plan use local/code-coverage my-api-service

# Multiple projects agents plan use local/schema-update </span> api-service </span> web-frontend </span> mobile-app

# With action arguments agents plan use local/code-coverage </span> my-api-service </span> --arg target_coverage_percent=85 </span> --arg test_framework=pytest

# With explicit automation profile agents plan use local/deploy-action </span> staging-env </span> --automation-profile manual

# With invariants attached at use time agents plan use local/code-coverage </span> my-api-service </span> --arg target_coverage_percent=85 </span> --invariant "All API calls over TCP must be mocked" </span> --invariant "Do not modify the payments module"

Parameters:

  • <PROJECT>: Project to apply the action to (positional, can be repeated for multi-project plans)
  • --arg: Action argument values (format: name=value)
  • --automation-profile: Override automation profile for this plan
  • --invariant: Invariant to attach to the created plan (can be repeated). These are added as plan-level invariants in addition to any inherited from the action, project, or global scope.

When the action is used:

  1. A new plan is created with a unique ULID
  2. The plan's automation profile is resolved (plan > action > project > global precedence)
  3. Any invariants from the action are carried forward as plan-level invariants, combined with any --invariant flags provided
  4. The plan enters the Strategize phase
  5. The Invariant Reconciliation Actor computes the effective invariant view (resolving conflicts using plan > action > project > global precedence)
  6. The strategy_actor begins analyzing the project(s)

What Strategize Does

When an action is used on projects, it becomes a plan in Strategize.

Strategize is:

  • read-only, producing a plan of attack,
  • responsible for gathering context from project resources,
  • responsible for collecting applicable invariants (from global, project, action, and plan scopes), computing the effective invariant view via the Invariant Reconciliation Actor (applying plan > action > project > global precedence), and recording them as invariant_enforced decisions,
  • responsible for generating a strategy and child plan blueprint (using subplan_spawn and subplan_parallel_spawn decisions),
  • not allowed to execute child plans or modify resources.

This "architect vs coder" separation is explicitly described as a core motivation.

Resource-aware dependency analysis: During the Strategize phase, the strategy actor employs specialized mechanisms to compute precise dependency closures:


# Pseudocode of what happens inside a strategy actor
def compute_closure_for_refactoring(target_module):
    closure = ResourceClosure()
<span style="opacity: 0.7;"># Direct file dependencies</span>
closure.add_files(find_imports(target_module))
closure.add_files(find_includes(target_module))

<span style="opacity: 0.7;"># Symbol dependencies</span>
<span style="color: magenta; font-weight: 600;">for</span> symbol <span style="color: magenta; font-weight: 600;">in</span> extract_exported_symbols(target_module):
    closure.add_files(find_symbol_usage(symbol, scope=<span style="color: #66cc66;">&#x27;project&#x27;</span>))

<span style="opacity: 0.7;"># Test dependencies</span>
closure.add_files(find_tests_for_module(target_module))

<span style="opacity: 0.7;"># Build system dependencies</span>
closure.add_files(find_build_references(target_module))

<span style="color: magenta; font-weight: 600;">return</span> closure

The system leverages several key insights:

  • Modular boundaries exist: Even in legacy codebases, there are natural boundaries
  • Changes are incremental: We don't convert 50,000 files atomically
  • Dependencies are sparse: Most modules depend on a small fraction of the codebase
  • Interfaces are narrow: Public APIs are much smaller than implementations

Strategize Data Model

A plan in Strategize contains all Action fields plus:

1) projects

A list of projects the plan is used on.

Important: A strategy plan may target multiple projects. Multi-project work in one "window" is considered a major usability advantage over tools that require being run from a single directory.

2) strategy_context

A structured object describing:

  • what resources were considered,
  • how they were retrieved,
  • what filtering/limits were applied,
  • what the actor saw.

This matters because a plan must be debuggable and correctable later.

Recommended fields:

  • resource_refs: IDs of resources used
  • queries: search queries performed
  • selected_chunks: chunk IDs + sources + reasons
  • constraints: context window limits, file ignore patterns
  • generated_summaries: if summarization occurred
3) strategy

The output plan:

  • steps (ordered and/or DAG),
  • conditions/branches ("if tests fail, do X"),
  • child plans to spawn (including which action templates to use, and whether they should run in parallel via subplan_parallel_spawn),
  • evaluation criteria (how to know success),
  • risk assessment.

Strategize should output not only narrative text but also a machine-usable blueprint:

  • list of tasks,
  • required skills,
  • expected outputs,
  • dependencies between tasks.

This blueprint becomes the input to Execute.

5) cost_estimate and risk_estimate (optional)

Cost and risk estimation is optional but recommended for production use.

When enabled, a specialized estimation actor analyzes:

  • The initial prompt/request
  • The strategy produced by the Strategize phase
  • Historical data from similar plans (if available)

And produces estimates for:

  • LLM tokens/cost range
  • Number of steps/child plans expected
  • Expected risk of rollbacks
  • Estimated execution time

Implementation: Similar to how there's a strategy_actor and execution_actor for each action, there can be an optional estimation_actor whose entire job is cost/risk estimation. This actor runs after Strategize completes (before Execute) and its output is informational only. Estimation failures are logged but never block the Execute transition.

Estimation actor resolution follows a 4-level fallback chain (highest priority first):

  1. CLI --estimation-actor override (applied post-creation by the CLI layer)
  2. Action YAML estimation_actor field
  3. (project-scoped key — reserved for future use)
  4. actor.default.estimation global config key (CLEVERAGENTS_DEFAULT_ESTIMATION_ACTOR)

When the resolved actor is set, PlanLifecycleService.complete_strategize() invokes _run_estimation() and emits a PLAN_ESTIMATION_COMPLETE event on success. The result is stored on the plan and plan.cost_estimate_usd is populated from the midpoint of the estimated cost range.


# Example: Action with estimation actor (defined in the YAML config file)
agents action create --config ./actions/expensive-refactor.yaml

This becomes critical in server/multi-user usage and cost controls.

Execution (Execute Phase)

!!! adr "Architecture Decision" The Execute phase, sandbox model, and execution safety mechanisms are defined in ADR-006: Plan Lifecycle and ADR-015: Sandbox and Checkpoint.

What Execute Does

Execute is where the plan actually performs work, but in a sandboxed environment that can later be reviewed and applied.

Key properties:

  1. Work happens in a sandbox All file modifications, generated artifacts, and intermediate outputs live in an isolated "execution workspace" until Apply.

  2. Execute may spawn child plans Child plan spawning is a first-class behavior of Execute: a parent plan can distribute work to child plans (sequentially via subplan_spawn or concurrently via subplan_parallel_spawn) and merge results.

  3. Execute must support checkpointing / rollback (when enabled) Checkpointable tools allow rolling back to a checkpoint ID to recover from partial failure or wrong turns.

  4. Execute produces a "reviewable diff" Diff review sandbox is described as a differentiating feature: users can inspect changes before applying.

Execution Workspace / Sandbox Model (Detailed)

!!! adr "Architecture Decision" Sandbox isolation, lazy sandboxing, and resource-type-specific sandbox strategies are detailed in ADR-015: Sandbox and Checkpoint.

A sandbox isolates plan execution from the real project resources until Apply.

!!! tip "Key Sandbox Principles" - [x] Lazy Sandboxing — Resources are sandboxed ==only when accessed==, not upfront - [x] Per-Plan Sandboxes — Each plan and child plan has its own isolated sandbox - [x] Resource-Defined Strategy — The sandbox strategy is defined on each resource, not globally - [x] Cleanup Behavior — Automatic cleanup on exit, crash recovery on next run, retention-based archival

??? info "Why Lazy Sandboxing?" A project may have many resources (git repo + 10 databases + cloud accounts), but a plan may only modify one resource. Only accessed resources are sandboxed, making this efficient for large projects with many linked resources.

Sandbox Implementation Strategies

Different resource types require different sandbox strategies:

Resource Type Strategy Rollback Mechanism
git-checkout git_worktree Git reset/checkout
git none N/A (represents a repo instance — not directly sandboxable)
fs-mount copy_on_write or overlay Restore from snapshot
fs-directory copy_on_write Restore from snapshot
Custom database types transaction_rollback Transaction rollback
Custom API types none Often not sandboxable

1. Git worktree / branch sandbox (preferred for code)

  • Create a worktree or temporary branch
  • All modifications are commits or staged changes
  • Apply merges/cherry-picks

Pros: natural rollback, diff support, efficient Cons: requires git

2. Filesystem copy sandbox

  • Copy project directory to a sandbox directory
  • Execute modifies sandbox copy
  • Apply syncs diff back

Pros: simple Cons: expensive for huge repos

3. Overlay filesystem sandbox

  • Use overlayfs-style "copy-on-write" to avoid full copies

Pros: efficient Cons: more complex, OS-dependent

4. Transaction-based sandbox (for databases)

  • Begin transaction at sandbox creation
  • All operations within transaction
  • Rollback on failure, commit on apply

Pros: native to databases Cons: long-running transactions can cause issues

5. No sandbox (for non-sandboxable resources)

  • Some resources cannot be sandboxed (certain APIs, cloud services)
  • User proceeds at their own risk
  • Plan should warn about non-sandboxable resources
Multi-Resource Sandboxing

When a plan accesses multiple resources:

  • Each resource gets its own sandbox (based on its defined strategy)
  • Sandboxes are independent
  • Apply commits each sandbox separately
  • If any sandbox Apply fails, others may still succeed (partial apply)

Complete isolation during execution prevents compound errors: Each plan executes in its own sandbox, which means:


Plan A (refactoring auth module):
- Sandbox A1: Contains only auth/*.cpp, auth_tests/*.cpp
- Cannot see Plan B's intermediate states
- Cannot accidentally depend on Plan B's half-done work

Plan B (updating API endpoints):

  • Sandbox B1: Contains only api/.cpp, api_tests/.cpp
  • Makes changes assuming current auth interface
  • Protected from Plan A's intermediate refactoring

Hierarchical merge resolution: When child plans complete, the parent plan performs intelligent merging:


def merge_subplan_results(subplan_results):
    # Group by resource type
    by_resource = group_by_resource_type(subplan_results)
<span style="opacity: 0.7;"># Apply resource-specific merge strategies</span>
<span style="color: magenta; font-weight: 600;">for</span> resource_type, changes <span style="color: magenta; font-weight: 600;">in</span> by_resource:
    <span style="color: magenta; font-weight: 600;">if</span> resource_type == <span style="color: #66cc66;">&#x27;git-checkout&#x27;</span>:
        merge_git_changes(changes)  <span style="opacity: 0.7;"># Three-way merge</span>
    <span style="color: magenta; font-weight: 600;">elif</span> resource_type == <span style="color: #66cc66;">&#x27;fs-mount&#x27;</span>:
        merge_fs_changes(changes)   <span style="opacity: 0.7;"># Copy-on-write reconciliation</span>
    <span style="color: magenta; font-weight: 600;">elif</span> resource_type.startswith(<span style="color: #66cc66;">&#x27;database&#x27;</span>):
        merge_db_changes(changes)   <span style="opacity: 0.7;"># Sequential application</span>

<span style="opacity: 0.7;"># Validate merged state</span>
run_integration_tests()

Execution Data Model

A plan in Execute contains:

1) execution_context

The context used for execution (often smaller/more tactical than strategy context).

2) execution_log

Structured timeline of:

  • tool calls (with parent skill noted),
  • actor calls,
  • outputs,
  • errors and retries,
  • checkpoints created.

This log is essential for debugging.

3) artifacts

Outputs produced:

  • changed files,
  • generated files,
  • reports,
  • diagrams,
  • test outputs,
  • diffs.
4) sandbox_ref

Pointer to the sandbox location/state:

  • path, branch name, workspace ID, container ID, etc.
5) checkpoint_graph (if enabled)

A record of checkpoints:

  • checkpoint ID
  • timestamp
  • tool responsible (and its parent skill)
  • resources affected
  • rollback instructions / metadata

Checkpointing in Execute (Core Safety Mechanism)

!!! adr "Architecture Decision" Checkpointing, rollback, and transaction safety are defined in ADR-015: Sandbox and Checkpoint.

The intended user-level behavior is:

  • "Give me a checkpoint ID."
  • Perform additional operations.
  • "Roll back to checkpoint X."

Not all tools can support this; checkpointing must be declared per tool (in its capability metadata).

Tool-level checkpointability

Each tool declares (via its capability metadata):

  • checkpointable: true|false
  • checkpoint_scope: what granularity of rollback is supported (file, transaction, commit, snapshot)
  • rollback_mechanism: how rollback occurs

Examples:

  • File tools (from a file-ops skill): snapshot file states pre-modification
  • Git tools (from a git-ops skill): create commit or stash; rollback is reset/checkout
  • Shell/CLI tools (running inside a container): rollback by restoring filesystem snapshot or reloading base image state

It should be noted that checkpointing is easier when tool scope is constrained (e.g., "only files within a docker image + git").

Plan-level rollback policy

Plans should have an option:

  • rollback_enabled: true|false

If disabled, the plan may use more generic/unsafe tools with fewer restrictions (useful for low-stakes tasks).

CheckpointService Operations

The CheckpointService provides the following key operations for checkpoint lifecycle management:

  • create_checkpoint(plan_id, sandbox_ref, reason, source_tool, phase, decision_id, checkpoint_type, size_bytes) — Creates a standard checkpoint record. The sandbox_ref is a git commit hash of the current HEAD.

  • create_workspace_snapshot(plan_id, sandbox_ref, decision_id, *, reason, phase) — Creates a diff-based workspace snapshot before decision execution. Captures only files that differ from the previous checkpoint (or the initial sandbox state when no prior checkpoints exist). The diff manifest is stored in checkpoint metadata under "diff_paths", "diff_based", and "diff_hash" keys. Returns the new Checkpoint.

  • selective_rollback(plan_id, checkpoint_id) — Rolls back to a specific checkpoint with atomic (all-or-nothing) semantics. If the rollback fails partway through, a best-effort recovery attempt restores the sandbox to its pre-rollback HEAD. Raises BusinessRuleViolation if recovery also fails. Raises ResourceNotFoundError if the checkpoint does not exist.

  • archive_artifacts(sandbox_path, artifact_paths, archive_dir) — Physically moves artifact files to an archive directory, preserving them outside the sandbox for later retrieval.

The service supports both database-backed persistence (via CheckpointRepository) and in-memory fallback for tests. Guard checks (whether a plan has been applied, whether a sandbox exists) are resolved via PlanLifecycleService when wired, querying persistent plan state rather than relying on in-memory flags.

Automatic Checkpoint Triggers

The execution engine supports four automatic checkpoint triggers, configurable via core.checkpoints.auto_create_on (default: all four enabled):

Trigger When Component
on_tool_write Before each write-tool execution ToolRunner
on_tool_write_complete After each write-tool execution ToolRunner
on_subplan_spawn Before first subplan execution attempt SubplanExecutionService
on_error When the Execute phase fails PlanExecutor

Configuration:

[core.checkpoints]
auto_create_on = ["on_tool_write", "on_tool_write_complete", "on_subplan_spawn", "on_error"]

To disable a specific trigger, remove it from the list. To disable all automatic checkpoints, set the list to empty (auto_create_on = []).

The CheckpointService is injected as an optional parameter into ToolRunner, SubplanExecutionService, and PlanExecutor. When checkpoint_service is None, all automatic checkpointing is skipped (backward-compatible no-op).

Execution should be treated like a transactional pipeline:

  • Each step either:

    • commits a checkpoint on success, or
    • rolls back to the previous checkpoint on failure.

This is explicitly motivated by "partial failure leaves codebase inconsistent" and the need for transaction rollback.

Execution Environment Routing

!!! adr "Architecture Decision" Execution environment routing, devcontainer integration, and the 6-level precedence chain are defined in ADR-043: Devcontainer Integration. Container resource types are defined in ADR-039: Container Resource Types.

When a plan enters the Execute phase, the runtime must determine where each tool invocation runs: on the host, or inside a container. This decision is called execution environment routing. The router evaluates a 6-level precedence chain and selects the first matching environment.

Precedence Chain (highest → lowest):

Priority Source Condition
1 Plan-level execution_environment with priority: override Always wins when set. Configured via agents plan use --execution-environment … --execution-env-priority override.
2 Project-level execution_environment with priority: override Wins unless a plan-level override exists. Configured via agents project context set --execution-environment … --execution-env-priority override.
3 Nearest-ancestor devcontainer Auto-detected devcontainer-instance resource that is a child (or descendant) of a resource linked to the project. Lazy-built on first access.
4 Plan-level execution_environment with priority: fallback Used only when no devcontainer is detected and no override exists above.
5 Project-level execution_environment with priority: fallback Used only when no closer-scoped environment exists.
6 Host The local operating system. Default when no container environment is configured or detected.

Routing Algorithm:

function resolve_execution_environment(plan, project):
    # Level 1: plan override
    if plan.execution_environment AND plan.execution_env_priority == "override":
        return plan.execution_environment

    # Level 2: project override
    if project.execution_environment AND project.execution_env_priority == "override":
        return project.execution_environment

    # Level 3: nearest-ancestor devcontainer
    for resource in project.linked_resources (ordered by DAG depth, shallowest first):
        devcontainer = find_child_of_type(resource, "devcontainer-instance")
        if devcontainer:
            if devcontainer.state == "detected":
                build_and_start(devcontainer)   # lazy activation
            return devcontainer

    # Level 4: plan fallback
    if plan.execution_environment AND plan.execution_env_priority == "fallback":
        return plan.execution_environment

    # Level 5: project fallback
    if project.execution_environment AND project.execution_env_priority == "fallback":
        return project.execution_environment

    # Level 6: host
    return HOST_ENVIRONMENT

Tool-Level Environment Preferences:

Individual tools may declare environment preferences via the environment field in their capability metadata (see §Tool Capability Metadata):

  • environment.required: Tool must run in this environment type (container or host). If the resolved environment doesn't match, the tool invocation fails with an error rather than silently running in the wrong environment.
  • environment.preferred: Tool prefers this environment type but will run wherever the router places it.
  • environment.specific: Tool targets a specific named resource (e.g., local/api-dev). If the named resource is available and running, the tool is routed there regardless of the general precedence chain.

When a tool declares environment.required: container but the router resolves to host, the runtime MUST raise an error. The operator can then either: (a) add a container resource to the project, or (b) change the tool's environment requirement.

Lazy Activation:

Devcontainers detected during auto-discovery are created in detected (not built) state. They are built and started only when the execution environment router first selects them. This avoids unnecessary container builds when the user never executes a plan that needs container isolation. Once built, the container remains available for subsequent plan executions until explicitly stopped or the resource is removed.

Tool-Based Resource Modification (Modern Architecture)

!!! adr "Architecture Decision" The tool-based resource modification approach and tool execution model are defined in ADR-011: Tool System.

IMPORTANT: CleverAgents does NOT parse LLM output to extract code. Instead, it uses the modern tool-based approach pioneered by Claude Code, Cursor, and Aider where:

  1. LLMs call tools directly (edit_file(), write_file(), delete_file(), etc.) — tools provided by referenced skills
  2. Tools operate on the sandbox - each tool invocation modifies sandbox state directly
  3. ChangeSet is built from tool invocations - not by parsing LLM text output
  4. Validation runs on sandbox state - after tools execute, not on parsed output

This architecture provides:

  • Atomic operations: Each tool call is a discrete, trackable change
  • No parsing ambiguity: Tools have structured parameters (path, content, etc.)
  • Resource-agnostic: Same pattern works for files, databases, APIs, any resource type
  • Safety by design: Tools run in sandbox with defined capabilities and restrictions
  • MCP compatibility: Tools from MCP-based skills map directly to MCP tools for external integrations
How It Works
sequenceDiagram
    participant LLM as LLM Agent
    participant Router as Tool Router
    participant Sandbox as Sandbox
    participant CS as ChangeSet
    participant Val as Validator

    LLM->>Router: Tool call (with parameters)
    Router->>Router: Validate parameters
    Router->>Router: Enforce capability restrictions
    Router->>Sandbox: Execute tool in sandbox
    Sandbox->>Sandbox: Operate on sandboxed state
    Sandbox->>Sandbox: Record invocation
    Sandbox->>Sandbox: Create checkpoint (if needed)
    Sandbox->>CS: Emit Change record
    CS->>CS: Accumulate into ChangeSet
    CS->>Val: Submit for validation
    Val->>Val: Run validators on sandbox state
    Val->>Val: Generate diff from ChangeSet
    Val-->>LLM: Present for review before Apply
Built-in Resource Tools

CleverAgents provides these core tools (via built-in skills) for resource manipulation:

Tool Description Creates Change?
read_file(path) Read file contents No
write_file(path, content) Create/overwrite file Yes
edit_file(path, changes) Apply targeted edits Yes
delete_file(path) Remove file Yes
move_file(src, dst) Rename/move file Yes
create_directory(path) Create directory Yes
list_files(pattern) List files matching glob No
search_files(pattern, content) Search file contents No
get_file_info(path) Get file metadata No

Each built-in tool automatically:

  • Operates within sandbox boundaries
  • Records changes to the ChangeSet
  • Validates parameters against project configuration
  • Enforces deny-list patterns (.git/, node_modules/, etc.)
Why Not Parse LLM Output?

The obsolete approach of parsing markdown code fences has fundamental problems:

  1. Ambiguity: Is text explanation or code? Where does one file end and another begin?
  2. Fragility: Models output varying formats; regex parsing is brittle
  3. Loss of semantics: You lose the intent (create vs modify vs delete)
  4. No atomicity: Can't rollback individual operations
  5. Resource-limited: Only works for files, not databases or other resources

The tool-based approach solves all of these by making each operation explicit, typed, and trackable.

Semantic Error Prevention

!!! adr "Architecture Decision" The multi-layer error prevention strategy and guardrail architecture are defined in ADR-018: Semantic Error Prevention.

CleverAgents provides multiple layers of proactive error prevention that catch semantic errors before they can propagate through the system.

Layer 1: Decision-time Validation During Strategize

Every decision includes semantic validation:


Decision: Refactor payment module to async
alternatives_considered:
  - "Convert to async/await patterns" (chosen)
  - "Use thread pool with channels" (rejected: doesn't integrate with async ecosystem)
  - "Keep synchronous with timeout" (rejected: doesn't solve core latency issue)
confidence_score: 0.85
validation_performed:
  - Checked all payment API consumers can handle async
  - Verified database driver supports async operations
  - Confirmed no regulatory requirement for sync processing

Layer 2: Execution-time Semantic Guards

The execution actor uses a tool node that references the independently registered local/validate-api-compat tool:


# Actor graph uses a named tool node for semantic validation
actors:
  code_executor:
    type: graph
    skills:
      - local/semantic-validators    # Skill containing validation tools for LLM tool-calling
    nodes:
      - name: semantic_validator
        type: tool
        tool: local/validate-api-compat # Named tool from Tool Registry

The local/validate-api-compat tool is independently registered via its own YAML:


# File: tools/validate-api-compat.yaml
cleveragents:
  version: "3.0"

tool: name: local/validate-api-compat description: "Check for breaking API changes and attempt auto-migration" source: custom

capability: writes: true checkpointable: true

code: | # Not just syntax checking - semantic validation old_api = extract_api_signature(previous_version) new_api = extract_api_signature(current_version)

breaking_changes = find_breaking_changes(old_api, new_api)
if breaking_changes:
    affected_consumers = find_api_consumers(breaking_changes)
    migration_plan = generate_migration(breaking_changes)

    if can_auto_migrate(affected_consumers, migration_plan):
        apply_migration(migration_plan)
    <span style="color: cyan; font-weight: 600;">else</span>:
        raise SemanticError(
            &quot;Breaking API changes require manual review&quot;,
            changes=breaking_changes,
            affected=affected_consumers
        )

The local/semantic-validators skill references this tool by name:


# File: skills/semantic-validators.yaml
skill:
  name: local/semantic-validators
  description: "Semantic validation tools for API compatibility and code invariants"
  tools:
    - local/validate-api-compat

Layer 3: Invariant Enforcement

!!! adr "Architecture Decision" Invariant scoping, enforcement mechanisms, and inheritance rules are defined in ADR-016: Invariant System.

Invariants are named constraints that guide and constrain plan execution. They can be attached at four scopes, all managed through the unified agents invariant command:

  • Global invariants: Apply to all plans across all projects. Added via agents invariant add --global.
  • Project invariants: Apply to all plans targeting a specific project. Added via agents invariant add --project. Can also be attached at creation time via agents project create --invariant.
  • Action invariants: Attached to an action and carried forward as plan-level invariants when the action is used. Added via agents invariant add --action. Can also be attached at creation time via agents action create --invariant.
  • Plan invariants: Apply to a specific plan and its child plans. Added via agents invariant add --plan. Can also be attached at creation time via agents plan use --invariant.

Precedence and conflict resolution: When invariants from different scopes conflict, narrower scopes override broader scopes:

  • Plan-level invariants override action-level, project-level, and global-level invariants.
  • Action-level invariants override project-level and global-level invariants.
  • Project-level invariants override global-level invariants.

Exception: Non-overridable global invariants (marked non_overridable: true) always take precedence over all other scopes, including plan-level invariants. See below.

The full precedence chain (highest to lowest): plan > action > project > global.

Conflict resolution is performed by the Invariant Reconciliation Actor — a dedicated actor responsible for comparing invariants across scopes, identifying conflicts, and producing the final effective invariant view for a plan. The Invariant Reconciliation Actor is set at three levels via --invariant-actor:

  1. Global config: Set via agents config set actor.default.invariant <ACTOR>. Defines the default Invariant Reconciliation Actor used when neither the project nor the plan specifies one.
  2. Project-level: Set via --invariant-actor on agents project create. If a project defines an Invariant Reconciliation Actor, it is used to reconcile that project's invariants against global invariants.
  3. Plan-level: Set via --invariant-actor on agents action create (carried forward when the action is used) or agents plan use (which overrides whatever was set on the action). If a plan has an Invariant Reconciliation Actor, it reconciles invariants from all scopes (plan, project, and global) and produces the final effective view for that plan.

The lookup order is: plan → project → global config. The first Invariant Reconciliation Actor found is used.

Invariant view calculation: When an action is used and a plan enters the Strategize phase, the Invariant Reconciliation Actor computes the effective invariant view by:

  1. Collecting all invariants from global, project, plan, and action scopes.
  2. Identifying conflicts (invariants from different scopes that contradict each other).
  3. Applying precedence rules (plan > action > project > global) to resolve conflicts, with non-overridable global invariants taking absolute precedence.
  4. Producing the final set of effective invariants.

Each effective invariant is then recorded as an invariant_enforced decision in the plan's decision tree. This makes invariants visible, auditable, and correctable through the standard decision correction mechanism.

Non-overridable global invariants: A global invariant may be marked non_overridable: true. When set, this invariant takes precedence over all lower-scope invariants regardless of the normal precedence chain -- even plan-level invariants cannot override it. This is intended for system-wide safety constraints that must never be relaxed (e.g., "Never commit secrets to version control"). Non-overridable invariants are set via agents invariant add --global --non-overridable "<constraint>". The non_overridable flag is only meaningful on GLOBAL-scoped invariants; it is ignored on project, action, and plan invariants.

Child plan inheritance: When a top-level plan spawns child plans, the parent's effective invariant view (already reconciled) is passed down to each child plan. Child plans do not re-run reconciliation — they inherit the parent's resolved view.

Correcting invariants: The correction mechanism for invariant_enforced decisions supports two operations:

  • Remove: Remove an existing invariant from the plan's decision tree (the invariant remains defined at its scope but is no longer enforced for this plan).
  • Add: Add a new invariant to the plan. When adding, the user can select from invariants already accessible to the plan (those defined at the plan, action, project, or global scope), or provide free-form text to create a new ad-hoc invariant.

# Add invariants at different scopes
agents invariant add --global "All public APIs must maintain backward compatibility"
agents invariant add --global "Payment processing must be idempotent"
agents invariant add --project local/api-service \
  "Database transactions must complete within 5 seconds"
agents invariant add --project local/api-service \
  "Authentication must always use OAuth2"
agents invariant add --plan <PLAN_ID> "All API calls over TCP must be mocked"
agents invariant add --action local/code-coverage "Test files must not import production secrets"

# List invariants agents invariant list --global agents invariant list --project local/api-service agents invariant list --plan <PLAN_ID> --effective # Shows reconciled view

# Remove an invariant agents invariant remove <INVARIANT_ID>

# Attach invariants at creation time (convenience) agents project create --invariant "All endpoints must validate auth tokens" local/api-service agents action create --config ./actions/code-coverage.yaml agents plan use local/code-coverage local/api-service --invariant "Mock all network calls"

# Correct an invariant decision (remove or replace via standard correction) agents plan correct <DECISION_ID> --mode=revert </span> --guidance "Remove this invariant - it does not apply to this module"

The system collects, reconciles, and checks invariants:


class InvariantEnforcer:
    def compute_effective_invariants(self, plan):
        """Compute the effective invariant view for a plan using the Invariant Reconciliation Actor."""
        # 1. Collect raw invariants from all scopes
        raw = self.collect_all_invariants(plan)
    <span style="opacity: 0.7;"># 2. Find the Invariant Reconciliation Actor (plan -&gt; project -&gt; global config)</span>
    reconciler = (
        self.get_plan_invariant_actor(plan)
        <span style="color: magenta; font-weight: 600;">or</span> self.get_project_invariant_actor(plan)
        <span style="color: magenta; font-weight: 600;">or</span> self.get_global_invariant_actor()
    )

    <span style="opacity: 0.7;"># 3. Reconcile: apply precedence (plan &gt; project &gt; global), resolve conflicts</span>
    effective = reconciler.reconcile(raw, precedence=[<span style="color: #66cc66;">&#x27;plan&#x27;</span>, <span style="color: #66cc66;">&#x27;project&#x27;</span>, <span style="color: #66cc66;">&#x27;global&#x27;</span>])
    <span style="color: magenta; font-weight: 600;">return</span> effective

<span style="color: magenta; font-weight: 600;">def</span> <span style="color: cyan; font-weight: 600;">collect_all_invariants</span>(self, plan):
    <span style="color: #66cc66;">&quot;&quot;&quot;Collect invariants from all scopes accessible to this plan.&quot;&quot;&quot;</span>
    invariants = []
    invariants.extend(self.get_global_invariants())
    <span style="color: magenta; font-weight: 600;">for</span> project <span style="color: magenta; font-weight: 600;">in</span> plan.projects:
        invariants.extend(self.get_project_invariants(project))
    invariants.extend(self.get_action_invariants(plan.action))
    invariants.extend(self.get_plan_invariants(plan))
    <span style="color: magenta; font-weight: 600;">return</span> invariants

<span style="color: magenta; font-weight: 600;">def</span> <span style="color: cyan; font-weight: 600;">check_invariant_preservation</span>(self, changes, enforced_invariants):
    <span style="color: #66cc66;">&quot;&quot;&quot;Check that changes respect all enforced invariants.&quot;&quot;&quot;</span>
    <span style="color: magenta; font-weight: 600;">for</span> invariant <span style="color: magenta; font-weight: 600;">in</span> enforced_invariants:
        <span style="color: magenta; font-weight: 600;">if</span> <span style="color: magenta; font-weight: 600;">not</span> self.verify_invariant(invariant, changes):
            <span style="color: magenta; font-weight: 600;">return</span> InvariantViolation(invariant, changes)
    <span style="color: magenta; font-weight: 600;">return</span> Success()

Layer 4: Predictive Error Prevention

The system learns from past failures:


Error Pattern Database:
  - pattern: "Async conversion in payment module"
    historical_failures:
      - "Race condition in payment confirmation"
      - "Timeout handling breaks idempotency"
    preventive_checks:
      - "Add explicit transaction boundaries"
      - "Verify idempotency keys are preserved"
      - "Check distributed lock acquisition"

Apply Phase

!!! adr "Architecture Decision" The Apply phase, conflict resolution, and apply strategies are defined in ADR-006: Plan Lifecycle.

What Apply Does

Apply takes the sandboxed work product and makes it "real" in the project.

Core properties:

  1. Apply is a controlled commit step Apply exists specifically to separate "generated work" from "committed work," enabling review and safer automation.

  2. Apply is often the highest-risk step It changes real systems. This is where approvals and checks matter most.

  3. Apply produces a terminal 'applied' state After successful apply, the plan reaches the applied terminal state within the Apply phase.

Apply should perform (configurable) validations before committing:

  • Diff review gate

    • If the select_tool threshold is not met (i.e., confidence < threshold, or threshold is 1.0) in the automation profile, show:

      • changed files summary,
      • full diff,
      • risk warnings.
  • Conflict resolution

    • If applying to a git repo, handle rebase/merge conflicts safely.
  • Audit log

    • Record who applied, what changed, when, and why.

Validation in Apply

Validation runs during Execute, not during Apply. By the time a plan reaches the Apply phase, all required validations have already passed. Apply commits the sandbox changes to the real resources and does not re-run validations.

For full details on validation — including the Validation type system, modes, attachment scoping, failure handling, fix-then-revalidate loops, and data model — see the Validation section under Core Concepts.

Apply Data Model

A plan in Apply includes:

  • apply_summary
  • applied_artifacts (final commit hash, merged PR link, file list)
  • final_validation_results (per-validation tool invocation results: passed, message, and data for each)
  • approval_record (if human approvals are required)
  • deployment_record (optional, if apply triggers deploy)

Apply Terminal States

Apply is the final phase. It does not transition to another phase — instead, the plan reaches one of several terminal states within the Apply phase:

When Apply succeeds:

  • plan.phase = apply
  • plan.state = applied
  • the sandbox may be cleaned up or archived depending on retention policy
  • changes have been committed to real project resources

When Apply cannot complete within constraints:

  • plan.phase = apply
  • plan.state = constrained
  • the Apply phase has determined that the current strategy's constraints prevent successful application (e.g., merge conflicts that violate invariants, validation failures that cannot be resolved within the strategy's scope, resource state drift that contradicts strategy assumptions)
  • depending on the access_network automation profile flag, this may automatically trigger a reversion to Strategize, or the system may pause for user decision
  • sandbox remains intact pending reversion or user action

When Apply fails:

  • plan.phase = apply
  • plan.state = errored
  • sandbox remains intact for inspection/retry

When Apply is cancelled:

  • plan.phase = apply
  • plan.state = cancelled
  • sandbox remains intact for inspection

Project

!!! adr "Architecture Decision" The project concept, resource linking, and project-level configuration are defined in ADR-009: Project Model.

A project is the boundary that answers:

  • "Where is the work happening?"
  • "What can this plan read and write?"
  • "What skills (and their tools) can this plan use?"
  • "What context is available?"

A project is a collection of linked resources and configuration. Projects link to independently registered resources from the Resource Registry — they do not define resources inline. A resource can be linked to multiple projects, enabling shared resources across teams and workflows.

Important: Projects are created via CLI commands, NOT YAML configuration files.

Project Types: Local vs Remote

Projects are classified based on their resources:

Type Definition Where Plans Can Execute
Local Contains at least one local-only resource Client only
Remote All resources are remotely accessible Client or Server

This distinction matters for server mode: the server can only execute plans on remote projects because it needs network access to all resources.

Project Creation (CLI)


# Step 1: Register resources independently
agents resource add git-checkout local/api-repo \
  --path /repos/api-service \
  --branch main

agents resource add local/database local/staging-db </span> --connection-string "postgresql://staging.example.com/mydb" </span> --read-only

# Step 2: Create the project agents project create "my-api-service"

# Step 3: Link resources to the project agents project link-resource "my-api-service" local/api-repo

agents project link-resource "my-api-service" local/staging-db --read-only

Resources are registered once and can be linked to multiple projects. The resource's type, sandbox strategy, and capabilities are defined by its resource type in the Resource Registry — not by the project.

Project Data Model

A project includes:

1) Identity
  • name — the namespaced project name (e.g., local/api-service). This serves as the project's globally unique identifier; no separate ULID or ID is generated. The namespacing scheme ([[server:]namespace/]name) ensures global uniqueness across systems.
  • namespace (follows same rules as actors: local/, <username>/, <orgname>/)
  • is_remote (boolean, derived from resources)
2) Linked Resources

Resources are the "things you can act on." Projects link to independently registered resources from the Resource Registry rather than defining them inline. This means:

  • A resource can be linked to multiple projects (shared resources).
  • The resource's type, capabilities, sandbox strategy, and DAG relationships are defined by the resource itself — not by the project.
  • Projects can apply project-level overrides when linking (e.g., marking a writable resource as read-only within a specific project context).

Each linked resource reference has:

  • resource_id (ULID reference to a Resource Registry entry). Resources can be referenced by name or ULID in CLI commands; name is resolved to resource_id at command time.
  • project_read_only (boolean — project-level read-only override)
  • alias (optional short name for referencing within the project)

The full resource details (type, location, sandbox strategy, capabilities, parent/child DAG, etc.) are stored in the Resource Registry. See the Resources section for details.

3) Context configuration

Project-level defaults:

  • ignore patterns (like .gitignore semantics)
  • max file size
  • indexing strategy
  • preferred chunking/summarization policy (even if evolving)
  • context retention policy
4) Execution Environment

!!! adr "Architecture Decision" Execution environment routing is defined in ADR-043: Devcontainer Integration and Container-Project Association.

Projects can configure a default execution environment — a container in which tools execute instead of on the host. This is set via agents project context set:


execution_environment:
  default: local/dev-container        # Resource name of the default container
  priority: fallback                 # fallback | override

Priority semantics:

  • fallback (default): Use this execution environment only when no devcontainer is auto-detected for the current resource or its ancestors. If a resource (or ancestor) has a .devcontainer/, the devcontainer wins.
  • override: Always use this execution environment, ignoring any auto-detected devcontainers.

When a project has multiple containers linked as resources, the execution_environment.default field specifies which one to use. Without this setting, the system relies on auto-detected devcontainers or falls back to host execution.

Plans can override the project-level execution environment via --execution-environment on agents plan use (see Execution Environment Routing).

Multi-Project Operations

A single plan may target multiple projects (e.g., updating shared schemas across services). This is considered a key UX advantage over "run in one directory" systems. Because resources are independently registered and can be linked to multiple projects, shared resources across projects are a natural part of the architecture.

In multi-project execution:

  • Strategize must clarify which steps affect which projects.
  • Execution must isolate sandboxes per project OR define a composite sandbox. When multiple projects share the same resource, a single sandbox for that resource is used.
  • Apply must commit changes to each project separately, with separate approval records if necessary.
  • Tool resource bindings are resolved per-project — the same tool may bind to different resources depending on which project context it runs in.

Namespaces

!!! adr "Architecture Decision" The namespace system, resolution rules, and ownership model are defined in ADR-002: Namespace System.

Namespaces define ownership, scoping, and discoverability of actors, tools, skills, resources, resource types, actions, projects, and plans.

All named entities use the format <namespace>/<name>.

Namespace Types

Namespace Scope Storage Examples
local/ Current machine only Local database local/my-reviewer, local/test-action
<username>/ Personal server namespace Server database freemo/code-analyzer, jsmith/deploy-script
<orgname>/ Organization namespace Server database cleverthis/standard-review, acme/deploy-action
openai/, anthropic/, etc. Built-in LLM actors N/A (built-in) openai/gpt-4, anthropic/claude-3-opus

Namespace Rules

  • local/

    • Reserved namespace for local-only items
    • Exists only on the current machine
    • Stored in local database
    • Fast iteration, no sharing
    • Default namespace when none specified
  • <username>/ (e.g., freemo/, jsmith/)

    • Personal namespace on the server
    • Created when user registers an account
    • Stored on server, synced when connected
    • Used for reusable entities (actions, actors, tools, skills, resources) a user wants across machines
    • Only the owning user can create/modify items
  • <orgname>/ (e.g., cleverthis/, acme/)

    • Organization namespace on the server
    • Created when organization is registered
    • Shared across team members
    • All namespaced entities (actions, actors, tools, skills, resources, projects) can be centrally managed
  • Built-in Provider Namespaces (openai/, anthropic/, google/, etc.)

    • Reserved for built-in LLM actors
    • Automatically available when API keys are configured
    • In server mode: available if logged in and server has keys
    • In local mode: requires environment variables or app configuration
    • Cannot be used for custom actors

Server-qualified Names

To disambiguate between servers (when connected to multiple):

  • dev:freemo/code-coverage (personal namespace on dev server)
  • prod:cleverthis/deploy-action (org namespace on prod server)

This enables a pattern where:

  • local machine runs a lightweight client
  • server stores canonical definitions
  • multiple servers can coexist

Actor

!!! adr "Architecture Decision" The actor abstraction, actor types, and actor graph composition are defined in ADR-010: Actor and Agent Architecture.

What an Actor Is

!!! adr "Architecture Decision" The canonical definition of an actor — anything conversational, actor-as-graph principle, hierarchical composition, and the actor/agent distinction — is formalized in ADR-031: Actor Abstraction Definition.

An actor is the abstraction that generalizes "agent" into "anything conversational."

!!! abstract "Actor at a Glance" * It can be as small as a ==single LLM agent==. * It can also be an ==entire graph== that itself calls other actors/tools. * Actors can be nested/hierarchical, enabling "orchestrator of orchestrators."

**Every custom actor IS a graph** (a LangGraph defined via YAML configuration). Even a simple actor wrapping a single LLM is technically a graph with one node.

Actor Naming

Actors are always named using <namespace>/<name> format:

??? example "Naming Examples" | Name | Namespace | Description | | :--- | :-------- | :---------- | | local/my-reviewer | local | Local actor on this machine | | freemo/code-analyzer | freemo | Personal server actor | | cleverthis/deploy-specialist | cleverthis | Organization actor | | openai/gpt-4 | openai | Built-in LLM actor |

Actor Definition (YAML Configuration)

Actors are defined via YAML configuration files. Tools and skills are also defined via their own YAML configuration files. YAML configuration is used for actors, tools, and skills (not for actions or projects).

Example actor configuration (see examples/ directory for full examples):


name: local/my-workflow

cleveragents: version: "3.0" default_actor: workflow_controller

actors: # Simple LLM actor with skills referenced by name my_assistant: type: llm config: actor: openai/gpt-4 # Reference to built-in actor temperature: 0.7 system_prompt: | You are a helpful assistant. Current task: {{ context.task_description }} skills: - local/file-ops # Grants access to all tools in this skill - local/git-ops

# LLM actor with a composite skill (includes many sub-skills) data_processor: type: llm config: actor: anthropic/claude-3-opus system_prompt: | You are a data processing assistant. skills: - local/data-toolkit # A skill containing analysis + transformation tools

# Actor referencing another actor reviewer: type: llm config: actor: local/code-reviewer # Reference to another custom actor memory_enabled: true max_history: 20 skills: - local/file-ops - local/git-ops

routes: main_workflow: type: graph entry_point: start nodes: - name: analyze type: agent agent: my_assistant - name: process type: agent agent: data_processor edges: - source: start target: analyze - source: analyze target: process - source: process target: end

context: global: task_description: "Default task"

Jinja2 Template Preprocessing

!!! adr "Architecture Decision" The two-phase Jinja2 + environment variable preprocessing pipeline for actor YAML files is defined in ADR-032: Jinja2 YAML Template Preprocessing.

Actor configuration YAML files are processed through a two-phase pipeline before the resulting data structure is validated and loaded:

  1. Phase 1 — Jinja2 Template Rendering: The raw file content is run through a sandboxed Jinja2 template engine, resolving {{ }} expressions, {% %} control structures, and {# #} comments into static YAML text.
  2. Phase 2 — Environment Variable Interpolation: After YAML parsing, all string values matching ${VAR} or ${VAR:default} are recursively replaced with OS environment variable values, with automatic type coercion.

!!! warning "Execution Order" Jinja2 templates are evaluated before YAML parsing. Environment variables are interpolated after YAML parsing. The two phases are never interleaved.

Jinja2 Template Syntax

The engine uses standard Jinja2 delimiters inside the YAML file:

Delimiter Purpose Example
{{ ... }} Variable expression {{ context.paper_details.topic }}
{% ... %} Block statement (if, for, block, etc.) {% if context.auto_finish_active %}
{# ... #} Comment (stripped from output) {# This is ignored #}

Variable expressions are used to inject dynamic values into prompts and configuration fields:

system_prompt: |
  You are writing a paper on {{ context.paper_details.topic | tojson }}.
  Target length: {{ context.paper_details.length | tojson }} words.
  Audience: {{ context.paper_details.audience | tojson }}.

Conditional blocks enable a single configuration to define behavior for multiple modes:

system_prompt: |
  You are a research assistant.
  {% if context.auto_finish_active %}
  Auto-finish mode is active. Do not ask questions. Proceed autonomously.
  {% else %}
  Engage in interactive conversation to refine the output.
  {% endif %}

For loops generate repetitive YAML structure from data:

system_prompt: |
  Available sections:
  {% for section in context.section_paths %}
  - {{ section }}
  {% endfor %}
Sandboxed Execution

All template rendering uses jinja2.sandbox.SandboxedEnvironment, which prevents templates from:

  • Executing arbitrary Python code
  • Accessing the filesystem
  • Calling os.system, eval, exec, or similar unsafe operations
  • Accessing private attributes of objects
Custom Jinja2 Filters

The engine registers four custom filters in addition to all standard Jinja2 built-in filters:

Filter Purpose Example
yaml Serializes any value to a YAML-formatted string {{ my_dict | yaml }}
indent Indents text by N spaces (default 2) {{ content | indent(4) }}
sum Sums a numeric iterable {{ values | sum }}
selectattr Selects a named attribute from each item in a sequence {{ items | selectattr('name') }}

All standard Jinja2 built-in filters are also available, including: tojson, default, lower, upper, trim, join, replace, length, first, last, sort, unique, map, reject, select, batch, slice, int, float, string, list, dictsort, escape, safe, truncate, wordwrap, center, format, title, capitalize, striptags, urlencode, abs, round, pprint, groupby, max, min, random, filesizeformat, wordcount, reverse.

Exposed Built-in Functions

The following safe Python built-in functions are exposed in the template context and can be called directly in expressions:

Function Purpose Example
range() Generates integer sequences {% for i in range(5) %}
abs() Absolute value {{ abs(score) }}
round() Rounds a number {{ round(value, 2) }}
len() Length of a collection {{ len(items) }}
min() Minimum value {{ min(scores) }}
max() Maximum value {{ max(scores) }}
sum() Sum of values {{ sum(counts) }}
Template Context Resolution

Template variables are resolved from a context dictionary. The context supports a nested context key convention:

  • {{ context.paper_details.topic }} → resolves context["paper_details"]["topic"]
  • {{ context.brainstorming_summary }} → resolves context["brainstorming_summary"]

The global_context top-level key in the actor configuration file populates this dictionary at load time. At runtime, the actor invocation context, plan context, and session context are merged with the following precedence: runtime context > plan context > session context > global_context from YAML.

Template Detection and Bypass

The engine detects Jinja2 content by scanning for {% or {{ markers in the raw text. Files without these markers bypass Jinja2 processing entirely and are parsed as plain YAML with zero template overhead.

Deferred Rendering

When Jinja2 markers are present but no context is available at load time, the engine performs deferred rendering: it parses the YAML with template markers intact as literal strings. Templates are preserved in the parsed structure and rendered later when runtime context becomes available. This is the mechanism by which system_prompt fields retain their Jinja2 templates for runtime evaluation.

Template Protection for system_prompt Fields

A special protection mechanism ensures Jinja2 syntax inside system_prompt fields survives the YAML loading process:

  1. During loading, all Jinja2 delimiters in the raw file are temporarily replaced with sentinel markers:

    • {{<<<TEMPLATE_START>>>
    • }}<<<TEMPLATE_END>>>
    • {%<<<BLOCK_START>>>
    • %}<<<BLOCK_END>>>
  2. The protected text is parsed as YAML.

  3. After parsing, the sentinels are restored to Jinja2 syntax only within system_prompt fields (recursively through all nested dictionaries and lists).

This allows system prompts to contain Jinja2 templates that are evaluated at runtime (when the actor's full context is available) rather than at file-load time.

YAML Post-Processing

After Jinja2 rendering, the engine applies automatic post-processing to fix common issues:

  1. Blank line cleanup: Removes extraneous blank lines generated by {% %} block tags between YAML keys.
  2. Multi-colon line splitting: Detects and splits lines where template expansion produces multiple key: value pairs on a single line.
  3. Indentation correction: Inserts indentation hints for {% for %} loops to ensure generated YAML maintains correct structure.
Environment Variable Interpolation

After YAML parsing, all string values are recursively scanned for environment variable references:

Pattern Behavior
${VAR} Replaced with os.environ["VAR"]. Raises ValueError if not set.
${VAR:default} Replaced with os.environ.get("VAR", "default"). Uses default if not set.

Automatic type coercion is applied after substitution:

Substituted Value Coerced Type Example
"true" / "false" (case-insensitive) bool True / False
Digits only (with optional leading -) int 42, -7
Digits with single . float 3.14, -0.5
Anything else str Unchanged

This enables configurations like:

env_vars:
  WORK_DIR: ${HOME}/workspace       # String: /home/user/workspace
  LOG_LEVEL: ${LOG_LEVEL:info}      # String: info (default)
  MAX_RETRIES: ${MAX_RETRIES:3}     # Integer: 3 (coerced from default)
  DEBUG: ${DEBUG_MODE:false}        # Boolean: False (coerced from default)

Environment variable interpolation is applied recursively to all nested dictionaries and lists, ensuring resolution at any depth.

Actor Arguments

All actors can receive arguments when invoked, including built-in actors. Arguments are passed when:

  1. An action is used on projects (arguments flow to strategy/execution actors)
  2. An actor is directly invoked

Arguments are injected into the actor's context and can be used in Jinja2 templates within prompts.

For built-in actors (like openai/gpt-4), common arguments include:

  • temperature
  • max_tokens
  • system_prompt

Actor Composition (Hierarchical References)

Actors can reference other actors by name:

actors:
  complex_workflow:
    type: llm
    config:
      actor: local/base-analyzer  # References another registered actor

Load order matters: Referenced actors must be loaded/defined before actors that depend on them.

This enables hierarchical composition where:

  • Actor A's graph can include nodes that call Actor B (by name)
  • Actor B itself is a graph that might call Actor C
  • And so on (no depth limit)

Circular actor references are prohibited and detected at registration time.

Actor vs Agent (Relationship)

!!! adr "Architecture Decision" The formal actor/agent distinction — actors as the general abstraction, agents as a specialized subset — is defined in ADR-031: Actor Abstraction Definition.

  • Agent: an actor that is specifically an LLM with tools and reasoning behaviors.

  • Actor: may be an agent, but may also be:

    • a composite workflow,
    • a multi-step graph,
    • a wrapper around a third-party system (as long as it's "text in → text out" conversationally).

Actor Definition Fields (Complete Reference)

The complete set of fields available in an actor definition. For the formal JSON Schema and additional annotated examples, see Actor Configuration Files in the Configuration section.

Field Type Required Description
name string Yes Namespaced actor name in <namespace>/<name> format. Used as the registered identity.
type string Yes Actor type: llm (language model), tool (tool collection), or graph (multi-node workflow).
description string Yes Human-readable description of what the actor does.
version string No Schema version (default: "1.0").
model string Yes (LLM/GRAPH) LLM model identifier (e.g., gpt-4, claude-3.5-sonnet).
system_prompt string No System prompt text. Supports Jinja2 templates for dynamic content.
tools list Yes (TOOL) List of tool references (strings) and/or inline tool definitions.
context_view string No Role-based context filtering: strategist, executor, reviewer, or full.
memory object No Conversation history settings (see Memory Configuration below).
context object No File inclusion and context window settings (see Context Configuration below).
route object Yes (GRAPH) Graph topology (see Route Configuration below).
env_vars object No Environment variable key-value mappings.
skills list[string] No List of skill names this actor can use. Each entry is a namespaced skill name (e.g., local/file-ops). Skills provide tool capabilities to the actor. See Actor References to Skills and Tools.
lsp list|object No LSP server bindings for language intelligence. Can be a list of namespaced LSP server names (explicit binding), an object with languages: list (language-based binding), or an object with auto: true (resource-auto binding). See LSP Integration.
lsp_capabilities list[string]|"all" No Controls which LSP capabilities are exposed as tools. When omitted or "all", all capabilities from bound servers are available. When a list, only the named capabilities are exposed (e.g., [diagnostics, hover, definitions]).
lsp_context_enrichment object No Controls automatic LSP context enrichment. Keys: diagnostics (bool, default true), type_annotations (bool, default false), max_diagnostics_per_file (int, default 50).

Additional fields available in the v2/runtime actor definition format (within the config: block of actors defined inside the actors: top-level key):

Field Type Required Description
config.provider string Yes (LLM) LLM provider identifier: openai, anthropic, google, azure, openrouter, etc.
config.model string Yes (LLM) Model identifier within the provider.
config.actor string No Combined provider/model format (alternative to separate provider + model).
config.system_prompt string No System prompt text with Jinja2 template support.
config.temperature float No Sampling temperature (0.0 to 2.0). Lower = more deterministic.
config.max_tokens integer No Maximum tokens in the generated response.
config.memory_enabled boolean No Enable conversation memory (default: false).
config.max_history integer No Maximum conversation turns retained in memory (default: 50).
config.unsafe boolean No Allow this actor to perform unsafe operations (default: false).
config.options object No Provider-specific options passed through to the underlying LLM API.
config.tools list Yes (tool) List of inline tool definitions (each with name and code).
config.response_format object No JSON schema for structured output from the LLM.
Memory Configuration
Field Type Default Description
enabled boolean true Whether to maintain conversation history.
max_messages integer null (unlimited) Maximum number of messages to retain.
max_tokens integer null (unlimited) Maximum tokens in retained history.
summarize_old boolean false Whether to summarize old messages instead of discarding them.
Context Configuration
Field Type Default Description
include_files list[string] [] File paths to include in the actor's context.
include_dirs list[string] [] Directory paths to include in the actor's context.
exclude_patterns list[string] [] Glob patterns to exclude from context (e.g., "**/__pycache__/**").
max_context_tokens integer null (model default) Maximum size of the context window in tokens.
Context View

The context_view field controls role-based context filtering:

Value Purpose Includes
strategist High-level planning view Project structure, goals, constraints, architectural summaries
executor Implementation view Source code, file contents, specific task details
reviewer Validation view Changes, diffs, test results, review criteria
full Complete view (use sparingly) All available context from all categories
Type-Specific Requirements
Actor Type Required Fields Optional Fields
llm model system_prompt, tools, context_view, memory, context, env_vars, lsp, lsp_capabilities, lsp_context_enrichment
tool tools (at least one) context_view, env_vars
graph model, route system_prompt, tools, context_view, memory, context, env_vars, lsp, lsp_capabilities, lsp_context_enrichment
Tool Definitions (Inline)

Tools in an actor can be either string references to registered tools or inline definitions:

tools:
  # String reference to a registered tool
  - files/read_file
  - files/list_directory

  # Inline tool definition
  - name: utils/count_lines
    description: Count the number of lines in a file
    parameters:
      - name: file_path
        type: str
        description: Path to the file
        required: true
        default: null
    code: |
      def count_lines(file_path: str) -> int:
          with open(file_path, 'r', encoding='utf-8') as f:
              return len(f.readlines())

Each inline tool parameter supports:

Field Type Required Description
name string Yes Parameter name (must be a valid Python identifier).
type string Yes Python type annotation as string (e.g., str, int, list[str]).
description string Yes Human-readable description.
required boolean No Whether the parameter must be provided (default: true).
default any No Default value if not provided (only for optional parameters).

Inline tool name must follow the namespace/tool_name format. The code field contains Python source code that defines a callable function.

Route Configuration (Graph Topology)

For type: graph actors, the route field defines the graph structure:

Field Type Required Description
nodes list[NodeDefinition] Yes All nodes in the graph.
edges list[EdgeDefinition] Yes All edges connecting nodes.
entry_node string Yes ID of the starting node.
exit_nodes list[string] Yes IDs of terminal nodes.

Node Definition:

Field Type Required Description
id string Yes Unique node identifier (alphanumeric with underscores/hyphens).
type string Yes Node type: agent, tool, conditional, or subgraph.
name string Yes Human-readable node name.
description string Yes Node purpose and behavior.
config object No Type-specific configuration (see below).

Node type-specific config:

Node Type Config Fields Description
agent model, prompt, tools LLM agent with optional tools
tool tool_name, parameters Deterministic tool execution
conditional conditions[].check, conditions[].route_to Routes based on state conditions (Python expressions)
subgraph actor_path Embeds another actor as a nested workflow

Edge Definition:

Field Type Required Description
from_node string Yes Source node ID.
to_node string Yes Target node ID.
condition string No Python expression for conditional routing.
priority integer No Edge priority for multiple outgoing edges (higher = evaluated first, default: 0).

Graph Validation:

  • All node IDs must be unique within the graph.
  • The entry_node must reference an existing node ID.
  • All exit_nodes must reference existing node IDs.
  • All edge from_node and to_node must reference existing node IDs.
  • The graph must be acyclic — cycles are detected via DFS and rejected at validation time.
  • All nodes must be reachable from the entry node.

Actor Composition and Graphs

!!! adr "Architecture Decision" Hierarchical actor composition, the actor-as-graph principle, and graph node types are defined in ADR-031: Actor Abstraction Definition.

Actors can reference:

  • other actors (by namespaced name)
  • skills (by namespaced name — all tools from referenced skills become available)
  • subgraphs

This is central to enabling both:

  • multi-agent orchestration, and
  • modular reuse of workflows.

Nodes in the Graph: Actors and Tools

!!! adr "Architecture Decision" The two graph node types (actor nodes and tool nodes) and their relationship to the skill and tool systems are defined in ADR-031: Actor Abstraction Definition and ADR-030: Skill Abstraction Definition.

Graph nodes can be any of:

  • an actor (another LLM agent or composite workflow, referenced by name),
  • a tool node — a deterministic, non-LLM step that directly invokes a tool. Tool nodes can:
    • Reference a named registered tool by its fully-qualified name (e.g., tool: local/run-migrations). The tool must be registered in the Tool Registry via agents tool add. Metadata can optionally be overridden at the point of use.
    • Define an anonymous inline tool using the same format as a tool YAML body (with anonymous: true). This is useful for one-off, workflow-specific operations that don't warrant separate registration.

This is a powerful simplification: actors provide intelligence, tools provide capability (both as graph nodes and through skills for LLM tool-calling), and skills organize tools into reusable collections. Everything participates in the same graph.

Tool node with named tool reference:


nodes:
  - name: run_db_migrate
    type: tool
    tool: local/run-migrations # Named tool from Tool Registry
    override:                           # Optional metadata override
      capability:
        human_approval_required: true
  • name: spawn_tests type: tool tool: local/create-subplan # Another named tool

Tool node with anonymous inline tool:


nodes:
  - name: custom_validation
    type: tool
    anonymous: true
    description: "Validate output format before proceeding"
    input_schema:
      type: object
      properties:
        data: { type: object }
    capability:
      read_only: true
    code: |
      # Inline Python — same format as a named tool YAML body
      if not params["data"].get("status"):
          raise ValueError("Missing status field")
      return {"valid": True}

LSP Integration

!!! adr "Architecture Decision" The Language Server Protocol integration architecture — LSP Registry, actor LSP binding, capability exposure, and server lifecycle — is defined in ADR-027: Language Server Protocol (LSP) Integration.

Actors that perform software development tasks — writing code, refactoring, reviewing, debugging — benefit from language intelligence: the ability to understand types, navigate symbol definitions, discover references, identify compilation errors, and comprehend the structural relationships within a codebase. The Language Server Protocol (LSP) is the standard for this kind of intelligence, and CleverAgents integrates LSP servers directly into the actor runtime so that agents gain the same semantic code understanding that human developers enjoy in their IDEs.

!!! abstract "LSP at a Glance" * LSP servers are registered in a global LSP Registry (namespaced like tools, skills, and actors). * Actors bind LSP servers via their YAML configuration — explicitly by name, by language, or automatically based on project resources. * Different nodes in an actor's graph can have different LSP bindings. * LSP capabilities are exposed as tools (via LSPToolAdapter) and as context enrichment (diagnostics/type info injected into ACMS hot context). * The LSP Runtime in the Infrastructure layer manages server lifecycle, workspace mapping, and file synchronization.

LSP Registry

Language servers are registered as first-class entities in the global LSP Registry, following the same patterns as the Tool Registry, Skill Registry, and Actor Registry. Each entry defines a language server — its command, the languages it covers, its capabilities, and any initialization options.

LSP entries are:

  • Namespaced as [[server:]namespace/]name (e.g., local/pyright, local/clangd, cleverthis/rust-analyzer).
  • Defined via YAML configuration and registered with agents lsp add --config <file>.
  • Managed via agents lsp list, agents lsp show, agents lsp remove.

Example LSP configuration:


name: local/pyright
description: "Pyright language server for Python type checking and intelligence"

command: pyright-langserver args: ["--stdio"] transport: stdio

languages:

  • python

capabilities:

  • diagnostics
  • hover
  • completions
  • definitions
  • references
  • rename
  • code_actions
  • formatting
  • signature_help
  • document_symbols
  • workspace_symbols

initialization: python: pythonPath: "${PYTHON_PATH:/usr/bin/python3}" analysis: typeCheckingMode: "basic" autoSearchPaths: true

LSP configuration fields:

Field Type Required Description
name string Yes Namespaced identifier (<namespace>/<name>).
description string No Human-readable description of the language server.
command string Yes Executable command to launch the language server process.
args list[string] No Command-line arguments for the server process.
transport string No Communication transport: stdio (default) or tcp.
languages list[string] Yes Programming languages this server provides intelligence for (e.g., python, typescript, rust).
capabilities list[string] No Explicit list of LSP capabilities this server provides. When omitted, capabilities are auto-discovered from the server's initialize response.
initialization object No LSP initializationOptions sent during the initialize handshake. Supports ${ENV_VAR} interpolation.
workspace_settings object No LSP workspace configuration sent via workspace/didChangeConfiguration.
Resource Language Discovery

Resources attached to projects represent codebases, file trees, and repositories that contain source code in one or more programming languages. The system determines what languages a resource contains through a layered discovery process:

  1. File extension analysis — File extensions (.py, .ts, .rs, .go, .cpp, etc.) are mapped to language identifiers using a built-in extension-to-language table. This is the fastest and most common method.
  2. Content analysis — For files without extensions or with ambiguous extensions, content markers are inspected: shebang lines (#!/usr/bin/env python3), magic comments, and structural patterns.
  3. UKO classification — The Universal Knowledge Ontology classifies resources at the technology-specific layer (Python, TypeScript, Rust, etc.), providing semantic language identification that persists across sessions.
  4. Explicit project-level declaration — Projects may explicitly declare their languages via configuration, overriding or supplementing automatic discovery.

Language discovery results are cached per resource and invalidated when content changes.

Actor LSP Binding

Actors declare LSP dependencies in their YAML configuration via the lsp: field. Three binding modes are supported, from fully explicit to fully automatic:

Explicit binding (by server name):


actors:
  code_analyst:
    type: llm
    config:
      actor: anthropic/claude-3-opus
      system_prompt: |
        You are a Python code analyst. Use LSP tools to understand
        type information and identify issues in the codebase.
    skills:
      - local/file-ops
    lsp:
      - local/pyright
      - local/ruff-lsp

Language-based binding (runtime resolves servers from registry):


actors:
  polyglot_reviewer:
    type: llm
    config:
      actor: openai/gpt-4
    lsp:
      languages:
        - python
        - typescript
        - rust

Resource-auto binding (auto-discover from project resources):


actors:
  universal_developer:
    type: llm
    config:
      actor: anthropic/claude-3-opus
      system_prompt: |
        You are a software developer.
    lsp:
      auto: true

When auto: true is set, the runtime inspects the project's bound resources via language discovery, determines which languages are present, and binds the appropriate LSP servers automatically. This is the most reusable pattern — an actor configured with lsp: { auto: true } works correctly regardless of the language of the project it is assigned to.

Jinja2 dynamic binding (template-driven resolution):


actors:
  dynamic_analyst:
    type: llm
    config:
      actor: openai/gpt-4
    lsp:
      {% for lang in context.project_languages %}
      - {{ lsp_registry.for_language(lang) | first }}
      {% endfor %}

Template variables available during LSP resolution:

Variable Type Description
lsp_registry object The LSP Registry. Supports .for_language(lang) returning matching server names and .all() returning all entries.
context.project_languages list[string] Languages detected in the current project's resources.
context.resource_languages list[string] Languages detected in the specific resource being processed.
Per-Node LSP Binding

Different nodes in an actor's graph can have different LSP configurations. This allows fine-grained control — for example, giving a strategy actor read-only diagnostics while giving an execution actor full LSP capabilities:


actors:
  strategy_planner:
    type: llm
    config:
      actor: openai/gpt-4
      system_prompt: |
        Plan the implementation strategy. Use LSP diagnostics
        to assess codebase health before proposing changes.
    lsp:
      - local/pyright
    lsp_capabilities:           # Restrict to read-only
      - diagnostics
      - hover
      - definitions
      - references

code_implementer: type: llm config: actor: anthropic/claude-3-opus system_prompt: | Implement code changes. Use full LSP capabilities for navigation, diagnostics, and refactoring. lsp: auto: true lsp_capabilities: all # Full capabilities (default)

routes: dev_workflow: type: graph entry_point: plan nodes: - name: plan type: agent agent: strategy_planner - name: implement type: agent agent: code_implementer edges: - source: plan target: implement - source: implement target: end

The lsp_capabilities field controls which LSP features are exposed to the actor as tools. When omitted or set to all, every capability declared by the bound LSP servers is available.

LSP Capability Exposure

LSP capabilities reach actors through two complementary mechanisms:

As Tools (via LSPToolAdapter)

The LSPToolAdapter is an Infrastructure-layer adapter (analogous to MCPToolAdapter for MCP) that translates LSP server capabilities into CleverAgents tools. When an actor with LSP bindings activates, the adapter generates tool definitions for each exposed capability and injects them into the actor's tool surface alongside skill-provided tools.

Available LSP tools:

Capability LSP Method Tool Name Description
diagnostics textDocument/publishDiagnostics lsp/diagnostics Retrieve compilation errors, warnings, and hints for a file
hover textDocument/hover lsp/hover Get type information and documentation at a position
completions textDocument/completion lsp/completions Get code completion suggestions at a position
definitions textDocument/definition lsp/definition Go to the definition of a symbol
references textDocument/references lsp/references Find all references to a symbol
rename textDocument/rename lsp/rename Compute a rename refactoring across files
code_actions textDocument/codeAction lsp/code-actions Get available code actions (quick fixes, refactors)
formatting textDocument/formatting lsp/format Format a document according to language conventions
signature_help textDocument/signatureHelp lsp/signature Get function signature information at a call site
document_symbols textDocument/documentSymbol lsp/symbols List all symbols (classes, functions, variables) in a file
workspace_symbols workspace/symbol lsp/workspace-symbols Search for symbols across the entire workspace

When multiple LSP servers are bound to an actor (e.g., local/pyright for Python and local/typescript-lsp for TypeScript), the tool adapter routes requests to the appropriate server based on the file's detected language. The tool names remain the same — routing is transparent to the actor.

As Context Enrichment

In addition to explicit tool calls, LSP servers can automatically enrich the actor's context:

  • Diagnostic injection: When a source file enters the actor's hot context, the LSP server's diagnostics for that file are appended as structured annotations. The actor "sees" type errors, unused imports, and other issues without explicitly calling lsp/diagnostics.
  • Type annotation overlay: Hover information for key symbols (function signatures, class hierarchies, variable types) can be pre-fetched and included as context metadata.

Context enrichment is controlled per actor:


actors:
  enriched_reviewer:
    type: llm
    config:
      actor: openai/gpt-4
    lsp:
      auto: true
    lsp_context_enrichment:
      diagnostics: true                # Auto-inject diagnostics (default: true)
      type_annotations: false           # Auto-inject type info (default: false)
      max_diagnostics_per_file: 50     # Limit to avoid context bloat
LSP Server Lifecycle

LSP servers are managed by the LSP Runtime in the Infrastructure layer:

  1. Startup — When an actor with LSP bindings activates (for a plan phase or agents actor run), the LSP Runtime starts the required language server processes. Each server is initialized with the initialize LSP handshake, passing initializationOptions from the registry entry and workspace root paths mapped from bound resources.

  2. Workspace Mapping — LSP workspace roots correspond to registered resource physical paths. When the actor operates within a sandbox (Execute phase), the sandbox's working directory is used as the workspace root, ensuring the language server analyzes sandboxed code — not the original.

  3. File Synchronization — As the actor reads and modifies files (via tools), the LSP Runtime sends textDocument/didOpen, textDocument/didChange, and textDocument/didClose notifications to keep the language server's view synchronized with the actor's mutations.

  4. Shared Instances — If multiple actors in the same plan or session require the same language server for the same workspace, the LSP Runtime shares a single server instance. Reference counting ensures the server stays alive as long as at least one actor needs it.

  5. Shutdown — When the actor deactivates (plan phase completes, session ends), the LSP Runtime sends shutdown and exit lifecycle messages. Shared instances shut down only when the last referencing actor deactivates.

  6. Crash Recovery — If a language server process crashes, the LSP Runtime restarts it automatically, re-sends the initialize handshake, re-opens tracked documents, and resumes operations without disrupting the actor's execution.

LSP in the Plan Lifecycle

LSP integration enhances each phase of the plan lifecycle:

Phase LSP Role Example Use
Strategize Read-only intelligence — diagnostics, type information, and symbol navigation inform strategy decisions Strategy actor calls lsp/diagnostics to assess codebase health, lsp/symbols to understand module structure, lsp/references to gauge impact of proposed changes
Execute Full intelligence — all capabilities available during code generation and modification Execution actor calls lsp/definition to understand APIs before using them, lsp/diagnostics to verify changes compile, lsp/rename to perform safe refactorings
Apply Validation intelligence — diagnostics confirm the final changeset is clean Apply-phase validations invoke lsp/diagnostics on all modified files to ensure no regressions before merge
Comparison with MCP

LSP and MCP are both standard protocols for external capability servers integrated into the actor runtime:

Aspect MCP (Model Context Protocol) LSP (Language Server Protocol)
Purpose General-purpose tool discovery and invocation Language-specific code intelligence
Scope Any callable operation (file I/O, API calls, data queries) Code analysis capabilities (diagnostics, navigation, completions)
Registry Tool Registry (via MCPToolAdapter) LSP Registry (via LSPToolAdapter)
Binding Skills compose MCP tools; actors acquire via skill references Actors bind LSP servers directly via lsp: configuration
Tool generation Tools pre-defined by MCP server, registered at discovery Tools generated dynamically from LSP server capabilities at activation
Lifecycle Managed by MCP SDK connection lifecycle Managed by LSP Runtime with workspace mapping and file synchronization

Both adapters follow the same architectural pattern: a standard protocol server in the Infrastructure layer is bridged into the actor's tool surface through a typed adapter. The key difference is that MCP tools are general-purpose and explicitly authored, while LSP tools are language-specific and automatically derived from the protocol's capability model.

Multi-Actor Configuration File (Complete Structure)

A single actor configuration YAML file can define an entire multi-actor system — multiple actors, graph routing, stream processing, context sharing, and message routing — all in one file. This is the format used for complex workflows like the scientific paper writer.

Top-Level Keys
Key Type Required Description
name string Yes Namespaced actor name (<namespace>/<name>).
cleveragents object No Metadata: version, logging, template engine, safety, default actor.
actors (or agents) object Yes Map of actor names to definitions. Both key names are accepted.
routes object No Map of route names to stream or graph topology definitions.
merges list No Stream merge operations combining multiple sources into one target.
splits list No Stream split operations dividing one source into multiple targets.
publications list No Output stream names (e.g., ["__output__"]).
templates object No Reusable template definitions for Jinja2 inheritance.
instances object No Instantiated templates with bound parameters.
global_context object No Key-value pairs accessible to all actors via {{ context.key }}.
context object No Alternative context block with global: sub-key.
prompts object No Named prompt templates referenceable by actors.
pipelines object No Hybrid pipeline definitions combining stream and graph stages.
cleveragents Metadata Block
cleveragents:
  version: "3.0"             # Schema version (default: "3.0")
  logging:
    level: "INFO"            # DEBUG, INFO, WARNING, ERROR (default: INFO)
  template_engine: "JINJA2"  # JINJA2 or NONE (default: JINJA2)
  unsafe: false              # Allow unsafe operations (default: false)
  default_actor: my_actor    # Default actor when multiple defined
Route Definitions

Routes connect actors via stream or graph topologies:

Stream Routes — reactive processing pipelines:

routes:
  chat_stream:
    type: stream
    stream_type: cold          # cold (default), hot, or replay
    operators:
      - type: map              # map or graph_execute
        params:
          agent: chat_agent    # Actor name for map operators
    publications:
      - __output__             # Output stream name
    subscriptions:
      - __input__              # Input stream name
    buffer_size: 10            # Stream buffer size (default: 10)
    initial_value: null        # Initial value (optional)

Graph Routes — LangGraph-based directed graph workflows:

routes:
  main:
    type: graph
    entry_point: start         # Entry node name (required)
    nodes:
      start:
        type: START            # Special start node
      end:
        type: END              # Special end node
      router:
        type: message_router   # Message-based routing node
        rules:                 # Routing rules (see below)
          - ...
      my_actor_node:
        type: agent            # Actor-backed node
        agent: my_actor        # References actor by name
        metadata: {}           # Optional metadata
    edges:
      - source: start
        target: router
      - source: router
        target: my_actor_node
        condition:
          context_value: next_node
          equals: my_actor_node
      - source: my_actor_node
        target: end
    checkpointing: false       # Enable checkpointing (default: false)
    checkpoint_dir: null       # Checkpoint storage directory
    enable_time_travel: false  # Enable time travel debugging (default: false)
    parallel_execution: false  # Allow parallel node execution (default: false)
    state_class: null          # Custom state class name
Graph Node Types
Type Purpose Key Fields
agent Node backed by an actor agent: <actor_name>
tool Node invoking tools tools: [<tool_ref>, ...]
function Node backed by a Python function function: <function_name>
conditional Branching node condition: { ... }
subgraph Delegates to another graph route subgraph: <route_name>
start / START Explicit start node (none)
end / END Terminal node (none)
message_router Content-based message routing rules: [...]
Message Router Node

The message_router node type routes messages to different actors based on message content. It uses a rules-based system:

router:
  type: message_router
  rules:
    # Prefix-based routing
    - type: prefix
      match: "GOTO_BRAINSTORMING"
      target: brainstorming
      strip_match: true        # Remove the prefix before forwarding

    # Contains-based routing
    - type: contains
      match: "SET_TOPIC:"
      target: discovery

    # Suffix-based routing (catch-all)
    - type: suffix
      match: ""                # Empty string matches everything
      target: workflow_controller

Each rule specifies:

Field Type Description
type string Match type: prefix, contains, or suffix.
match string Pattern to match against the message content.
target string Node name to route the message to.
strip_match boolean Whether to strip the matched pattern from the message (default: false).

Rules are evaluated in order; the first matching rule determines the target node.

Routing Prefixes (Inter-Actor Communication)

Actors communicate with each other and the routing system via routing prefixes — special string prefixes prepended to output text that the message router interprets:

Prefix Pattern Purpose Example
GOTO_<NODE>: Route to a specific node GOTO_BRAINSTORMING:Start the brainstorm
SET_<FIELD>: Set a context field value SET_TOPIC:Quantum computing
ROUTE_<TARGET>: Route to a sub-target ROUTE_ASK_TOPIC:What topic?
COMMAND_OUTPUT: Display output directly to user COMMAND_OUTPUT:Help text here
DISCOVERY_RESPONSE: Response from discovery stage DISCOVERY_RESPONSE:Topic set

Tool actors return these prefixes as their result variable. The message router parses the prefix and routes accordingly. The message content after the colon is forwarded to the target node.

Conditional Edges

Graph edges can include conditions that control routing based on graph state:

edges:
  # Unconditional edge
  - source: start
    target: router

  # Conditional edge — routes based on context value
  - source: router
    target: brainstorming
    condition:
      context_value: next_node
      equals: brainstorming

  # Conditional edge — routes based on boolean flag
  - source: passthrough
    target: auto_driver
    condition:
      context_value: auto_finish_active
      equals: true
Merges and Splits

Merges combine multiple input streams into a single stream:

merges:
  - sources: [__input__]    # Special __input__ = user input
    target: main            # Route name to send merged input to

Splits divide a single stream into multiple output streams:

splits:
  - source: main_output
    targets: [log_stream, display_stream]

Special stream names:

  • __input__ — the user's input
  • __output__ — the final output displayed to the user
Inline Tool Code Model

Tool-type actors define their behavior entirely in inline Python code within the code: field:

my_tool_actor:
  type: tool
  config:
    tools:
      - name: my_tool
        code: |
          import sys
          # Available variables:
          #   input_data  — the input text/message passed to this tool
          #   context     — shared mutable context dictionary
          #   result      — set this variable to define the tool's output

          msg = input_data or ''
          context['last_input'] = msg

          if msg.startswith('!help'):
              result = "COMMAND_OUTPUT:Available commands: !help, !next"
          else:
              result = f"GOTO_PROCESSOR:{msg}"

          print(f"DEBUG: {result}", file=sys.stderr)

The inline code execution model provides three implicit variables:

Variable Type Description
input_data string The input text/message passed to the tool.
context dict Shared mutable context dictionary. Changes persist across invocations.
result string Set this variable to define the tool's output.

The context dictionary is the primary mechanism for inter-actor state sharing. All actors in the same configuration share the same context, enabling data flow between stages.

Context Sharing

The context dictionary (accessible in tool code and Jinja2 templates) serves as shared state:

# Set via global_context in YAML:
global_context:
  writing_stage: intro
  paper_details:
    topic: null
    length: null
    audience: null

# Or via context.global:
context:
  global:
    conversation_mode: true
    default_actor: openai/gpt-4

At runtime, tool actors read and modify context freely:

# In tool code:
context['writing_stage'] = 'brainstorming'  # Update stage
topic = context.get('paper_details', {}).get('topic')  # Read nested value
context.setdefault('history', []).append(msg)  # Append to list

In Jinja2 templates (system prompts):

system_prompt: |
  Paper topic: {{ context.paper_details.topic | tojson }}
  {% if context.auto_finish_active %}
  Proceed autonomously.
  {% endif %}
Stream-to-Graph Bridge

Routes can include bridge configuration for dynamic topology changes:

routes:
  adaptive:
    type: stream
    bridge:
      upgrade_conditions:
        message_count_threshold: 5
      downgrade_conditions:
        idle_timeout: 30
      state_extractor: "extract_graph_state"
      state_flattener: "flatten_to_stream"
      preserve_subscriptions: true
      preserve_checkpointing: true
Publications

The publications key defines output streams at the route or top level:

# Route-level publications
routes:
  chat_stream:
    type: stream
    publications:
      - __output__

# Top-level publications
publications:
  - __output__

Actor Configuration File Loading

Actor configuration files can be loaded from either JSON or YAML format:

  1. The loader first attempts JSON parsing (json.loads)
  2. If JSON parsing fails, it falls back to the YAML pipeline (Jinja2 preprocessing + yaml.safe_load + environment variable interpolation)

The resolution order for provider and model values when loading:

  1. CLI override (--provider, --model)
  2. Top-level provider / model keys
  3. Top-level provider_type / model_id aliases
  4. v2-extracted values from actors.<name>.config.provider / .model

For unsafe flag: the result is true if any of the following is true: the top-level unsafe key, the v2-extracted unsafe flag, or the CLI --unsafe flag.

Agent

!!! adr "Architecture Decision" The agent specialization and its relationship to the actor abstraction are defined in ADR-010: Actor and Agent Architecture.

Agent Definition

In CleverAgents, an agent is a specialized actor with:

  • a conversational interface,
  • tool-calling capability,
  • potentially memory, planning heuristics, and role identity.

Examples of agent roles:

  • planner/architect (strategy actor)
  • coder/implementer (execution actor)
  • reviewer/qa agent
  • release/apply agent

The transcript explicitly discusses role separation like planner/coder/reviewer in context views/memory proposals.

Agent Behavior Configuration

Agents should be configurable without code changes:

  • prompt templates
  • tool sets
  • safety constraints
  • style constraints (verbosity, code style)
  • reliability controls (self-checks, validations)

A design goal is user empowerment: "users customize LLM behavior without modifying core code."

Tools

!!! adr "Architecture Decision" The tool system, tool adapter layer, and tool lifecycle are defined in ADR-011: Tool System.

What a Tool Is

A tool is a namespaced, independently registered, callable operation. It is the ==atomic unit of execution== in CleverAgents — the smallest piece of functionality that can read, write, or transform resources. Tools are defined in their own YAML configuration files, managed through the agents tool CLI commands, and registered in the Tool Registry.

Tools follow the same <namespace>/<name> naming convention as actors, skills, and other entities (e.g., local/run-migrations, cleverthis/validate-api, local/create-subplan). They support optional server-qualified prefixes for multi-server disambiguation (e.g., dev:freemo/custom-analysis).

Validation as a Tool subtype: A Validation is a specialized subtype of Tool that extends the Tool class with validation-specific metadata (mode: required/informational) and a structured JSON return format ({ "passed": bool, "message": string, "data": object }). Because Validation extends Tool via standard class inheritance, it inherits all base properties and behaviors — registration, resource bindings, lifecycle hooks, capability metadata, source types (custom, MCP, agent_skill, builtin, and the validation-specific wrapped source for Validations that delegate to an existing Tool), and the ability to exist as a tool node in an actor graph. A Validation can also wrap an existing plain Tool via the wraps field, reusing the Tool's implementation and interpreting its output through a transform function — see the Tool Wrapping subsection under Core Concepts > Validation for details.

!!! note "Key Constraints on Validations" - Always read-only: Validations observe and report but ==never modify resources==. writes is always false and checkpointable is always false. - Shared namespace: Validations and plain Tools share the same naming namespace in the Tool Registry. A name conflict results in an error. - Superset/subset semantics: A Validation can be used anywhere a Tool is expected (since it IS a Tool), but not vice versa. - Unified management: Listed via agents tool list --type validation, inspected via agents tool show, removed via agents tool remove. Only add, attach, and detach have validation-specific CLI commands.

Validations can be attached to resources directly, or to resources through projects or plans. The same mechanisms used to determine what tools can operate on what resources carry over to determining what validations apply to a particular resource. See the Validation section (immediately following this Tools section) for full details on the Validation type system, modes, attachment scoping, failure handling, and data model.

The Dual Role of Tools

Tools serve two distinct roles in CleverAgents:

  1. As components of a Skill: A skill references tools by name to assemble a reusable capability collection. When an actor references a skill, all of that skill's tools (including those from included child skills) become available to the actor's LLM agent for tool-calling.

  2. As tool nodes in an Actor graph: An actor's graph definition can include type: tool nodes that directly invoke a specific tool. This is used for deterministic, non-LLM steps in a workflow — e.g., spawning a child plan, running validation, or executing a migration. The tool node either references a named registered tool or defines an anonymous inline tool.

block-beta
    columns 3
    space:3
    block:header:3
        A["Tool: Dual Role"]
    end
    space:3
    block:role1:1
        B["Role 1: In a Skill"]
        C["Skill: local/devops"]
        D["tools:"]
        E["  - local/run-migrations"]
        F["  - local/validate-schema"]
        G["(tool-calling by LLM)"]
    end
    space:1
    block:role2:1
        H["Role 2: In an Actor Graph"]
        I["Actor Graph node:"]
        J["  name: run_db"]
        K["  type: tool"]
        L["  tool: local/run-migrations"]
        M["(deterministic invoke)"]
    end

Tool Configuration (YAML)

Tools are defined in their own YAML configuration files, separate from skills and actors. A tool YAML file declares the tool's identity, schema, capability metadata, and implementation:


# File: tools/run-migrations.yaml
cleveragents:
  version: "3.0"

tool: name: local/run-migrations description: "Run database migrations for the API service"

source: custom # mcp | agent_skill | builtin | custom

# Resource bindings — what resources this tool needs access to resources: db: type: local/database access: read_write required: true description: "Target database for migrations"

input_schema: type: object properties: direction: type: string enum: [up, down] count: type: integer default: 1 required: [direction]

capability: writes: true write_scope: resource_slots: [db] # References the "db" resource slot checkpointable: true checkpoint_scope: transaction side_effects: [schema_mutation]

code: | import subprocess direction = params["direction"] count = params.get("count", 1) db = ctx.resources["db"] # Access the bound database resource result = subprocess.run( ["alembic", direction, str(count)], capture_output=True, text=True, cwd=db.sandbox.root ) return {"stdout": result.stdout, "returncode": result.returncode}

Another example — a tool that wraps an MCP server endpoint:


# File: tools/create-github-issue.yaml
cleveragents:
  version: "3.0"

tool: name: local/create-github-issue description: "Create a GitHub issue via MCP"

source: mcp mcp_server: command: "npx @anthropic/mcp-github" env: GITHUB_TOKEN: "${GITHUB_TOKEN}" tool_name: create_issue # The tool name as exposed by the MCP server

capability: writes: true write_scope: [github:issues] checkpointable: false

And an Agent Skill tool:


# File: tools/deploy-staging.yaml
cleveragents:
  version: "3.0"

tool: name: local/deploy-staging description: "Deploy the current branch to the staging environment"

source: agent_skill agent_skill: path: ./skills/deploy-to-staging sandbox_policy: container allowed_tools: ["Bash(docker:)", "Bash(kubectl:)", "Read"]

capability: writes: true checkpointable: false side_effects: [deploy, infrastructure]

Tool Registration and Management

Tools are managed through the agents tool CLI commands:


# Register a new tool from its YAML configuration
agents tool add --config ./tools/run-migrations.yaml

# Update an existing tool (re-reads the config file, overwrites registration) agents tool add --config ./tools/run-migrations.yaml --update

# List all registered tools agents tool list

# Show details for a tool (schema, capability, references) agents tool show local/run-migrations

# Remove a tool agents tool remove local/run-migrations

Once registered, a tool is available to be referenced by skills (in their tools list) and by actor graphs (as type: tool nodes). Tools persist in the database (local or server) and follow the same namespace rules as actors and skills.

Anonymous Tools

An anonymous tool is an inline tool definition that appears directly in a skill YAML or an actor graph node. Anonymous tools use the same format as a named tool's YAML definition (same input_schema, capability, and code fields) but lack a namespaced name. They are:

  • Not registered in the Tool Registry
  • Not reusable — they exist only within the YAML file where they are defined
  • Useful for one-off operations that are too specific to warrant separate registration

Anonymous tools in a skill YAML:


skill:
  name: local/my-skill
  tools:
    - local/run-migrations          # Named tool reference
    - local/validate-api-compat     # Named tool reference

anonymous_tools: # Inline definitions, same format as tool YAML - description: "One-off data cleanup for this project" input_schema: type: object properties: table: { type: string } capability: writes: true checkpointable: true code: | # ... Python code ... return {"cleaned": count}

Anonymous tools in an actor graph node:


nodes:
  - name: custom_step
    type: tool
    anonymous: true
    description: "Inline validation specific to this workflow"
    input_schema:
      type: object
      properties:
        data: { type: object }
    capability:
      read_only: true
    code: |
      # ... Python code ...
      return {"valid": True}

The anonymous tool format is intentionally identical to the body of a named tool YAML — this means promoting an anonymous tool to a named, registered tool is a simple copy-paste into its own YAML file and agents tool add.

Metadata Overrides

When referencing a named tool in a skill or actor graph, its registered metadata can optionally be overridden at the point of use. This allows context-specific adjustments without modifying the tool's global registration.

Overriding tool metadata in a skill:


skill:
  name: local/strict-devops
  tools:
    - name: local/run-migrations
      override:
        capability:
          human_approval_required: true # Override: require approval in this skill
          write_scope: [database:staging] # Override: restrict scope for this context
- local/validate-api-compat            # No overrides, use as registered

Overriding tool metadata in an actor graph node:


nodes:
  - name: safe_migrate
    type: tool
    tool: local/run-migrations
    override:
      capability:
        human_approval_required: true

Overriding tool metadata when including a sub-skill:

When a skill includes another skill (importing all its tools), individual tools from the included skill can have their metadata overridden:


skill:
  name: local/production-ops
  includes:
    - name: local/devops-toolkit
      tool_overrides:
        - tool: local/run-migrations
          override:
            capability:
              human_approval_required: true # In this context, require human approval
              write_scope: [database:production]
    - <span style="color: cyan;">tool</span>: local/create-github-issue
      <span style="color: cyan; font-weight: 600;">override</span>:
        <span style="color: cyan; font-weight: 600;">capability</span>:
          <span style="color: cyan; font-weight: 600;">human_approval_required</span>: <span style="color: magenta; font-weight: 600;">true</span> <span style="opacity: 0.7;"># Require approval in this context</span>

Override rules:

  1. Overrides are shallow-merged — only the specified fields are replaced; unspecified fields retain their registered values.
  2. Overrides never persist back to the Tool Registry — they apply only at the point of use.
  3. Built-in tool metadata cannot be overridden (it is authoritative from the implementation).
  4. The override scope is limited to capability and description fields. Schema (input_schema, output_schema) cannot be overridden because it would break callers' expectations.

Resource Bindings

!!! adr "Architecture Decision" Resource binding resolution, slot declarations, and project-specific binding are defined in ADR-008: Resource System and ADR-011: Tool System.

Tools operate on resources — git repositories, filesystems, databases, and more. The resource binding system declares and resolves the relationship between a tool and the resources it needs access to.

Resource Slots

A tool declares one or more resource slots in its YAML configuration. Each slot is a typed placeholder that specifies:

  • Slot name: A logical name used to reference the resource within the tool's code and parameters.
  • Resource type: The resource type required (e.g., git, fs-mount, local/database). The bound resource must be of this type (or a compatible subtype).
  • Access mode: read_only, write_only, or read_write.
  • Description: Human-readable explanation of what the tool uses this resource for.
  • Required/optional: Whether the slot must be bound for the tool to function.

Example tool YAML with resource slots:


tool:
  name: local/run-migrations
  description: "Run database migrations"
  source: custom

resources: db: type: local/database access: read_write required: true description: "Target database for migrations"

capability: writes: true write_scope: [db:migrations] # References the "db" slot checkpointable: true

code: | direction = params["direction"] db_resource = ctx.resources["db"] # Access the bound resource result = db_resource.handler.execute_migration(direction, db_resource.sandbox) return {"status": "ok"}

A tool that works with multiple resources:


tool:
  name: local/cross-repo-diff
  description: "Compare files across two git repositories"
  source: custom

resources: source_repo: type: git-checkout access: read_only required: true description: "Source repository to compare from" target_repo: type: git-checkout access: read_only required: true description: "Target repository to compare against"

capability: read_only: true

code: | source = ctx.resources["source_repo"] target = ctx.resources["target_repo"] # ... compare files across repos ...

Three Binding Modes

Resource slots are resolved to actual resources through one of three binding modes:

1. Contextual Binding (default)

The slot declares a resource type requirement, and the system resolves it from the plan's project context at activation time. This is the most common mode — the tool says "I need a git-checkout resource" and the system finds one among the project's linked resources.


resources:
  repo:
    type: git-checkout
    access: read_write
    # No `bind` field → contextual binding

Resolution rules for contextual binding:

  • The system searches the plan's project for linked resources matching the slot's type.
  • If exactly one resource of the right type exists, it is automatically bound.
  • If multiple resources match, the system uses the slot name as a hint (e.g., a slot named repo prefers a resource with alias repo). If ambiguous, the plan execution raises an error requiring explicit binding.
  • If no resource matches, the tool cannot be activated for this plan (a validation error is raised during plan creation).

2. Static Binding

The slot is hardcoded to a specific registered resource by name. This is useful for tools that always operate on the same resource, regardless of project context.


resources:
  docs:
    type: fs-mount
    access: read_only
    bind: local/company-docs # Static: always this resource
    description: "Company documentation corpus"

Static bindings are resolved at registration time and validated — the named resource must exist and be of the correct type.

3. Parameter Binding

The resource reference is passed as a tool argument at invocation time. This is useful for tools that operate on user-specified resources.


resources:
  target:
    type: git-checkout
    access: read_only
    from_param: repository # Bound from the "repository" input parameter
    description: "Repository to analyze"

input_schema: type: object properties: repository: type: string description: "Name of the registered resource to analyze" required: [repository]

The from_param field links a resource slot to an input parameter. At invocation time, the system resolves the parameter value as a resource name from the Resource Registry and validates type compatibility.

Binding Resolution Flow
stateDiagram-v2
    [*] --> ToolActivation: Actor references skill or tool node

    state "For Each Resource Slot" as ForEach {
        state binding_check <<choice>>
        [*] --> binding_check: Check binding type

        binding_check --> StaticBinding: has bind field
        binding_check --> ContextualBinding: no bind, no from_param
        binding_check --> ParameterBinding: has from_param

        state "Static Binding" as StaticBinding {
            [*] --> ResolveByName: Resolve from Resource Registry
            ResolveByName --> ValidateType: Validate type compatibility
        }

        state "Contextual Binding" as ContextualBinding {
            [*] --> SearchProject: Search plan's project resources
            SearchProject --> FilterByType: Filter by resource type
            state match_check <<choice>>
            FilterByType --> match_check
            match_check --> AutoBind: One match
            match_check --> TryAlias: Multiple matches
            match_check --> ValidationError: No matches
            TryAlias --> AliasMatch: Try alias/name match
            AliasMatch --> AutoBind: Match found
            AliasMatch --> ValidationError: No match
        }

        state "Parameter Binding" as ParameterBinding {
            [*] --> DeferBinding: Defer to invocation time
        }

        StaticBinding --> StoreBindings
        AutoBind --> StoreBindings
        ParameterBinding --> StoreBindings
        state "Store in ToolActivationContext" as StoreBindings
    }

    state "Tool Invocation" as Invocation {
        [*] --> ResolveParams: Resolve parameter-bound slots\nfrom invocation params
        [*] --> ValidateAccess: Ensure sandbox exists\nValidate access mode
        ResolveParams --> Execute
        ValidateAccess --> Execute
        state "Execute tool with bound resources" as Execute
    }

    ForEach --> Invocation
    Invocation --> [*]
Built-in Tool Resource Bindings

Built-in tools have implicit resource slots that do not need to be declared in YAML (they are hardcoded in the implementation):

Built-in Tool Group Implicit Slot Slot Type Access
file_operations (read_file, write_file, edit_file, etc.) directory fs-directory or git-checkout read_write
directory_operations (create_directory, list_directory, etc.) directory fs-directory or git-checkout read_write
search_operations (search_files, find_definition, etc.) directory fs-directory or git-checkout read_only
git_operations (git_status, git_diff, git_log, etc.) repo git-checkout read_only

Built-in tools accept both fs-directory and git-checkout types for file operations because a git-checkout resource's worktree root is an fs-directory. When binding to a git-checkout, the resource router automatically resolves to the worktree root fs-directory child for file operations. A standalone fs-mount resource also works — the router resolves through the fs-mount → root fs-directory chain.

Resource Discovery via Bindings

The binding system enables powerful resource discovery queries:

  • "What tools can modify this resource?" → Find all tools with resource slots matching the resource's type and access: read_write or access: write_only.
  • "What tools can read this virtual file?" → Find the virtual file's physical children, then find tools with slots matching each physical resource's type.
  • "What resources does this tool need?" → Inspect the tool's declared resource slots.
  • "Is this tool compatible with this project?" → Check if the project's linked resources can satisfy all of the tool's required resource slots.
Transitive Reachability

!!! adr "Architecture Decision" Tool reachability, access projection, and read/write routing are defined in ADR-037: Tool Reachability and Access Projection.

Tool reachability extends beyond direct binding. A tool bound to a git-checkout can transitively reach every descendant fs-file through the DAG's contains edges.

Forward reachability: Given a tool T bound to resource R, the set of all resources T can access is {R} {all descendants of R via contains edges}.

Inverse reachability: Given a resource r, the set of tools that can reach it is found by walking up the containment hierarchy from r, collecting all ancestors, and finding tools with resource slots compatible with any ancestor's type.

Cross-equivalence reachability: If resource r has a virtual parent, the system also finds tools that can reach any sibling physical manifestation of the same virtual resource. This answers: "What tools can reach the same logical resource through any physical path?"

Example: fs-file at /repo/src/main.py is reachable by:

  • write_file (bound to git-checkout ancestor → forward reach)
  • lsp_hover (bound to lsp-workspace → reaches equivalent lsp-document sibling)
  • docker_exec (bound to container-instance → reaches equivalent fs-file in container mount)
Access Projection

When a tool bound to ancestor resource R accesses descendant resource d, the access projection computes how d is identified within R's access space. Each resource type handler implements a project_access method that returns an AccessProjection:

  • access_path: The path in the binding resource's namespace (e.g., src/main.py for filesystem, file:///repo/src/main.py for LSP).
  • protocol: The access mechanism (filesystem, lsp-textdocument, container-exec, sql, etc.).
  • crosses_sandbox: Whether this projection crosses a sandbox boundary (important: an LSP server reading from the real filesystem crosses the sandbox and sees pre-sandbox content).
  • read_richness: A score indicating how much information this access path provides (LSP: 10, filesystem: 1). Used for read routing.
Read/Write Routing

When a virtual resource has multiple physical manifestations reachable through different tools, the system routes reads and writes through different paths:

For writes: Route to the canonical write target — the physical manifestation in the strongest sandbox domain (preference: git_worktree > snapshot > copy_on_write > transaction_rollback). This ensures writes are sandbox-tracked and checkpointable.

For reads: Route to the richest available source, ranked by read_richness:

Source Richness Provides
lsp-document 10 Type info, symbols, diagnostics, go-to-def, references, completions
Semantic index 5 Pre-computed symbol index, dependency graph
fs-file via git-checkout 1 Raw file content, git history
fs-file via container mount 1 Raw file content
git-tree-entry 1 File content at specific commit

The routing algorithm selects the highest-richness source that is available, current (coherence checks pass), and compatible with the query type.

Tool Registry

CleverAgents maintains a Tool Registry — a persistent catalog of all independently registered tools:

@startuml
skinparam classAttributeIconSize 0
skinparam classFontSize 13
skinparam noteFontSize 11
skinparam defaultFontSize 12

class ToolRegistry {
  - toolIndex : Map<String, ToolRecord>
  --
  + add(config_path) : ToolRecord
  + update(name, config_path) : ToolRecord
  + remove(name) : void
  + lookup(name) : ToolRecord
  + list(filters) : ToolRecord[]
}

class ToolRecord {
  + name : String
  + description : String
  + source : String {mcp|agent_skill|builtin|custom}
  + config_path : String
  + input_schema : JSONSchema
  + output_schema : JSONSchema
  + capability_metadata : CapabilityMetadata
  + resource_slots : List<ResourceSlot>
  + code : String
}

ToolRegistry "1" *-- "0..*" ToolRecord : indexes >

note right of ToolRegistry
  **Populated by:**
  - agents tool add CLI command
  - Dynamic refresh on MCP notifications

  **Consumed by:**
  - Skill registration
  - Actor graph construction
  - Resource binding resolution
  - Plan validation
end note
@enduml

The Tool Registry works alongside the Skill Registry (described in the Skills section). Skills reference tools by name from the Tool Registry; the Skill Registry's flattened tool sets are composed from Tool Registry entries plus any anonymous inline tools.

Tool Interface and Architecture

Each individual tool — whether independently registered or defined as an anonymous inline tool — conforms to a uniform interface regardless of its source:

@startuml
skinparam classAttributeIconSize 0
skinparam classFontSize 13
skinparam defaultFontSize 12
skinparam linetype ortho

class Tool {
}

class Identity {
  + name : String
  + qualified_name : String
  + source : ToolSource
}

class Schema {
  + input_schema : JSONSchema
  + output_schema : JSONSchema
}

class CapabilityMetadata {
  + read_only : Boolean
  + writes : Boolean
  + write_scope : String
  + idempotent : Boolean
  + checkpointable : Boolean
  + side_effects : List<String>
  + cost_profile : String
  + human_approval_required : Boolean
}

class ResourceBindings {
  + slots : Map<String, ResourceSlot>
}

class ResourceSlot {
  + type : String
  + access : String
  + required : Boolean
  + bind : String
  + from_param : String
  + description : String
}

interface Lifecycle <<interface>> {
  + discover() : ToolDescriptor
  + activate() : void
  + execute(params, ctx) : Result
  + deactivate() : void
}

class ExecutionContext {
  + sandbox : Sandbox
  + plan : Plan
  + changes : List<Change>
  + resources : Map<String, BoundResource>
}

enum ToolSource {
  mcp
  agent_skill
  builtin
  custom
}

Tool *-- Identity
Tool *-- Schema
Tool *-- CapabilityMetadata
Tool *-- ResourceBindings
Tool *-- Lifecycle
Tool o-- ExecutionContext : uses at runtime >
ResourceBindings *-- "0..*" ResourceSlot
Identity --> ToolSource
@enduml

Every tool implements the same four lifecycle methods. The tool adapter layer is responsible for translating source-specific behavior into these methods.

Tool Adapter Layer

!!! adr "Architecture Decision" The adapter pattern for tool sources and the uniform tool interface are defined in ADR-011: Tool System.

Each tool source has a corresponding adapter that translates source-specific protocols into the uniform tool interface:

@startuml
skinparam classAttributeIconSize 0
skinparam classFontSize 13
skinparam defaultFontSize 12

interface "ToolInterface" as UTI <<interface>> {
  + discover() : ToolDescriptor
  + activate() : void
  + execute(params, ctx) : Result
  + deactivate() : void
}

class MCPToolAdapter {
  + discover() : ToolDescriptor
  .. tools/list RPC → descriptors ..
  + activate() : void
  .. spawn server, init JSON-RPC ..
  + execute(params, ctx) : Result
  .. tools/call RPC → result ..
  + deactivate() : void
  .. shutdown server ..
}

class AgentSkillAdapter {
  + discover() : ToolDescriptor
  .. parse SKILL.md frontmatter ..
  + activate() : void
  .. load SKILL.md body into agent context ..
  + execute(params, ctx) : Result
  .. agent follows instructions, runs scripts ..
  + deactivate() : void
  .. remove from context ..
}

class BuiltinAdapter {
  + discover() : ToolDescriptor
  .. return hardcoded descriptors ..
  + activate() : void
  .. no-op ..
  + execute(params, ctx) : Result
  .. call native Python impl ..
  + deactivate() : void
  .. no-op ..
}

MCPToolAdapter .up.|> UTI
AgentSkillAdapter .up.|> UTI
BuiltinAdapter .up.|> UTI
@enduml
MCPToolAdapter

Bridges external MCP servers into the tool model:

  1. discover(): Spawns the MCP server process (or connects to a remote Streamable HTTP endpoint), performs the MCP initialize handshake, negotiates capabilities, then calls tools/list to enumerate available tools. Each MCP tool becomes a separate ToolDescriptor with its inputSchema and inferred capability metadata.

  2. activate(): Ensures the MCP server process is running and the JSON-RPC connection is healthy. For remote servers, validates the authentication token. Registers for notifications/tools/list_changed so CleverAgents can dynamically update available tools.

  3. execute(): Translates a Tool.execute(params, ctx) call into an MCP tools/call JSON-RPC request. Before dispatching:

    • Rewrites file paths to sandbox-relative paths
    • Validates params against inputSchema
    • Checks capability metadata against plan access policy
    • Creates a checkpoint if the tool is marked checkpointable

    After the MCP tool returns its content[] response, the adapter:

    • Error detection: If isError: true is present in the response, the error message is extracted from content[0].text per the MCP 1.4.0 protocol (the non-standard top-level error key is not used). Falls back to "unknown error" when content is absent or malformed.
    • Parses the result into the CleverAgents Result format
    • Records any resource mutations as Change objects in the plan's ChangeSet
  4. deactivate(): Sends a clean shutdown to the MCP server process and closes the JSON-RPC connection.

Capability inference: MCP tools expose limited metadata (name, description, inputSchema). The adapter infers extended capability metadata using heuristics:

  • Tools whose names contain read, get, list, search, findread_only: true
  • Tools whose names contain write, create, update, delete, setwrites: true
  • All inferences can be overridden via the overrides block in tool or skill YAML
AgentSkillAdapter

Bridges Agent Skills Standard (SKILL.md folders) into the tool model. Agent Skills are fundamentally different from MCP tools — they are instruction-driven rather than schema-driven. An Agent Skill is not a single function call; it is a bundle of procedural knowledge that an LLM agent loads into its context and follows.

  1. discover(): Scans the configured skill directory for a SKILL.md file. Parses only the YAML frontmatter (name, description, optional compatibility, metadata, allowed-tools) to produce a lightweight ToolDescriptor. This metadata is injected into the agent's system prompt in a structured format so the LLM can decide when the skill is relevant:

    
    <style="color: cyan; font-weight: 600;">available_agent_skills>
      <style="color: cyan; font-weight: 600;">agent_skill>
        <style="color: cyan; font-weight: 600;">name>deploy-to-staging</style="color: cyan; font-weight: 600;">name>
        <style="color: cyan; font-weight: 600;">description>Deploy the current branch to the staging environment.</style="color: cyan; font-weight: 600;">description>
        <style="color: cyan; font-weight: 600;">tool>local/deploy-staging</style="color: cyan; font-weight: 600;">tool>
      </style="color: cyan; font-weight: 600;">agent_skill>
    </style="color: cyan; font-weight: 600;">available_agent_skills>
    

    Discovery is low-cost — only ~50100 tokens per Agent Skill for metadata. The full instructions are not loaded until activation.

  2. activate(): When the LLM agent determines (or is instructed) that a task matches the skill's description, the adapter loads the full SKILL.md Markdown body into the agent's active context. This injects step-by-step instructions, examples, edge cases, and references to bundled scripts. The tool is now "active" — the agent has the procedural knowledge to execute it.

    If the skill references additional files (references/*.md, scripts/*.py, assets/*), these are made available on demand — the agent can read them as needed, following the Agent Skills Standard's progressive disclosure model.

  3. execute(): Unlike MCP tools (which are single RPC calls), Agent Skill execution is agent-mediated. The LLM agent follows the loaded instructions, potentially:

    • Running bundled scripts via shell execution (sandboxed)
    • Reading reference files for additional context
    • Using other available tools (e.g., built-in file operations) as part of the procedure
    • Making multiple tool calls in sequence to accomplish the workflow

    The adapter wraps this execution in a tool execution context so that all mutations are tracked, sandboxed, and checkpointable. Script execution respects the allowed_tools and sandbox_policy declared in the tool's YAML.

  4. deactivate(): Removes the skill's instructions from the agent's active context to free up token budget. The skill's metadata remains available for re-activation.

Key design principle: Agent Skills extend the agent's knowledge, not just its toolset. An Agent Skill can teach an agent a multi-step workflow that involves calling multiple other tools, making decisions based on intermediate results, and following domain-specific best practices — something a single MCP tool call cannot express.

BuiltinToolAdapter

Wraps CleverAgents' native resource operations as tools:

  1. discover(): Returns hardcoded ToolDescriptor objects for each built-in operation. These descriptors have fully specified capability metadata since the implementation is first-party.

  2. activate(): No-op. Built-in tools are always available.

  3. execute(): Calls the native Python implementation directly. Built-in tools operate through the resource abstraction layer, automatically integrating with sandbox path mapping, change tracking, and checkpointing.

  4. deactivate(): No-op.

Built-in tool groups:

File Operations (file_operations):


read_file(path: str) -> str
write_file(path: str, content: str) -> None
edit_file(path: str, edits: list[Edit]) -> None
delete_file(path: str) -> None
move_file(source: str, destination: str) -> None
copy_file(source: str, destination: str) -> None

Directory Operations (directory_operations):


create_directory(path: str) -> None
list_directory(path: str, pattern: str = "*") -> list[str]
delete_directory(path: str, recursive: bool = False) -> None

Search Operations (search_operations):


search_files(pattern: str, content_pattern: str = None) -> list[Match]
find_definition(symbol: str) -> list[Location]
find_references(symbol: str) -> list[Location]

Git Operations (git_operations, when resource is a git repository):


git_status() -> GitStatus
git_diff(path: str = None) -> str
git_log(count: int = 10) -> list[Commit]
git_blame(path: str) -> list[BlameLine]

Each built-in tool:

  • Has fully defined capability metadata
  • Operates through the resource abstraction layer
  • Automatically tracks changes to the ChangeSet
  • Respects sandbox boundaries and deny-lists

Tool Capability Metadata (Critical for Safety)

MCP's metadata is not sufficient (read-only/idempotent is not enough; write scope is unclear). CleverAgents extends every tool — regardless of source — with a uniform capability metadata schema:


capability:
  read_only: bool # Whether tool only performs read operations
  writes: bool # Whether tool can modify resources
  write_scope:             # What the tool is allowed to mutate
    - file_paths: ["src/**", "tests/**"]     # Path patterns within bound resources
    - resource_slots: ["repo", "db"]         # Resource slot names (from resource bindings)
    - environment:             # Execution environment compatibility
        required: container | host | any  # Where the tool CAN run (default: any)
        preferred: container | host        # Where the tool PREFERS to run (optional)
        specific: <resource-name>           # A specific container required (optional)
  idempotent: bool # Whether repeated calls produce same result
  checkpointable: bool # Whether tool supports checkpoint/rollback
  checkpoint_scope: str # What can be rolled back (file, transaction, commit, snapshot)
  side_effects:            # Non-reversible effects
    - install_packages
    - mutate_infra
    - send_email
  cost_profile:            # Usage constraints
    rate_limit: "10/min"
    estimated_cost: "$0.01/call"
  human_approval_required: bool # Whether a human must approve invocation

Where metadata comes from per source:

Source Metadata origin Override mechanism
Built-in Hardcoded in implementation Not overridable (authoritative)
MCP Inferred from tool name/description + MCP annotations overrides block in tool or skill YAML; override at skill/actor reference point
Agent Skill Declared in SKILL.md frontmatter metadata + inferred from allowed-tools Tool YAML capability block; override at skill/actor reference point
Custom Manually declared in tool YAML capability block override at skill/actor reference point (author is the source of truth for registered values)

Read-Only Actions and Tool Access Control

When an action is marked read_only: true, it can only use tools that have read_only: true in their capability metadata. This is enforced at runtime by the tool execution context — any attempt to invoke a tool with writes: true from a read-only plan raises a AccessDeniedError.

Tool Execution Flow

When an LLM agent decides to use a tool (regardless of source), the following flow occurs through the unified execution pipeline:


1. LLM generates tool call
   e.g., edit_file(path="src/main.py", changes=[...])
   or:   local/github.create_issue(title="Bug fix", body="...")
   or:   (activates Agent Skill "deploy-to-staging" via instructions)
                                ↓
2. Tool Router receives call
   - Resolves tool by name from the Tool Registry or actor's skill tool sets
   - Validates parameters against inputSchema
    - Checks capability metadata against plan's access policy:
     • Is this tool in allowed skill categories?
     • Does the plan allow writes?
     • Is human approval required?
   - If denied → return AccessDeniedError to LLM
                                ↓
3. Resource Binding Resolution & Sandbox Context
   - Resolve resource bindings for this tool:
     • Static bindings: already resolved at registration
     • Contextual bindings: resolve from plan's project resources
     • Parameter bindings: resolve from invocation arguments
   - Validate resource type compatibility for each slot
   - Validate access mode (e.g., read_write tool on read_only resource → error)
   - Ensure sandbox exists for each bound resource (lazy sandboxing)
   - Maps logical paths to sandbox-relative paths via bound resource handlers
   - Inject bound resources into ctx.resources[slot_name]
   - If tool is checkpointable → create pre-execution checkpoint
                                ↓
4. Adapter-Specific Execution
   - MCP: sends tools/call JSON-RPC to server process
   - Agent Skill: agent follows loaded SKILL.md instructions,
     running scripts and tools in sandboxed shell
   - Built-in: calls native Python implementation directly
   - Custom: executes inline code with sandboxed context
                                ↓
5. Change Recording
   - If tool modified resources → create Change record(s)
   - Append Change(s) to plan's ChangeSet
   - Update sandbox state
   - If checkpointable → record checkpoint for rollback
                                ↓
6. Result Return
   - Normalize result to uniform Result type
   - Return to LLM agent for continued reasoning

Change Tracking from Tool Invocations

Critical Architecture Point: The ChangeSet is NOT built by parsing LLM output. It is built by recording the effects of tool invocations:


class ToolExecutionContext:
    """Context provided to every tool execution, regardless of source."""
<span style="color: magenta; font-weight: 600;">def</span> <span style="color: cyan; font-weight: 600;">__init__</span>(self, plan: Plan, sandbox: Sandbox,
             resources: <span style="color: cyan;">dict</span>[<span style="color: cyan;">str</span>, BoundResource]):
    self.plan = plan
    self.sandbox = sandbox
    self.resources = resources    <span style="opacity: 0.7;"># slot_name → BoundResource</span>
    self.changes: <span style="color: cyan;">list</span>[Change] = []

<span style="color: magenta; font-weight: 600;">def</span> <span style="color: cyan; font-weight: 600;">record_change</span>(self, change: Change) -&gt; <span style="color: magenta; font-weight: 600;">None</span>:
    <span style="color: #66cc66;">&quot;&quot;&quot;Record a change made by a tool.&quot;&quot;&quot;</span>
    self.changes.append(change)
    self.plan.changeset.add_change(change)

class WriteFileTool: """Example: built-in tool for writing files."""

<span style="color: magenta; font-weight: 600;">def</span> <span style="color: cyan; font-weight: 600;">execute</span>(self, path: <span style="color: cyan;">str</span>, content: <span style="color: cyan;">str</span>, ctx: ToolExecutionContext) -&gt; <span style="color: magenta; font-weight: 600;">None</span>:
    handler = ctx.sandbox.get_handler(path)
    change = handler.write(path, content, ctx.sandbox)
    ctx.record_change(change)

This approach means:

  • Every resource modification is explicit and tracked
  • The ChangeSet accurately reflects what was done, not what was said
  • Rollback is precise (replay inverse of recorded changes)
  • Audit logs show exactly which tool invocation produced each change

MCP Integration Details

!!! adr "Architecture Decision" MCP adoption, tool-to-skill mapping, and actor-graph usage are defined in ADR-029: Model Context Protocol (MCP) Adoption.

CleverAgents integrates with the Model Context Protocol (MCP) to discover and invoke external tools, then normalizes them into the Tool Registry with extended capability metadata and sandbox-aware execution. MCP tools are composed into skills and appear as tool nodes in actor graphs, giving them the same lifecycle, change-tracking, and checkpoint semantics as built-in tools.

MCP Concepts Mapping
MCP Concept CleverAgents Equivalent Extension
Tool Independently registered Tool (source: mcp), referenced by skills Extended capability metadata (write_scope, checkpointable, side_effects)
Resource Resource Extended to support both read AND write operations
Prompt Action template Full plan lifecycle (Strategize → Execute → Apply)
Server MCP Server connection (declared in tool or skill YAML) Managed by MCPToolAdapter with lifecycle and reconnection
JSON-RPC Internal adapter protocol Abstracted behind Tool.execute()
MCP Server Lifecycle Management

CleverAgents manages MCP server processes as part of the tool/skill/actor lifecycle:

sequenceDiagram
    participant CLI as CLI
    participant Reg as Registry
    participant ActorRT as Actor Runtime
    participant Adapter as MCPToolAdapter
    participant MCP as MCP Server

    note over CLI,Reg: Phase 1 - Registration
    CLI->>Reg: agents tool add or skill add
    Reg->>Reg: Validate server command or endpoint
    Reg->>Reg: Store server config in record

    note over ActorRT,MCP: Phase 2 - Actor Activation
    ActorRT->>Adapter: Activate (for each MCP server)
    Adapter->>MCP: Spawn process (stdio) or connect (HTTP)
    Adapter->>MCP: MCP initialize handshake
    MCP-->>Adapter: Capabilities
    Adapter->>MCP: tools list
    MCP-->>Adapter: Tool descriptors
    Adapter->>MCP: Subscribe notifications tools list_changed

    note over ActorRT,MCP: Phase 3 - Execution
    ActorRT->>Adapter: LLM generates tool call
    Adapter->>MCP: tools call (JSON-RPC)
    MCP-->>Adapter: Result
    Adapter->>ActorRT: Result + Change tracking

    note over ActorRT,MCP: Phase 4 - Deactivation
    ActorRT->>Adapter: Deactivate
    Adapter->>MCP: Clean shutdown
Sandbox Path Rewriting for MCP Tools

MCP servers operate on real filesystem paths, but CleverAgents executes plans in sandboxes. The MCPToolAdapter transparently rewrites paths:


# Before sending to MCP server:
# Logical path: "src/main.py"
# Sandbox path: "/tmp/sandbox-01HXM/worktree/src/main.py"
# The adapter rewrites the tool arguments so the MCP server
# operates on sandboxed state without knowing about the sandbox.

This ensures MCP tools respect sandbox boundaries even though they have no awareness of the CleverAgents sandbox model.

Agent Skills Integration Details

!!! adr "Architecture Decision" The Agent Skills standard integration is defined in ADR-028: Agent Skills Standard (AgentSkills.io).

Agent Skills follow the Agent Skills standard from https://AgentSkills.io, which defines the SKILL.md structure and the progressive disclosure model used by the runtime. Within actor graphs, Agent Skills appear as tool nodes — the actor runtime loads the skill's instructions into the LLM context when the node activates, enabling the agent to follow multi-step procedures that may invoke MCP tools, built-in tools, or other Agent Skills during execution.

Discovery and Progressive Disclosure

Agent Skills follow a three-tier progressive disclosure model that maps directly to the tool lifecycle:

Tier What loads When Token cost
Metadata name + description from SKILL.md frontmatter Tool registration / actor activation ~50100 tokens per Agent Skill
Instructions Full SKILL.md Markdown body When LLM determines task matches the skill (activate phase) Recommended < 5000 tokens
Resources scripts/, references/, assets/ On demand during execution Variable

This means an actor can have dozens of Agent Skill tools available but only pay the token cost for their metadata at startup. Full instructions load only when relevant.

Agent Skills vs. MCP Tools: When to Use Which
Dimension MCP Tools Agent Skills
Interaction model Single function call with JSON params/result Multi-step procedure with instructions the agent follows
Knowledge type "Here is a function you can call" "Here is how to accomplish a complex task"
Statefulness Stateless per-call Stateful across multiple tool calls within a procedure
Authoring Implement an MCP server (code) Write a SKILL.md file (prose + optional scripts)
Portability Any MCP-compatible host Any Agent Skills-compatible agent
Best for Atomic operations (CRUD, queries, API calls) Complex workflows (code review processes, deployment procedures, data analysis pipelines)

Both can coexist in the same skill. A common pattern is an Agent Skill tool that teaches the agent a workflow which involves calling multiple MCP tools:


Agent Skill Tool: "local/deploy-staging"
  SKILL.md instructions:
    1. Run tests using the built-in shell tool
    2. Create a PR using the GitHub MCP tool (create_pull_request)
    3. Wait for CI using the GitHub MCP tool (get_check_runs)
    4. Deploy using the AWS MCP tool (ecs_update_service)
    5. Verify deployment using the HTTP MCP tool (fetch_url)

Tool-Level Checkpointability

Each tool declares whether it supports checkpoint/rollback via its capability metadata. Checkpointing behavior varies by source:

Source Checkpoint mechanism
Built-in file ops Snapshot file state pre-modification; rollback restores file content
Built-in git ops Create commit or stash; rollback via git reset / git checkout
MCP tools Adapter-created checkpoints of affected resources; rollback replays inverse operations where possible
Agent Skills Composite — each sub-tool-call within the skill's execution is individually checkpointed
Custom (containerized) Filesystem snapshot or container image layer; rollback restores snapshot

Checkpointing is easier when tool scope is constrained (e.g., "only files within a sandbox worktree + git"). The capability metadata's checkpoint_scope field communicates what granularity of rollback is supported.

When require_checkpoints is enabled on a plan, the plan may only use tools that have checkpointable: true. If disabled, the plan may use more generic/unsafe tools with fewer restrictions (useful for low-stakes tasks).

Validation

!!! adr "Architecture Decision" The validation system, gate types, and validation lifecycle are defined in ADR-013: Validation Abstraction.

What a Validation Is

!!! abstract "Validation in Brief" A Validation is a specialized subtype of Tool designed to verify that work performed during plan execution meets specified quality, correctness, or compliance criteria. A Validation observes the current state of resources, evaluates a condition, and returns a structured ==pass/fail result==. It never modifies resources — it only reads and reports.

Because Validation extends Tool via standard class inheritance, it is not a separate concept bolted onto the system — it is a tool, with additional constraints and metadata. This means validation benefits from the entire tool infrastructure: source types, resource bindings, lifecycle hooks, input/output schemas, registry management, skill composition, and actor graph integration. No parallel system is required.

!!! success "Safety Guarantee" Validations are ==always safe to run==. They cannot cause side effects, corrupt state, or require rollback. This enables aggressive parallelization, speculative validation, and retry without concern.

Relationship to Tool

A Validation inherits all base Tool properties:

Inherited from Tool Additional in Validation
name (namespaced) mode (required or informational)
description Structured return format enforcement
source (custom, mcp, agent_skill, builtin, wrapped) Read-only enforcement (writes: false, checkpointable: false)
input_schema (JSON Schema) Attachment scoping (resource-centric, with optional project/plan scope)
output_schema (JSON Schema) Attachment ULID (system-assigned per attachment)
capability metadata wraps (optional reference to an existing Tool)
resource_slots (resource bindings) transform (Python function to convert wrapped Tool output to validation format)
lifecycle hooks (discover/activate/execute/deactivate)
code (inline implementation)
mcp_server / mcp_tool_name (MCP source)
timeout
idempotent

Shared namespace: Validations and plain Tools share the same naming namespace in the Tool Registry. A name like local/run-tests can refer to either a Tool or a Validation — but never both simultaneously. Attempting to register a Validation with a name already taken by a plain Tool (or vice versa) results in a conflict error. This shared namespace enables unified management: agents tool list shows both types, agents tool show works for both, and agents tool remove removes either type.

Superset/subset semantics: Because Validation extends Tool, a Validation can be used anywhere a Tool is expected — it can appear in a skill's tool list, serve as a tool node in an actor graph, or be invoked directly by an LLM agent. However, a plain Tool cannot be used where a Validation is specifically required. For example, agents validation attach rejects a name that resolves to a plain Tool rather than a Validation. This distinction is enforced by the type discriminator stored in the Tool Registry: each entry is tagged as either tool or validation.

Read-Only Enforcement

!!! danger "Hard Constraint — Not a Convention" Validations are always read-only:

| Property | Value | Reason |
| :------- | :---: | :----- |
| `writes` | `false` | Must never modify, create, or delete resources |
| `checkpointable` | `false` | No state changes → nothing to checkpoint |
| `read_only` | `true` | Implicit inverse of `writes` |

This constraint is enforced at ==multiple levels==:

  1. Registration time: The agents validation add command does not accept --writes/--no-writes or --checkpointable/--no-checkpointable flags. Any such values in the validation YAML are silently overridden to false.
  2. Runtime enforcement: The execution runtime treats validation invocations as read-only operations. Resource bindings are resolved with access: read_only semantics. If a validation's implementation attempts to write, the sandbox enforces the constraint and the invocation fails.
  3. Sandbox interaction: When a validation executes within a plan's sandbox, it reads the sandbox state but cannot modify it — verifying work-in-progress without risk of corruption.

The read-only guarantee has an important architectural consequence: validations are always safe to run. They cannot cause side effects, corrupt state, or require rollback. This enables aggressive parallelization, speculative validation (running validations before all work is complete to provide early feedback), and retry without concern for accumulated state changes.

Validation Mode

Each Validation has a mode that determines how the system responds to failure:

=== "Required (mode: required)"

!!! danger "Hard Gate"
    Required validations ==must pass== for execution to proceed to the Apply phase. They define the "definition of done" for a plan.

- When a required validation fails, the execution actor attempts to **fix the underlying issue** (e.g., fix failing tests, correct lint errors) within the bounds of the current strategy, then re-runs the validation.
- This fix-then-revalidate loop continues up to the configured retry limit.
- If self-fix is exhausted, the actor may request a strategy revision or escalate to the user, depending on the automation profile.

=== "Informational (mode: informational)"

!!! info "Advisory Check"
    Informational validations are ==recorded but do not block== execution. Useful for advisory checks where failure is noteworthy but not blocking.

- When an informational validation fails, the result (including `message` and `data`) is included in the plan's validation summary for human review, but the plan continues normally.
- Use cases: bundle size reports, code complexity metrics, deprecation warnings, performance benchmarks.

??? tip "Gradual Promotion Pattern" The distinction between required and informational allows teams to introduce new validations gradually — start as ==informational== to measure impact, then promote to ==required== once the codebase is clean.

The mode is set at registration time (in the validation YAML or via --required/--informational on agents validation add) and applies globally to that validation. The mode cannot be overridden per-attachment — if different scopes need different enforcement levels for the same check, register separate validations with different names and modes.

Structured Return Format

Every Validation must return a JSON object conforming to this structure:

{
  "passed": true,
  "message": "All 247 tests passed, 94% coverage",
  "data": {
    "tests_run": 247,
    "tests_passed": 247,
    "tests_failed": 0,
    "coverage_percent": 94.2,
    "coverage_threshold": 80,
    "duration_seconds": 12.4
  }
}
Field Type Required Description
passed boolean Yes Whether the validation passed (true) or failed (false). This is the only field that drives system behavior (gating for required, recording for informational).
message string No Human-readable summary of the result. Displayed in CLI output, plan summaries, and used by the execution actor to understand failures. Should be concise but actionable (e.g., "3 lint errors in src/api/handler.py" rather than just "failed").
data object No Arbitrary structured data in any format the validation chooses. This is the validation's primary output channel for rich, machine-readable results. Examples: test coverage reports, lint error lists with file/line/column, security vulnerability details, performance measurement data. The data field has no required schema — each validation defines its own structure.

Return format enforcement: If a validation returns output that is not valid JSON, or valid JSON that lacks the passed boolean field, the system treats the invocation as an error (distinct from a validation failure). The validation result is recorded as "passed": false with a system-generated message indicating the malformed return, and the original output is preserved in data.raw_output for debugging.

The structured return format serves multiple consumers:

  • The execution actor reads message and data to understand failures and attempt fixes. A well-structured data field (e.g., with file paths and line numbers) enables more targeted fix attempts.
  • The plan summary records all validation results for human review and auditing.
  • Downstream automation (CI scripts, dashboards) can parse data programmatically via agents --format json plan show.

Attachment Scoping

Validations are not globally active — they must be explicitly attached to one or more scopes to take effect. This attachment model provides fine-grained control over which validations run for which work.

Attachment Model

A validation is always attached to a resource. The optional --project or --plan flag controls the scope under which the validation is active for that resource:

  1. Direct attachment (agents validation attach <RESOURCE> <VALIDATION> [args...])

    The validation is always active when that resource is accessed by any plan, regardless of which project or plan is involved. This scope is for invariants that must hold for a resource in all contexts — e.g., a lint check that must pass for a repository no matter what project is using it.

    Use cases:

    • Repository-wide lint/format checks
    • Schema validation on database resources
    • Security scanning on any codebase resource
    • License compliance checks
  2. Project-scoped attachment (agents validation attach --project <PROJECT> <RESOURCE> <VALIDATION> [args...])

    The validation is active only for that resource when it is being interacted with through the specified project. Different projects using the same resource can have different validation requirements. This is the most common attachment scope.

    Use cases:

    • Unit test suites specific to a project
    • Type checking for a TypeScript project (not relevant for a Python project using the same repo)
    • API compatibility checks for a microservice project
    • Bundle size checks for a frontend project
  3. Plan-scoped attachment (agents validation attach --plan <PLAN_ID> <RESOURCE> <VALIDATION> [args...])

    The validation is active only for that resource when it is being interacted with through the specified plan. This is the most targeted scope, useful for one-off or experimental validations.

    Use cases:

    • Temporary extra checks during a risky migration
    • One-time regression validation for a specific bug fix
    • Experimental validations being tested before promoting to project scope
    • User-added ad hoc checks during an interactive session

Since validations accept arguments, the same validation can be attached to the same resource multiple times with different arguments. For example, local/run-tests might be attached to local/api-repo through project local/api-service with --coverage-threshold 90 and also through project local/staging with --coverage-threshold 70.

Attachment Resolution

When a plan executes, the system collects all applicable validations for each resource the plan accesses. The resolution algorithm:

  1. Collect direct validations: For every resource the plan accesses, collect all validations directly attached to that resource (no scope flag).
  2. Collect project-scoped validations: For every resource the plan accesses, collect all validations attached to that resource with a --project scope matching the plan's target project.
  3. Collect plan-scoped validations: For every resource the plan accesses, collect all validations attached to that resource with a --plan scope matching the plan itself.
  4. Union: Take the union of all collected validations per resource. If the same validation name appears from multiple scopes for the same resource (e.g., attached both directly and through a project), each attachment is treated as a distinct invocation — they may have different arguments and thus produce different results. When the same validation name appears from multiple scopes with identical arguments, the most specific scope wins (plan > project > direct) and the validation runs once.
  5. Child plan inheritance: Child plans (spawned via subplan_spawn or subplan_parallel_spawn) inherit their parent plan's project-scoped and plan-scoped resource/validation associations by default. Direct validations are collected independently based on which resources the child plan accesses (which may differ from the parent).
Attachment Identity

Each attachment is identified by a system-assigned ULID. This ULID is:

  • Returned by agents validation attach when the attachment is created.
  • Displayed by agents tool show <VALIDATION> in the "Attached To" section and by agents project show in the validations list.
  • Required by agents validation detach <ATTACHMENT_ID> to remove a specific attachment.

The ULID is necessary because the same validation can be attached to the same resource multiple times — directly, through different projects, through different plans, and even to the same resource and scope with different arguments. The ULID uniquely identifies which specific attachment to remove.

Attachment Arguments

When attaching a validation, optional arguments can be provided after the validation name:


$ agents validation attach --project local/api-service local/api-repo local/run-tests --coverage-threshold 90
$ agents validation attach --project local/staging-api local/api-repo local/run-tests --coverage-threshold 70

These arguments are stored with the attachment and passed to the validation tool's input_schema at execution time. This enables a single validation definition to be reused across scopes with different thresholds or configurations. If no arguments are provided, the validation uses its input_schema defaults.

Validation Lifecycle in Plan Execution

Validation participates in the plan lifecycle at well-defined points:

During Strategize

Validations are not executed during Strategize. However, the strategy actor is aware of which validations are attached (via the attachment resolution described above) and factors them into the strategy:

  • The strategy may allocate time/tokens for validation fix-up loops.
  • The strategy may note that certain validations are informational and thus non-blocking.
  • The decision tree may include validation_checkpoint decisions that mark points where validation should be run.
During Execute

Validation is the final step of the Execute phase. The execution actor's workflow proceeds as follows:

  1. Perform work: The actor executes the strategy — writing code, modifying resources, running tools — all within the sandbox.
  2. Collect applicable validations: The system resolves all validations from the resource-direct, project, and plan scopes (as described in Attachment Resolution above).
  3. Execute validations: Each applicable validation is invoked as a standard tool call. The validation reads the sandbox state and returns its structured result. Validations may be run in parallel since they are read-only and cannot interfere with each other.
  4. Process results:
    • All required validations pass: Execution is complete. The plan transitions to the review/Apply phase.
    • Any required validation fails: The execution actor enters the fix-then-revalidate loop (see Validation Failure Handling below).
    • Informational validations fail: Results are recorded in the plan's validation summary. No fix attempts are made.
Validation is Not Run During Apply

!!! warning "Apply is the Point of No Return" Validation runs during Execute, not during Apply. By the time a plan reaches Apply, all required validations have already passed. Apply commits the sandbox changes to the real resources.

If post-apply verification is needed (e.g., integration tests against the real system), implement it as a separate plan or external CI pipeline — ==not as a validation attachment==.

**Rationale**: Introducing validation during Apply would create situations where committed changes might need to be rolled back, defeating the purpose of the sandbox model.

Validation Failure Handling

Because validations are tools, their execution follows the standard tool invocation flow. Each validation returns a structured JSON result with { "passed": true/false, "message": "...", "data": {...} }. The execution actor interprets these results based on the validation's mode.

Required Validation Failure

When a required validation returns "passed": false during Execute:

  1. Diagnosis: The execution actor examines the validation's message and data fields to understand the nature of the failure. A well-structured data field (e.g., with specific file paths, line numbers, error messages) enables more targeted fix attempts.

  2. Self-fix attempt: The execution actor attempts to fix the issue within the bounds of the current strategy. For example:

    • A failing test: the actor reads the test output, identifies the broken assertion, and fixes the code.
    • A lint error: the actor reads the lint report and corrects the formatting or style violation.
    • A type error: the actor reads the type checker output and fixes the type mismatch.
  3. Re-validation: After each fix attempt, the actor re-invokes the failing validation. If it passes, the loop ends. If it still fails, the actor examines the new message and data for the updated failure state.

  4. Retry limit: The fix-then-revalidate loop runs up to the configured retry limit (default: 3 attempts, configurable per plan or in the automation profile). After the limit is reached, self-fix stops.

  5. Strategy revision: If the actor determines that the failure cannot be resolved within the current strategy's constraints (e.g., the strategy says "modify only handler.py" but the test failure requires changes to model.py), it may request a strategy revision. This triggers a re-run of the Strategize phase for the affected subtree of the decision tree. Whether this happens automatically or requires user approval depends on the automation profile's delete_content flag.

  6. Escalation: If strategy revision also fails, or if the automation profile requires human approval for strategy changes, the plan pauses and requests user guidance via agents plan prompt:

    
    agents plan prompt <plan_id> "Try using mock objects for the database tests"
    
  7. Terminal failure: If the user does not intervene (or explicitly cancels), the plan fails with state: failed. The sandbox is preserved for inspection.

The flow can be summarized as:


validatefailfixre-validatefailfixre-validate → ... → retry limitrequest strategy revisionre-strategizere-executevalidatestill failingescalate to useruser provides guidanceresumestill failingplan fails
Informational Validation Failure

When an informational validation returns "passed": false:

  1. The result (including message and data) is recorded in the plan's validation_summary.
  2. Execution continues normally — no fix attempts, no blocking, no escalation.
  3. The informational failure is visible in agents plan show, plan summaries, and any rendering format.
  4. Informational failures may trigger notifications (if configured) but never block plan progression.
Validation Error vs. Validation Failure

!!! note "Important Distinction" | Condition | Meaning | Handling | | :-------- | :------ | :------- | | Validation failure (passed: false) | Validation ran successfully, condition not met | Mode-specific (fix loop for required, record for informational) | | Validation error | Validation itself failed to execute (runtime exception, timeout, malformed return) | ==Always treated as required failure== regardless of mode |

When a validation error occurs:

  • The result is recorded with passed: false, a system-generated message describing the error, and the raw error details in data.
  • If the validation is required, the fix-then-revalidate loop attempts to resolve the error (e.g., by fixing a broken test configuration).
  • If the validation is informational, the error is still recorded but does not block.
  • Repeated validation errors (e.g., the validation tool itself is misconfigured) are surfaced prominently in the plan summary.

Validation in Actor Graphs

Because Validations are Tools, they can appear as tool nodes in actor graphs. This enables explicit, deterministic validation steps within workflows:


# Actor graph with explicit validation nodes
graph:
  nodes:
    - name: implement
      type: agent
      actor: local/code-writer
    - name: run_tests
      type: tool
      tool: local/run-tests       # This is a Validation (subtype of Tool)
    - name: lint_check
      type: tool
      tool: local/lint-check      # Also a Validation
    - name: fix_issues
      type: agent
      actor: local/code-fixer
  edges:
    - [implement, run_tests]
    - [implement, lint_check]
    - condition: "not run_tests.passed or not lint_check.passed"
      from: [run_tests, lint_check]
      to: fix_issues
    - [fix_issues, run_tests]    # Retry loop
    - [fix_issues, lint_check]

When a Validation appears as a tool node, its return value is available to downstream nodes via the standard graph data flow. The passed, message, and data fields can be used in edge conditions, passed as input to subsequent nodes, or logged for the plan summary.

This graph-based approach complements the attachment model: attached validations run automatically at the end of Execute (the system handles collection and invocation), while graph-embedded validations run at explicitly defined points in the workflow (the actor author controls placement). Both approaches can coexist — a plan may have attached validations that run at the end plus graph-embedded validations that run at intermediate checkpoints.

Validation and Skills

Validations can be included in skills just like any other tool. Since a Validation is a Tool, a skill YAML can reference it by name:


# Skill: local/python-quality
skill:
  name: local/python-quality
  description: "Python code quality tools and validations"
  tools:
    - local/run-tests         # Validation (required)
    - local/lint-check        # Validation (required)
    - local/type-check        # Validation (required)
    - local/format-code       # Plain Tool (writes)
    - local/run-benchmarks    # Validation (informational)

When an actor references this skill, the Validations are available as tools that the LLM agent can call. This is distinct from validation attachment — being included in a skill makes the validation callable by the actor's LLM, but does not make it a required gate. Attachment determines gating; skill inclusion determines availability.

A common pattern: a team's skill includes their validations for on-demand use by the LLM (the actor can run tests proactively during development), while the same validations are also attached to the project (ensuring they run as a mandatory gate at the end of Execute even if the LLM forgot to run them).

Tool Wrapping

A Validation can wrap an existing Tool, reusing its implementation without duplicating code. This is the recommended approach when a plain Tool already performs the work a Validation needs (e.g., running tests, generating a report, scanning for vulnerabilities) and the Validation only needs to interpret the Tool's output as pass/fail.

The Problem

Consider a team that already has a registered Tool called local/run-tests — it runs the test suite and returns a structured report (test counts, coverage data, output logs). Now they want a Validation that gates execution on all tests passing. Without wrapping, they would need to duplicate the entire test-running implementation inside a new Validation YAML, differing only in the final pass/fail interpretation. This duplication creates maintenance burden: if the test runner configuration changes, both the Tool and the Validation must be updated.

The Solution: wraps + transform

A Validation YAML can declare a wraps field that references an existing registered Tool by name. At execution time, the system invokes the wrapped Tool, captures its output, and passes that output through a transform function that produces the Validation's structured return format ({ "passed", "message", "data" }).


# validations/tests-pass.yaml
# Wraps the existing local/run-tests tool — no test logic duplicated

name: local/tests-pass description: "Validate that all unit tests pass (wraps local/run-tests)"

wraps: local/run-tests

transform: | def transform(tool_output): passed = tool_output.get("returncode") == 0 return { "passed": passed, "message": "All tests passed" if passed else f"Tests failed (exit {tool_output.get('returncode')})", "data": tool_output }

validation: mode: required

timeout: 600

When local/tests-pass is invoked (either by attachment or direct call):

  1. The system resolves wraps: local/run-tests to the registered Tool.
  2. The Validation's input arguments are mapped to the wrapped Tool's input arguments using the argument_mapping (see below). If no argument_mapping is defined, the Validation's input arguments are passed through to the wrapped Tool as-is.
  3. The wrapped Tool is invoked with the mapped arguments.
  4. The wrapped Tool's output is captured (whatever it returns — JSON, text, etc.).
  5. The transform function is called with the captured output.
  6. The transform function returns the Validation's structured result ({ "passed", "message", "data" }).

This is opaque to the consumer — the caller sees only a standard Validation with its structured return format. The wrapping is an implementation detail.

What Gets Inherited

When a Validation uses wraps, the following properties are inherited from the wrapped Tool unless explicitly overridden in the Validation YAML:

Property Inherited? Override behavior
input_schema Yes If the Validation YAML defines input_schema, it replaces the wrapped Tool's schema entirely. If omitted, the Validation accepts the same inputs as the wrapped Tool. When a custom input_schema is defined, an argument_mapping should also be provided to map the Validation's inputs to the wrapped Tool's expected inputs.
argument_mapping No Not inherited. When present, maps the Validation's input arguments to the wrapped Tool's input arguments. When absent, input arguments are passed through unchanged. See the Argument Mapping section.
resource_slots Yes If the Validation YAML defines resource_slots, they replace the wrapped Tool's slots. If omitted, the Validation inherits the same resource bindings.
timeout Yes If the Validation YAML specifies timeout, it overrides. If omitted, the wrapped Tool's timeout is used.
description No The Validation must provide its own description (it serves a different purpose than the wrapped Tool).
source No Ignored — the source is implicitly wrapped. The Validation does not declare source or code.
capability No Overridden — writes and checkpointable are forced to false regardless of the wrapped Tool's values.
The transform Function

The transform field contains a Python function that converts the wrapped Tool's output to the Validation's return format. The function signature is:


def transform(tool_output) -> dict:
    """
    Args:
        tool_output: The wrapped Tool's return value. The type depends on the
                     Tool's implementation — typically a dict (for JSON-returning
                     tools) but may be a string or other type.
    Returns:
        A dict with at minimum {"passed": bool}. May also include
        "message" (str) and "data" (any).
    """

The transform function runs in a sandboxed Python environment with no write access to the filesystem or network. It is a pure data transformation — it receives the Tool's output and produces the Validation's result. If the transform function raises an exception, the Validation is treated as an error (see Validation Error vs. Validation Failure).

If transform is omitted, the system applies a default transform that expects the wrapped Tool's output to already contain a passed field:

  • If the output is a dict with a passed boolean, it is used as-is (pass-through).
  • If the output is a dict without passed, the validation errors with a message indicating the wrapped tool's output is not in validation format and a transform function is required.
  • If the output is not a dict, the validation errors similarly.
Argument Mapping (argument_mapping)

When a Validation wraps a Tool, the Validation may accept different input arguments than the wrapped Tool. The argument_mapping field specifies how the Validation's input arguments map to the wrapped Tool's expected input arguments. This is necessary when:

  • The Validation's input_schema defines different field names than the wrapped Tool's input_schema.
  • The Validation accepts a subset of the wrapped Tool's arguments and wants to provide fixed values for the rest.
  • The Validation renames arguments for clarity in the validation context.

The argument_mapping is a dictionary where keys are the wrapped Tool's input parameter names and values are either:

  • A string referencing a Validation input parameter name (forwarded as-is).
  • A fixed literal value (string, number, boolean) that is always passed to the wrapped Tool regardless of the Validation's inputs.

# Example: Validation wraps local/run-tests but renames arguments
name: local/tests-pass
wraps: local/run-tests

argument_mapping: test_directory: source_dir # Forward Validation's "source_dir" → Tool's "test_directory" coverage_enabled: true # Always pass true for coverage_enabled verbose: false # Always pass false for verbose

transform: | def transform(tool_output): return {"passed": tool_output.get("returncode") == 0, "message": "Tests completed"}

validation: mode: required

When argument_mapping is omitted, the Validation's input arguments are passed through to the wrapped Tool unchanged. This is the common case when the Validation inherits the wrapped Tool's input_schema without modification (i.e., the Validation YAML does not define its own input_schema).

When argument_mapping is present, only the mapped arguments are forwarded to the wrapped Tool. Any Validation input arguments not referenced in the mapping are not passed to the wrapped Tool. Any wrapped Tool arguments not listed as keys in the mapping receive no value (and must either be optional in the Tool's schema or have defaults).

Read-Only Semantics for Wrapped Tools

Validations are always read-only, but the wrapped Tool may not be. The wrapping enforces read-only semantics:

  • The wrapped Tool is invoked within the Validation's read-only execution context. Resource bindings are resolved with access: read_only regardless of what the wrapped Tool's resource_slots specify.
  • If the wrapped Tool's implementation attempts to write (e.g., mutate files, insert database rows), the sandbox enforces the read-only constraint and the invocation fails as a validation error.
  • At registration time, if the wrapped Tool has writes: true, the system emits a warning (not an error) advising the user that the wrapped Tool is marked as writing and may fail at runtime under read-only enforcement. This allows wrapping tools that are conservatively marked writes: true but whose actual behavior for the given inputs is read-only.

This design means wrapping is always safe from the Validation perspective — the worst case is a runtime error, never an unintended write.

Multiple Validations Wrapping the Same Tool

The same Tool can be wrapped by multiple Validations with different transform functions and different modes. This is a common pattern:


# The base tool runs tests and produces a comprehensive report
# Tool: local/run-tests (already registered)

# Validation 1: All tests must pass (required) # validations/tests-pass.yaml name: local/tests-pass description: "All unit tests must pass" wraps: local/run-tests transform: | def transform(tool_output): return { "passed": tool_output.get("returncode") == 0, "message": f"{tool_output.get('tests_passed', 0)}/{tool_output.get('tests_run', 0)} tests passed", "data": tool_output } validation: mode: required


# Validation 2: Coverage must exceed threshold (informational for now)
# validations/coverage-check.yaml
name: local/coverage-check
description: "Coverage must exceed threshold (advisory)"
wraps: local/run-tests
transform: |
  def transform(tool_output):
      coverage = tool_output.get("coverage_percent", 0)
      threshold = 80
      return {
          "passed": coverage >= threshold,
          "message": f"Coverage: {coverage}% (threshold: {threshold}%)",
          "data": {"coverage_percent": coverage, "threshold": threshold}
      }
validation:
  mode: informational

Both validations wrap the same local/run-tests tool but extract different signals. When both are attached to the same project, each Validation independently invokes the wrapped Tool and applies its own transform function. A future optimization may deduplicate these invocations when the forwarded arguments are identical (see Validation Ordering and Parallelism).

When to Wrap vs. When to Write From Scratch
Scenario Approach
An existing Tool already does the work; you just need pass/fail Wrap it — use wraps + transform
Multiple Validations need different interpretations of the same Tool's output Wrap it multiple times with different transforms
The validation logic is simple and self-contained (e.g., run a shell command, check exit code) Write from scratch — use source: custom with inline code
The validation needs logic that the existing Tool doesn't provide (different inputs, different execution) Write from scratch — the Tool's implementation isn't relevant
The existing Tool has side effects that are incompatible with read-only enforcement Write from scratch — wrapping would fail at runtime

Validation Data Model

The validation-related fields stored in the plan data model:

Field Location Description
validation_summary plan.execution Array of validation results collected during Execute. Each entry includes the validation name, mode, passed, message, data, execution duration, and attempt number.
final_validation_results plan.apply Snapshot of the final validation state at the time execution completed (all required validations passing). Identical to the last state of validation_summary but stored separately for quick reference.
validation_attempts plan.execution Total number of validation invocations across all fix-then-revalidate loops. Useful for understanding how much effort was spent on validation remediation.
validation_fix_history plan.execution Per-validation log of fix attempts. Each entry records: the validation that failed, the fix the actor applied, and whether the subsequent re-validation passed. Enables auditing of the fix-then-revalidate process.

A validation result entry in validation_summary:


{
  "validation": "local/run-tests",
  "mode": "required",
  "passed": true,
  "message": "All 247 tests passed, 94% coverage",
  "data": {
    "tests_run": 247,
    "tests_passed": 247,
    "coverage_percent": 94.2
  },
  "duration_ms": 12400,
  "attempt": 2,
  "attachment_id": "01HXM5A1B2C3D4E5F6G7H8J9K0",
  "attachment_resource": "local/api-repo",
  "attachment_scope": "project",
  "attachment_scope_target": "local/api-service"
}

Validation Ordering and Parallelism

Since validations are read-only, they are inherently safe to run in parallel. The execution runtime may parallelize validation invocations subject to:

  • Resource constraints: Validations that require significant compute (e.g., full test suites) may be serialized to avoid resource exhaustion.
  • Timeout budgets: Each validation has its own timeout (from the tool configuration). The total validation phase has an implicit budget derived from the sum of individual timeouts, but the runtime may use parallelism to finish faster.
  • Dependency hints: A future extension may allow declaring ordering dependencies between validations (e.g., "run lint before tests" to fail fast on formatting issues). Currently, all validations run independently.

Wrapped Tool deduplication (deferred): When multiple Validations wrap the same Tool (via wraps), a future optimization could invoke the wrapped Tool once and fan out its output to each Validation's transform function. However, deduplication is only valid when multiple Validations wrap the same Tool and receive the exact same forwarded arguments (after argument_mapping resolution) and are evaluated in the same calling chain. This complexity — particularly around argument equivalence and cache invalidation during fix-then-revalidate loops — means deduplication is deferred for future design. The current implementation invokes the wrapped Tool separately for each Validation that wraps it.

The recommended execution strategy is:

  1. Run all validations in parallel (each Validation independently invokes its wrapped Tool if applicable).
  2. Collect results.
  3. If any required validation fails, fix and re-run only the failing validations (not all of them).
  4. Repeat until all pass or retry limit is reached.

Registering and Managing Validations

Validations are registered via agents validation add and attached via agents validation attach. These are the only validation-specific CLI commands. All other management operations use the standard agents tool commands:

Operation Command Notes
Register agents validation add --config <FILE> Validation-specific. Config file defines all properties including mode (required/informational).
Update agents validation add --config <FILE> --update Uses the --update flag to overwrite existing registration.
Attach agents validation attach [--project|--plan] <RESOURCE> <VALIDATION> [args...] Returns an attachment ULID. Resource is always required; project/plan scope is optional.
Detach agents validation detach [--yes] <ATTACHMENT_ID> Uses the attachment ULID, not the validation name.
List agents tool list --type validation Shared with tools. Use --type validation to filter.
Show agents tool show <NAME> Shows validation-specific fields when the entry is a Validation.
Remove agents tool remove <NAME> Shared with tools. Automatically detaches from all scopes.

See the agents validation and agents tool CLI Reference sections for full command details and examples.

Typical Validation Workflow

A typical workflow for setting up validations on a project:


# 1. Define validation YAML files
# (see Configuration > Validation Configuration Files for schema and examples)

# 2. Register validations agents validation add --config ./validations/run-tests.yaml agents validation add --config ./validations/lint-check.yaml agents validation add --config ./validations/type-check.yaml agents validation add --config ./validations/check-bundle-size.yaml

# 3. Attach to resource through a project (active only when this resource is accessed through this project) agents validation attach --project local/api-service local/api-repo local/run-tests agents validation attach --project local/api-service local/api-repo local/type-check agents validation attach --project local/api-service local/api-repo local/check-bundle-size

# 4. Attach directly to a resource (always active for any plan accessing this resource) agents validation attach local/api-repo local/lint-check

# 5. Verify setup agents tool list --type validation --namespace local agents tool show local/run-tests

# 6. Run a plan — validations execute automatically at end of Execute phase agents plan use local/implement-feature local/api-service

Design Rationale

Why Validation extends Tool rather than being a separate concept:

  • Reuses the entire tool infrastructure (registry, bindings, lifecycle, sources, schemas) without duplication.
  • Validations can be composed into skills, used in actor graphs, and invoked by LLM agents — all for free.
  • A single namespace and registry avoids the complexity of managing two parallel systems.
  • Tool-aware features (resource bindings, MCP integration, Agent Skills) apply to validations automatically.

Why read-only is enforced rather than advisory:

  • Validations that modify state defeat the purpose of the sandbox model.
  • Read-only guarantees enable safe parallelization and retry without side effects.
  • It makes validations categorically safe — no risk analysis needed for running a validation.

Why attachments use ULIDs rather than name+scope pairs:

  • A validation may be attached to the same scope multiple times with different arguments.
  • ULIDs provide an unambiguous handle for removal without complex composite keys.
  • Consistent with the ULID-based identity model used for plans, resources, and decisions.

Why validation runs during Execute, not Apply:

  • Apply is a controlled commit step. Validation failures during Apply would require rolling back committed changes.
  • The sandbox model ensures all verification occurs before the point of no return.
  • Post-apply verification (integration tests, smoke tests) is a separate concern best handled by external CI or follow-up plans.

Skills

!!! adr "Architecture Decision" The skill composition model, skill inclusion, and tool bundling are defined in ADR-012: Skill System.

What a Skill Is

!!! adr "Architecture Decision" The canonical definition of a skill — its four tool sources, flattening model, and role as the unit of capability assignment — is formalized in ADR-030: Skill Abstraction Definition.

A skill is a namespaced, reusable collection of tools that is registered in the system via its own YAML configuration file and managed through agents skill CLI commands. Skills are the unit of capability composition in CleverAgents — they define what an actor can do by assembling tools into coherent, reusable bundles.

A skill is not a single tool. It is a container that references one or more tools (by name from the Tool Registry) and/or defines anonymous inline tools, along with metadata describing the collection's purpose, capabilities, and safety characteristics. Tools are independently registered, callable operations (see the Tools section above). Skills organize them into reusable groups.

Key properties of a skill:

  • Named and namespaced: Skills follow the same <namespace>/<name> naming convention as actors, tools, actions, and plans (e.g., local/github-ops, cleverthis/file-management, local/deploy-tools).
  • Defined in their own YAML files: Skills are NOT defined inline in actor configurations. They have their own configuration files and are managed as independent, reusable entities.
  • A collection of tools: Each skill references named tools from the Tool Registry and/or defines anonymous inline tools. Skills can also expose tools from MCP servers, Agent Skills Standard folders, and built-in tool groups.
  • Hierarchically composable: A skill can include other skills by reference, inheriting all of their tools. This enables layered composition — a "full-stack" skill might include a "file-ops" skill, a "git-ops" skill, and a "github" skill. When including a sub-skill, individual tool metadata can optionally be overridden.
  • Referenced by actors: Actors reference skills by fully-qualified name. The actor's graph gains access to all tools within the referenced skills.

The Skill / Tool Distinction

!!! adr "Architecture Decision" The formal distinction between skills (containers) and tools (atomic operations), and the source-agnostic flattening model, are defined in ADR-030: Skill Abstraction Definition.

@startuml
skinparam packageStyle rectangle
skinparam defaultFontSize 12
skinparam componentFontSize 12

package "Skill: local/devops-toolkit" as Skill {
  package "builtin: file_operations" as FileOps {
    component [read_file()] as T1
    component [write_file()] as T2
    component [edit_file()] as T3
  }

  package "builtin: git_operations" as GitOps {
    component [git_status()] as T4
    component [git_diff()] as T5
  }

  package "mcp: github-server" as GH {
    component [create_issue()] as T6
    component [create_pr()] as T7
    component [list_repos()] as T8
  }

  package "custom" as Custom {
    component [run_migrations()] as T9
  }

  package "Included Skills" as Includes {
    component [local/pdf-processing\n(adds pdf tools)] as I1
    component [local/data-analysis\n(adds analysis tools)] as I2
  }
}
@enduml

Tools are independently registered, atomic units of execution (see the Tools section above for full details). Each tool has:

  • A namespaced name, description, and JSON Schema for inputs/outputs
  • Its own YAML configuration file, registered via agents tool add
  • A source type (mcp, agent_skill, builtin, custom)
  • Capability metadata (read_only, writes, checkpointable, etc.)
  • A lifecycle: discover(), activate(), execute(params, ctx), deactivate()

Skills are the organizational units. Each skill has:

  • A namespaced name
  • A YAML configuration file defining its tool composition
  • Zero or more named tool references (pointing to independently registered tools in the Tool Registry)
  • Zero or more anonymous inline tools (one-off tools defined directly in the skill YAML)
  • Zero or more included child skills (whose tools are merged in, with optional per-tool metadata overrides)
  • Tool sources: MCP servers, Agent Skills folders, built-in tool groups
  • Metadata (description, capability summary)

When an actor references a skill, it gains access to the flattened set of all tools — named tool references, anonymous tools, tools from MCP/Agent Skills/builtins, and those inherited from included child skills.

Skill Configuration (YAML)

Skills are defined in their own YAML configuration files, separate from tool and actor configurations. A skill YAML file declares which registered tools it includes (by name), which other skills it includes, and optionally defines anonymous inline tools:


# File: skills/devops-toolkit.yaml
cleveragents:
  version: "3.0"

skill: name: local/devops-toolkit description: "Full-stack development tools for file ops, git, GitHub, and deployment"

# ── Named Tool References ─────────────────────────────── # Reference independently registered tools by name. # These tools must already be registered via agents tool add. # Optional metadata overrides can be applied per tool. tools: - local/run-migrations # Simple reference, use as registered - name: local/deploy-staging # Reference with metadata override override: capability: human_approval_required: true # Require approval in this skill context

# ── Include other skills ───────────────────────────────── # All tools from included skills become part of this skill. # Included skills must already be registered in the system. # Individual tools from included skills can have metadata overridden. includes: - local/file-ops # built-in file + directory + search tools - local/git-ops # built-in git tools - name: local/github # MCP-based GitHub tools, with per-tool overrides tool_overrides: - tool: local/create-github-issue override: capability: write_scope: [github:issues:org-only]

# ── MCP Server Tools ───────────────────────────────────── # Connect to MCP servers and expose their tools. # Tools discovered from MCP servers are auto-registered in the # Tool Registry if not already present. mcp_servers: - name: linear command: "npx @anthropic/mcp-linear" env: LINEAR_API_KEY: "${LINEAR_API_KEY}" # Optional: override inferred capability metadata per tool overrides: - tool: create_issue writes: true write_scope: [linear:issues] - tool: list_issues read_only: true

# ── Agent Skills (SKILL.md folders) ────────────────────── # Each Agent Skill folder is loaded as a composite tool. # The agent discovers it via metadata, activates it by # loading SKILL.md instructions, and follows them. agent_skills: - path: ./skills/code-review-checklist sandbox_policy: none

# ── Built-in Tool Groups ───────────────────────────────── # Opt-in to built-in tool groups provided by CleverAgents. builtins: - group: shell_operations

# ── Anonymous Tools ────────────────────────────────────── # Inline tool definitions for one-off, skill-specific operations. # Same format as a named tool YAML body but without a name. # These are NOT registered in the Tool Registry and are NOT reusable. anonymous_tools: - description: "One-off cleanup for legacy migration artifacts" input_schema: type: object properties: directory: { type: string } capability: writes: true checkpointable: true checkpoint_scope: file code: | import os, glob directory = params["directory"] removed = [] for f in glob.glob(os.path.join(ctx.sandbox.root, directory, "*.legacy")): os.remove(f) removed.append(f) return {"removed": removed, "count": len(removed)}

Here is an example of a simpler skill that wraps only built-in tools, suitable for common reuse:


# File: skills/file-ops.yaml
cleveragents:
  version: "3.0"

skill: name: local/file-ops description: "File and directory operations"

builtins: - group: file_operations # read, write, edit, delete, move, copy - group: directory_operations # create, list, delete dirs

And an example of a skill that is purely MCP-based:


# File: skills/github.yaml
cleveragents:
  version: "3.0"

skill: name: local/github description: "GitHub operations via MCP"

mcp_servers: - name: github command: "npx @anthropic/mcp-github" env: GITHUB_TOKEN: "${GITHUB_TOKEN}" overrides: - tool: create_issue writes: true write_scope: [github:issues] checkpointable: false - tool: create_pull_request writes: true write_scope: [github:pulls] checkpointable: false - tool: list_repos read_only: true - tool: get_file_contents read_only: true

Skill Hierarchy and Composition

Skills can include other skills via the includes field. When a skill includes another, all tools from the child skill (and transitively, all tools from any skills it includes) become part of the parent skill's flattened tool set.


local/full-stack-dev
  ├── includes: local/file-ops
  │     └── builtins: file_operations, directory_operations
  ├── includes: local/git-ops
  │     └── builtins: git_operations
  ├── includes: local/github
  │     └── mcp_servers: github (create_issue, create_pr, list_repos, ...)
  ├── agent_skills: code-review-checklist
  └── tools: local/run-migrations, local/deploy-staging  (named tool refs)

Flattened tool set available to actors referencing local/full-stack-dev: read_file, write_file, edit_file, delete_file, move_file, copy_file, create_directory, list_directory, delete_directory, git_status, git_diff, git_log, git_blame, create_issue, create_pr, list_repos, get_file_contents, code-review-checklist (agent skill), local/run-migrations, local/deploy-staging (named tools)

Rules for skill composition:

  1. Circular includes are forbidden. The system validates the include graph at registration time and rejects cycles.
  2. Tool name conflicts: If two included skills provide tools with the same name, the conflict is resolved by qualification — the tool must be referenced as <skill_name>.<tool_name> (e.g., local/github.create_issue vs local/linear.create_issue). Direct tools (defined in the skill itself) take precedence over included tools.
  3. Included skills must be registered before the including skill can be added. The agents skill add command validates this.
  4. Depth is unlimited but the flattened tool set is computed at registration time and cached. Deep hierarchies do not incur runtime overhead.

Skill Registration and Management

Skills are managed through the agents skill CLI commands. Named tools referenced by skills must first be registered via agents tool add (see the Tools section):


# First, register any named tools the skill will reference
agents tool add --config ./tools/run-migrations.yaml
agents tool add --config ./tools/deploy-staging.yaml

# Then register the skill (which references those tools by name) agents skill add --config ./skills/devops-toolkit.yaml

# Update an existing skill (re-reads the config file, overwrites registration) agents skill add --config ./skills/devops-toolkit.yaml --update

# List all registered skills agents skill list

# Show details for a skill (tools, includes, metadata) agents skill show local/devops-toolkit

# List all tools provided by a skill (flattened, including from child skills) agents skill tools local/devops-toolkit

# Remove a skill agents skill remove local/devops-toolkit

Once registered, a skill is available to be referenced by any actor configuration. Skills persist in the database (local or server) and follow the same namespace rules as actors, tools, and actions.

Actor References to Skills and Tools

Actors reference skills by name to make collections of tools available for LLM tool-calling. Additionally, actor graphs can include tool nodes that directly reference named tools or define anonymous inline tools (see Nodes in the Graph in the Actor section).

The actor's configuration lists which skills it should have access to:


# File: actors/code-assistant.yaml
cleveragents:
  version: "3.0"
  default_actor: code_assistant

actors: code_assistant: type: llm config: actor: anthropic/claude-3-opus temperature: 0.3 system_prompt: | You are a code assistant with access to file, git, and GitHub tools. Current task: {{ context.task_description }}

# Reference skills by fully-qualified name. # All tools from these skills become available to this actor. skills: - local/file-ops - local/git-ops - local/github

# Or reference a single composite skill that includes all of the above full_stack_assistant: type: llm config: actor: anthropic/claude-3-opus system_prompt: | You are a full-stack development assistant.

<span style="color: cyan; font-weight: 600;">skills</span>:
  - local/full-stack-dev    # includes file-ops, git-ops, github, etc.

Server-qualified skill references: When connected to multiple servers, skills can be disambiguated with a server prefix, same as actors:


skills:
  - dev:freemo/custom-analysis     # from dev server, personal namespace
  - prod:cleverthis/deploy-tools   # from prod server, org namespace
  - local/file-ops                 # local skill

Skill Registry

To enable plan validation and discovery at scale, CleverAgents maintains two registries that work together:

  1. Tool Registry — a persistent catalog of all independently registered tools (described in the Tools section above). Managed via agents tool add/remove/list/show.

  2. Skill Registry — a persistent catalog of all registered skills and their flattened tool sets. The Skill Registry composes its tool sets by resolving named tool references from the Tool Registry, incorporating anonymous inline tools, and merging tools from included child skills.

@startuml
skinparam classAttributeIconSize 0
skinparam classFontSize 13
skinparam defaultFontSize 12

class SkillRegistry {
  - skillIndex : Map<String, SkillRecord>
  --
  + add(config_path) : SkillRecord
  + update(name, config_path) : SkillRecord
  + remove(name) : void
  + lookup(name) : SkillRecord
  + list(filters) : SkillRecord[]
  + tools(name) : ToolDescriptor[]
  + validate_plan(plan) : ValidationResult
  + refresh(name) : void
}

class SkillRecord {
  + name : String
  + description : String
  + config_path : String
  + includes : List<String>
  + tool_refs : List<String>
  + anonymous_tools : List<ToolDef>
  + flattened_tools : List<ToolDescriptor>
  + overrides : Map<String, Object>
  + capability_summary : CapabilitySummary
}

SkillRegistry "1" *-- "0..*" SkillRecord : indexes >

note right of SkillRegistry
  **Populated by:**
  - agents skill add CLI command
  - Dynamic refresh on MCP notifications

  **Depends on:**
  - Tool Registry (resolve named tool references)

  **Consumed by:**
  - Actor activation
  - Plan validation
  - Agent context injection
end note
@enduml

Both registries persist in the database (local SQLite or server). MCP server tools are refreshed dynamically when notifications/tools/list_changed events are received.

Session

!!! adr "Architecture Decision" Session persistence, session lifecycle, and session-plan relationships are defined in ADR-020: Session Model.

What a Session Is

A session is a user's interactive thread with CleverAgents across time.

!!! abstract "Session Responsibilities" - [x] Maintain ==conversational continuity== across interactions - [x] Store plan references for active and past plans - [x] Persist memory (if enabled) across CLI invocations - [x] Provide a UI anchor (CLI invocation, TUI workspace, web session)

Session and Memory Persistence

The notes include a known issue: conversation history can be lost between CLI invocations depending on connection string configuration, implying the system needs a stable memory service backend.

!!! warning "Persistence Requirements" - Sessions must have ==stable IDs== - Sessions must be ==resumable== across CLI invocations - Session storage backend must be ==configured explicitly== - If session persistence is disabled, the UX must be explicit about it — ==no silent history loss==

Server

!!! adr "Architecture Decision" The server architecture, multi-user support, and local/server mode duality are defined in ADR-023: Server Mode. The server application architecture is defined in ADR-048: Server Application Architecture. The A2A standard adoption is defined in ADR-047: A2A Standard Adoption.

What a Server Is

A server is an optional, separate application that enables:

  • multi-user access
  • shared org namespaces (<username>/ and <orgname>/)
  • persistent plan records (PostgreSQL)
  • remote plan execution (via LangGraph Platform)
  • governance and auditing
  • entity synchronization across devices and team members

The server shares the same Domain and Application layers as the client (per ADR-001), differing only in Infrastructure and Presentation layers. Its sole client-facing interface is an A2A JSON-RPC 2.0 endpoint — there is no REST API.

Single-user local mode is the default (so setup is easy), but the architecture anticipates server mode for shared skills, org-level actions, and collaborative workflows.

Client-Only vs Server Mode

Mode Description Protocol Plan Execution
Client-only No server connection. All data in local database. Agent runs as local subprocess. A2A over stdio Always local
Server mode Connected to a CleverAgents server. Namespaced items sync. A2A over HTTP Local or server (via LangGraph Platform RemoteGraph)

It is possible to run a client with no server at all. Server is optional.

Server Configuration and Connection

Connecting to a server requires two configuration keys:

Key Env Var Purpose
server.url CLEVERAGENTS_SERVER_URL URL of the CleverAgents A2A server endpoint
server.token CLEVERAGENTS_SERVER_TOKEN Authentication token (obtained via server registration or team invite)

When server.url is set, the client switches to server mode: A2A methods flow over HTTP instead of stdio, and server namespaces become available. The connection lifecycle follows the A2A standard: the client discovers the server's Agent Card (capability exchange), authenticates via the declared HTTP auth scheme, and begins sending message/send or message/stream requests.

Cloud Plan Execution

In server mode, actor graphs are deployed to LangGraph Platform and invoked via RemoteGraph. This means different actors for different plan phases (strategy, execution, estimation) each deploy as separate RemoteGraphs, enabling independent scaling without limiting plan capabilities.

When an agent executing on the server needs to access client-local resources (files, terminals), it uses _cleveragents/ extension methods (fs/read_text_file, fs/write_text_file, terminal/*) which the client handles locally via the A2A multi-turn interaction pattern (Task enters input-required state). This enables server-hosted plan execution even when some resources exist only on the client machine.

Entity Sync and Sharing

Entity synchronization between client and server uses A2A extension methods (_cleveragents/sync/*):

  • Auto-sync (server.sync.auto): Entities sync on connection and at server.sync.interval (default: 300s)
  • Pull: Server namespace entities are downloaded to local cache
  • Push: Local entity definitions can be explicitly pushed to a server namespace
  • Status: Compare local and server entity versions to detect drift
  • The local/ namespace is never synced — it exists only on the client

Multi-Device Experience

With server mode, a user can:

  • Start a plan on their laptop, close the lid, and monitor progress from another device
  • Share entity definitions (actors, skills, actions) across team members via server namespaces
  • Run long-running plans on server infrastructure without keeping a client connected
  • Access the same session history from CLI, TUI, or IDE plugin on different machines

IDE Integration via A2A

IDE plugins (e.g., VS Code, JetBrains) communicate with CleverAgents exclusively through A2A — the same protocol used by CLI and TUI. The IDE plugin can operate in either local mode (spawning an agent subprocess over stdio) or server mode (connecting to a remote CleverAgents server over HTTP). The _cleveragents/ extension methods for file and terminal operations (fs/*, terminal/*) allow the agent to interact with the IDE's workspace and integrated terminal.

Agent-to-Agent Protocol (A2A)

!!! adr "Architecture Decision" The Agent-to-Agent Protocol is defined in ADR-026: Agent-to-Agent Protocol (A2A). The adoption of the A2A standard is defined in ADR-047: A2A Standard Adoption.

CleverAgents adopts the Agent-to-Agent Protocol standard (a2a-protocol.org) as the sole communication protocol for all client-server interaction. A2A is built on JSON-RPC 2.0 and serves as the fundamental boundary between the Presentation and Application layers — every client operation flows through A2A regardless of deployment mode. No client ever bypasses A2A to touch storage, repositories, or internal domain services directly.

A2A solves a fundamental architectural problem: CleverAgents has multiple presentation surfaces (CLI, TUI, IDE plugin) and multiple deployment modes (local and server), but every client must observe identical behavior regardless of how or where it connects. The A2A standard provides a mature, ecosystem-aligned protocol surface that third parties can implement against.

!!! tip "For the complete architectural deep-dive — transport modes, method catalog, extension methods, wire format, authentication, and error handling — see the Server and Client Architecture section under Architecture."

A2A Standard Operations

The A2A standard defines operations for the core agent interaction lifecycle. These map directly to CleverAgents concepts:

A2A Operation Direction CleverAgents Mapping
Agent Card discovery Client → Server Capability negotiation — advertises supported _cleveragents/ extensions, automation profiles, tool registries
HTTP auth (OAuth2 / API key) Client → Server Token-based authentication (server.token) via auth schemes declared in Agent Card
message/send Client → Server SessionWorkflow.tell() — send user message to orchestrator actor; creates or updates a Task
message/stream Client → Server SessionWorkflow.tell() with streaming — returns TaskStatusUpdateEvent / TaskArtifactUpdateEvent via SSE

A2A uses a Task-centric model: each message/send or message/stream creates or updates a Task that tracks the lifecycle of the interaction. Tasks transition through states: submittedworkingcompleted (or failed, canceled, input-required).

A2A Streaming Events

The A2A standard defines Server-Sent Events (SSE) for real-time streaming from server to client during message/stream:

Event Type CleverAgents Mapping
TaskStatusUpdateEvent Task state transitions, streaming agent response tokens during plan execution
TaskArtifactUpdateEvent Plan artifacts, tool invocation results, generated outputs

Events within a single plan lifecycle preserve causal ordering: a phase transition event always arrives after the corresponding prior phase completion.

Multi-Turn Interactions (Server → Client)

A2A supports multi-turn interactions for operations that require client-side input. When the server-hosted agent needs client-local resources or human approval, the Task enters input-required state:

Interaction Pattern Purpose
Task input-required state Human-in-the-loop approval — maps to automation profile gates
_cleveragents/fs/read_text_file Agent reads a file on the client machine (local project resources)
_cleveragents/fs/write_text_file Agent writes a file on the client machine
_cleveragents/terminal/create Agent requests a terminal on the client machine (sandbox execution)
_cleveragents/terminal/output Terminal output streaming
_cleveragents/terminal/release / wait_for_exit / kill Terminal lifecycle management

These multi-turn interactions enable a server-hosted agent to access client-local resources without requiring the server to have direct access to the user's filesystem or terminal.

CleverAgents Extension Methods

Platform operations beyond the core agent conversation use A2A extension methods — declared in the Agent Card's extensions section per the A2A extensibility mechanism. All CleverAgents extensions use the _cleveragents/ namespace:

Extension Group Methods Service(s)
Plan lifecycle _cleveragents/plan/use, execute, apply, cancel, status, tree, explain, correct, diff, artifacts, prompt, rollback, list PlanService, PlanLifecycle, CorrectionFlow
Registries _cleveragents/registry/{entity}/list, show, add, update, remove (for each entity type: actor, skill, tool, validation, resource, resource_type, project, action, automation_profile, invariant, lsp) ActorService, ToolService, SkillService, ResourceService, ProjectService
Context _cleveragents/context/show, inspect, simulate, set ContextService
Sync _cleveragents/sync/pull, push, status SyncService
Namespace _cleveragents/namespace/list, show, members NamespaceService
Health _cleveragents/health/check, _cleveragents/diagnostics/run Health/diagnostic services

Every CLI command listed in the CLI Commands section maps to either a standard A2A operation or a _cleveragents/ extension method. When a user runs agents plan status <ID>, the CLI sends a _cleveragents/plan/status JSON-RPC request through the active transport and renders the response. The CLI is a thin rendering layer — it contains no business logic.

Transport Modes

A2A operates over two transports provided by the A2A Python SDK:

Mode Transport How It Works Authentication
Local A2A over stdio Client spawns agent as subprocess; JSON-RPC messages flow over stdin/stdout. Platform extension methods are resolved in-process via A2aLocalFacade. Bypassed (local user permissions)
Server A2A over HTTP Client connects to CleverAgents server via A2A SDK HTTP transport. All methods (standard + extensions) flow through the single A2A endpoint. HTTP auth schemes declared in Agent Card (OAuth2, API key) + Authorization: Bearer header

In local mode, the agent runs as a subprocess. Standard A2A operations (message/send, message/stream) drive the conversation. Extension methods (_cleveragents/*) are intercepted by A2aLocalFacade and routed to in-process Application-layer services. No serialization beyond JSON-RPC framing, no network, no authentication overhead.

In server mode, the client connects to the CleverAgents server. All communication — both agent conversations and platform operations — flows through the single A2A JSON-RPC 2.0 endpoint. The server delegates actor execution to LangGraph Platform via RemoteGraph.

A third configuration supports external A2A agents: standard A2A operations go to the external agent's server, while _cleveragents/ extension methods go to the CleverAgents server. This enables interoperability with any A2A-compliant agent.

sequenceDiagram
    participant U as User
    participant C as CLI / TUI / IDE
    participant A2A as A2A Client (SDK)
    participant S as A2A Server
    participant LG as LangGraph Platform

    rect rgb(220, 240, 255)
        Note over C,A2A: Local Mode (stdio)
        U->>C: agents plan status <ID>
        C->>A2A: _cleveragents/plan/status {JSON-RPC 2.0}
        A2A->>A2A: A2aLocalFacade → PlanService.get_status()
        A2A-->>C: JSON-RPC result
        C-->>U: Rendered output
    end

    rect rgb(255, 235, 220)
        Note over C,LG: Server Mode (HTTP)
        U->>C: agents session tell "Refactor auth"
        C->>A2A: message/send {JSON-RPC 2.0}
        A2A->>S: HTTP POST (JSON-RPC)
        S->>LG: RemoteGraph.invoke(actor_graph)
        LG-->>S: Actor result
        S-->>A2A: TaskStatusUpdateEvent / TaskArtifactUpdateEvent (SSE)
        A2A-->>C: Streaming response
        C-->>U: Rendered output
    end
Wire Format

All A2A communication uses JSON-RPC 2.0 framing:

Request:

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "message/send",
  "params": { "message": { "role": "user", "parts": [{ "kind": "text", "text": "Refactor the auth module" }] }, "taskId": "task_01HXR..." }
}

Response:

{
  "jsonrpc": "2.0",
  "id": 1,
  "result": { "status": "accepted" }
}

Streaming event (SSE via message/stream):

{
  "jsonrpc": "2.0",
  "method": "task/statusUpdate",
  "params": { "taskId": "task_01HXR...", "status": { "state": "working" }, "message": { "role": "agent", "parts": [{ "kind": "text", "text": "I'll start by..." }] } }
}

Extension method request:

{
  "jsonrpc": "2.0",
  "id": 42,
  "method": "_cleveragents/plan/status",
  "params": { "plan_id": "01HXRCF1..." }
}

Error response:

{
  "jsonrpc": "2.0",
  "id": 42,
  "error": { "code": -32001, "message": "Plan not found", "data": { "plan_id": "01HXRCF1..." } }
}
Authentication

Authentication uses HTTP auth schemes declared in the server's Agent Card:

  1. Client fetches the server's Agent Card (via /.well-known/agent.json or configured URL)
  2. Agent Card declares supported authentication schemes (OAuth2, API key, Bearer token)
  3. Client authenticates using the declared scheme — typically Authorization: Bearer <server.token> header
  4. All subsequent requests carry the authentication credentials

Local mode (stdio) bypasses authentication entirely — the agent subprocess runs with the user's local permissions.

Versioning
  • The JSON-RPC protocol version is always "2.0" (in the jsonrpc field)
  • A2A protocol version is declared in the Agent Card and sent via the A2A-Version HTTP header
  • CleverAgents extension version is declared in the Agent Card's extensions section under _cleveragents.version
  • Servers support the current extension version plus one prior minor version
  • Backward-compatible additions (new optional params, new extension methods) are permitted within a major version; breaking changes require a major version bump

Plan Execution Location

Where a plan executes depends on the project type:

Project Type Client Execution Server Execution
Local (has local-only resources) Yes No (server can't access local resources)
Remote (all resources remotely accessible) Yes Yes

When acting on local projects, the client must be running because only the client can access local resources.

Remote projects can execute on either:

  • The client (if user prefers local execution)
  • The server (for long-running plans, since client may be transient)

Server Execution Benefits

Server execution is useful when:

  • Plans take a long time to execute
  • Client may disconnect (laptop closes, network issues)
  • Multiple team members need to monitor plan progress
  • Centralized logging and auditing required

No Plan Queuing

Plans are not queued. When a plan is used on projects and executed, it runs immediately. There is no worker queue or delayed execution model.

Multi-user Risks and Prompt Injection

Prompt injection isn't critical in single-user mode but becomes important for multi-user server environments.

Server mode must include:

  • access boundaries
  • prompt sanitization / safe templating
  • resource access controls
  • auditing

Resources

!!! adr "Architecture Decision" The resource model, resource types, DAG structure, and resource lifecycle are defined in ADR-008: Resource System.

A resource is an independently registered entity representing anything that a plan can reason about or manipulate — git repositories, filesystems, databases, APIs, documents, and more. Resources are first-class citizens in CleverAgents, managed through the agents resource CLI commands and stored in the Resource Registry.

CleverAgents extends the MCP resource concept to support both read AND write operations (MCP resources are read-only). Unlike MCP's flat resource model, CleverAgents resources form a directed acyclic graph (DAG) with parent/child relationships, support physical vs virtual distinction for content identity tracking, and are governed by a resource type system that constrains their structure and behavior.

Resources are registered independently of projects. Projects link to resources from the Resource Registry — a resource can be linked to multiple projects, enabling shared resources across teams and workflows.

What a Resource Is

A resource has:

  • Name and namespace: User-added resources follow the same <namespace>/<name> naming convention as actors, tools, skills, and other entities (e.g., local/api-repo, cleverthis/staging-db). Auto-discovered child resources do not have names — they are identified by ULID only. Every resource (whether user-added or auto-discovered) always has a system-assigned ULID.
  • Resource type: Every resource has a type (e.g., git, fs-mount, git-branch, fs-file) that determines its properties, CLI arguments, allowed parent/child relationships, sandbox strategy, and handler implementation.
  • Physical or virtual nature: Resources are either physical (a specific, concrete manifestation) or virtual (an abstract identity linking equivalent physical resources). This is determined by the resource type.
  • Value/properties: Type-specific properties (a file path, a URL, a connection string, a commit hash, etc.).
  • Parent/child relationships: Resources form a DAG. A resource can have multiple parents and multiple children, subject to type constraints.
  • Capabilities: Whether the resource is readable, writable, sandboxable, and checkpointable.

Resource Types

A resource type is a schema-level definition that constrains a category of resources. Resource types define:

  • CLI arguments: What arguments agents resource add <type> accepts (for user-addable types), including which are required vs optional and validation rules.
  • Physical or virtual: Whether instances of this type are physical or virtual resources.
  • Allowed parent types: What resource types are valid parents.
  • Allowed child types: What resource types are valid children, and whether they are auto-discovered or manually linkable.
  • Auto-discovery behavior: What child resources are automatically created when an instance of this type is registered (e.g., registering a git-checkout resource auto-discovers a git child and an fs-directory child for the worktree root; the git child in turn auto-discovers remotes, branches, commits, and tree entries).
  • User addable: Whether users can create instances of this type directly via agents resource add <type>. Types with user_addable: false are only generated as auto-discovered children of other resources.
  • Sandbox strategy: The default sandboxing approach for instances of this type.
  • Handler: The resource handler implementation that provides read/write/sandbox/checkpoint operations.
  • Inheritance: Optionally, a parent type from which this type inherits all of the above via the inherits field.
Resource Type Inheritance

!!! adr "Architecture Decision" Resource type inheritance is defined in ADR-042: Resource Type Inheritance.

Resource types support single-inheritance specialization via an inherits field. A subtype inherits all properties, capabilities, child type constraints, sandbox strategy, and handler behavior from its parent type, and can selectively override or extend any inherited field.

Core polymorphism guarantee: Tools bound to a parent type automatically work with all subtypes. Auto-discovery child type matching and DAG queries that reference a parent type automatically include subtypes.

Field resolution: When the system resolves a field for a resource type, it walks the inheritance chain from the most specific type to the root. If the subtype declares the field, the subtype's value is used (override). If omitted, the parent's value is inherited.

Collection field merging: Fields that are collections (cli_args, child_types, parent_types) use additive merging by default — the subtype's entries are appended to the parent's, with same-name entries replaced. A subtype can use <field>_replace: true to replace a collection entirely.

Inheritance rules:

  1. Single inheritance only (no diamond).
  2. Maximum chain depth of 5 levels.
  3. No circular inheritance (validated at registration time).
  4. Built-in types may not inherit from custom (namespaced) types.
  5. Removing a parent type is prohibited while subtypes exist.

Example:


# A subtype that inherits from container-instance
name: devcontainer-instance
inherits: container-instance
description: "A container provisioned from a devcontainer.json configuration"

# Only fields that differ from or extend container-instance need to be declared. # All other fields (capabilities, sandbox_strategy, child_types, etc.) are inherited. cli_args: # Inherited args from container-instance remain available. # Additional args specific to devcontainer:

  • name: config-path type: path required: false description: "Path to .devcontainer/devcontainer.json or .devcontainer/ directory"

handler: class: DevcontainerHandler module: cleveragents.resource.handlers.devcontainer

Built-in Resource Types

Built-in types are organized into four layers: git (version control structure), git-checkout (a composition that bridges git metadata with a local directory), filesystem (physical files on disk), and container (containerized execution environments). Virtual types link equivalent physical resources across these layers through content/identity matching. There are 34 built-in types total (24 physical + 1 subtype + 9 virtual), of which 4 are user-addable as top-level resources (git-checkout, git, fs-mount, fs-directory), plus container types documented in ADR-039 and the devcontainer-instance subtype documented in ADR-043.

Each table below includes the full parent/child relationship constraints. Allowed Parents lists what types may be a parent of this type, with cardinality (how many parents of that type are allowed) and whether the relationship is required or optional. Allowed Children lists what types may be children, with cardinality and whether they are required or optional. 0..* means zero or more, 1 means exactly one (required), 0..1 means zero or one (optional).

Physical types — Git layer (version control structure — every instance is a concrete manifestation in a specific repository):

Type User Addable Sandbox Allowed Parents Allowed Children Description
git yes none git-checkout (0..1, optional) git-remote (0.., optional), git-branch (0.., optional), git-tag (0.., optional), git-commit (0.., optional), git-stash (0.., optional), git-submodule (0.., optional) A git repository — the object database, refs, and full history. Accessible via a local .git directory path or a remote URL. Every git resource is a specific, concrete repo instance (local or hosted on a remote server).
git-remote no none git (1, required) (none) A remote URL configured on a git repo (e.g., origin → https://github.com/org/repo). Auto-discovered child of git. Optionally linked as a child of a remote virtual resource when the same URL appears in multiple repos.
git-branch no (inherits) git (1, required) git-commit (0..*, optional) A named branch ref (e.g., main, feature/auth). Auto-discovered child of git. Optionally linked as a child of a branch virtual resource when the same branch name + HEAD exists in multiple repos.
git-tag no (inherits) git (1, required) (none) A tag ref — lightweight or annotated (e.g., v1.0.0). Auto-discovered child of git. Optionally linked as a child of a tag virtual resource when the same tag name + target exists in multiple repos.
git-commit no (inherits) git-branch (1..*, required), git (1, required) git-tree (1, required) A specific commit object. Auto-discovered child of git-branch (a commit can belong to multiple branches). Each commit contains exactly one root git-tree. Also a direct child of git for ancestry traversal. Optionally linked as a child of a commit virtual resource when the same commit hash exists in multiple repos (e.g., shared history between fork and upstream).
git-tree no (inherits) git-commit (1..*, required) git-tree-entry (0.., optional), git-tree (0.., optional) A tree object — a directory listing at a specific commit. Each tree contains entries (blobs and subtrees). Auto-discovered child of git-commit. Trees are recursive: a tree can contain subtrees as children. Optionally linked as a child of a tree virtual resource when the same tree hash exists across commits/repos.
git-tree-entry no (inherits) git-tree (1, required) (none) A blob entry in a tree — a specific file's content at a specific path and mode. Auto-discovered child of git-tree. Represents the leaf of git's content-addressable storage. Optionally linked as a child of a file virtual resource when byte-identical content with the same name and permissions exists in the filesystem layer.
git-stash no (inherits) git (1, required) (none) A stash entry (e.g., stash@{0}). Auto-discovered child of git. Represents work-in-progress saved via git stash.
git-submodule no (inherits) git (1, required) (none) A submodule reference — a pointer to another git repository at a specific commit and path. Auto-discovered child of git. Optionally linked as a child of a submodule virtual resource when the same submodule URL + path exists in multiple repos.

Physical types — Git checkout (composition: git metadata + local worktree directory):

Type User Addable Sandbox Allowed Parents Allowed Children Description
git-checkout yes git_worktree (none — always a top-level resource) git (1, required), fs-directory (1, required) A locally checked-out git repository. The composition type — auto-discovers a git child (the repo's object database, branches, tags, commits, trees, and remotes) and an fs-directory child (the worktree root directory, e.g., /home/user/projects/my-app). Most users register this type. The worktree root IS a directory, not a mount point, so git-checkout composes with fs-directory directly.

Physical types — Filesystem layer (files on disk, used by git checkouts and standalone directories alike):

Type User Addable Sandbox Allowed Parents Allowed Children Description
fs-mount yes copy_on_write (none — always a top-level resource) fs-directory (1, required) A physical mount point on the local system (e.g., /mnt/data, /home). Represents the mount itself — the filesystem type (ext4, btrfs, etc.) is a property. The root directory is a required auto-discovered fs-directory child. Used for registering entire mount points or storage volumes.
fs-directory yes copy_on_write git-checkout (0..1, optional), fs-mount (0..1, optional), fs-directory (0..1, optional) fs-directory (0.., optional), fs-file (0.., optional), fs-symlink (0.., optional), fs-hardlink (0.., optional) A directory on the filesystem. Can be the root child of fs-mount (mount point root), the worktree root child of git-checkout, a subdirectory child of another fs-directory, or a standalone user-registered directory. Optionally linked as a child of a directory virtual resource when equivalent directory content exists elsewhere. When user-addable, accepts --path flag.
fs-file no (inherits) fs-directory (1, required) (none) A regular file on the local filesystem. Auto-discovered child of fs-directory. Optionally linked as a child of a file virtual resource when byte-identical content with the same name and permissions exists elsewhere (in another fs-file or a git-tree-entry).
fs-symlink no (inherits) fs-directory (1, required) (none) A symbolic link on the local filesystem. Auto-discovered child of fs-directory. Optionally linked as a child of a symlink virtual resource when a symlink with the same name and target exists elsewhere.
fs-hardlink no (inherits) fs-directory (1, required) (none) A hard link on the local filesystem — a file with link count > 1. Auto-discovered child of fs-directory. The system tracks hard link relationships by inode to avoid treating the same underlying data as distinct resources.

Virtual types (abstract identity types that link equivalent physical resources — never user-addable, no sandbox):

Virtual types use simple names that mirror their physical counterparts. A virtual resource answers the question: "where else does this same thing exist?" Two physical resources share a virtual parent when they are equivalent by the virtual type's criteria (same content, same name, same permissions, same hash, etc.).

Type Allowed Children Description
file fs-file (0.., optional), git-tree-entry (0.., optional) Cross-layer file identity. Links physical fs-file and git-tree-entry resources that represent the same file — identical content bytes, filename, and permissions. Answers: "this file in the working tree and this blob in git's tree are the same file."
directory fs-directory (0.., optional), git-tree (0.., optional) Cross-layer directory identity. Links physical fs-directory and git-tree resources whose recursive contents are equivalent. Answers: "this directory on disk matches this tree object in git."
symlink fs-symlink (0.., optional), git-tree-entry (0.., optional) Cross-layer symlink identity. Links physical fs-symlink and git-tree-entry (mode 120000) resources with the same name and target.
commit git-commit (0..*, optional) Cross-repo commit identity. Links git-commit resources across different repos that share the same commit hash — typically a fork and its upstream. Answers: "this commit exists in both repos."
branch git-branch (0..*, optional) Cross-repo branch identity. Links git-branch resources across different repos with the same branch name and HEAD commit hash.
tag git-tag (0..*, optional) Cross-repo tag identity. Links git-tag resources across different repos with the same tag name and target object.
remote git-remote (0..*, optional) Cross-repo remote identity. Links git-remote resources across different repos that point to the same URL. Answers: "these repos share the same upstream."
submodule git-submodule (0..*, optional) Cross-repo submodule identity. Links git-submodule resources across different repos with the same submodule URL and path.
tree git-tree (0..*, optional) Cross-repo/cross-commit tree identity. Links git-tree resources across different commits or repos that have the same tree hash — identical directory structure and contents.

Key design notes:

  • Virtual types are never user-addable — they are created and maintained automatically by the system when equivalent physical resources are detected.
  • Virtual types have no sandbox strategy because they represent abstract identities, not concrete locations. Tools always operate on the physical children.
  • Physical types list virtual parents in their Allowed Parents column (via the "optionally linked" descriptions). For example, an fs-file can be linked as a child of a file virtual resource when its content matches a git-tree-entry. This is how the DAG connects physical and virtual layers.
  • git represents a specific git repository instance — whether hosted remotely (e.g., on GitHub's servers) or locally (a .git directory on disk). It is physical because it is a concrete manifestation that exists somewhere, not an abstract identity. A git resource can be created from a remote URL alone (accessing the object database via the git protocol) or from a local .git directory.
  • git-checkout is the type most users register for repos they've cloned. It composes a git child (repo metadata) and an fs-directory child (the worktree root directory). The worktree root is a directory on an existing filesystem — NOT a separate mount point — so git-checkout links directly to fs-directory, not fs-mount. This means the worktree's files use the same fs-directory and fs-file types regardless of how they were created.
  • git-commit has a git-tree child (the root tree object), which in turn has git-tree-entry and nested git-tree children. This properly models git's internal object structure: commits point to trees, trees contain entries (blobs) and subtrees.
  • fs-mount represents a physical mount point, not a directory. The filesystem type (ext4, btrfs, etc.) is a property on the mount. The root directory is an auto-discovered fs-directory child. Use fs-mount for registering mount points and storage volumes. Use fs-directory (user-addable) for registering arbitrary directories.
  • fs-directory is user-addable, allowing users to register standalone directories (e.g., a build output folder, a config directory) without wrapping them in an fs-mount. An fs-directory registered standalone or as a child of fs-mount/git-checkout uses the same type and auto-discovers the same fs-file, fs-symlink, fs-hardlink, and nested fs-directory children.
  • git-tree-entry represents a blob in git's tree — a specific file's content at a specific path and mode. The corresponding file on disk (if checked out) is an fs-file. The file virtual type bridges these two when their content, name, and permissions are identical.
  • 4 user-addable types: git-checkout (local repo), git (remote or local metadata-only), fs-mount (mount point), fs-directory (arbitrary directory).
Built-in Type Hierarchy

The parent-child relationships between built-in resource types form three layers — git structure, git checkout (composition), and filesystem — bridged by virtual identity types:

@startuml
skinparam defaultFontSize 11
skinparam objectFontSize 11
skinparam packageStyle rectangle

package "GIT-CHECKOUT (composition)" as GC #LightBlue {
  object "git-checkout" as gc {
    /home/user/projects/myapp
  }
}

package "GIT STRUCTURE" as GS #LightYellow {
  object "git" as git {
    repo object DB
  }
  object "git-remote" as remote {
    origin
  }
  object "git-tag" as tag {
    v1.0.0
  }
  object "git-submodule" as submod {
    lib/shared
  }
  object "git-branch" as branch {
    main
  }
  object "git-stash" as stash {
    stash@0
  }
  object "git-commit" as commit {
    a1b2c3d
  }
  object "git-tree" as tree {
    e8f1... root
  }
  object "git-tree-entry" as entry1 {
    README.md
  }
  object "git-tree" as subtree {
    src/ subtree
  }
  object "git-tree-entry" as entry2 {
    src/app.ts
  }
  object "git-tree-entry" as entry3 {
    src/main.ts
  }
}

package "FILESYSTEM (worktree)" as FS #LightGreen {
  object "fs-directory" as fsroot {
    worktree root
  }
  object "fs-directory" as srcdir {
    src/
  }
  object "fs-file" as readme {
    README.md
  }
  object "fs-file" as app {
    app.ts
  }
  object "fs-file" as main {
    main.ts
  }
}

package "STANDALONE FS-DIRECTORY" as SFS #Wheat {
  object "fs-directory" as sfs {
    /opt/deploy/myapp
  }
  object "fs-directory" as ssrcdir {
    src/
  }
  object "fs-file" as sreadme {
    README.md
  }
  object "fs-file" as sapp {
    app.ts
  }
  object "fs-file" as smain {
    main.ts
  }
  object "fs-symlink" as symlink {
    link.txt
  }
}

package "STANDALONE FS-MOUNT" as SM #LightCoral {
  object "fs-mount" as mount {
    /mnt/data
  }
  object "fs-directory" as mountroot {
    root: /
  }
}

package "VIRTUAL LAYER (abstract identities)" as VL #Lavender {
  object "file" as vfile {
    app.ts @ sha256:9f8e...
  }
  object "directory" as vdir {
    src/ @ merkle:3d4f...
  }
  object "commit" as vcommit {
    a1b2c3d
  }
  object "branch" as vbranch {
    main @ a1b2c3d
  }
  object "remote" as vremote {
    github.com/org/repo
  }
  object "tree" as vtree {
    e8f1...9d2a
  }
}

gc --> git
gc --> fsroot

mount --> mountroot

git --> remote
git --> tag
git --> submod
git --> branch
git --> stash

branch --> commit
commit --> tree
tree --> entry1
tree --> subtree
subtree --> entry2
subtree --> entry3

fsroot --> srcdir
fsroot --> readme
srcdir --> app
srcdir --> main

sfs --> ssrcdir
sfs --> sreadme
sfs --> symlink
ssrcdir --> sapp
ssrcdir --> smain

app ..> vfile : equivalent
sapp ..> vfile : equivalent
entry2 ..> vfile : equivalent
srcdir ..> vdir : equivalent
ssrcdir ..> vdir : equivalent
subtree ..> vdir : equivalent
commit ..> vcommit : equivalent
branch ..> vbranch : equivalent
remote ..> vremote : equivalent
tree ..> vtree : equivalent
@enduml

Reading the diagram:

  • Top left: A git-checkout resource decomposes into a git child (left — the repository's full structure) and an fs-directory child (center — the worktree root directory at /home/user/projects/myapp). The worktree root is a directory, not a mount point.
  • Git structure (left column): The git child contains git-remote (origin), git-tag (v1.0.0), git-submodule (lib/shared), git-stash (stash@{0}), and git-branch (main). The branch contains git-commit objects, each commit contains a root git-tree, and trees contain git-tree-entry (blobs) and nested git-tree (subtrees). This properly models git's internal object structure.
  • Filesystem (center/right): The worktree's fs-directory contains src/ (an fs-directory), README.md (an fs-file), and files including fs-symlink resources. A standalone fs-directory (/opt/deploy/myapp) with the same structure. A standalone fs-mount (/mnt/data) with a root fs-directory child.
  • Virtual layer (boxed area): Shows how virtual types link equivalent physical resources:
    • A file virtual resource links fs-file and git-tree-entry resources that have the same content, filename, and permissions — bridging the filesystem and git layers.
    • A directory virtual resource links fs-directory and git-tree resources with the same recursive content.
    • A commit virtual resource links git-commit resources across repos with the same commit hash.
    • A branch virtual resource links git-branch resources across repos with the same name and HEAD.
    • A remote virtual resource links git-remote resources across repos with the same URL.
    • A tree virtual resource links git-tree resources across repos/commits with the same tree hash.

Key separations:

  • git vs git-checkout: A git resource represents a specific repository instance (local or remote). It can exist without any local directory (created from a remote URL — the repo exists on the remote server). A git-checkout always has both a git child and an fs-directory child — it represents a locally cloned repo with files on disk.
  • git-tree-entry vs fs-file: A git-tree-entry is a blob entry in git's tree (path + content hash + mode). An fs-file is a physical file on disk. When a repo is checked out, both exist and typically have matching content — the file virtual type links them when content, name, and permissions match.
  • git-tree vs fs-directory: A git-tree is a tree object in git's object database (a directory listing at a commit). An fs-directory is a physical directory on disk. The directory virtual type links them when their recursive contents match.
  • git-commitgit-treegit-tree-entry: This chain properly models git internals. Commits point to a root tree, trees contain entries (blobs) and subtrees. The old design skipped the tree level; the current design preserves it.
  • fs-mount vs fs-directory: An fs-mount is a mount point (e.g., /mnt/data). The filesystem type (ext4, btrfs, etc.) is a property. The root directory is an fs-directory child. An fs-directory is a directory — it can be a child of fs-mount, git-checkout, or another fs-directory, or it can be registered standalone by the user.
  • git-checkout composes with fs-directory, not fs-mount: A git checkout's worktree is a directory on an existing filesystem, not a separate mount point. This is why git-checkout's required child is fs-directory (the worktree root), not fs-mount.
  • Virtual types bridge physical equivalents: file bridges fs-file + git-tree-entry (cross-layer). directory bridges fs-directory + git-tree (cross-layer). commit, branch, tag, remote, submodule, and tree link the same git structural element across different repos.
Concrete Example: A Made-Up Project

Consider a web application called "Acme Dashboard" with three registered resources:

  1. local/acme-app — a checked-out git repo at /home/alice/projects/acme-dashboard (type: git-checkout)
  2. local/acme-upstream — a git repo accessed via remote URL, not cloned (type: git)
  3. local/acme-deploy — a standalone directory at /opt/deploy/acme-dashboard containing a production build snapshot (type: fs-directory)

Registration:


# 1) Checked-out git repo (has local files on disk)
agents resource add git-checkout local/acme-app \
  --path /home/alice/projects/acme-dashboard --branch main

# 2) Git repo via remote URL (no local checkout — metadata only) agents resource add git local/acme-upstream </span> --url git@github.com:acmecorp/dashboard.git

# 3) Standalone directory (not a git repo — just files on disk) agents resource add fs-directory local/acme-deploy </span> --path /opt/deploy/acme-dashboard

What gets auto-discovered for each:

local/acme-app (type: git-checkout) discovers two children — a git and an fs-directory (the worktree root):

@startwbs
* local/acme-app\n(git-checkout / physical)
** local/acme-app:repo\n(git / physical)
*** acme-app:repo:origin\n(git-remote)\ngit@github.com:acmecorp/dashboard.git
*** acme-app:repo:v1.0.0\n(git-tag)
*** acme-app:repo:lib/shared\n(git-submodule @ c4d5e6f)
*** acme-app:repo:stash@0\n(git-stash)
*** acme-app:repo:main\n(git-branch)
**** main:a7f3e21\n(git-commit)
***** main:a7f3e21:tree\n(git-tree / root)
****** a7f3e21:README.md\n(git-tree-entry)
****** a7f3e21:package.json\n(git-tree-entry)
****** a7f3e21:src/\n(git-tree / subtree)
******* a7f3e21:src/app.ts\n(git-tree-entry)
******* a7f3e21:src/api.ts\n(git-tree-entry)
******* a7f3e21:src/utils.ts\n(git-tree-entry)
*** acme-app:repo:develop\n(git-branch)
**** develop:b2c4d8e\n(git-commit)
***** develop:b2c4d8e:tree\n(git-tree)
****** b2c4d8e:src/\n(git-tree)
******* b2c4d8e:src/app.ts\n(git-tree-entry)
******* b2c4d8e:src/api.ts\n(git-tree-entry / modified)
** local/acme-app:worktree\n(fs-directory / physical)\n/home/alice/projects/acme-dashboard/
*** worktree:src/\n(fs-directory)
**** worktree:src/app.ts\n(fs-file)
**** worktree:src/api.ts\n(fs-file)
**** worktree:src/utils.ts\n(fs-file)
*** worktree:package.json\n(fs-file)
*** worktree:README.md\n(fs-file)
*** worktree:docs -> ../docs\n(fs-symlink)
@endwbs

When the git-checkout contains a .devcontainer/devcontainer.json, an additional devcontainer-instance child is auto-discovered:

@startwbs
* local/acme-app\n(git-checkout / physical)
** local/acme-app:repo\n(git / physical)
*** (branches, commits, trees, ...)
** local/acme-app:worktree\n(fs-directory / physical)
*** worktree:.devcontainer/\n(fs-directory)
**** worktree:.devcontainer/devcontainer.json\n(fs-file)
*** worktree:src/\n(fs-directory)
**** worktree:src/app.ts\n(fs-file)
*** worktree:package.json\n(fs-file)
** local/acme-app:devcontainer\n(devcontainer-instance / discovered)
*** [container-mount — pending activation]
*** [container-exec-env — pending activation]
*** [container-port — pending activation]
@endwbs

The devcontainer-instance is in discovered state — its container-mount, container-exec-env, and container-port children are only created when the container is activated during plan execution. This is consistent with lazy sandboxing: no container is built until a tool actually needs to execute inside it.

The git-checkout cleanly separates two concerns: the git child contains version control structure (remotes, branches, tags, stashes, submodules, commits, trees, and tree entries — git's full object model), while the fs-directory child is the worktree root directory containing the actual files on disk. Note how git's internal structure is fully modeled: git-commitgit-tree (root tree object) → git-tree-entry (blobs) and nested git-tree (subtrees). The worktree root is a directory (fs-directory), not a mount point — git-checkout does not own an fs-mount resource because a git checkout's worktree is just a directory on an existing filesystem. When content matches (as it does for a clean checkout), virtual types link the fs-file and git-tree-entry resources.

local/acme-upstream (type: git, remote URL — NOT checked out) discovers:

@startwbs
* local/acme-upstream\n(git / physical)\ngit@github.com:acmecorp/dashboard.git
** acme-upstream:origin\n(git-remote)
** acme-upstream:v1.0.0\n(git-tag)
** acme-upstream:main\n(git-branch)
*** main:a7f3e21\n(git-commit)
**** main:a7f3e21:tree\n(git-tree)
***** a7f3e21:src/\n(git-tree)
****** a7f3e21:src/app.ts\n(git-tree-entry)
****** a7f3e21:src/api.ts\n(git-tree-entry)
** acme-upstream:develop\n(git-branch)
*** ...\n(remaining structure)
@endwbs

A standalone git resource has full access to branches, tags, commits, trees, and tree entries — everything in the git object database — but no fs-directory child and no fs-file resources. There are no files on the local disk (the repo exists on GitHub's servers). Tools that need local file access cannot bind to it.

This is the key difference from git-checkout: a git resource represents a specific repo instance. Plans can reason about history, diffs between branches, remote relationships — without a local clone. A git-checkout adds the local worktree directory on top.

local/acme-deploy (type: fs-directory, standalone) discovers:

@startwbs
* local/acme-deploy\n(fs-directory / physical)\n/opt/deploy/acme-dashboard/
** acme-deploy:src/\n(fs-directory)
*** acme-deploy:src/app.ts\n(fs-file)
*** acme-deploy:src/api.ts\n(fs-file)
*** acme-deploy:src/utils.ts\n(fs-file)
** acme-deploy:package.json\n(fs-file)
** acme-deploy:README.md\n(fs-file)
@endwbs

A standalone fs-directory — no git metadata, no branches, no commits, no tree entries. Just a directory containing files and subdirectories. Uses the same fs-directory and fs-file types as the git checkout's worktree root.

Virtual resource linking across all three:

After all three resources are registered, the system detects equivalent physical resources and creates virtual parents to link them:

@startuml
skinparam defaultFontSize 10
skinparam objectFontSize 10
skinparam packageStyle rectangle
left to right direction

package "PHYSICAL: local/acme-app" as P1 #LightBlue {
  object "fs-directory" as acmeWtSrc {
    worktree:src/
  }
  object "fs-file" as acmeWtApp {
    worktree:src/app.ts
  }
  object "fs-file" as acmeWtUtils {
    worktree:src/utils.ts
  }
  object "fs-file" as acmeWtApi {
    worktree:src/api.ts
  }
  object "git-tree" as acmeGitSrc {
    main:a7f3e21:src/
  }
  object "git-tree-entry" as acmeGitApp {
    main:src/app.ts
  }
  object "git-tree-entry" as acmeGitUtils {
    main:src/utils.ts
  }
  object "git-tree-entry" as acmeGitApi {
    main:src/api.ts
  }
  object "git-commit" as acmeCommit {
    main:a7f3e21
  }
  object "git-branch" as acmeBranch {
    main
  }
  object "git-tag" as acmeTag {
    v1.0.0
  }
  object "git-remote" as acmeRemote {
    origin
  }
  object "git-tree" as acmeTree {
    main:a7f3e21:tree
  }
  object "git-submodule" as acmeSubmod {
    lib/shared
  }
}

package "PHYSICAL: local/acme-deploy" as P2 #LightGreen {
  object "fs-directory" as deploySrc {
    src/
  }
  object "fs-file" as deployApp {
    src/app.ts
  }
  object "fs-file" as deployUtils {
    src/utils.ts
  }
  object "fs-file" as deployApi {
    src/api.ts
  }
}

package "PHYSICAL: local/acme-upstream" as P3 #LightYellow {
  object "git-tree" as upstreamSrc {
    main:a7f3e21:src/
  }
  object "git-tree-entry" as upstreamApp {
    main:src/app.ts
  }
  object "git-tree-entry" as upstreamUtils {
    main:src/utils.ts
  }
  object "git-tree-entry" as upstreamApi {
    main:src/api.ts
  }
  object "git-commit" as upstreamCommit {
    main:a7f3e21
  }
  object "git-branch" as upstreamBranch {
    main
  }
  object "git-tag" as upstreamTag {
    v1.0.0
  }
  object "git-remote" as upstreamRemote {
    origin
  }
  object "git-tree" as upstreamTree {
    main:a7f3e21:tree
  }
}

package "VIRTUAL LAYER\n(auto-created by equivalence)" as VL #Lavender {
  object "directory" as vDir {
    src/ (merkle:3d4f...)
  }
  object "file" as vAppTs {
    app.ts (sha256:9f8e...)
  }
  object "file" as vUtilsTs {
    utils.ts (sha256:a2b1...)
  }
  object "file" as vApiTs {
    api.ts (sha256:e1d3...)
  }
  object "commit" as vCommit {
    a7f3e21
  }
  object "branch" as vBranch {
    main @ a7f3e21
  }
  object "tag" as vTag {
    v1.0.0
  }
  object "remote" as vRemote {
    github.com:acmecorp/dashboard.git
  }
  object "tree" as vTree {
    e8f1...9d2a
  }
  object "submodule" as vSubmod {
    lib/shared
  }
}

acmeWtSrc ..> vDir
deploySrc ..> vDir
acmeGitSrc ..> vDir
upstreamSrc ..> vDir

acmeWtApp ..> vAppTs
deployApp ..> vAppTs
acmeGitApp ..> vAppTs
upstreamApp ..> vAppTs

acmeWtUtils ..> vUtilsTs
deployUtils ..> vUtilsTs
acmeGitUtils ..> vUtilsTs
upstreamUtils ..> vUtilsTs

acmeWtApi ..> vApiTs
deployApi ..> vApiTs
acmeGitApi ..> vApiTs
upstreamApi ..> vApiTs

note "NOT linked: develop:b2c4d8e:src/api.ts\n(different content on develop branch)" as N1

acmeCommit ..> vCommit
upstreamCommit ..> vCommit

acmeBranch ..> vBranch
upstreamBranch ..> vBranch

acmeTag ..> vTag
upstreamTag ..> vTag

acmeRemote ..> vRemote
upstreamRemote ..> vRemote

acmeTree ..> vTree
upstreamTree ..> vTree

acmeSubmod ..> vSubmod
@enduml

How the directory virtual type works:

The directory virtual resource for src/ exists because the src/ directory has identical recursive content across multiple physical locations. Its children include both fs-directory resources (physical directories on disk) and git-tree resources (tree objects in git's object database). The system detects directory equivalence by computing a Merkle hash over the sorted child content hashes. If someone adds a file to the deploy directory but not the git checkout's worktree, the Merkle hashes diverge, and local/acme-deploy:src/ is unlinked from the directory virtual parent.

How the file virtual type works:

The file virtual type links physical resources that represent the same file — identical content bytes, filename, and permissions. It bridges across layers: an fs-file on disk and a git-tree-entry in git's tree are linked when they have matching content. This is the primary cross-layer bridge. When content diverges (e.g., an uncommitted edit), the virtual link is broken.

How commit works across repos:

When two git resources share history (e.g., a fork and its upstream), the same commit hash will appear in both repos' branches. The commit virtual type links these — the commit object a7f3e21 in the local repo and a7f3e21 in the upstream repo are the same commit, and the virtual parent captures this identity.

Divergence scenario:

If a developer edits src/api.ts in the deploy directory (/opt/deploy/acme-dashboard/src/api.ts), the system detects the content hash change and:

  1. Unlinks local/acme-deploy:src/api.ts from file: api.ts (sha256:e1d3...8a9b) (content no longer matches).
  2. Unlinks local/acme-deploy:src/ from directory: src/ (merkle:3d4f...a2b1) (directory contents no longer identical — the Merkle hash has changed).
  3. The git checkout's worktree files and git-tree-entry resources remain linked (their content hasn't changed).
  4. If the edit makes the deploy file match some other known content hash, a new virtual link may be created.

Additional resource types (databases, APIs, cloud infrastructure, etc.) can be added as custom resource types via agents resource type add.

Cloud Infrastructure Resource Types

Cloud infrastructure types follow a hierarchical model with two layers:

  1. Generic cloud base types (cloud-*) — provider-agnostic abstractions for common cloud concepts (compute, network, storage, IAM, observability, messaging, containers). These are abstract (not user-addable) and serve as inheritance roots.
  2. Provider-specific types (e.g., aws-*) — concrete types that inherit from the generic base layer and model a specific provider's resource hierarchy.

Generic Cloud Base Types (19 types — abstract, not user-addable):

Type Category Description
cloud-account Structure Cloud provider account or subscription
cloud-region Structure Geographic region within an account
cloud-network Network Virtual network (VPC, VNet, etc.)
cloud-subnet Network Subnet within a virtual network
cloud-security-group Network Network security rules / firewall group
cloud-load-balancer Network Network load balancer
cloud-compute-instance Compute Virtual machine or compute instance
cloud-object-store Storage Object / blob storage bucket
cloud-block-storage Storage Block storage volume
cloud-identity-principal IAM IAM user or service principal
cloud-role IAM IAM role
cloud-policy IAM IAM or access policy document
cloud-log-group Observability Log aggregation group
cloud-alarm Observability Monitoring alarm or alert
cloud-queue Messaging Message queue
cloud-topic Messaging Pub/sub notification topic
cloud-container-repo Containers Container image registry / repository
cloud-container-cluster Containers Container orchestration cluster
cloud-container-service Containers Container workload / service

AWS Provider Types (39 types — inheriting from generic base where applicable):

Type Inherits User Addable Parent Types Category
aws-account cloud-account yes (root) Account
aws-region cloud-region no aws-account Structure
aws-vpc cloud-network no aws-region Network
aws-subnet cloud-subnet no aws-vpc Network
aws-igw no aws-vpc Network
aws-nat-gw no aws-subnet, aws-vpc Network
aws-route-table no aws-vpc Network
aws-nacl no aws-vpc Network
aws-security-group cloud-security-group no aws-vpc Network
aws-alb cloud-load-balancer no aws-vpc Network
aws-nlb cloud-load-balancer no aws-vpc Network
aws-target-group no aws-vpc Network
aws-listener no aws-alb, aws-nlb Network
aws-ec2-instance cloud-compute-instance no aws-subnet, aws-region Compute
aws-ami no aws-region Compute
aws-launch-template no aws-region Compute
aws-asg no aws-region Compute
aws-s3-bucket cloud-object-store no aws-region Storage
aws-ebs-volume cloud-block-storage no aws-region Storage
aws-efs-filesystem no aws-region Storage
aws-iam-user cloud-identity-principal no aws-account IAM
aws-iam-role cloud-role no aws-account IAM
aws-iam-policy cloud-policy no aws-account IAM
aws-iam-instance-profile no aws-account IAM
aws-cloudwatch-log-group cloud-log-group no aws-region Observability
aws-cloudwatch-alarm cloud-alarm no aws-region Observability
aws-cloudwatch-metric no aws-region Observability
aws-eventbridge-bus no aws-region Observability
aws-eventbridge-rule no aws-eventbridge-bus Observability
aws-eventbridge-target no aws-eventbridge-rule Observability
aws-sqs-queue cloud-queue no aws-region Messaging
aws-sns-topic cloud-topic no aws-region Messaging
aws-sns-subscription no aws-sns-topic Messaging
aws-ecr-repo cloud-container-repo no aws-region Containers
aws-ecs-cluster cloud-container-cluster no aws-region Containers
aws-ecs-service cloud-container-service no aws-ecs-cluster Containers
aws-ecs-task-def no aws-region Containers
aws-eks-cluster cloud-container-cluster no aws-region Containers
aws-eks-nodegroup no aws-eks-cluster Containers

GCP and Azure are registered as flat provider-level types inheriting from cloud-account. Full hierarchies for these providers are deferred to future PRs.

Only aws-account is user-addable — it is the top-level entry point carrying credential CLI args (--access-key-id, --secret-access-key, --session-token, --region, --profile). All other AWS types are children discovered or created within the account/region/VPC containment hierarchy.

Cloud resource execution is stubbed — the handler validates configuration and resolves credentials but raises NotImplementedError for actual sandbox provisioning. Cloud SDK integration is planned for a future milestone.

Database Resource Types

Built-in database types use the transaction_rollback sandbox strategy:

Type User Addable Sandbox Description
postgres yes transaction_rollback PostgreSQL database connection
mysql yes transaction_rollback MySQL database connection
sqlite yes transaction_rollback SQLite database file
duckdb yes transaction_rollback DuckDB database (file-based or in-memory)

Networked databases (postgres, mysql) accept --connection-string, --host, --port, --dbname, --user, --password CLI args. File-based databases (sqlite, duckdb) accept --path. Database hierarchy restructuring (inheritance from a generic database base type) is deferred to a separate effort.

Custom Resource Types

Custom resource types are defined in YAML configuration files and registered via agents resource type add. Once registered, a custom type automatically becomes available as a new subcommand under agents resource add.


# File: resource-types/database.yaml
cleveragents:
  version: "3.0"

resource_type: name: local/database description: "A SQL database (PostgreSQL, MySQL, SQLite, etc.)" physical_or_virtual: physical user_addable: true

# CLI arguments for agents resource add local/database cli_arguments: - name: connection-string type: string required: true description: "Database connection string (e.g., postgresql://host/dbname)" validation: pattern: "^(postgresql|mysql|sqlite)://" - name: schema type: string required: false description: "Default schema to use" - name: read-only type: boolean required: false default: false description: "Whether the database should be treated as read-only"

# Sandbox and handler sandbox_strategy: transaction_rollback handler: DatabaseHandler checkpointable: true

# Allowed parent types (empty means can be top-level) allowed_parent_types: []

# Child types child_types: - type: local/db-schema auto_discover: true manual_link: false description: "Discovered database schemas" - type: local/db-table auto_discover: true manual_link: false description: "Discovered tables within schemas"

# Capabilities capabilities: readable: true writable: true sandboxable: true checkpointable: true

When this type is registered:


agents resource type add --config ./resource-types/database.yaml
# Now available: agents resource add local/database <NAME> --connection-string CONN [--schema SCHEMA] [--read-only]

The user_addable field determines whether the type appears as a subcommand. Types with user_addable: false are only auto-generated as children — for example, git-remote, git-branch, git-commit, and git-tree-entry are never created directly by users but are discovered when a git (or git-checkout) resource is registered.

The Resource DAG

Resources form a directed acyclic graph (DAG), not a simple tree. A resource can have multiple parents and multiple children, subject to type constraints. The diagram below shows how a git-checkout, a standalone git repo (remote), a standalone fs-directory, and virtual resources interconnect:

@startuml
skinparam defaultFontSize 10
skinparam objectFontSize 10
skinparam packageStyle rectangle

package "GIT-CHECKOUT: local/app" as GCO #LightBlue {
  object "git-checkout" as gco {
    local/app
  }
  object "git" as gcoGit {
    local/app:repo
  }
  object "git-remote" as gcoRemote {
    origin
  }
  object "git-tag" as gcoTag {
    v1.0.0
  }
  object "git-branch" as gcoBranch {
    main
  }
  object "git-commit" as gcoCommit {
    a1b2c3d
  }
  object "git-tree" as gcoTree {
    e8f1... root
  }
  object "git-tree-entry" as gcoEntry1 {
    README.md
  }
  object "git-tree" as gcoSubtree {
    src/
  }
  object "git-tree-entry" as gcoEntry2 {
    src/app.ts
  }
  object "git-tree-entry" as gcoEntry3 {
    src/main.ts
  }
  object "fs-directory" as gcoFs {
    local/app:worktree
  }
  object "fs-directory" as gcoSrcDir {
    src/
  }
  object "fs-file" as gcoReadme {
    README.md
  }
  object "fs-file" as gcoApp {
    app.ts
  }
  object "fs-file" as gcoMain {
    main.ts
  }
}

package "GIT (remote): local/upstream" as GR #LightYellow {
  object "git" as gr {
    local/upstream
  }
  object "git-remote" as grRemote {
    origin
  }
  object "git-tag" as grTag {
    v1.0.0
  }
  object "git-branch" as grBranch {
    main
  }
  object "git-commit" as grCommit {
    a1b2c3d
  }
  object "git-tree" as grTree {
    e8f1... root
  }
  object "git-tree-entry" as grEntry1 {
    README.md
  }
  object "git-tree" as grSubtree {
    src/
  }
  object "git-tree-entry" as grEntry2 {
    src/app.ts
  }
  object "git-tree-entry" as grEntry3 {
    src/main.ts
  }
}

package "STANDALONE FS-DIRECTORY: local/deploy" as SFD #LightGreen {
  object "fs-directory" as sfd {
    /opt/deploy/myapp
  }
  object "fs-directory" as sfdSrcDir {
    src/
  }
  object "fs-file" as sfdReadme {
    README.md
  }
  object "fs-file" as sfdApp {
    app.ts
  }
  object "fs-file" as sfdMain {
    main.ts
  }
}

package "VIRTUAL LAYER" as VL #Lavender {
  object "file" as vFile {
    app.ts@v1
  }
  object "directory" as vDir {
    src/@v1
  }
  object "commit" as vCommit {
    a1b2c3d
  }
  object "branch" as vBranch {
    main@a1b...
  }
  object "tag" as vTag {
    v1.0.0
  }
  object "remote" as vRemote {
    github.com/org/repo
  }
  object "tree" as vTree {
    e8f1...9d2a
  }
}

' Physical hierarchy
gco --> gcoGit
gco --> gcoFs
gcoGit --> gcoRemote
gcoGit --> gcoTag
gcoGit --> gcoBranch
gcoBranch --> gcoCommit
gcoCommit --> gcoTree
gcoTree --> gcoEntry1
gcoTree --> gcoSubtree
gcoSubtree --> gcoEntry2
gcoSubtree --> gcoEntry3
gcoFs --> gcoSrcDir
gcoFs --> gcoReadme
gcoSrcDir --> gcoApp
gcoSrcDir --> gcoMain

gr --> grRemote
gr --> grTag
gr --> grBranch
grBranch --> grCommit
grCommit --> grTree
grTree --> grEntry1
grTree --> grSubtree
grSubtree --> grEntry2
grSubtree --> grEntry3

sfd --> sfdSrcDir
sfd --> sfdReadme
sfdSrcDir --> sfdApp
sfdSrcDir --> sfdMain

' Virtual equivalence links
gcoApp ..> vFile
sfdApp ..> vFile
gcoEntry2 ..> vFile
grEntry2 ..> vFile
gcoSrcDir ..> vDir
sfdSrcDir ..> vDir
gcoSubtree ..> vDir
grSubtree ..> vDir
gcoCommit ..> vCommit
grCommit ..> vCommit
gcoBranch ..> vBranch
grBranch ..> vBranch
gcoTag ..> vTag
grTag ..> vTag
gcoRemote ..> vRemote
grRemote ..> vRemote
gcoTree ..> vTree
grTree ..> vTree
@enduml

Key properties of the DAG:

  1. Multiple parents: A git-tree-entry has a git-tree parent (git structure) and potentially a file virtual parent (content identity). An fs-file has an fs-directory parent (filesystem hierarchy) and potentially a file virtual parent. An fs-directory can be a child of git-checkout (as its worktree root), a child of fs-mount (as the mount root), a child of another fs-directory (as a subdirectory), a child of a directory virtual (identity), or a standalone user-registered resource.
  2. Multiple children: A git-checkout has a git child and an fs-directory child. A git resource has git-remote, git-branch, git-tag, git-stash, git-submodule, and git-commit children. A git-commit has a git-tree child. A git-tree has git-tree-entry and nested git-tree children.
  3. Cycles are forbidden: The graph is always a DAG. The system validates this when links are created.
  4. Type constraints: Not any resource can be a child of any other — the parent's resource type defines which child types are allowed (see the Allowed Parents / Allowed Children columns in the built-in types tables).
  5. Cross-layer bridge: Virtual resources link equivalent physical resources from different layers (git structure, filesystem, different repos). The file virtual type bridges fs-file + git-tree-entry. The directory virtual type bridges fs-directory + git-tree. The commit, branch, tag, remote, submodule, and tree virtual types link the same git structural element across different repositories.
Purpose of the Resource DAG

!!! adr "Architecture Decision" The operational semantics of the resource DAG — what the DAG is for at runtime — are defined in ADR-036: Resource DAG Operational Semantics. Tool reachability and access projection are defined in ADR-037. Cross-mechanism coordination is defined in ADR-038.

The DAG is not just a structural record of resource relationships — it is the topology of the system's operational world. Every runtime decision about how to reach a resource, how to safely change it, and what else is affected flows through the DAG. The DAG answers five fundamental questions:

Question DAG Feature Runtime Operation
What is this? Virtual resource identity (hub node linking equivalents) Equivalence class queries, divergence detection
Where does it live? Physical manifestations and containment hierarchy Auto-discovery, resource registration
How do I reach it? Tool binding → containment edges → access projection Forward/inverse reachability, read/write routing
How do I safely change it? Sandbox boundaries and cross-mechanism coordination Sandbox boundary algebra, coherence-aware commit
What else is affected? Descendant invalidation, ancestor propagation, sibling sync Change propagation, access control propagation

These decompose into ten specific operational purposes:

  1. Tool reachability: Knowing which tools can reach a resource transitively through containment. A tool bound to a git-checkout can reach every fs-file descendant. The inverse query — given a file, what tools can reach it — requires walking up containment edges.

  2. Equivalence and alternative paths: Virtual resources link physical resources that represent the same logical identity. When one path is unavailable, the system finds alternatives through equivalence.

  3. Read/write routing: Different physical manifestations of the same virtual resource offer different access richness. An lsp-document provides semantic reads (types, diagnostics); an fs-file via git-checkout provides sandbox-tracked writes. The DAG enables routing reads through the richest path and writes through the canonical sandbox-tracked path.

  4. Sandbox boundary algebra: Not every resource is independently sandboxable. A file's sandbox boundary is its containing git-checkout or fs-directory. All resources sharing a sandbox boundary share one sandbox instance. The function sandbox_boundary(r) walks up containment edges to find the nearest sandboxable ancestor.

  5. Cross-mechanism sandbox coordination: When the same virtual resource has physical manifestations in different sandbox domains (e.g., git-checkout and container mount), writes through one path must be coordinated with the other at commit time. The coherence property (transparent, cached, independent) on physical-to-virtual edges determines what coordination is needed.

  6. Dependency ordering: The DAG provides topological orderings for lifecycle operations — sandbox creation (top-down), commit (bottom-up), rollback (top-down), cleanup (bottom-up), auto-discovery (top-down).

  7. Change propagation: When a file changes, the DAG determines what else is affected: parent directories need status updates, virtual parents need identity re-evaluation, equivalent physical siblings need cache invalidation.

  8. Access control propagation: A read_only constraint on a git-checkout propagates to all descendant files. The effective writability of a resource is the conjunction of its own writability and all its containment ancestors' writability.

  9. Auto-discovery scope: The DAG defines the cascade structure for resource auto-discovery — directories before files, git repos before branches, containers before mounts.

  10. Tool capability inference: A tool's effective write scope is bounded by the sandbox domain of its bound resource. A tool bound to an fs-file cannot create sibling directories; a tool bound to a git-checkout can reach the entire worktree.

Physical vs Virtual Resources

Resources are either physical or virtual, a distinction determined by their resource type.

Physical resources are specific, concrete manifestations. Each physical resource is a particular instance that exists somewhere — a file at a particular path on a particular machine, a git repository at a particular URL on a particular server, a commit in a particular repo. Physical resources can be directly read and written by tools. Most resources are physical.

Virtual resources represent an abstract identity that links equivalent physical resources. They answer the question: "where else does this same thing exist?" A file virtual resource links all the physical fs-file and git-tree-entry resources that have the same content, filename, and permissions. A commit virtual resource links git-commit resources across repos that share the same commit hash. Virtual resources have no location of their own and cannot be directly read or written.

Rules for the physical/virtual boundary:

  1. The children of a virtual resource can be physical resources, other virtual resources, or a combination of both.
  2. A physical resource's parents can be either physical or virtual resources.
  3. Not all physical resources need a virtual parent. Virtual resource linking is optional and only applies when equivalence tracking is meaningful.

Equivalence linking:

Two physical resources share a virtual parent when they are equivalent by the virtual type's criteria. Each virtual type has its own equivalence semantics:

  • file: Physical files (fs-file or git-tree-entry) share a file parent when they have the same content bytes (SHA-256), the same filename, and the same permissions. This is the primary cross-layer bridge — it links filesystem files with git blob entries. Example: local/app:worktree:src/main.py (an fs-file) and local/app:repo:main:a1b...:src/main.py (a git-tree-entry) both have the same content in a clean checkout, so they share a file virtual parent.

  • directory: Physical directories (fs-directory) and git trees (git-tree) share a directory parent when their full recursive contents are equivalent. This bridges the filesystem and git layers — the src/ directory on disk and the src/ subtree in git's tree object can be linked when their contents match.

  • symlink: Physical symlinks (fs-symlink) and git tree entries with symlink mode (git-tree-entry mode 120000) share a symlink parent when they have the same name and target.

  • commit: Physical commits (git-commit) in different repos share a commit parent when they have the same commit hash. This is common when repos share history (e.g., a fork and its upstream).

  • branch: Physical branches (git-branch) in different repos share a branch parent when they have the same name and the same HEAD commit hash. A push or local commit on one repo causes divergence.

  • tag: Physical tags (git-tag) in different repos share a tag parent when they have the same tag name and target object.

  • remote: Physical remotes (git-remote) in different repos share a remote parent when they point to the same URL. Answers: "these repos share the same upstream."

  • submodule: Physical submodules (git-submodule) in different repos share a submodule parent when they have the same submodule URL and path.

  • tree: Physical tree objects (git-tree) in different commits or repos share a tree parent when they have the same tree hash — identical directory structure and contents.

Divergence detection:

When a physical resource's content changes (e.g., a file is edited, a directory gains a new file, a branch advances), the system detects that it may no longer match its virtual parent's identity. At that point:

  • If the edit causes the physical resource to diverge from all other physical siblings under the same virtual parent, the physical resource is unlinked from that virtual parent.
  • If the edit makes it match a different virtual resource's identity, it may be re-linked.
  • Content equivalence is tracked via content hashing: SHA-256 for files, Merkle hashes for directories, tree hashes for git trees, commit hashes for commits, HEAD+name for branches, name+target for tags, URL for remotes.
  • Cascading divergence: When a file link breaks, the parent directory virtual resource is also re-evaluated (since its identity depends on all child identities).

This enables powerful queries like:

  • "What physical locations exist for this file content?" → Find all physical resources sharing the file virtual parent.
  • "What tools can edit this file?" → Find all tools with resource bindings compatible with the physical resource types.
  • "Has this file been modified in any location?" → Check if all physical siblings still share the same file virtual parent.
  • "Are these two repos in sync?" → Check if their branches share a branch virtual parent.
  • "Which directories are identical across deployments?" → Find directory virtual resources with multiple physical children.
  • "What submodules are shared across projects?" → Find submodule virtual resources with multiple physical children.
Lazy Virtual Node Materialization

!!! adr "Architecture Decision" Lazy virtual node materialization is defined in ADR-038: Cross-Mechanism Sandbox Coordination.

Virtual resource nodes are not created eagerly for every physical resource. They are materialized lazily when a second physical manifestation sharing the same identity is discovered.

Lifecycle:

  1. Single manifestation: A physical resource exists with no virtual parent. It has a content hash but no equivalence link. This is the common case — most files exist in only one location.

  2. Second manifestation discovered: When a new physical resource is registered (or auto-discovered) and its identity matches an existing physical resource's identity (per the virtual type's equivalence rule), a virtual node is created and both physical resources are linked as children.

  3. Additional manifestations: Subsequent physical resources matching the same identity are linked to the existing virtual node.

  4. Manifestation removed: When a physical resource is deregistered or diverges, its edge to the virtual parent is removed. If only one physical child remains, the virtual node is removed (collapsed back to single manifestation). If zero remain, the virtual node is removed entirely.

Each virtual type defines an identity function mapping physical resources to canonical identity keys:

Virtual Type Identity Key
file SHA-256(content) + filename + permissions
directory Merkle hash of recursive child identities
commit Commit hash
branch Branch name + HEAD commit hash
tag Tag name + target object
remote Normalized URL
submodule Submodule URL + path
tree Tree hash
symlink Symlink name + target path

Materialization is triggered by: resource registration, auto-discovery, content change detection, and on-demand refresh.

Coherence Property

!!! adr "Architecture Decision" The coherence property and cross-mechanism coordination protocol are defined in ADR-038: Cross-Mechanism Sandbox Coordination.

Every edge from a physical resource to its virtual parent carries a coherence property describing how changes to this physical manifestation relate to other manifestations of the same virtual identity:

Coherence Meaning Sync Action at Commit
transparent Changes are immediately visible in sibling manifestations (shared storage). No action — verify by content hash.
cached The sibling caches content; changes require an explicit refresh signal. Send invalidation signal (e.g., LSP workspace/didChangeWatchedFiles).
independent Siblings are fully independent copies; changes are invisible until explicit sync. Copy content from canonical target, or unlink the sibling (accept divergence).

Common coherence assignments:

Scenario Coherence
fs-file via git-checkout ↔ fs-file via container bind mount transparent (same inode through bind mount)
fs-file via git-checkout ↔ fs-file via devcontainer workspace bind mount transparent (default devcontainer behavior: bind mount)
fs-file via git-checkout ↔ fs-file via devcontainer workspace volume mount independent (configurable via workspaceMount in devcontainer.json)
fs-filelsp-document cached (LSP buffers file content)
fs-file via git-checkout ↔ fs-file via container volume mount independent (separate copy)
git-commit in repo A ↔ git-commit in repo B independent (same hash but separate stores)
Cross-Mechanism Write Coordination

When a plan modifies a physical resource that has siblings under the same virtual parent (i.e., equivalent physical resources in different sandbox domains), the system coordinates at sandbox commit time using a write-then-sync protocol:

  1. During execution: Tools write through their bound physical resource into its sandbox domain. No cross-domain propagation occurs. Other manifestations see pre-sandbox content.

  2. At commit time: The system identifies dirty virtual resources (virtual resources with at least one modified physical child), checks for conflicts (multiple dirty children of the same virtual), and propagates changes based on coherence.

  3. Conflict resolution: If multiple physical manifestations of the same virtual resource were modified during the same plan:

    • canonical-wins (default): The canonical write target's changes are accepted; others are discarded with a warning.
    • merge: Three-way merge for text content.
    • fail: Reject the commit; surface the conflict for human resolution.
    • last-writer-wins: Accept the most recent modification.

The canonical write target for a virtual resource is the physical manifestation in the strongest sandbox domain (git_worktree > snapshot > copy_on_write > transaction_rollback), with user override available. Writes should be routed to the canonical target whenever possible to avoid conflicts.

Resource Registration (CLI)

Resources are created via agents resource add <type> with type-specific arguments. Auto-discovered children are created automatically:


# Register a checked-out git repository (most common)
agents resource add git-checkout local/api-repo --path /home/user/projects/api-service --branch main
# Auto-discovers: git child (repo metadata, remotes, branches, commits, tree entries)
#                 + fs-directory child (worktree root directory, subdirectories, files)

# Register a git repo via remote URL (no local checkout — exists on remote server) agents resource add git local/upstream --url git@github.com:org/upstream.git # Auto-discovers: remotes, branches, tags, commits, trees, tree entries, stashes, submodules # (no fs-directory — not checked out locally)

# Register a standalone directory (not a git repo — just files on disk) agents resource add fs-directory local/docs --path /opt/docs/api-reference # Auto-discovers: subdirectories, files, symlinks, hardlinks

# Register a standalone filesystem mount (entire volume) agents resource add fs-mount local/data-volume --mount-path /mnt/data # Auto-discovers: root fs-directory, subdirectories, files, symlinks, hardlinks

# Link resources to projects (resources must be registered first) agents project link-resource local/api-service local/api-repo agents project link-resource local/api-service local/docs --read-only

Auto-Discovery

When a resource is registered, its resource type's handler auto-discovers child resources. This process:

  1. Scans the resource to identify children (e.g., a git-checkout handler creates a git child and an fs-directory child for the worktree root; a git handler lists remotes, branches, tags, commits, stashes, and submodules; a git-commit handler creates a root git-tree child; a git-tree handler lists tree entries and subtrees; an fs-mount handler creates a root fs-directory and discovers its contents; an fs-directory handler discovers subdirectories, files, symlinks, and hardlinks; a git-checkout or fs-directory handler additionally detects .devcontainer/devcontainer.json and creates a devcontainer-instance child in discovered state — see ADR-043).
  2. Creates child resource records in the Resource Registry identified by auto-generated ULIDs. Auto-discovered children do not receive names — they are identified by ULID only, even if their resource type is user-addable.
  3. Reuses existing resources: If a discovered child matches an already-registered resource (same type + same value/location), the existing resource is linked as a child rather than creating a duplicate. For example, if local/docs (an fs-directory resource at /home/user/projects/api-service) is already registered and the git-checkout handler discovers its worktree root at the same path, it links to the existing local/docs resource instead of creating a new one.
  4. Links virtual resources: When auto-discovery detects that a physical resource's content matches an existing virtual resource (via content hashing), it links the physical resource as a child of the virtual resource.

Auto-discovered children are marked as auto: true in the DAG, distinguishing them from manually linked children. Auto-discovered links cannot be manually unlinked (they are managed by the handler), while manually created links can be freely managed.

Auto-discovery runs:

  • At registration time (agents resource add)
  • On refresh (when the system detects changes, e.g., new commits, new files)
  • On demand (when a tool accesses the resource and the handler detects staleness)
Devcontainer Auto-Discovery

!!! adr "Architecture Decision" Devcontainer auto-discovery is defined in ADR-043: Devcontainer Integration and Container-Project Association.

When git-checkout or fs-directory auto-discovery scans the filesystem tree, if a .devcontainer/devcontainer.json file is found, a devcontainer-instance child resource is created with provisioning_state: discovered. The devcontainer.json is parsed to populate configuration properties (image, features, workspace mount, ports), but no container is built or started — consistent with the system's lazy sandboxing philosophy.

Key behaviors:

  • Lazy activation: The container is only built when a plan first needs to execute a tool inside it. Child resources (container-mount, container-exec-env, container-port) are created on activation, not at discovery time.
  • Multiple devcontainers: Nested .devcontainer/ directories (e.g., per-service devcontainers in a monorepo) each produce separate devcontainer-instance resources. The devcontainer spec supports multiple named configurations via .devcontainer/<name>/devcontainer.json.
  • Workspace mount: The devcontainer's workspace mount defaults to the parent git-checkout path (or fs-directory path). The container-mount relationship is recorded at discovery time but not materialized until activation.
  • Auto-detected execution environment: The discovered devcontainer automatically becomes the default execution environment for tools operating on the parent resource, subject to execution environment precedence rules (see Execution Environment Routing).

Resource Capabilities

Each resource declares its capabilities, derived from its resource type:

Capability Description
readable Whether the resource can be read
writable Whether the resource can be modified
sandboxable Whether the resource supports sandbox isolation
checkpointable Whether the resource supports checkpoint/rollback

These capabilities are used by the tool execution flow to validate that a tool's resource binding is compatible — a tool declaring access: read_write on a resource slot cannot be bound to a read-only resource.

Resource Registry

CleverAgents maintains a Resource Registry — a persistent catalog of all registered resources and their DAG relationships:

@startuml
skinparam classAttributeIconSize 0
skinparam classFontSize 13
skinparam defaultFontSize 12

class ResourceRegistry {
  - resourceIndex : Map<String, ResourceRecord>
  - typeIndex : Map<String, ResourceTypeRecord>
  --
  + add(type, name, properties) : ResourceRecord
  + update(name, properties) : ResourceRecord
  + remove(name) : void
  + lookup(name) : ResourceRecord
  + list(filters) : ResourceRecord[]
  + tree(name, depth) : DAG_subtree
  + link_child(parent, child) : void
  + unlink_child(parent, child) : void
  + refresh(name) : void
  + find_by_content(hash) : ResourceRecord[]
  + find_virtual_parent(resource) : ResourceRecord
}

class ResourceRecord {
  + name : String
  + type : String
  + physical_or_virtual : PhysVirt
  + properties : Map<String, Object>
  + capabilities : Capabilities
  + parents : List<ResourceRecord>
  + children : List<ResourceRecord>
  + content_hash : String
  + linked_projects : List<String>
  + created_at : DateTime
  + updated_at : DateTime
}

class ResourceTypeRecord {
  + name : String
  + description : String
  + source : String
  + physical_or_virtual : PhysVirt
  + user_addable : Boolean
  + cli_arguments : List<CLIArgument>
  + allowed_parent_types : List<String>
  + child_types : Map<String, ChildTypeConfig>
  + sandbox_strategy : String
  + handler : String
  + capabilities : Capabilities
  + config_path : String
}

enum PhysVirt {
  physical
  virtual
}

ResourceRegistry "1" *-- "0..*" ResourceRecord : indexes resources >
ResourceRegistry "1" *-- "0..*" ResourceTypeRecord : indexes types >
ResourceRecord --> PhysVirt
ResourceTypeRecord --> PhysVirt

note right of ResourceRegistry
  **Populated by:**
  - agents resource add CLI
  - Auto-discovery during registration
  - Content-identity linking

  **Consumed by:**
  - Tool binding resolution
  - Sandbox creation
  - Change tracking
  - Project linking
  - Plan validation
end note
@enduml

The Resource Registry persists in the database (local SQLite or server). It works alongside the Tool Registry and Skill Registry.

Resource Sandbox Strategy

Each resource defines its own sandbox strategy, determined by its resource type. This is critical because:

  1. The same resource may be accessed through different tools and skills
  2. Different resource types require different sandboxing approaches
  3. Some resources cannot be sandboxed at all
Resource Type Sandbox Strategy Rollback Mechanism
git-checkout git_worktree Git reset/checkout
git none N/A (represents a repo instance — not directly sandboxable)
fs-mount copy_on_write, filesystem_copy, or overlay Restore from snapshot or delete copy
fs-directory copy_on_write or filesystem_copy Restore from snapshot or delete copy
Custom database types transaction_rollback Transaction rollback
Custom API types none (often not sandboxable) N/A

Filesystem sandbox strategies differ in their prerequisites and tradeoffs:

  • copy_on_write: Leverages the filesystem's native copy-on-write capability (e.g., BTRFS, ZFS). The filesystem preserves original data blocks when edits occur, creating lightweight snapshots without duplicating data upfront. Only available on CoW-capable filesystems.
  • filesystem_copy: Performs an explicit full copy of the resource directory (e.g., via cp). Works on all writable filesystems regardless of CoW support, at the cost of duplicating data upfront.
  • overlay: Uses an overlay filesystem (e.g., OverlayFS) to layer changes on top of the original directory. Writes go to the upper layer while the lower layer remains untouched. Requires OS-level overlay mount support.

The sandbox strategy is inherited by child resources from their parent unless the child type defines its own. For example, git-branch, git-commit, git-tree, and git-tree-entry all inherit from their git ancestor. fs-file, fs-symlink, and fs-hardlink inherit from their fs-directory parent.

Resource Type Sandbox Strategy Rollback Mechanism
container-instance snapshot Container commit/checkpoint
devcontainer-instance (inherits container-instance) snapshot (inherited) Container commit/checkpoint (inherited)
container-volume snapshot Volume snapshot
container-mount (inherits from container-instance) (inherits)
container-exec-env (inherits from container-instance) (inherits)
lsp-server none N/A (server process, not directly sandboxable)
lsp-workspace none (delegates to backing resource) N/A
lsp-document none (delegates to backing file) N/A
Sandbox Boundary Algebra

!!! adr "Architecture Decision" The sandbox boundary algebra is defined in ADR-036: Resource DAG Operational Semantics.

Not every resource in the DAG is independently sandboxable — a file cannot be sandboxed in isolation. Instead, the DAG has sandbox boundaries: specific nodes at which sandboxing is physically implementable. All resources within a sandbox boundary's domain share one sandbox instance.

Definitions:

  • Sandbox boundary: A resource b where b.capabilities.sandboxable == true and b.sandbox_strategy != none. Examples: git-checkout (git_worktree), fs-directory (copy_on_write), container-instance (snapshot).

  • sandbox_boundary(r): The nearest ancestor of r (inclusive) along contains edges that is a sandbox boundary. If r itself is a boundary, returns r. If no boundary exists in r's ancestor chain, returns None (unsandboxable).

  • Sandbox domain: The set of all resources {r : sandbox_boundary(r) == b} — resources whose nearest sandbox boundary is b. All resources in a domain share the same sandbox instance during plan execution.

Properties:

  1. Sandbox domains partition all sandboxable resources into disjoint groups.
  2. The SandboxManager keys sandboxes by (plan_id, sandbox_boundary_id), not by individual resource ID.
  3. Multiple files in the same git checkout share one git_worktree sandbox.
  4. When a virtual resource has physical manifestations in different sandbox domains (e.g., a file in both a git-checkout domain and a container-instance domain), cross-mechanism coordination is needed at commit time (see Cross-Mechanism Write Coordination above).

Dependency ordering for lifecycle operations:

Operation Traversal Direction Rationale
Sandbox creation Top-down (parent before child) Parent must be sandboxed before children can be accessed within it
Sandbox commit Bottom-up (child before parent) Child changes finalized before parent incorporates them
Sandbox rollback Top-down (parent before child) Rolling back parent implicitly rolls back children
Sandbox cleanup Bottom-up (child before parent) Clean up children before removing parent

Lazy Sandboxing

Resources are sandboxed lazily when accessed, not upfront. This is different from indexing — resources are indexed immediately when registered, but sandboxes are only created when execution needs to modify a resource:

  1. A project may link many resources (e.g., git repo + databases + cloud accounts)
  2. A plan may only need to modify one resource
  3. Only the accessed resources are sandboxed
  4. Each plan/child plan has its own sandbox containing only edited resources

This is efficient for large projects where most resources remain untouched.

Resource Access Tracking

The system tracks which tools access which resources through the tool-resource binding system. This tracking happens at multiple levels:

  1. Declaration-time: Tool YAML declares resource slots with type and access mode (see Resource Bindings in the Tools section).
  2. Activation-time: When a tool is activated for a plan, its resource slots are bound to specific resources. The system records these bindings.
  3. Execution-time: Every tool invocation logs which bound resources were actually accessed, what operations were performed (read/write/delete), and what paths or objects were touched.

This enables:

  • Accurate sandbox scoping: Only sandbox resources that will actually be modified.
  • Rollback feasibility analysis: Know exactly which resources were modified and whether they support rollback.
  • Security auditing: Complete record of which tools accessed which resources and how.
  • Cross-resource impact analysis: Determine if changes to a resource affect tools bound to sibling or child resources.
  • Virtual resource consistency: Detect when a physical resource diverges from its virtual parent.

Unified Resource Abstraction Layer

CleverAgents provides a unified abstraction that allows tools to work with any resource type through a consistent interface. This enables:

  1. Resource-agnostic tools: A tool like read_content(path) works whether the path refers to a file, database record, or API endpoint.
  2. Consistent sandbox semantics: All resources support the same sandbox lifecycle (create, read, write, checkpoint, rollback).
  3. Pluggable resource handlers: New resource types can be added by registering custom resource types without modifying existing tools.
  4. Unified change tracking: All resource modifications flow into the same ChangeSet model.
Resource Handler Interface

Every resource type provides a handler that implements this interface:


class ResourceHandler(Protocol):
    """Handler for a specific resource type."""
<span style="color: magenta; font-weight: 600;">def</span> <span style="color: cyan; font-weight: 600;">read</span>(self, path: <span style="color: cyan;">str</span>, sandbox: Sandbox) -&gt; Content:
    <span style="color: #66cc66;">&quot;&quot;&quot;Read content from the sandboxed resource.&quot;&quot;&quot;</span>
    ...

<span style="color: magenta; font-weight: 600;">def</span> <span style="color: cyan; font-weight: 600;">write</span>(self, path: <span style="color: cyan;">str</span>, content: Content, sandbox: Sandbox) -&gt; Change:
    <span style="color: #66cc66;">&quot;&quot;&quot;Write content and return the Change record.&quot;&quot;&quot;</span>
    ...

<span style="color: magenta; font-weight: 600;">def</span> <span style="color: cyan; font-weight: 600;">delete</span>(self, path: <span style="color: cyan;">str</span>, sandbox: Sandbox) -&gt; Change:
    <span style="color: #66cc66;">&quot;&quot;&quot;Delete resource and return the Change record.&quot;&quot;&quot;</span>
    ...

<span style="color: magenta; font-weight: 600;">def</span> <span style="color: cyan; font-weight: 600;"><span style="color: cyan;">list</span></span>(self, pattern: <span style="color: cyan;">str</span>, sandbox: Sandbox) -&gt; <span style="color: cyan;">list</span>[<span style="color: cyan;">str</span>]:
    <span style="color: #66cc66;">&quot;&quot;&quot;List paths matching pattern.&quot;&quot;&quot;</span>
    ...

<span style="color: magenta; font-weight: 600;">def</span> <span style="color: cyan; font-weight: 600;">diff</span>(self, path: <span style="color: cyan;">str</span>, sandbox: Sandbox) -&gt; <span style="color: cyan;">str</span>:
    <span style="color: #66cc66;">&quot;&quot;&quot;Generate diff between sandbox and original state.&quot;&quot;&quot;</span>
    ...

<span style="color: magenta; font-weight: 600;">def</span> <span style="color: cyan; font-weight: 600;">supports_operation</span>(self, operation: OperationType) -&gt; <span style="color: cyan;">bool</span>:
    <span style="color: #66cc66;">&quot;&quot;&quot;Check if this resource supports the given operation.&quot;&quot;&quot;</span>
    ...

<span style="color: magenta; font-weight: 600;">def</span> <span style="color: cyan; font-weight: 600;">discover_children</span>(self, resource: ResourceRecord) -&gt; <span style="color: cyan;">list</span>[ResourceRecord]:
    <span style="color: #66cc66;">&quot;&quot;&quot;Auto-discover child resources (called at registration and refresh).&quot;&quot;&quot;</span>
    ...

<span style="color: magenta; font-weight: 600;">def</span> <span style="color: cyan; font-weight: 600;">content_hash</span>(self, path: <span style="color: cyan;">str</span>, sandbox: Sandbox) -&gt; <span style="color: cyan;">str</span>:
    <span style="color: #66cc66;">&quot;&quot;&quot;Compute content hash for identity tracking.&quot;&quot;&quot;</span>
    ...

<span style="color: magenta; font-weight: 600;">def</span> <span style="color: cyan; font-weight: 600;">create_sandbox</span>(self, resource: ResourceRecord) -&gt; Sandbox:
    <span style="color: #66cc66;">&quot;&quot;&quot;Create a sandbox for this resource using its type&#x27;s strategy.&quot;&quot;&quot;</span>
    ...

<span style="color: magenta; font-weight: 600;">def</span> <span style="color: cyan; font-weight: 600;">create_checkpoint</span>(self, sandbox: Sandbox) -&gt; Checkpoint:
    <span style="color: #66cc66;">&quot;&quot;&quot;Create a checkpoint within the sandbox.&quot;&quot;&quot;</span>
    ...

<span style="color: magenta; font-weight: 600;">def</span> <span style="color: cyan; font-weight: 600;">rollback_to</span>(self, sandbox: Sandbox, checkpoint: Checkpoint) -&gt; <span style="color: magenta; font-weight: 600;">None</span>:
    <span style="color: #66cc66;">&quot;&quot;&quot;Roll back sandbox state to a checkpoint.&quot;&quot;&quot;</span>
    ...

<span style="color: magenta; font-weight: 600;">def</span> <span style="color: cyan; font-weight: 600;">project_access</span>(
    self,
    binding_resource: ResourceRecord,
    target_resource: ResourceRecord,
    containment_path: <span style="color: cyan;">list</span>[ResourceRecord],
    sandbox: Sandbox | <span style="color: magenta; font-weight: 600;">None</span>,
) -&gt; AccessProjection | <span style="color: magenta; font-weight: 600;">None</span>:
    <span style="color: #66cc66;">&quot;&quot;&quot;Compute how to reach target_resource from binding_resource.

    Returns an AccessProjection with access_path, protocol,
    crosses_sandbox flag, and read_richness score. Returns None
    if this handler cannot project access to the target type.
    Used by the tool reachability and read/write routing system.
    &quot;&quot;&quot;</span>
    ...

Built-in Resource Handlers
Resource Type Handler Read Write Delete Sandbox Strategy
git-checkout GitCheckoutHandler git_worktree
git GitHandler none
git-commit, git-tree, git-tree-entry GitObjectHandler (inherits)
git-branch, git-tag, git-stash GitRefHandler (inherits)
git-remote, git-submodule GitConfigHandler (inherits)
fs-mount FilesystemHandler copy_on_write
fs-directory FilesystemHandler copy_on_write
fs-file, fs-symlink, fs-hardlink FilesystemHandler (inherits)
container-runtime ContainerRuntimeHandler none
container-image ContainerImageHandler none
container-instance ContainerInstanceHandler snapshot
devcontainer-instance (inherits container-instance) DevcontainerHandler snapshot (inherited)
container-mount, container-exec-env, container-port ContainerChildHandler varies (inherits)
container-volume ContainerVolumeHandler snapshot
container-network ContainerNetworkHandler none
executable ExecutableHandler none
lsp-server LSPServerHandler none
lsp-workspace LSPWorkspaceHandler none
lsp-document LSPDocumentHandler none

Additional handlers are provided by custom resource types when they are registered.

!!! adr "Architecture Decision" Container resource types are defined in ADR-039: Container and Execution Environment Resource Types. The devcontainer-instance subtype, container-project association patterns, and execution environment routing are defined in ADR-043: Devcontainer Integration and Container-Project Association. Resource type inheritance (the inherits mechanism) is defined in ADR-042: Resource Type Inheritance. LSP resource types are defined in ADR-040: LSP Resource Types.

Resource Path Resolution

Paths in tool invocations are resolved through a resource routing system that uses tool-resource bindings to determine the target resource:

sequenceDiagram
    participant Tool as Tool Invocation
    participant Router as Resource Router
    participant Handler as GitCheckoutHandler

    Tool->>Router: edit_file(path='src/main.py', ...)
    Router->>Router: Check tool's resource bindings
    Note right of Router: slot 'repo' bound to<br/>local/api-repo (git-checkout)
    Router->>Router: Resolve path within bound resource
    Note right of Router: local/api-repo:worktree:src/main.py
    Router->>Handler: Route to resource handler
    Handler->>Handler: Resolve path to sandbox worktree
    Handler->>Handler: Operate on sandboxed state
    Handler-->>Tool: Return Change record

When a tool has multiple resource slots bound, the path scheme or slot name disambiguates:


# Explicit slot reference
path://repo/src/main.py      → routes to the "repo" slot's bound resource
path://docs/api/readme.md    → routes to the "docs" slot's bound resource

Default: unqualified paths route to the tool's primary resource slot

src/main.py → routes to the first (or only) resource slot

Context

!!! adr "Architecture Decision" The Advanced Context Management System, UKO ontology, CRP protocol, and context strategies are defined in ADR-014: Context Management (ACMS).

The Advanced Context Management System (ACMS) is CleverAgents' fully pluggable, strategy-driven framework for assembling context for actors. It replaces and subsumes the basic context tier system with a comprehensive architecture built around three core innovations:

  1. A Universal Knowledge Ontology (UKO) -- an inheritance-based RDF ontology hierarchy that represents any resource (source code, documents, databases, infrastructure) at multiple levels of abstraction, with full provenance back to the originating resource and temporal versioning across revisions.

  2. A Context Request Protocol (CRP) -- a structured vocabulary through which actors (via skills) declare what information they need, at what level of detail, and with what scope, while they are reasoning. This replaces static "context view" approaches with a dynamic, demand-driven model.

  3. A Context Assembly Pipeline -- a pluggable, 10-component pipeline that replaces the former monolithic Strategy Coordinator and Fusion Engine. Multiple independent context strategies can be registered, each working with different data backends and different abstraction levels of the UKO. The pipeline orchestrates strategy selection, budget allocation, parallel execution, fragment deduplication, depth resolution, scoring, budget packing, ordering, preamble generation, and skeleton compression -- each step backed by a replaceable Protocol implementation configurable at global, project, or plan scope.

The system is designed around the hierarchical nature of plans: parent plans see wide, shallow context (breadth); child plans see narrow, deep context (depth). Context inheritance flows parent-to-child with progressive focusing, and every context assembly respects a dynamically-changing token budget. This architecture is domain-agnostic: the same breadth/depth/DetailDepth mechanics work identically whether the underlying resources are source code repositories, technical documents, database schemas, or infrastructure configurations.

Critical Design Decision: All indexing happens immediately when resources are added to projects or when code changes. There is no "on-demand" indexing during agent execution. This ensures that agents always have instant access to search capabilities without any indexing delays. The computational cost is paid once upfront, not repeatedly during agent operations.

The full architectural design of the ACMS -- including the UKO ontology hierarchy, backend abstraction layer, Context Assembly Pipeline (the 10-component pluggable pipeline that replaced the former Strategy Coordinator and Fusion Engine), index synchronization, performance characteristics, and plugin architecture -- is specified in the Architecture > ACMS Architecture section.

Core Data Types

The ACMS defines several data types that are used throughout the system by actors, skills, strategies, and the plan lifecycle.

DetailDepth and DetailLevelMap

At the most general level (UKO Layer 0), detail depth is simply a non-negative integer — 0 being the most minimal representation and each increment revealing more. There is no fixed upper bound; the maximum meaningful depth depends on the domain and the complexity of the information unit. This is the DetailDepth type.

Each UKO domain extension then registers a DetailLevelMap — a table of named labels mapped to specific integer depths. This allows domain users to work with meaningful names (like MODULE_LISTING or TABLE_OF_CONTENTS) rather than raw integers, while the underlying system always operates on the universal integer scale. Maps are inherited: a language-specific map includes all levels from its parent paradigm map, which includes all levels from the general code map.


DetailDepth = int  # Non-negative integer, 0 = most minimal, no upper bound

@dataclass class DetailLevelMap: """Maps named detail levels to integer depths for a UKO domain. Inherited: a child map includes all entries from its parent map, and may insert additional levels at any integer position.""" domain: str # UKO namespace (e.g., "uko-code:", "uko-py:") parent: DetailLevelMap | None # Parent map to inherit from levels: dict[str, int] # Named level -> integer depth max_depth: int # Maximum meaningful depth for this domain

<span style="color: cyan; font-weight: 600;">def</span> <span style="color: #66cc66;">resolve</span>(<span style="color: #5599ff;">self</span>, depth: <span style="color: #66cc66;">int | str</span>) -> <span style="color: #66cc66;">int</span>:
    <span style="color: #888;">&quot;&quot;&quot;Resolve a named level or integer to an integer depth.&quot;&quot;&quot;</span>
    <span style="color: cyan; font-weight: 600;">if</span> <span style="color: #66cc66;">isinstance</span>(depth, <span style="color: #66cc66;">int</span>):
        <span style="color: cyan; font-weight: 600;">return</span> <span style="color: #66cc66;">min</span>(depth, <span style="color: #5599ff;">self</span>.max_depth)
    <span style="color: #888;"># Look up named level in this map, then parent maps</span>
    <span style="color: cyan; font-weight: 600;">if</span> depth <span style="color: cyan; font-weight: 600;">in</span> <span style="color: #5599ff;">self</span>.levels:
        <span style="color: cyan; font-weight: 600;">return</span> <span style="color: #5599ff;">self</span>.levels[depth]
    <span style="color: cyan; font-weight: 600;">if</span> <span style="color: #5599ff;">self</span>.parent:
        <span style="color: cyan; font-weight: 600;">return</span> <span style="color: #5599ff;">self</span>.parent.resolve(depth)
    <span style="color: cyan; font-weight: 600;">raise</span> <span style="color: #ff6666;">ValueError</span>(<span style="color: #66cc66;">f&quot;Unknown detail level: {depth}&quot;</span>)

Universal (Layer 0) Semantics:

At the universal level, each integer depth has a domain-agnostic meaning. The principle is that depth 0 answers "what exists?", and each subsequent depth answers progressively more detailed questions:

Depth Universal Question Answered What Gets Included
0 What exists? Just the name/identifier of the information unit.
1 How is it organized? Names of immediate children (structural skeleton).
2 What are the key relationships? Children + dependency/reference edges to other units.
3 What is each thing's purpose? + Short descriptions/summaries for each child.
4 What is the structural shape? + Type information, size/count metadata, categories.
5+ How does it work? What does it say? Progressively more content, up to complete verbatim content at max depth.
max Everything. Complete content — nothing omitted.

The exact number of meaningful depths varies by domain (source code may have 12+ levels, a flat config file may only have 4). The system never forces content into a fixed number of buckets.

Source Code DetailLevelMap (uko-code:, extended by uko-oo:, uko-py:, etc.):

The general software domain defines a base set of named levels. Paradigm-specific and language-specific extensions insert additional levels where their semantics provide meaningful distinctions.

Depth uko-code: Name Content Shown
0 MODULE_LISTING Module/package names only, no internal structure.
1 MODULE_GRAPH Module names + inter-module dependency edges (imports graph).
2 MEMBER_LISTING + Names of top-level members within each module (classes, functions, constants) — no signatures.
3 MEMBER_SUMMARY + One-line docstring or LLM-generated summary for each member.
4 SIGNATURES + Full type signatures (parameter names, types, return types) for all callables and type definitions.
5 SIGNATURES_WITH_DOCS + Complete docstrings/comments attached to each member.
6 STRUCTURAL_OUTLINE + Control flow structure within callable bodies (branches, loops, try/except) shown as outline, no expressions.
7 KEY_LOGIC + Key expressions: return statements, assertions, assignments to important variables.
8 NEAR_COMPLETE + All statements except comments, logging, and boilerplate (imports, __all__, etc.).
9 FULL_SOURCE Complete source code — nothing omitted.

Object-Oriented effective map (uko-oo:) — inherits from uko-code:, inserts CLASS_HIERARCHY and VISIBILITY_ANNOTATED:

When the uko-oo: extension is active, the effective DetailLevelMap is re-numbered with consecutive integers. The two inserted levels shift all subsequent depths upward:

Depth Name Origin Content Shown
0 MODULE_LISTING uko-code: Module/package names only, no internal structure.
1 MODULE_GRAPH uko-code: Module names + inter-module dependency edges (imports graph).
2 MEMBER_LISTING uko-code: + Names of top-level members within each module (classes, functions, constants) — no signatures.
3 CLASS_HIERARCHY uko-oo: Inheritance chains and interface implementation relationships between classes.
4 MEMBER_SUMMARY uko-code: + One-line docstring or LLM-generated summary for each member.
5 SIGNATURES uko-code: + Full type signatures (parameter names, types, return types) for all callables and type definitions.
6 SIGNATURES_WITH_DOCS uko-code: + Complete docstrings/comments attached to each member.
7 VISIBILITY_ANNOTATED uko-oo: Public/protected/private modifiers on all members + abstract/final markers.
8 STRUCTURAL_OUTLINE uko-code: + Control flow structure within callable bodies (branches, loops, try/except) shown as outline, no expressions.
9 KEY_LOGIC uko-code: + Key expressions: return statements, assertions, assignments to important variables.
10 NEAR_COMPLETE uko-code: + All statements except comments, logging, and boilerplate (imports, __all__, etc.).
11 FULL_SOURCE uko-code: Complete source code — nothing omitted.

Python-specific effective map (uko-py:) — inherits from uko-oo:, inserts DECORATED_SIGNATURES, TYPE_STUBS, and appends WITH_TESTS:

When the uko-py: extension is active, the effective map builds on uko-oo: and re-numbers again:

Depth Name Origin Content Shown
0 MODULE_LISTING uko-code: Module/package names only, no internal structure.
1 MODULE_GRAPH uko-code: Module names + inter-module dependency edges (imports graph).
2 MEMBER_LISTING uko-code: + Names of top-level members within each module (classes, functions, constants) — no signatures.
3 CLASS_HIERARCHY uko-oo: Inheritance chains and interface implementation relationships between classes.
4 MEMBER_SUMMARY uko-code: + One-line docstring or LLM-generated summary for each member.
5 SIGNATURES uko-code: + Full type signatures (parameter names, types, return types) for all callables and type definitions.
6 SIGNATURES_WITH_DOCS uko-code: + Complete docstrings/comments attached to each member.
7 DECORATED_SIGNATURES uko-py: @property, @staticmethod, @classmethod, custom decorator chains shown on each member.
8 VISIBILITY_ANNOTATED uko-oo: Public/protected/private modifiers on all members + abstract/final markers.
9 STRUCTURAL_OUTLINE uko-code: + Control flow structure within callable bodies (branches, loops, try/except) shown as outline, no expressions.
10 KEY_LOGIC uko-code: + Key expressions: return statements, assertions, assignments to important variables.
11 TYPE_STUBS uko-py: Type annotations from .pyi stub files merged with source, typing module usage.
12 NEAR_COMPLETE uko-code: + All statements except comments, logging, and boilerplate (imports, __all__, etc.).
13 FULL_SOURCE uko-code: Complete source code — nothing omitted.
14 WITH_TESTS uko-py: Full source + associated test cases for each callable (from uko-code:testsCallable edges).

Document DetailLevelMap (uko-doc:):

Depth uko-doc: Name Content Shown
0 TITLE_ONLY Document title / section heading only.
1 TABLE_OF_CONTENTS_L1 Title + top-level (depth-1) section/chapter headings.
2 TABLE_OF_CONTENTS_L2 + Second-level subsection headings.
3 FULL_TOC Complete table of contents to all heading depths.
4 TOC_WITH_SUMMARIES Full TOC + one-sentence abstract per section (LLM-generated or extracted from first paragraph).
5 SECTION_OVERVIEWS + Topic keywords per section + cross-reference targets (which other sections this section discusses).
6 TOPIC_SENTENCES + First sentence of every paragraph within each section.
7 PARAGRAPH_SUMMARIES + LLM-generated summary for each paragraph (2-3 sentences compressed to 1).
8 STRUCTURAL_DETAIL + All figure/table captions + code block headers + list item first lines + blockquote sources.
9 NEAR_COMPLETE + Full paragraph text, but with inline formatting stripped and code blocks truncated to first/last 3 lines.
10 FULL_CONTENT Complete document content — all text, formatting, code blocks, figures, footnotes.

Database DetailLevelMap (uko-data:):

Depth uko-data: Name Content Shown
0 SCHEMA_LISTING Schema/database names only.
1 TABLE_LISTING + Table and view names within each schema.
2 COLUMN_LISTING + Column names within each table (no types).
3 TYPED_COLUMNS + Column data types + nullability.
4 CONSTRAINTS + Primary keys, unique constraints, check constraints, defaults.
5 RELATIONSHIPS + Foreign key relationships (full referential graph between tables).
6 INDEXES_AND_STATS + Index definitions + estimated row counts + basic statistics (cardinality, avg row size).
7 DDL Full CREATE TABLE / CREATE VIEW DDL (reconstructed from metadata).
8 DDL_WITH_TRIGGERS + Trigger definitions + stored procedure signatures that reference each table.
9 FULL_PROCEDURES + Complete stored procedure and function bodies.
10 WITH_SAMPLE_DATA + Sample rows (configurable N, default 5) for each table.
11 FULL_CATALOG Complete catalog: DDL + all procedure bodies + triggers + sample data + value distribution histograms + query plan statistics.

Infrastructure DetailLevelMap (uko-infra:):

Depth uko-infra: Name Content Shown
0 SERVICE_LISTING Service/component names only.
1 SERVICE_GRAPH + Service dependency edges (which services connect to which).
2 ENDPOINT_LISTING + Endpoint paths/ports for each service.
3 ENDPOINT_DETAIL + HTTP methods, parameters, authentication requirements per endpoint.
4 CONFIG_KEYS + Configuration key names and their sections.
5 CONFIG_VALUES + Configuration values (with secrets masked).
6 RESOURCE_LIMITS + Resource allocations (CPU, memory, replicas, storage).
7 FULL_CONFIG Complete configuration files for each service.
8 WITH_DEPLOYMENT + Deployment descriptors (Dockerfiles, Kubernetes manifests, Compose files).

How depth resolution works in practice: When a context request specifies depth="SIGNATURES" and the target node is a uko-py:Module, the system looks up SIGNATURES in the uko-py: DetailLevelMap first. If found, it uses that integer. If not, it walks up to uko-oo:, then uko-code:, then uko: until it finds a match. If the request specifies depth=4 (a raw integer), it is used directly — the system renders the target node at integer depth 4 regardless of what named level that corresponds to.

ContextRequest

A structured request for context, issued by an actor or skill via the CRP.


@dataclass
class ContextRequest:
    """A structured request for context, issued by an actor or skill."""
<span style="color: #888;"># === What to find ===</span>
query: <span style="color: cyan;">str</span> | <span style="color: cyan;">None</span> = <span style="color: magenta; font-weight: 600;">None</span>          <span style="color: #888;"># Natural language query</span>
entities: <span style="color: cyan;">list</span>[<span style="color: cyan;">str</span>] = field(default_factory=<span style="color: cyan;">list</span>)  <span style="color: #888;"># Named entities to focus on</span>
uko_types: <span style="color: cyan;">list</span>[<span style="color: cyan;">str</span>] = field(default_factory=<span style="color: cyan;">list</span>)  <span style="color: #888;"># UKO types to filter</span>

<span style="color: #888;"># === Scope control ===</span>
focus: <span style="color: cyan;">list</span>[<span style="color: cyan;">str</span>] = field(default_factory=<span style="color: cyan;">list</span>)
<span style="color: #888;"># URIs or identifiers of specific items to focus on</span>

breadth: <span style="color: cyan;">int</span> = <span style="color: yellow;">2</span>
<span style="color: #888;"># How many hops outward in the dependency/reference graph.</span>

depth: <span style="color: cyan;">int</span> | <span style="color: cyan;">str</span> = <span style="color: yellow;">3</span>
<span style="color: #888;"># How much detail to include for each item found.</span>
<span style="color: #888;"># May be a raw integer (0-N) or a named level string (e.g., "SIGNATURES")</span>
<span style="color: #888;"># resolved via the active DetailLevelMap for the target node's UKO type.</span>

<span style="color: #888;"># === Focus depth gradient ===</span>
depth_gradient: <span style="color: cyan;">bool</span> = <span style="color: magenta; font-weight: 600;">True</span>
<span style="color: #888;"># When True, items closer to the focus get more detail.</span>

<span style="color: #888;"># === Temporal scope ===</span>
temporal: <span style="color: cyan; font-weight: 600;">TemporalScope</span> = <span style="color: cyan; font-weight: 600;">TemporalScope</span>.CURRENT

<span style="color: #888;"># === Budget ===</span>
max_tokens: <span style="color: cyan;">int</span> | <span style="color: cyan;">None</span> = <span style="color: magenta; font-weight: 600;">None</span>

<span style="color: #888;"># === Strategy hints ===</span>
preferred_strategies: <span style="color: cyan;">list</span>[<span style="color: cyan;">str</span>] = field(default_factory=<span style="color: cyan;">list</span>)
required_backends: <span style="color: cyan;">list</span>[<span style="color: cyan;">str</span>] = field(default_factory=<span style="color: cyan;">list</span>)

<span style="color: #888;"># === Priority and purpose ===</span>
priority: <span style="color: cyan;">float</span> = <span style="color: yellow;">0.5</span>           <span style="color: #888;"># 0.0 = background, 1.0 = critical</span>
purpose: <span style="color: cyan;">str</span> = <span style="color: #66cc66;">""</span>               <span style="color: #888;"># Why is this context needed?</span>

ContextFragment

The atomic unit of context returned by strategies and consumed by the Context Assembly Pipeline's fusion phase (FragmentDeduplicator, DetailDepthResolver, FragmentScorer, BudgetPacker, FragmentOrderer).


@dataclass
class ContextFragment:
    """A single piece of context assembled by a strategy."""
    uko_node: str                     # UKO URI of the source node
    content: str                      # Rendered text content
    detail_depth: int                 # Resolved integer depth of this fragment
    token_count: int                  # Token count of content
    relevance_score: float            # 0.0-1.0 relevance to the request
    provenance: FragmentProvenance     # Trace back to resource + location
    metadata: dict = field(default_factory=dict)
AssembledContext

The output of a context assembly cycle -- the final, budget-respecting payload delivered to an actor.


@dataclass
class AssembledContext:
    """The fused, budget-respecting context payload."""
    fragments: list[ContextFragment]   # Ordered context fragments
    total_tokens: int                  # Total token count
    budget_used: float                 # Fraction of budget consumed (0.0-1.0)
    strategies_used: list[str]         # Which strategies contributed
    context_hash: str                  # Cryptographic hash for snapshot
    preamble: str | None               # Optional structure summary
    provenance_map: dict               # Fragment -> resource/location mapping
    skeleton_fragments: tuple[ContextFragment, ...] = ()  # Compressed parent context for child plan inheritance

Context Request Protocol (CRP)

Instead of static context views, actors (through their skills) actively request context as they reason. The CRP defines a structured vocabulary for these requests.

This is fundamentally different from existing approaches where context is assembled once before the actor runs. With CRP:

  1. The actor starts with an initial context (assembled from the plan's context view, parent context inheritance, and strategy defaults).
  2. As the actor reasons, skills can issue refinement requests to pull in additional context dynamically.
  3. Each request triggers a new context assembly cycle with the remaining token budget.
The builtin/context Skill

Actors issue context requests through a context skill that is automatically injected into every actor's skill set:

# Built-in skill, always available
skill:
  name: builtin/context
  description: "Request additional context during reasoning"

anonymous_tools: - name: request_context description: "Request specific context to be added to the conversation" input_schema: type: object properties: query: { type: string, description: "What information do you need?" } focus: { type: array, items: { type: string }, description: "Specific files, classes, or functions to focus on" } breadth: { type: integer, default: 2, description: "How many dependency hops outward (0-5)" } depth: { oneOf: [{ type: integer, minimum: 0 }, { type: string }], default: 3, description: "Detail depth — integer (0-N) or named level from the active DetailLevelMap (e.g., 'SIGNATURES', 'FULL_SOURCE')" } purpose: { type: string, description: "Why do you need this context?" } required: [purpose]

- <span style="color: cyan; font-weight: 600;">name</span>: <span style="color: #66cc66;">query_history</span>
  <span style="color: cyan; font-weight: 600;">description</span>: <span style="color: #66cc66;">"Query historical context about past decisions and changes"</span>
  <span style="color: cyan; font-weight: 600;">input_schema</span>:
    <span style="color: cyan; font-weight: 600;">type</span>: <span style="color: #66cc66;">object</span>
    <span style="color: cyan; font-weight: 600;">properties</span>:
      <span style="color: cyan; font-weight: 600;">query</span>: { <span style="color: cyan; font-weight: 600;">type</span>: <span style="color: #66cc66;">string</span> }
      <span style="color: cyan; font-weight: 600;">scope</span>: { <span style="color: cyan; font-weight: 600;">type</span>: <span style="color: #66cc66;">string</span>, <span style="color: cyan; font-weight: 600;">enum</span>: [<span style="color: #66cc66;">"current_plan"</span>, <span style="color: #66cc66;">"plan_tree"</span>, <span style="color: #66cc66;">"all_plans"</span>],
               <span style="color: cyan; font-weight: 600;">default</span>: <span style="color: #66cc66;">"plan_tree"</span> }
    <span style="color: cyan; font-weight: 600;">required</span>: [<span style="color: #66cc66;">query</span>]

- <span style="color: cyan; font-weight: 600;">name</span>: <span style="color: #66cc66;">get_context_budget</span>
  <span style="color: cyan; font-weight: 600;">description</span>: <span style="color: #66cc66;">"Check remaining context token budget"</span>
  <span style="color: cyan; font-weight: 600;">input_schema</span>:
    <span style="color: cyan; font-weight: 600;">type</span>: <span style="color: #66cc66;">object</span>
    <span style="color: cyan; font-weight: 600;">properties</span>: {}

Context Strategy Protocol

Every context strategy implements this protocol. Strategies are pluggable -- built-in strategies ship with CleverAgents, and custom strategies can be registered via configuration.

@runtime_checkable
class ContextStrategy(Protocol):
    """A pluggable context assembly strategy."""
<span style="color: cyan;">@property</span>
<span style="color: magenta; font-weight: 600;">def</span> <span style="color: cyan; font-weight: 600;">name</span>(<span style="color: cyan;">self</span>) -> <span style="color: cyan;">str</span>: ...

<span style="color: cyan;">@property</span>
<span style="color: magenta; font-weight: 600;">def</span> <span style="color: cyan; font-weight: 600;">capabilities</span>(<span style="color: cyan;">self</span>) -> <span style="color: cyan;">StrategyCapabilities</span>: ...

<span style="color: magenta; font-weight: 600;">def</span> <span style="color: cyan; font-weight: 600;">can_handle</span>(<span style="color: cyan;">self</span>, request: <span style="color: cyan;">ContextRequest</span>,
               backends: <span style="color: cyan;">BackendSet</span>) -> <span style="color: cyan;">float</span>:
    <span style="color: #66cc66;">"""Returns 0.0-1.0 confidence that this strategy can usefully
    contribute to this request."""</span>
    ...

<span style="color: magenta; font-weight: 600;">def</span> <span style="color: cyan; font-weight: 600;">assemble</span>(<span style="color: cyan;">self</span>, request: <span style="color: cyan;">ContextRequest</span>,
             backends: <span style="color: cyan;">BackendSet</span>,
             budget: <span style="color: cyan;">int</span>,
             plan_context: <span style="color: cyan;">PlanContext</span>) -> <span style="color: cyan;">list</span>[<span style="color: cyan;">ContextFragment</span>]:
    <span style="color: #66cc66;">"""Execute the strategy. Must respect the budget."""</span>
    ...

<span style="color: magenta; font-weight: 600;">def</span> <span style="color: cyan; font-weight: 600;">explain</span>(<span style="color: cyan;">self</span>) -> <span style="color: cyan;">str</span>: ...

@dataclass
class StrategyCapabilities:
    """Declares what a strategy is capable of."""
    uses_text: bool = False
    uses_vector: bool = False
    uses_graph: bool = False
    uses_temporal: bool = False
    uko_levels: list[str] = field(default_factory=list)
    resource_types: list[str] = field(default_factory=list)
    supports_depth_breadth: bool = False
    supports_plan_hierarchy: bool = False
    supports_temporal: bool = False
    quality_score: float = 0.5
Built-in Strategies
Strategy Quality Backends Required Description
simple-keyword 0.3 Text only Basic keyword/regex text search. Universal fallback.
semantic-embedding 0.6 Vector Vector similarity search for semantically related content.
breadth-depth-navigator 0.85 Graph Graph-aware UKO traversal with depth/breadth projection. Primary strategy for code-aware context.
arce 0.95 All Multi-modal pipeline combining text, vector, and graph with intent analysis. Highest quality.
temporal-archaeology 0.5 Graph + Cold tier Historical pattern discovery from past decisions and archived context.
plan-decision-context 0.7 Warm/Cold tiers Retrieves context from parent/ancestor plan decisions.
Strategy Registration

Strategies are registered through configuration. The context assembly pipeline itself is also fully pluggable — see Architecture > ACMS > Context Assembly Pipeline for the ten pluggable pipeline components.

[context.strategies]
enabled = ["simple-keyword", "semantic-embedding", "arce", "breadth-depth-navigator"]

[context.strategies.custom] "my-domain-strategy" = "my_package.strategies.DomainStrategy"

# Pipeline component overrides (see Architecture > ACMS > Context Assembly Pipeline) [context.pipeline] strategy-selector = "builtin:ConfidenceWeightedSelector" # default budget-allocator = "builtin:ProportionalBudgetAllocator" # default fragment-scorer = "my_extensions.scorers:DomainAwareScorer" # custom override

Hierarchical Plan Context

In a plan hierarchy, each level sees a different slice of the project's resources. Context inheritance flows parent-to-child with progressive focusing. This applies universally across all UKO domains — the same breadth/depth mechanics work for codebases, documents, databases, and infrastructure.

Source Code Example:

Level Breadth Depth Example Content
Root Entire project 0 (MODULE_LISTING) Module names, dependency graph edges
Subplan A src/auth/ module + direct deps 4 (SIGNATURES) Function signatures with types, class inheritance chains
Sub-subplan A1 AuthManager class + 2-hop deps 9 (FULL_SOURCE) Complete function bodies, all parameters, all call sites

Document Example (e.g., a technical specification):

Level Breadth Depth Example Content
Root Entire document 1 (TABLE_OF_CONTENTS_L1) Document title + top-level chapter headings
Subplan A "Architecture" chapter + sections that discussesTopic it 5 (SECTION_OVERVIEWS) Section headings + topic keywords + cross-reference targets + one-sentence abstracts
Sub-subplan A1 "ACMS Architecture" section + 2-hop topic references 10 (FULL_CONTENT) Complete section text with all paragraphs, figures, code blocks

Database Example (e.g., schema migration planning):

Level Breadth Depth Example Content
Root Entire database 1 (TABLE_LISTING) Schema names + table and view names
Subplan A auth schema + tables with foreign keys into it 5 (RELATIONSHIPS) Table names + typed columns + constraints + foreign key graph
Sub-subplan A1 users table + dependent views and stored procedures 9 (FULL_PROCEDURES) Complete DDL + triggers + stored procedure bodies + sample data

The skeleton is a compact, low-depth representation (typically depth 0-1) of the parent's context window that is passed to every child. This ensures children always have the "big picture" available, even though they focus on a narrow area. The skeleton typically consumes 5-15% of the child's token budget (controlled by skeleton_ratio). For a source code resource, the skeleton is a MODULE_LISTING (depth 0) — just module names. For a document resource, the skeleton is a TABLE_OF_CONTENTS_L1 (depth 1) — the table of contents. For a database resource, it is a TABLE_LISTING (depth 1) — schema and table names.

Depth/Breadth Projection

The depth/breadth projection system allows fine-grained control over how much of the knowledge graph is materialized into context. It operates on the UKO graph structure.

  • Breadth (integer, 0-N): How many hops in the uko:references / uko:dependsOn / uko:contains^-1 graph to traverse from the focus items.
  • Depth (integer or named level): How much content to include for each item found. Specified as a raw integer or a named level resolved via the active DetailLevelMap.
  • Depth gradient: When enabled, detail decreases with distance from focus.

Source Code Projection Example:


Request: focus=["class://AuthManager"], breadth=2, depth=9 (FULL_SOURCE), gradient=True

Result (token costs approximate):

Distance 0 — depth 9 (FULL_SOURCE): class AuthManager: # 800 tokens """Manages user authentication...""" def authenticate(self, username, password): ...full body... def validate_token(self, token): ...full body...

Distance 1 — depth 4 (SIGNATURES): class BaseManager: # 120 tokens def connect(self) -> Connection: ... def disconnect(self) -> None: ... class CryptoUtils: # 80 tokens def hash_password(pwd: str) -> str: ... def verify_hash(pwd: str, hash: str) -> bool: ... class UserDB: # 100 tokens def find_user(username: str) -> User | None: ... def create_user(user: User) -> None: ...

Distance 2 — depth 0 (MODULE_LISTING): class Connection # 20 tokens class User # 15 tokens class DatabasePool # 15 tokens

Total: ~1,150 tokens (vs ~15,000 if everything were depth 9)

Document Projection Example:


Request: focus=["uko-doc:section/security-architecture"], breadth=2, depth=10 (FULL_CONTENT), gradient=True

Result (token costs approximate):

Distance 0 — depth 10 (FULL_CONTENT): ## 5. Security Architecture # 2,400 tokens Complete section text including all paragraphs, code examples, diagrams, and subsections: 5.1 Authentication Flow 5.2 Authorization Model 5.3 Token Management

Distance 1 — depth 6 (TOPIC_SENTENCES — sections that discuss security topics): ## 3. API Design # 180 tokens Section headings + first sentence of each paragraph: "The API uses JWT tokens for authentication..." "Rate limiting is enforced per-client..." ## 8. Deployment # 120 tokens "TLS termination occurs at the load balancer..." "Secrets are injected via Vault..."

Distance 2 — depth 0 (TITLE_ONLY — sections referenced by distance-1 sections): ## 2. System Overview # 30 tokens ## 9. Monitoring # 25 tokens ## Appendix A: Threat Model # 20 tokens

Total: ~2,775 tokens (vs ~18,000 if the entire document were depth 10)

Database Projection Example:


Request: focus=["uko-data:table/auth.users"], breadth=2, depth=11 (FULL_CATALOG), gradient=True

Result (token costs approximate):

Distance 0 — depth 11 (FULL_CATALOG): CREATE TABLE auth.users ( # 650 tokens id UUID PRIMARY KEY DEFAULT gen_random_uuid(), email VARCHAR(255) UNIQUE NOT NULL, password_hash VARCHAR(60) NOT NULL, ...complete DDL + triggers + sample rows + statistics... );

Distance 1 — depth 7 (DDL — tables with foreign keys to/from users): CREATE TABLE auth.sessions ( # 180 tokens id UUID PRIMARY KEY, user_id UUID REFERENCES auth.users(id), ...DDL with constraints... ); CREATE TABLE auth.roles ( # 120 tokens ...DDL with constraints... ); CREATE VIEW auth.active_users AS ... # 90 tokens

Distance 2 — depth 1 (TABLE_LISTING — tables referenced by distance-1 tables): auth.permissions # 20 tokens auth.audit_log # 15 tokens public.organizations # 15 tokens

Total: ~1,090 tokens (vs ~8,500 if the entire schema were depth 11)

Integration with Plan Lifecycle


Plan Lifecycle                        ACMS Actions
─────────────────                     ────────────────────────────
Plan created (agents plan use)        ResourceScope resolved.
                                      ScopedBackendViews created.

Strategize phase begins InitialContextAssembler runs: - Inherits parent context (if subplan) - Runs Context Assembly Pipeline - Produces AssembledContext - Injects into actor's system prompt

Strategy actor reasons Actor may issue ContextRequests via builtin/context skill. Each request triggers re-assembly with remaining budget.

Strategy actor produces decisions Each Decision's context_snapshot captures AssembledContext hash + provenance map.

Execute phase begins New InitialContextAssembler run with execute-phase view. Decisions from Strategize are in warm tier.

Execution actor works Dynamic ContextRequests as needed.

Subplan spawned PlanContextInheritance computes child context from parent. SkeletonCompressor propagates parent context as skeleton. New ResourceScope (possibly narrower).

Apply phase Minimal context assembly (validation results, diff summary).

Plan completes Hot context archived to warm. Warm context ages to cold based on retention policy.

Output Rendering Framework

!!! adr "Architecture Decision" The output rendering framework, format system, and reactive output sessions are defined in ADR-021: CLI and Output Rendering.

Overview

CleverAgents uses a unified Output Rendering Framework to decouple command output data from its visual presentation. Every CLI command produces structured output through a common abstraction layer, and the active format determines how that output is rendered to the terminal (or piped to external consumers). The format is set via the global --format flag, the format config key, or defaults to rich.

The framework is reactive-first: commands do not build a static data structure and hand it to a renderer. Instead, commands open an output session, create element handles for each piece of output (a panel, a table, a progress indicator), and write data to those handles — potentially from multiple concurrent producers. The session coordinates with a materialization strategy selected by the active format, which decides when and how each element's content reaches the terminal. A rich session renders updates in-place as they arrive; a plain session buffers each element and flushes sequentially; a json session accumulates everything and serializes once at the end. Producer code is format-agnostic — it writes to handles without knowing which format is active.

This architecture is designed for modularity, extensibility, and future-proofing — the same session-based output can be consumed by the CLI, a future TUI, a web frontend, or programmatic integrations. The design uses a pipeline of composable stages: session lifecycle management, typed element handles, event-driven materialization, and format-specific element rendering.

Architecture

Rendering Pipeline

All CLI output flows through a five-stage reactive pipeline:

flowchart LR
    A["Command Logic"] --> B["OutputSession\n(lifecycle)"]
    B --> C["ElementHandles\n(typed producers)"]
    C --> D["MaterializationStrategy\n(format-driven policy)"]
    D --> E["Terminal / Pipe\n(stdout/stderr)"]
  1. Command Logic opens an OutputSession and creates typed element handlesPanelHandle, TableHandle, ProgressHandle, etc. — for each piece of output the command will produce. Handles are created in declaration order, which determines the canonical order in which elements appear in sequential formats.

  2. OutputSession is the central coordinator. It owns the set of active handles, tracks their lifecycle (open → writing → closed), emits ElementEvent objects to the active materialization strategy, and provides a snapshot() method that returns a static StructuredOutput representing the accumulated state at any point in time.

  3. ElementHandles are the producer-facing API. Each handle is typed for a specific element kind (panel, table, tree, etc.) and exposes write methods appropriate to that kind (add_row(), set_entry(), set_step_status(), etc.). Handles are thread-safe — multiple concurrent coroutines or threads can write to different handles simultaneously. Handles are format-agnostic — the producer never knows or cares which format is active.

  4. MaterializationStrategy is a polymorphic observer selected by the active format. It receives ElementEvent notifications from the session and decides when and how to render content. Each strategy delegates the actual visual rendering of an element's accumulated state to a paired ElementRenderer.

  5. Terminal/Pipe receives the final byte stream. The framework auto-detects whether stdout is a TTY and degrades gracefully (e.g., rich falls back to table when piped to a non-TTY unless --format rich was explicitly set).

OutputSession

The OutputSession is the core abstraction that replaces direct construction of static output objects. Commands receive a session (typically injected by the CLI framework) and interact with it throughout their execution:


class OutputSession:
    """A live output document that coordinates element production and materialization.
    
    The session manages the lifecycle of all output elements for a single command
    invocation. It is the bridge between format-agnostic producer code and the
    format-specific materialization strategy.
    
    Thread Safety:
        The session is thread-safe. Multiple producers may create and write to
        handles concurrently. The session serializes event delivery to the
        materialization strategy using an internal event queue.
    
    Lifecycle:
        session = OutputSession.open(command, strategy)
        handle_a = session.panel("Title")       # create handles
        handle_b = session.table("Results", ...) 
        handle_a.set_entry(...)                  # write to handles (concurrent OK)
        handle_b.add_row(...)
        handle_a.close()                         # close handles when done
        handle_b.close()
        session.close()                          # finalize the session
    """
<span style="opacity: 0.7;"># --- Session lifecycle ---</span>

command: <span style="color: cyan;">str</span>                                  <span style="opacity: 0.7;"># The command that owns this session</span>
session_id: <span style="color: cyan;">str</span>                               <span style="opacity: 0.7;"># Unique session identifier (ULID)</span>
created_at: <span style="color: cyan;">datetime</span>                          <span style="opacity: 0.7;"># Session creation timestamp</span>

_strategy: MaterializationStrategy            <span style="opacity: 0.7;"># The active materialization strategy</span>
_handles: <span style="color: cyan;">OrderedDict</span>[<span style="color: cyan;">str</span>, ElementHandle]     <span style="opacity: 0.7;"># handle_id → handle, in declaration order</span>
_event_queue: asyncio.Queue[ElementEvent]     <span style="opacity: 0.7;"># Internal event queue for serialization</span>
_state: SessionState                          <span style="opacity: 0.7;"># &quot;open&quot; | &quot;closing&quot; | &quot;closed&quot;</span>
_lock: threading.Lock                         <span style="opacity: 0.7;"># Protects handle creation/removal</span>

<span style="color: yellow;">@classmethod</span>
<span style="color: magenta; font-weight: 600;">def</span> <span style="color: cyan; font-weight: 600;">open</span>(cls, command: <span style="color: cyan;">str</span>, strategy: MaterializationStrategy,
         metadata: <span style="color: cyan;">dict</span> | <span style="color: magenta; font-weight: 600;">None</span> = <span style="color: magenta; font-weight: 600;">None</span>) -&gt; <span style="color: #66cc66;">&quot;OutputSession&quot;</span>:
    <span style="color: #66cc66;">&quot;&quot;&quot;Open a new output session.</span>

Called by the CLI framework before command execution. The strategy is selected based on the resolved format (see Format Resolution). Args: command: The command string (e.g., "project show"). strategy: The materialization strategy for the active format. metadata: Optional command metadata (user, timestamp, etc.). Returns: A new OutputSession ready for element creation. """ ...

<span style="opacity: 0.7;"># --- Element handle factories ---</span>
<span style="opacity: 0.7;"># Each factory creates a typed handle, registers it with the session in</span>
<span style="opacity: 0.7;"># declaration order, and emits an ElementCreated event to the strategy.</span>

<span style="color: magenta; font-weight: 600;">def</span> <span style="color: cyan; font-weight: 600;">panel</span>(self, title: <span style="color: cyan;">str</span>, *,
          border_style: <span style="color: cyan;">str</span> = <span style="color: #66cc66;">&quot;rounded&quot;</span>,
          priority: <span style="color: cyan;">str</span> = <span style="color: #66cc66;">&quot;normal&quot;</span>,
          collapse_hint: <span style="color: cyan;">str</span> = <span style="color: #66cc66;">&quot;auto&quot;</span>,
          metadata: <span style="color: cyan;">dict</span> | <span style="color: magenta; font-weight: 600;">None</span> = <span style="color: magenta; font-weight: 600;">None</span>) -&gt; <span style="color: #66cc66;">&quot;PanelHandle&quot;</span>:
    <span style="color: #66cc66;">&quot;&quot;&quot;Create a panel element handle for key-value pair output.&quot;&quot;&quot;</span>
    ...

<span style="color: magenta; font-weight: 600;">def</span> <span style="color: cyan; font-weight: 600;">table</span>(self, title: <span style="color: cyan;">str</span> | <span style="color: magenta; font-weight: 600;">None</span>, *,
          columns: <span style="color: cyan;">list</span>[<span style="color: #66cc66;">&quot;ColumnDef&quot;</span>],
          summary: <span style="color: cyan;">dict</span> | <span style="color: magenta; font-weight: 600;">None</span> = <span style="color: magenta; font-weight: 600;">None</span>,
          max_rows_hint: <span style="color: cyan;">int</span> | <span style="color: magenta; font-weight: 600;">None</span> = <span style="color: magenta; font-weight: 600;">None</span>,
          sort_key: <span style="color: cyan;">str</span> | <span style="color: magenta; font-weight: 600;">None</span> = <span style="color: magenta; font-weight: 600;">None</span>,
          priority: <span style="color: cyan;">str</span> = <span style="color: #66cc66;">&quot;normal&quot;</span>,
          collapse_hint: <span style="color: cyan;">str</span> = <span style="color: #66cc66;">&quot;auto&quot;</span>,
          metadata: <span style="color: cyan;">dict</span> | <span style="color: magenta; font-weight: 600;">None</span> = <span style="color: magenta; font-weight: 600;">None</span>) -&gt; <span style="color: #66cc66;">&quot;TableHandle&quot;</span>:
    <span style="color: #66cc66;">&quot;&quot;&quot;Create a table element handle for tabular data.&quot;&quot;&quot;</span>
    ...

<span style="color: magenta; font-weight: 600;">def</span> <span style="color: cyan; font-weight: 600;">tree</span>(self, root_label: <span style="color: cyan;">str</span>, *,
         root_style: <span style="color: cyan;">str</span> | <span style="color: magenta; font-weight: 600;">None</span> = <span style="color: magenta; font-weight: 600;">None</span>,
         max_depth_hint: <span style="color: cyan;">int</span> | <span style="color: magenta; font-weight: 600;">None</span> = <span style="color: magenta; font-weight: 600;">None</span>,
         show_guides: <span style="color: cyan;">bool</span> = <span style="color: magenta; font-weight: 600;">True</span>,
         priority: <span style="color: cyan;">str</span> = <span style="color: #66cc66;">&quot;normal&quot;</span>,
         collapse_hint: <span style="color: cyan;">str</span> = <span style="color: #66cc66;">&quot;auto&quot;</span>,
         metadata: <span style="color: cyan;">dict</span> | <span style="color: magenta; font-weight: 600;">None</span> = <span style="color: magenta; font-weight: 600;">None</span>) -&gt; <span style="color: #66cc66;">&quot;TreeHandle&quot;</span>:
    <span style="color: #66cc66;">&quot;&quot;&quot;Create a tree element handle for hierarchical data.&quot;&quot;&quot;</span>
    ...

<span style="color: magenta; font-weight: 600;">def</span> <span style="color: cyan; font-weight: 600;">progress</span>(self, label: <span style="color: cyan;">str</span>, *,
             total: <span style="color: cyan;">int</span> | <span style="color: magenta; font-weight: 600;">None</span> = <span style="color: magenta; font-weight: 600;">None</span>,
             indeterminate: <span style="color: cyan;">bool</span> = <span style="color: magenta; font-weight: 600;">False</span>,
             steps: <span style="color: cyan;">list</span>[<span style="color: cyan;">str</span>] | <span style="color: magenta; font-weight: 600;">None</span> = <span style="color: magenta; font-weight: 600;">None</span>,
             priority: <span style="color: cyan;">str</span> = <span style="color: #66cc66;">&quot;normal&quot;</span>,
             metadata: <span style="color: cyan;">dict</span> | <span style="color: magenta; font-weight: 600;">None</span> = <span style="color: magenta; font-weight: 600;">None</span>) -&gt; <span style="color: #66cc66;">&quot;ProgressHandle&quot;</span>:
    <span style="color: #66cc66;">&quot;&quot;&quot;Create a progress indicator handle.</span>

Args: label: Display label for the progress indicator. total: Total units of work (None for indeterminate). indeterminate: If True, show a spinner instead of a progress bar. steps: Named steps to track (creates ProgressStep objects with initial status "pending"). """ ...

<span style="color: magenta; font-weight: 600;">def</span> <span style="color: cyan; font-weight: 600;">status</span>(self, message: <span style="color: cyan;">str</span>, *,
           level: <span style="color: cyan;">str</span> = <span style="color: #66cc66;">&quot;info&quot;</span>,
           detail: <span style="color: cyan;">str</span> | <span style="color: magenta; font-weight: 600;">None</span> = <span style="color: magenta; font-weight: 600;">None</span>,
           priority: <span style="color: cyan;">str</span> = <span style="color: #66cc66;">&quot;normal&quot;</span>,
           metadata: <span style="color: cyan;">dict</span> | <span style="color: magenta; font-weight: 600;">None</span> = <span style="color: magenta; font-weight: 600;">None</span>) -&gt; <span style="color: #66cc66;">&quot;StatusHandle&quot;</span>:
    <span style="color: #66cc66;">&quot;&quot;&quot;Create a status message handle.&quot;&quot;&quot;</span>
    ...

<span style="color: magenta; font-weight: 600;">def</span> <span style="color: cyan; font-weight: 600;">text</span>(self, content: <span style="color: cyan;">str</span> = <span style="color: #66cc66;">&quot;&quot;</span>, *,
         wrap: <span style="color: cyan;">bool</span> = <span style="color: magenta; font-weight: 600;">True</span>,
         indent: <span style="color: cyan;">int</span> = 0,
         priority: <span style="color: cyan;">str</span> = <span style="color: #66cc66;">&quot;normal&quot;</span>,
         metadata: <span style="color: cyan;">dict</span> | <span style="color: magenta; font-weight: 600;">None</span> = <span style="color: magenta; font-weight: 600;">None</span>) -&gt; <span style="color: #66cc66;">&quot;TextHandle&quot;</span>:
    <span style="color: #66cc66;">&quot;&quot;&quot;Create a text block handle.&quot;&quot;&quot;</span>
    ...

<span style="color: magenta; font-weight: 600;">def</span> <span style="color: cyan; font-weight: 600;">code</span>(self, content: <span style="color: cyan;">str</span> = <span style="color: #66cc66;">&quot;&quot;</span>, *,
         language: <span style="color: cyan;">str</span> | <span style="color: magenta; font-weight: 600;">None</span> = <span style="color: magenta; font-weight: 600;">None</span>,
         line_numbers: <span style="color: cyan;">bool</span> = <span style="color: magenta; font-weight: 600;">False</span>,
         highlight_lines: <span style="color: cyan;">list</span>[<span style="color: cyan;">int</span>] | <span style="color: magenta; font-weight: 600;">None</span> = <span style="color: magenta; font-weight: 600;">None</span>,
         priority: <span style="color: cyan;">str</span> = <span style="color: #66cc66;">&quot;normal&quot;</span>,
         metadata: <span style="color: cyan;">dict</span> | <span style="color: magenta; font-weight: 600;">None</span> = <span style="color: magenta; font-weight: 600;">None</span>) -&gt; <span style="color: #66cc66;">&quot;CodeHandle&quot;</span>:
    <span style="color: #66cc66;">&quot;&quot;&quot;Create a code block handle.&quot;&quot;&quot;</span>
    ...

<span style="color: magenta; font-weight: 600;">def</span> <span style="color: cyan; font-weight: 600;">diff</span>(self, *,
         file_a: <span style="color: cyan;">str</span> | <span style="color: magenta; font-weight: 600;">None</span> = <span style="color: magenta; font-weight: 600;">None</span>,
         file_b: <span style="color: cyan;">str</span> | <span style="color: magenta; font-weight: 600;">None</span> = <span style="color: magenta; font-weight: 600;">None</span>,
         priority: <span style="color: cyan;">str</span> = <span style="color: #66cc66;">&quot;normal&quot;</span>,
         metadata: <span style="color: cyan;">dict</span> | <span style="color: magenta; font-weight: 600;">None</span> = <span style="color: magenta; font-weight: 600;">None</span>) -&gt; <span style="color: #66cc66;">&quot;DiffHandle&quot;</span>:
    <span style="color: #66cc66;">&quot;&quot;&quot;Create a diff block handle.&quot;&quot;&quot;</span>
    ...

<span style="color: magenta; font-weight: 600;">def</span> <span style="color: cyan; font-weight: 600;">separator</span>(self, style: <span style="color: cyan;">str</span> = <span style="color: #66cc66;">&quot;line&quot;</span>) -&gt; <span style="color: #66cc66;">&quot;SeparatorHandle&quot;</span>:
    <span style="color: #66cc66;">&quot;&quot;&quot;Create a visual separator. Separators are auto-closed on creation.&quot;&quot;&quot;</span>
    ...

<span style="color: magenta; font-weight: 600;">def</span> <span style="color: cyan; font-weight: 600;">action_hint</span>(self, commands: <span style="color: cyan;">list</span>[<span style="color: cyan;">str</span>],
                description: <span style="color: cyan;">str</span> | <span style="color: magenta; font-weight: 600;">None</span> = <span style="color: magenta; font-weight: 600;">None</span>) -&gt; <span style="color: #66cc66;">&quot;ActionHintHandle&quot;</span>:
    <span style="color: #66cc66;">&quot;&quot;&quot;Create an action hint. Action hints are auto-closed on creation.&quot;&quot;&quot;</span>
    ...

<span style="opacity: 0.7;"># --- Session operations ---</span>

<span style="color: magenta; font-weight: 600;">def</span> <span style="color: cyan; font-weight: 600;">snapshot</span>(self) -&gt; <span style="color: #66cc66;">&quot;StructuredOutput&quot;</span>:
    <span style="color: #66cc66;">&quot;&quot;&quot;Return a static snapshot of all elements accumulated so far.</span>

The snapshot captures the current state of every handle (open or closed) as a StructuredOutput object. This is used by accumulate-mode strategies (json/yaml) at session end, and is available at any time for logging, debugging, or programmatic inspection. """ ...

<span style="color: magenta; font-weight: 600;">def</span> <span style="color: cyan; font-weight: 600;">close</span>(self, *, exit_code: <span style="color: cyan;">int</span> = 0) -&gt; <span style="color: #66cc66;">&quot;StructuredOutput&quot;</span>:
    <span style="color: #66cc66;">&quot;&quot;&quot;Close the session and finalize all output.</span>

Any handles still open are force-closed (with a warning logged). Emits a SessionEnd event to the strategy. Returns the final StructuredOutput snapshot. Args: exit_code: The command's exit code (included in the snapshot). Returns: The final StructuredOutput snapshot. """ ...

<span style="opacity: 0.7;"># --- Context manager support ---</span>

<span style="color: magenta; font-weight: 600;">def</span> <span style="color: cyan; font-weight: 600;">__enter__</span>(self) -&gt; <span style="color: #66cc66;">&quot;OutputSession&quot;</span>:
    <span style="color: magenta; font-weight: 600;">return</span> self

<span style="color: magenta; font-weight: 600;">def</span> <span style="color: cyan; font-weight: 600;">__exit__</span>(self, exc_type, exc_val, exc_tb) -&gt; <span style="color: magenta; font-weight: 600;">None</span>:
    <span style="color: #66cc66;">&quot;&quot;&quot;Auto-close session on context exit.</span>

If exiting due to an exception, sets exit_code to 1 and emits an error status element before closing. """ ...

Element Handles

Element handles are the producer-facing API. Each handle type wraps a specific element kind and provides methods appropriate to that kind. All handles share a common base:


class ElementHandle(Generic[E]):
    """Base class for all element handles.
    
    An element handle is a write-only view of an output element. Producers use
    handles to incrementally build element content without knowledge of the
    active format or materialization strategy.
    
    Type Parameter:
        E: The OutputElement subclass this handle wraps (e.g., Panel, Table).
    
    Thread Safety:
        Individual handle methods are thread-safe. Multiple threads may call
        methods on *different* handles concurrently. Concurrent writes to the
        *same* handle are serialized via an internal lock.
    
    Lifecycle:
        handle = session.table(...)    # Created by session factory
        handle.add_row(...)            # Write operations (zero or more)
        handle.close()                 # Finalize (required unless auto-closed)
    
    Closed Handle Behavior:
        Calling any write method on a closed handle raises ElementClosedError.
    """
handle_id: <span style="color: cyan;">str</span>                       <span style="opacity: 0.7;"># Unique handle identifier (ULID)</span>
element_type: <span style="color: cyan;">str</span>                    <span style="opacity: 0.7;"># Semantic type (&quot;panel&quot;, &quot;table&quot;, etc.)</span>
declaration_index: <span style="color: cyan;">int</span>               <span style="opacity: 0.7;"># Position in session&#x27;s declaration order</span>

_session: OutputSession              <span style="opacity: 0.7;"># Owning session (for event emission)</span>
_element: E                          <span style="opacity: 0.7;"># The accumulated element state</span>
_state: HandleState                  <span style="opacity: 0.7;"># &quot;open&quot; | &quot;closed&quot;</span>
_lock: threading.Lock                <span style="opacity: 0.7;"># Serializes writes to this handle</span>

<span style="color: magenta; font-weight: 600;">def</span> <span style="color: cyan; font-weight: 600;">close</span>(self) -&gt; <span style="color: magenta; font-weight: 600;">None</span>:
    <span style="color: #66cc66;">&quot;&quot;&quot;Close this handle, signaling that no more data will be written.</span>

Emits an ElementClosed event to the materialization strategy. For buffered strategies, this triggers rendering of the element. """ ...

<span style="color: yellow;">@property</span>
<span style="color: magenta; font-weight: 600;">def</span> <span style="color: cyan; font-weight: 600;">is_open</span>(self) -&gt; <span style="color: cyan;">bool</span>:
    <span style="color: #66cc66;">&quot;&quot;&quot;Whether this handle is still accepting writes.&quot;&quot;&quot;</span>
    ...

<span style="color: yellow;">@property</span>
<span style="color: magenta; font-weight: 600;">def</span> <span style="color: cyan; font-weight: 600;">element</span>(self) -&gt; E:
    <span style="color: #66cc66;">&quot;&quot;&quot;The accumulated element state (read-only snapshot).&quot;&quot;&quot;</span>
    ...

<span style="color: magenta; font-weight: 600;">def</span> <span style="color: cyan; font-weight: 600;">__enter__</span>(self) -&gt; <span style="color: cyan;">Self</span>:
    <span style="color: magenta; font-weight: 600;">return</span> self

<span style="color: magenta; font-weight: 600;">def</span> <span style="color: cyan; font-weight: 600;">__exit__</span>(self, exc_type, exc_val, exc_tb) -&gt; <span style="color: magenta; font-weight: 600;">None</span>:
    <span style="color: #66cc66;">&quot;&quot;&quot;Auto-close handle on context exit.&quot;&quot;&quot;</span>
    <span style="color: magenta; font-weight: 600;">if</span> self.is_open:
        self.close()

class PanelHandle(ElementHandle[Panel]): """Handle for building a Panel element incrementally. Panels are titled groups of key-value pairs. Entries can be added, updated, or removed after creation. """

<span style="color: magenta; font-weight: 600;">def</span> <span style="color: cyan; font-weight: 600;">set_entry</span>(self, key: <span style="color: cyan;">str</span>, value: <span style="color: cyan;">str</span>, *,
              style_hint: <span style="color: cyan;">str</span> | <span style="color: magenta; font-weight: 600;">None</span> = <span style="color: magenta; font-weight: 600;">None</span>,
              icon: <span style="color: cyan;">str</span> | <span style="color: magenta; font-weight: 600;">None</span> = <span style="color: magenta; font-weight: 600;">None</span>) -&gt; <span style="color: magenta; font-weight: 600;">None</span>:
    <span style="color: #66cc66;">&quot;&quot;&quot;Set or update a key-value entry in the panel.</span>

If an entry with the given key already exists, it is updated. Otherwise, a new entry is appended. Emits an ElementUpdated event. """ ...

<span style="color: magenta; font-weight: 600;">def</span> <span style="color: cyan; font-weight: 600;">set_entries</span>(self, entries: <span style="color: cyan;">dict</span>[<span style="color: cyan;">str</span>, <span style="color: cyan;">str</span>], *,
                style_hints: <span style="color: cyan;">dict</span>[<span style="color: cyan;">str</span>, <span style="color: cyan;">str</span>] | <span style="color: magenta; font-weight: 600;">None</span> = <span style="color: magenta; font-weight: 600;">None</span>) -&gt; <span style="color: magenta; font-weight: 600;">None</span>:
    <span style="color: #66cc66;">&quot;&quot;&quot;Set multiple entries at once (batch update). Emits a single event.&quot;&quot;&quot;</span>
    ...

<span style="color: magenta; font-weight: 600;">def</span> <span style="color: cyan; font-weight: 600;">remove_entry</span>(self, key: <span style="color: cyan;">str</span>) -&gt; <span style="color: magenta; font-weight: 600;">None</span>:
    <span style="color: #66cc66;">&quot;&quot;&quot;Remove an entry by key. Emits an ElementUpdated event.&quot;&quot;&quot;</span>
    ...

class TableHandle(ElementHandle[Table]): """Handle for building a Table element incrementally. Tables are the primary element for streamed data. Rows can be added one at a time or in batches as data becomes available from queries, API calls, or concurrent operations. """

<span style="color: magenta; font-weight: 600;">def</span> <span style="color: cyan; font-weight: 600;">add_row</span>(self, row: <span style="color: cyan;">dict</span>) -&gt; <span style="color: magenta; font-weight: 600;">None</span>:
    <span style="color: #66cc66;">&quot;&quot;&quot;Append a single row to the table.</span>

The row dict maps column names to cell values. Missing columns are filled with None. Extra columns not in the schema are ignored. Emits an ElementUpdated event (type=row_added). """ ...

<span style="color: magenta; font-weight: 600;">def</span> <span style="color: cyan; font-weight: 600;">add_rows</span>(self, rows: <span style="color: cyan;">list</span>[<span style="color: cyan;">dict</span>]) -&gt; <span style="color: magenta; font-weight: 600;">None</span>:
    <span style="color: #66cc66;">&quot;&quot;&quot;Append multiple rows in a batch. Emits a single ElementUpdated event.&quot;&quot;&quot;</span>
    ...

<span style="color: magenta; font-weight: 600;">def</span> <span style="color: cyan; font-weight: 600;">set_summary</span>(self, summary: <span style="color: cyan;">dict</span>) -&gt; <span style="color: magenta; font-weight: 600;">None</span>:
    <span style="color: #66cc66;">&quot;&quot;&quot;Set or update the summary/aggregation row. Emits an ElementUpdated event.&quot;&quot;&quot;</span>
    ...

<span style="color: magenta; font-weight: 600;">def</span> <span style="color: cyan; font-weight: 600;">set_sort_key</span>(self, column: <span style="color: cyan;">str</span>, *, descending: <span style="color: cyan;">bool</span> = <span style="color: magenta; font-weight: 600;">False</span>) -&gt; <span style="color: magenta; font-weight: 600;">None</span>:
    <span style="color: #66cc66;">&quot;&quot;&quot;Change the sort key. Emits an ElementUpdated event.&quot;&quot;&quot;</span>
    ...

class TreeHandle(ElementHandle[Tree]): """Handle for building a Tree element incrementally. Trees are built by adding child nodes to existing nodes. The root node is created with the handle. Subtrees can be constructed incrementally as hierarchical data is discovered. """

<span style="color: magenta; font-weight: 600;">def</span> <span style="color: cyan; font-weight: 600;">add_child</span>(self, parent_path: <span style="color: cyan;">str</span> | <span style="color: magenta; font-weight: 600;">None</span>, label: <span style="color: cyan;">str</span>, *,
              style_hint: <span style="color: cyan;">str</span> | <span style="color: magenta; font-weight: 600;">None</span> = <span style="color: magenta; font-weight: 600;">None</span>,
              collapsed: <span style="color: cyan;">bool</span> = <span style="color: magenta; font-weight: 600;">False</span>,
              metadata: <span style="color: cyan;">dict</span> | <span style="color: magenta; font-weight: 600;">None</span> = <span style="color: magenta; font-weight: 600;">None</span>) -&gt; <span style="color: cyan;">str</span>:
    <span style="color: #66cc66;">&quot;&quot;&quot;Add a child node to the tree.</span>

Args: parent_path: Slash-separated path to the parent node (None = root). label: Display label for the new node. style_hint: Optional color/style hint. collapsed: Whether this node starts collapsed in interactive renderers. metadata: Arbitrary data attached to the node. Returns: The full path to the newly created node (for use as parent_path in subsequent add_child calls). Emits an ElementUpdated event. """ ...

<span style="color: magenta; font-weight: 600;">def</span> <span style="color: cyan; font-weight: 600;">set_node_style</span>(self, path: <span style="color: cyan;">str</span>, style_hint: <span style="color: cyan;">str</span>) -&gt; <span style="color: magenta; font-weight: 600;">None</span>:
    <span style="color: #66cc66;">&quot;&quot;&quot;Update the style of an existing node. Emits an ElementUpdated event.&quot;&quot;&quot;</span>
    ...

class ProgressHandle(ElementHandle[ProgressIndicator]): """Handle for updating a ProgressIndicator element. Progress handles are unique in that they are expected to receive many rapid updates. The materialization strategy may throttle update events to avoid overwhelming the terminal (e.g., limiting redraws to 10/sec). """

<span style="color: magenta; font-weight: 600;">def</span> <span style="color: cyan; font-weight: 600;">set_progress</span>(self, current: <span style="color: cyan;">int</span>, total: <span style="color: cyan;">int</span> | <span style="color: magenta; font-weight: 600;">None</span> = <span style="color: magenta; font-weight: 600;">None</span>) -&gt; <span style="color: magenta; font-weight: 600;">None</span>:
    <span style="color: #66cc66;">&quot;&quot;&quot;Update the progress counter.</span>

Args: current: Current progress value. total: Total value (can change, e.g., when total is discovered late). Emits an ElementUpdated event (may be throttled by the strategy). """ ...

<span style="color: magenta; font-weight: 600;">def</span> <span style="color: cyan; font-weight: 600;">set_step_status</span>(self, step_label: <span style="color: cyan;">str</span>, status: <span style="color: cyan;">str</span>) -&gt; <span style="color: magenta; font-weight: 600;">None</span>:
    <span style="color: #66cc66;">&quot;&quot;&quot;Update the status of a named step.</span>

Args: step_label: The label of the step to update. status: New status — "pending" | "active" | "done" | "error" | "skipped". Emits an ElementUpdated event. """ ...

<span style="color: magenta; font-weight: 600;">def</span> <span style="color: cyan; font-weight: 600;">set_label</span>(self, label: <span style="color: cyan;">str</span>) -&gt; <span style="color: magenta; font-weight: 600;">None</span>:
    <span style="color: #66cc66;">&quot;&quot;&quot;Update the progress label text. Emits an ElementUpdated event.&quot;&quot;&quot;</span>
    ...

<span style="color: magenta; font-weight: 600;">def</span> <span style="color: cyan; font-weight: 600;">increment</span>(self, delta: <span style="color: cyan;">int</span> = 1) -&gt; <span style="color: magenta; font-weight: 600;">None</span>:
    <span style="color: #66cc66;">&quot;&quot;&quot;Increment progress by delta. Convenience wrapper around set_progress.&quot;&quot;&quot;</span>
    ...

class StatusHandle(ElementHandle[StatusMessage]): """Handle for a status message. Status handles are typically created and immediately closed (fire-and-forget messages). However, they can be kept open for messages that may be revised (e.g., a "Working..." status that becomes "Done" or "Failed"). """

<span style="color: magenta; font-weight: 600;">def</span> <span style="color: cyan; font-weight: 600;">set_message</span>(self, message: <span style="color: cyan;">str</span>) -&gt; <span style="color: magenta; font-weight: 600;">None</span>:
    <span style="color: #66cc66;">&quot;&quot;&quot;Update the status message text. Emits an ElementUpdated event.&quot;&quot;&quot;</span>
    ...

<span style="color: magenta; font-weight: 600;">def</span> <span style="color: cyan; font-weight: 600;">set_level</span>(self, level: <span style="color: cyan;">str</span>) -&gt; <span style="color: magenta; font-weight: 600;">None</span>:
    <span style="color: #66cc66;">&quot;&quot;&quot;Change the status level. Emits an ElementUpdated event.&quot;&quot;&quot;</span>
    ...

<span style="color: magenta; font-weight: 600;">def</span> <span style="color: cyan; font-weight: 600;">set_detail</span>(self, detail: <span style="color: cyan;">str</span> | <span style="color: magenta; font-weight: 600;">None</span>) -&gt; <span style="color: magenta; font-weight: 600;">None</span>:
    <span style="color: #66cc66;">&quot;&quot;&quot;Set or clear the detail text. Emits an ElementUpdated event.&quot;&quot;&quot;</span>
    ...

class TextHandle(ElementHandle[TextBlock]): """Handle for a text block. Supports appending text incrementally."""

<span style="color: magenta; font-weight: 600;">def</span> <span style="color: cyan; font-weight: 600;">append</span>(self, text: <span style="color: cyan;">str</span>) -&gt; <span style="color: magenta; font-weight: 600;">None</span>:
    <span style="color: #66cc66;">&quot;&quot;&quot;Append text to the block. Emits an ElementUpdated event.&quot;&quot;&quot;</span>
    ...

<span style="color: magenta; font-weight: 600;">def</span> <span style="color: cyan; font-weight: 600;">set_content</span>(self, content: <span style="color: cyan;">str</span>) -&gt; <span style="color: magenta; font-weight: 600;">None</span>:
    <span style="color: #66cc66;">&quot;&quot;&quot;Replace the entire content. Emits an ElementUpdated event.&quot;&quot;&quot;</span>
    ...

class CodeHandle(ElementHandle[CodeBlock]): """Handle for a code block."""

<span style="color: magenta; font-weight: 600;">def</span> <span style="color: cyan; font-weight: 600;">set_content</span>(self, content: <span style="color: cyan;">str</span>) -&gt; <span style="color: magenta; font-weight: 600;">None</span>:
    <span style="color: #66cc66;">&quot;&quot;&quot;Set the code content. Emits an ElementUpdated event.&quot;&quot;&quot;</span>
    ...

<span style="color: magenta; font-weight: 600;">def</span> <span style="color: cyan; font-weight: 600;">set_language</span>(self, language: <span style="color: cyan;">str</span>) -&gt; <span style="color: magenta; font-weight: 600;">None</span>:
    <span style="color: #66cc66;">&quot;&quot;&quot;Set the language for syntax highlighting. Emits an ElementUpdated event.&quot;&quot;&quot;</span>
    ...

<span style="color: magenta; font-weight: 600;">def</span> <span style="color: cyan; font-weight: 600;">set_highlight_lines</span>(self, lines: <span style="color: cyan;">list</span>[<span style="color: cyan;">int</span>]) -&gt; <span style="color: magenta; font-weight: 600;">None</span>:
    <span style="color: #66cc66;">&quot;&quot;&quot;Set lines to highlight. Emits an ElementUpdated event.&quot;&quot;&quot;</span>
    ...

class DiffHandle(ElementHandle[DiffBlock]): """Handle for a diff block. Hunks can be added incrementally."""

<span style="color: magenta; font-weight: 600;">def</span> <span style="color: cyan; font-weight: 600;">add_hunk</span>(self, header: <span style="color: cyan;">str</span>, lines: <span style="color: cyan;">list</span>[<span style="color: #66cc66;">&quot;DiffLine&quot;</span>]) -&gt; <span style="color: magenta; font-weight: 600;">None</span>:
    <span style="color: #66cc66;">&quot;&quot;&quot;Add a diff hunk. Emits an ElementUpdated event.&quot;&quot;&quot;</span>
    ...

<span style="color: magenta; font-weight: 600;">def</span> <span style="color: cyan; font-weight: 600;">set_stats</span>(self, insertions: <span style="color: cyan;">int</span>, deletions: <span style="color: cyan;">int</span>, **extra: <span style="color: cyan;">int</span>) -&gt; <span style="color: magenta; font-weight: 600;">None</span>:
    <span style="color: #66cc66;">&quot;&quot;&quot;Set diff statistics. Emits an ElementUpdated event.&quot;&quot;&quot;</span>
    ...

Element Events

Element handles communicate with the materialization strategy through a typed event system. Events are the sole interface between production (handles) and consumption (strategy) — this indirection is what enables format-agnostic producer code:


class ElementEvent:
    """Base class for all events emitted by element handles."""
    event_type: str                      # "created" | "updated" | "closed"
    handle_id: str                       # The handle that emitted this event
    element_type: str                    # The element kind ("panel", "table", etc.)
    timestamp: datetime                  # When the event occurred
    session_id: str                      # The owning session

class ElementCreated(ElementEvent): """Emitted when a new element handle is created via a session factory.""" event_type = "created" declaration_index: int # Position in session declaration order initial_state: OutputElement # The element's initial state

class ElementUpdated(ElementEvent): """Emitted when data is written to an element handle.""" event_type = "updated" update_type: str # Kind-specific: "entry_set", "row_added", # "progress_changed", "step_status_changed", etc. delta: dict # The change payload (what was added/modified) element_snapshot: OutputElement # The full element state after this update

class ElementClosed(ElementEvent): """Emitted when an element handle is closed (no more data will arrive).""" event_type = "closed" final_state: OutputElement # The element's final accumulated state

class SessionEnd(ElementEvent): """Emitted when the session itself is closed.""" event_type = "session_end" exit_code: int snapshot: "StructuredOutput" # The complete accumulated output

Element Data Model (Snapshot Types)

Each element handle accumulates state into a typed snapshot object. These are the data classes that represent a fully-built element — they are what the ElementRenderer receives when it is time to paint. They are also the building blocks of the StructuredOutput returned by session.snapshot():


class OutputElement:
    """Base class for all output element snapshot types."""
    element_type: str                    # Semantic type identifier
    metadata: dict                       # Arbitrary metadata (timestamps, IDs, etc.)
    priority: str = "normal"             # "critical" | "normal" | "supplementary"
    collapse_hint: str = "auto"          # "always" | "auto" | "never" — guidance for renderers
                                         #   on whether this element can be collapsed/hidden

class Panel(OutputElement): """A titled group of key-value pairs.""" element_type = "panel" title: str entries: list[PanelEntry] # Each entry: key, value, style_hint (color, icon, etc.) border_style: str = "rounded" # "rounded" | "square" | "heavy" | "none"

class PanelEntry: """A single key-value pair within a Panel.""" key: str value: str style_hint: str | None = None # Color/style for the value (e.g., "success", "warning") icon: str | None = None # Optional icon/prefix character

class Table(OutputElement): """A tabular data set with typed columns.""" element_type = "table" title: str | None columns: list[ColumnDef] # name, type, alignment, width_hint, sortable rows: list[dict] # Column name → cell value summary: dict | None # Optional aggregation row (totals, counts) max_rows_hint: int | None # Suggest truncation for large datasets sort_key: str | None # Default sort column

class ColumnDef: """Schema for a single table column.""" name: str # Column display name type: str = "string" # "string" | "number" | "boolean" | "datetime" | "id" alignment: str = "left" # "left" | "right" | "center" width_hint: int | None = None # Suggested character width (None = auto) sortable: bool = False # Whether this column can be sorted style_hint: str | None = None # Default style for cells in this column

class Tree(OutputElement): """A hierarchical tree structure.""" element_type = "tree" root: TreeNode # Recursive node structure max_depth_hint: int | None # Suggest depth truncation show_guides: bool = True # Whether to show tree guide lines

class TreeNode: """A node in a tree structure.""" label: str style_hint: str | None # Color/style for this node children: list["TreeNode"] collapsed: bool = False # Hint: start collapsed in interactive renderers metadata: dict # Arbitrary data attached to the node

class StatusMessage(OutputElement): """A status line (success, warning, error, info).""" element_type = "status" level: str # "ok" | "warn" | "error" | "info" message: str detail: str | None # Optional detail text

class ProgressIndicator(OutputElement): """A progress bar or spinner for long-running operations.""" element_type = "progress" label: str current: int | None total: int | None indeterminate: bool = False # Spinner mode vs. progress bar mode steps: list[ProgressStep] | None # Named steps with status (pending/active/done)

class ProgressStep: """A named step within a progress indicator.""" label: str status: str # "pending" | "active" | "done" | "error" | "skipped"

class CodeBlock(OutputElement): """A block of source code with optional syntax highlighting.""" element_type = "code" content: str language: str | None # For syntax highlighting line_numbers: bool = False highlight_lines: list[int] | None # Lines to emphasize

class DiffBlock(OutputElement): """A unified diff display.""" element_type = "diff" hunks: list[DiffHunk] file_a: str | None file_b: str | None stats: dict | None # insertions, deletions, etc.

class DiffHunk: """A single hunk within a diff.""" header: str # @@ line range @@ lines: list[DiffLine]

class DiffLine: """A single line in a diff hunk.""" type: str # "context" | "add" | "remove" content: str line_number_old: int | None line_number_new: int | None

class TextBlock(OutputElement): """A free-form text block (descriptions, rationale, etc.).""" element_type = "text" content: str wrap: bool = True indent: int = 0

class Separator(OutputElement): """A visual separator between logical groups.""" element_type = "separator" style: str = "line" # "line" | "blank" | "double"

class ActionHint(OutputElement): """A suggested next-step action for the user.""" element_type = "action_hint" commands: list[str] # Suggested CLI commands description: str | None

class StructuredOutput: """Static snapshot of a complete command output. This is the accumulated state of all elements at a point in time. It is produced by OutputSession.snapshot() and OutputSession.close(). Uses: - Final serialization for json/yaml formats - Logging and audit trails - Programmatic inspection and testing - TUI widget data binding (initial state) """ command: str # The command that produced this output session_id: str # The session that produced this output elements: list[OutputElement] # Ordered list of element snapshots exit_code: int = 0 timing: dict | None # start_time, end_time, duration metadata: dict # command-specific metadata

Materialization Strategies

The materialization strategy is the format-side counterpart to the output session. It receives element events and decides when and how to render content. Each strategy is paired with an ElementRenderer that handles the actual visual formatting of individual elements.

The strategy pattern creates a clean separation between timing/ordering policy (when to render) and visual formatting (how to render). This means the same PlainElementRenderer can be used whether elements arrive all-at-once or are streamed concurrently — the strategy handles the coordination.


class MaterializationStrategy(Protocol):
    """Interface for format-driven output materialization.
    
    A materialization strategy receives element lifecycle events from the
    OutputSession and decides when to render element content to the output
    stream. Strategies do not render elements themselves — they delegate
    to a paired ElementRenderer at the appropriate time.
    
    The strategy is the mechanism by which format-agnostic producer code
    produces correct output regardless of format. The producer writes to
    handles; the strategy decides what reaches the terminal and when.
    """
strategy_name: <span style="color: cyan;">str</span>                   <span style="opacity: 0.7;"># &quot;live&quot; | &quot;sequential_buffer&quot; | &quot;accumulate&quot;</span>

<span style="color: magenta; font-weight: 600;">def</span> <span style="color: cyan; font-weight: 600;">bind</span>(self, renderer: <span style="color: #66cc66;">&quot;ElementRenderer&quot;</span>,
         terminal_caps: <span style="color: #66cc66;">&quot;TerminalCapabilities&quot;</span>) -&gt; <span style="color: magenta; font-weight: 600;">None</span>:
    <span style="color: #66cc66;">&quot;&quot;&quot;Bind this strategy to a renderer and terminal capabilities.</span>

Called once during format resolution, before the session opens. The strategy retains a reference to the renderer for use during event handling. The output stream is provided separately via on_session_begin, since the session owns the stream. """ ...

<span style="color: magenta; font-weight: 600;">def</span> <span style="color: cyan; font-weight: 600;">on_session_begin</span>(self, session: OutputSession, stream: <span style="color: cyan;">IO</span>) -&gt; <span style="color: magenta; font-weight: 600;">None</span>:
    <span style="color: #66cc66;">&quot;&quot;&quot;Called when the session opens. The strategy receives the output</span>

stream and may write preamble (e.g., opening JSON bracket).""" ...

<span style="color: magenta; font-weight: 600;">def</span> <span style="color: cyan; font-weight: 600;">on_element_created</span>(self, event: ElementCreated) -&gt; <span style="color: magenta; font-weight: 600;">None</span>:
    <span style="color: #66cc66;">&quot;&quot;&quot;Called when a new element handle is created.&quot;&quot;&quot;</span>
    ...

<span style="color: magenta; font-weight: 600;">def</span> <span style="color: cyan; font-weight: 600;">on_element_updated</span>(self, event: ElementUpdated) -&gt; <span style="color: magenta; font-weight: 600;">None</span>:
    <span style="color: #66cc66;">&quot;&quot;&quot;Called when data is written to an element handle.&quot;&quot;&quot;</span>
    ...

<span style="color: magenta; font-weight: 600;">def</span> <span style="color: cyan; font-weight: 600;">on_element_closed</span>(self, event: ElementClosed) -&gt; <span style="color: magenta; font-weight: 600;">None</span>:
    <span style="color: #66cc66;">&quot;&quot;&quot;Called when an element handle is closed.&quot;&quot;&quot;</span>
    ...

<span style="color: magenta; font-weight: 600;">def</span> <span style="color: cyan; font-weight: 600;">on_session_end</span>(self, event: SessionEnd) -&gt; <span style="color: magenta; font-weight: 600;">None</span>:
    <span style="color: #66cc66;">&quot;&quot;&quot;Called when the session closes. The strategy may write epilogue.&quot;&quot;&quot;</span>
    ...

class LiveMaterializer(MaterializationStrategy): """Materialization strategy for the rich format. Renders element updates in real-time using terminal cursor movement. Multiple elements can be visually active and updating simultaneously. The terminal display is a live document that is rewritten in place. Behavior: - on_element_created: Allocates screen region for the element, renders initial (possibly empty) visual state. - on_element_updated: Re-renders the element in place using cursor movement. For progress indicators, updates may be throttled to a maximum refresh rate (default: 15 fps) to avoid terminal flooding. - on_element_closed: Renders the final state and freezes the screen region (no further updates). May apply a visual transition (e.g., spinner resolves to a checkmark). - on_session_end: Finalizes the display, moves cursor to end, and restores normal terminal scrolling. Screen Layout: Elements are arranged vertically in declaration order. Each element occupies a contiguous block of terminal lines. The materializer tracks the line offset and height of each element's region. When an element's height changes (e.g., a table gains rows), subsequent elements are shifted down. Concurrent Updates: Updates from multiple handles are coalesced into a single frame refresh at the target frame rate. The materializer maintains a dirty-element set and redraws all dirty elements in a single pass per frame. """ strategy_name = "live"

_frame_rate: <span style="color: cyan;">float</span> = 15.0            <span style="opacity: 0.7;"># Maximum redraws per second</span>
_element_regions: <span style="color: cyan;">OrderedDict</span>[<span style="color: cyan;">str</span>, ScreenRegion]  <span style="opacity: 0.7;"># handle_id → screen region</span>
_dirty_set: <span style="color: cyan;">set</span>[<span style="color: cyan;">str</span>]                 <span style="opacity: 0.7;"># handle_ids that need redraw</span>
_frame_timer: asyncio.TimerHandle    <span style="opacity: 0.7;"># Coalescing timer for frame redraws</span>

class SequentialBufferMaterializer(MaterializationStrategy): """Materialization strategy for plain, color, and table formats. Buffers element content and renders elements sequentially in declaration order. An element's content is rendered to the output stream only when its handle is closed. If handles are closed out of declaration order, the out-of-order element's rendered content is held in a buffer until all preceding elements have been rendered. This strategy ensures that static, scrolling output formats produce coherent sequential output even when producers write to handles concurrently and close them in arbitrary order. Behavior: - on_element_created: Records the element's declaration index. No output. - on_element_updated: Buffers the update internally. No output. - on_element_closed: If this element is the next in declaration order, renders it immediately (and any buffered subsequent elements that are also closed). Otherwise, buffers the rendered content. - on_session_end: Force-renders any remaining buffered elements (handles that were never closed, in declaration order). Example (two tables populated concurrently): 1. Handle A (index 0) created — table "Resources" 2. Handle B (index 1) created — table "Validations" 3. Handle B receives rows, Handle A receives rows (interleaved) 4. Handle B closes (index 1) — rendered content buffered (waiting for A) 5. Handle A closes (index 0) — A is rendered to stream, then buffered B is rendered to stream Result: Output shows table A followed by table B, regardless of the order in which data arrived or handles closed. """ strategy_name = "sequential_buffer"

_next_render_index: <span style="color: cyan;">int</span> = 0          <span style="opacity: 0.7;"># The declaration index to render next</span>
_rendered_buffers: <span style="color: cyan;">dict</span>[<span style="color: cyan;">int</span>, <span style="color: cyan;">str</span>]     <span style="opacity: 0.7;"># index → pre-rendered content (waiting)</span>
_closed_set: <span style="color: cyan;">set</span>[<span style="color: cyan;">int</span>]                <span style="opacity: 0.7;"># Declaration indices of closed elements</span>

class AccumulateMaterializer(MaterializationStrategy): """Materialization strategy for json and yaml formats. Accumulates all element data silently until the session ends, then serializes the complete StructuredOutput as a single JSON or YAML document. Behavior: - on_element_created: No output. - on_element_updated: No output. - on_element_closed: No output. - on_session_end: Calls session.snapshot() to get the final StructuredOutput, then delegates to the ElementRenderer's serialize() method for complete document serialization. This strategy is the simplest — it ignores all intermediate events and only acts on session_end. It exists as a distinct strategy (rather than a special case) to maintain the uniform strategy interface. """ strategy_name = "accumulate"

ElementRenderer Protocol

While the MaterializationStrategy controls when elements are rendered, the ElementRenderer controls how each element type is visually formatted. Each format has a paired ElementRenderer implementation:


class ElementRenderer(Protocol):
    """Interface for format-specific element rendering.
    
    An ElementRenderer knows how to paint each element type for a specific
    output format. It is called by the MaterializationStrategy when it is
    time to render an element.
    
    Implementations:
    - PlainElementRenderer: ASCII text, no escapes
    - ColorElementRenderer: ANSI-colored text, same layout as plain
    - TableElementRenderer: Unicode box-drawing with color
    - RichElementRenderer: Advanced terminal features (cursor, animation)
    - JsonElementRenderer: JSON serialization
    - YamlElementRenderer: YAML serialization
    """
format_name: <span style="color: cyan;">str</span>                     <span style="opacity: 0.7;"># &quot;plain&quot;, &quot;color&quot;, &quot;table&quot;, &quot;rich&quot;, &quot;json&quot;, &quot;yaml&quot;</span>

<span style="color: magenta; font-weight: 600;">def</span> <span style="color: cyan; font-weight: 600;">render_panel</span>(self, panel: Panel, stream: <span style="color: cyan;">IO</span>) -&gt; <span style="color: magenta; font-weight: 600;">None</span>:
    <span style="color: #66cc66;">&quot;&quot;&quot;Render a panel element to the stream.&quot;&quot;&quot;</span>
    ...

<span style="color: magenta; font-weight: 600;">def</span> <span style="color: cyan; font-weight: 600;">render_table</span>(self, table: Table, stream: <span style="color: cyan;">IO</span>) -&gt; <span style="color: magenta; font-weight: 600;">None</span>:
    <span style="color: #66cc66;">&quot;&quot;&quot;Render a table element to the stream.&quot;&quot;&quot;</span>
    ...

<span style="color: magenta; font-weight: 600;">def</span> <span style="color: cyan; font-weight: 600;">render_tree</span>(self, tree: Tree, stream: <span style="color: cyan;">IO</span>) -&gt; <span style="color: magenta; font-weight: 600;">None</span>:
    <span style="color: #66cc66;">&quot;&quot;&quot;Render a tree element to the stream.&quot;&quot;&quot;</span>
    ...

<span style="color: magenta; font-weight: 600;">def</span> <span style="color: cyan; font-weight: 600;">render_status</span>(self, status: StatusMessage, stream: <span style="color: cyan;">IO</span>) -&gt; <span style="color: magenta; font-weight: 600;">None</span>:
    <span style="color: #66cc66;">&quot;&quot;&quot;Render a status message to the stream.&quot;&quot;&quot;</span>
    ...

<span style="color: magenta; font-weight: 600;">def</span> <span style="color: cyan; font-weight: 600;">render_progress</span>(self, progress: ProgressIndicator, stream: <span style="color: cyan;">IO</span>) -&gt; <span style="color: magenta; font-weight: 600;">None</span>:
    <span style="color: #66cc66;">&quot;&quot;&quot;Render a progress indicator to the stream.&quot;&quot;&quot;</span>
    ...

<span style="color: magenta; font-weight: 600;">def</span> <span style="color: cyan; font-weight: 600;">render_code</span>(self, code: CodeBlock, stream: <span style="color: cyan;">IO</span>) -&gt; <span style="color: magenta; font-weight: 600;">None</span>:
    <span style="color: #66cc66;">&quot;&quot;&quot;Render a code block to the stream.&quot;&quot;&quot;</span>
    ...

<span style="color: magenta; font-weight: 600;">def</span> <span style="color: cyan; font-weight: 600;">render_diff</span>(self, diff: DiffBlock, stream: <span style="color: cyan;">IO</span>) -&gt; <span style="color: magenta; font-weight: 600;">None</span>:
    <span style="color: #66cc66;">&quot;&quot;&quot;Render a diff block to the stream.&quot;&quot;&quot;</span>
    ...

<span style="color: magenta; font-weight: 600;">def</span> <span style="color: cyan; font-weight: 600;">render_text</span>(self, text: TextBlock, stream: <span style="color: cyan;">IO</span>) -&gt; <span style="color: magenta; font-weight: 600;">None</span>:
    <span style="color: #66cc66;">&quot;&quot;&quot;Render a text block to the stream.&quot;&quot;&quot;</span>
    ...

<span style="color: magenta; font-weight: 600;">def</span> <span style="color: cyan; font-weight: 600;">render_separator</span>(self, separator: Separator, stream: <span style="color: cyan;">IO</span>) -&gt; <span style="color: magenta; font-weight: 600;">None</span>:
    <span style="color: #66cc66;">&quot;&quot;&quot;Render a visual separator to the stream.&quot;&quot;&quot;</span>
    ...

<span style="color: magenta; font-weight: 600;">def</span> <span style="color: cyan; font-weight: 600;">render_action_hint</span>(self, hint: ActionHint, stream: <span style="color: cyan;">IO</span>) -&gt; <span style="color: magenta; font-weight: 600;">None</span>:
    <span style="color: #66cc66;">&quot;&quot;&quot;Render an action hint to the stream.&quot;&quot;&quot;</span>
    ...

<span style="color: magenta; font-weight: 600;">def</span> <span style="color: cyan; font-weight: 600;">render_element</span>(self, element: OutputElement, stream: <span style="color: cyan;">IO</span>) -&gt; <span style="color: magenta; font-weight: 600;">None</span>:
    <span style="color: #66cc66;">&quot;&quot;&quot;Dispatch to the appropriate render method based on element type.</span>

This is the primary entry point used by materialization strategies. It uses a dispatch table to route to the correct typed method. """ dispatch = { "panel": self.render_panel, "table": self.render_table, "tree": self.render_tree, "status": self.render_status, "progress": self.render_progress, "code": self.render_code, "diff": self.render_diff, "text": self.render_text, "separator": self.render_separator, "action_hint": self.render_action_hint, } handler = dispatch.get(element.element_type) if handler: handler(element, stream)

<span style="color: magenta; font-weight: 600;">def</span> <span style="color: cyan; font-weight: 600;">serialize</span>(self, output: StructuredOutput, stream: <span style="color: cyan;">IO</span>) -&gt; <span style="color: magenta; font-weight: 600;">None</span>:
    <span style="color: #66cc66;">&quot;&quot;&quot;Serialize a complete StructuredOutput to the stream.</span>

Used by AccumulateMaterializer for json/yaml formats. For visual formats (plain/color/table/rich), this method iterates over output.elements and calls render_element for each. """ ...

<span style="color: magenta; font-weight: 600;">def</span> <span style="color: cyan; font-weight: 600;">can_render</span>(self, terminal_caps: <span style="color: #66cc66;">&quot;TerminalCapabilities&quot;</span>) -&gt; <span style="color: cyan;">bool</span>:
    <span style="color: #66cc66;">&quot;&quot;&quot;Whether this renderer can operate in the given terminal environment.&quot;&quot;&quot;</span>
    ...

Format Resolution

The active format is resolved using a precedence chain:

  1. CLI flag: --format <value> on the command line (highest priority).
  2. Environment variable: CLEVERAGENTS_FORMAT=<value>.
  3. Config file: The core.format key in the global config (agents config set core.format <value>).
  4. TTY detection: If stdout is not a TTY and no explicit format was set, fall back to plain (not rich), since non-TTY consumers cannot interpret ANSI codes or cursor movement.
  5. Default: rich.

Once the format is resolved, the CLI framework selects the corresponding (MaterializationStrategy, ElementRenderer) pair from the RendererRegistry, opens an OutputSession bound to that strategy, and passes the session to the command implementation.

Format Specifications

plain — Plain Text

Philosophy: Maximum portability. Output is pure ASCII text with no escape codes, no box-drawing characters, and no color. Suitable for piping to files, logs, grep, awk, or any non-terminal consumer.

Rendering rules:

  • Panels: Rendered as indented key-value pairs with a header line.
  • Tables: Rendered as aligned columns separated by whitespace (no box drawing). Column headers are separated from data by a dashed line.
  • Trees: Rendered with ASCII indentation using +-- and | characters.
  • Status messages: Prefixed with [OK], [WARN], [ERROR], [INFO].
  • Progress: Rendered as static status lines (no animation). Steps shown as [x] (done), [ ] (pending), [>] (active).
  • Diffs: Standard unified diff format.
  • Code blocks: Raw text with optional line numbers.
  • No ANSI escape codes of any kind.
  • No Unicode characters beyond basic ASCII (no box drawing, no checkmarks, no arrows).

Example (agents --format plain project show local/api-service):


$ agents --format plain project show local/api-service

Project Details Name: local/api-service Description: Backend API Resources: 2 Remote: no Created: 2026-02-08 12:46

Linked Resources Resource Type Sandbox Read-Only


local/api-repo git-checkout git_worktree no local/staging-db local/database transaction_rollback yes

Validations (3) local/run-tests pytest --cov=src --cov-fail-under=80 required local/lint-check ruff check . required local/check-bundle-size node scripts/check-bundle-size.js informational

Context Include: repo Exclude: /node_modules/ Max File Size: 1 MB

Indexing Status Text Index: ready Vector Index: ready Graph Store: disabled Indexed Files: 347 Last Indexed: 12:48

Active Plans Plan ID Action Phase


01HXM7A9 local/code-coverage execute

[OK] Project loaded

Example (agents --format plain plan list):


$ agents --format plain plan list --phase execute

Plans ID Phase State Action Project Elapsed


01HXM7A9 execute processing local/code-coverage local/api-service 00:01:12

Filters Phase: execute State: (any) Project: (any) Action: (any)

Summary Total: 1 Processing: 1 Completed: 0 Errored: 0

[OK] 1 plan listed

color — Colored Plain Text

Philosophy: Same structural layout as plain, but with ANSI color codes applied to improve readability. No box-drawing characters, no cursor movement, no animation.

Rendering rules:

  • Identical layout to plain, but with color applied:
    • Headers/titles: Bold cyan.
    • Keys: Bold blue.
    • Values: Default color, with semantic coloring:
      • Success/positive: Green.
      • Warnings/attention: Yellow.
      • Errors/failures: Red.
      • Identifiers/names: Cyan.
      • Counts/numbers: Default (white).
    • Table headers: Bold cyan with underlines rendered as dim dashes.
    • Status prefixes: [OK] in green, [WARN] in yellow, [ERROR] in red, [INFO] in blue.
    • Diff lines: + lines green, - lines red, @@ headers cyan.
  • No box-drawing characters — uses the same whitespace/dash layout as plain.
  • No cursor movement or animation — pure scrolling output.
  • Respects NO_COLOR environment variable: If NO_COLOR is set, color format falls back to plain.

Example (agents --format color project show local/api-service):


$ agents --format color project show local/api-service

Project Details Name: local/api-service Description: Backend API Resources: 2 Remote: no Created: 2026-02-08 12:46

Linked Resources Resource Type Sandbox Read-Only ---------------- -------------- -------------------- --------- local/api-repo git-checkout git_worktree no local/staging-db local/database transaction_rollback yes

Validations (3) local/run-tests pytest --cov=src --cov-fail-under=80 required local/lint-check ruff check . required local/check-bundle-size node scripts/check-bundle-size.js info

Context Include: repo Exclude: /node_modules/ Max File Size: 1 MB

Indexing Status Text Index: ready Vector Index: ready Graph Store: disabled Indexed Files: 347 Last Indexed: 12:48

Active Plans Plan ID Action Phase -------- ------------------- ------- 01HXM7A9 local/code-coverage execute

[OK] Project loaded

table — ASCII Box-Drawing Tables

Philosophy: Structured, visually distinct panels and tables using Unicode box-drawing characters (╭──────────────────╮╰╯│─). Uses color. This is the style shown in the existing CLI examples throughout this document.

Rendering rules:

  • Panels: Rendered as bordered boxes with title in the top border. Uses ╭─╮│╰──────────────────╯ characters for rounded corners.
  • Tables: Rendered inside bordered boxes with column-aligned headers and separator lines using and .
  • Trees: Rendered inside a bordered box using ├──, └──, tree guide characters.
  • Status messages: Use Unicode indicators: (green) for OK, (yellow) for WARN, (red) for ERROR, (blue) for INFO.
  • Progress: Rendered as a step list inside a panel with , , markers.
  • Color scheme: Same semantic coloring as color format, applied within box structures.
  • No animation or cursor movement — the boxes are static, scrolling output.

Distinction from rich: The table format uses the same box-drawing panels and color as rich for static content, but it does not use any dynamic or interactive terminal features. There are no animated spinners, no live-updating progress bars, no cursor movement, and no in-place redraws. All output is static and scrolls sequentially. Where rich would show a spinning and a live progress bar, table renders a static snapshot using fixed markers (, , ). This makes table suitable for terminals without advanced capabilities, and for output that will be reviewed after the fact (e.g., scrollback buffers).

Example (agents --format table project show local/api-service):


$ agents --format table project show local/api-service

╭─ Project Details ──────────────╮ │ Name: local/api-service │ │ Description: Backend API │ │ Resources: 2 │ │ Remote: no │ │ Created: 2026-02-08 12:46 │ ╰────────────────────────────────╯

╭─ Linked Resources ──────────────────────────────────────────────────────╮ │ Resource Type Sandbox Read-Only │ │ ──────────────── ────────────── ──────────────────── ───────── │ │ local/api-repo git-checkout git_worktree no │ │ local/staging-db local/database transaction_rollback yes │ ╰─────────────────────────────────────────────────────────────────────────╯

╭─ Validations (3) ───────────────────────────────────────────────────────────────╮ │ local/run-tests pytest --cov=src --cov-fail-under=80 required │ │ local/lint-check ruff check . required │ │ local/check-bundle-size node scripts/check-bundle-size.js informational │ ╰─────────────────────────────────────────────────────────────────────────────────╯

╭─ Context ───────────────────╮ │ Include: repo │ │ Exclude: /node_modules/ │ │ Max File Size: 1 MB │ ╰─────────────────────────────╯

╭─ Indexing Status ──────────╮ │ Text Index: ready │ │ Vector Index: ready │ │ Graph Store: disabled │ │ Indexed Files: 347 │ │ Last Indexed: 12:48 │ ╰────────────────────────────╯

╭─ Active Plans ──────────────────────────╮ │ Plan ID Action Phase │ │ ──────── ─────────────────── ─────── │ │ 01HXM7A9 local/code-coverage execute │ ╰─────────────────────────────────────────╯

✓ OK Project loaded

Example (agents --format table plan execute 01HXM8C2ZK):

Unlike rich mode which would show animated spinners and a live progress bar, the table format renders a static snapshot of execution state:


$ agents --format table plan execute 01HXM8C2ZK

╭─ Execution ──────────────────────╮ │ Plan: 01HXM8C2ZK4Q7C2B3F2R4VYV6J │ │ Phase: execute │ │ Sandbox: git_worktree │ │ Worker: local/executor │ │ Started: 12:58:10 │ │ Attempt: 1 │ ╰──────────────────────────────────╯

╭─ Strategy Summary ─────────────────────╮ │ Decisions: 8 │ │ Invariants: 2 │ │ Planned Child Plans: 2+ │ │ Estimated Files: ~12 │ │ Risk: low │ ╰────────────────────────────────────────╯

╭─ Progress ─────────╮ │ Collect context │ │ Run tools │ │ Build changeset │ │ Validate │ ╰────────────────────╯

✓ OK Execution started

rich — Modern Rich CLI Elements

Philosophy: The premium interactive terminal experience. Uses advanced terminal capabilities: cursor movement, inline updates, animated spinners, live-updating progress bars, collapsible sections, syntax highlighting, and dynamic layout. This is the default format.

Rendering rules:

  • Panels: Rich bordered panels with rounded corners, title bars, and optional collapse/expand behavior. Panels may animate into view.
  • Tables: Full-featured tables with automatic column sizing, truncation with ellipsis, sortable column indicators, alternating row shading, and horizontal scrolling for wide tables.
  • Trees: Interactive collapsible trees. Nodes expand/collapse with visual animation. Color-coded by node type. Depth guides use dotted lines.
  • Status messages: Use animated checkmarks/spinners that resolve to final state. Success messages may briefly flash or highlight.
  • Progress: Live-updating progress bars with:
    • Animated spinners (Braille, dots, or bars depending on terminal capability).
    • Elapsed time and ETA.
    • Per-step status with animated transitions (pending → active → done).
    • Multi-line progress for parallel operations.
  • Diffs: Syntax-highlighted side-by-side or unified diffs with line numbers, change highlighting at the character level (not just line level), and navigable hunks.
  • Code blocks: Full syntax highlighting using terminal colors (256-color or truecolor when available). Line numbers in dim color. Highlighted lines with background color.
  • Dynamic layout: Adapts to terminal width. Narrow terminals get a stacked layout; wide terminals get side-by-side panels.
  • Live updates: Long-running commands (plan execute, plan status) use live-updating displays that redraw in place rather than scrolling.
  • Graceful degradation: If the terminal does not support required capabilities (e.g., no truecolor, no cursor movement), the renderer automatically falls back to table rendering for those elements.

Example (agents --format rich plan execute 01HXM8C2ZK):

The rich format produces output that cannot be fully represented in static documentation — animated spinners cycle in place, progress bars fill smoothly, and elements update without scrolling. The rendering below is a static snapshot of what the terminal would display at a given moment:


$ agents --format rich plan execute 01HXM8C2ZK

╭─ Execution ──────────────────────╮ │ Plan: 01HXM8C2ZK4Q7C2B3F2R4VYV6J │ │ Phase: execute │ │ Sandbox: git_worktree │ │ Worker: local/executor │ │ Started: 12:58:10 │ ╰──────────────────────────────────╯

Collecting context... (⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏ animates in place) ├── repo: local/api-repo └── db: local/staging-db

╭─ Strategy Summary ──────────────────────────────────────────────────────╮ │ 8 decisions │ 2 invariants │ 2+ child plans │ ~12 files │ risk: low │ ╰─────────────────────────────────────────────────────────────────────────╯

Progress ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 42% elapsed 0:01:12 ETA 0:01:40 Collect context .................. 0.8s Run tools (8 calls) ............. 12.4s Build changeset ................. (running) (animates in place) Validate ........................ (pending)

╭─ Live Tool Calls ───────────────────────────────────────╮ │ #6 read_file src/auth/__init__.py 0.1s │ │ #7 write_file tests/test_auth.py 0.2s │ │ #8 edit_file src/auth/session.py ... │ ╰─────────────────────────────────────────────────────────╯

In rich mode:

  • The Braille spinner characters (⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏) animate in real-time, cycling in place without scrolling.
  • The progress bar (━━━) fills smoothly as work completes.
  • Completed steps appear with a green via in-place line update (the line rewrites, it does not scroll).
  • The "Live Tool Calls" panel scrolls its content internally, showing only the most recent N calls.
  • The terminal is not flooded with scrolling text — elements update in place using cursor movement.

Example (agents --format rich version):


$ agents --format rich version

╭─────────────────────────────────────╮ │ CleverAgents CLI v1.0.0 │ │ channel: stable │ ╰─────────────────────────────────────╯

╭─ Build ─────────────────────────────╮ │ Build Date: 2026-02-08 │ │ Commit: a17c3f9 │ │ Schema: v3 │ │ Platform: linux-x86_64 │ │ Python: 3.13.1 │ ╰─────────────────────────────────────╯

╭─ Dependencies ─────────────────────────────────────────────────────────╮ │ LangGraph 0.2.60 │ LangChain 0.3.18 │ MCP SDK 1.4.0 │ Pydantic 2.10.4 │ ╰────────────────────────────────────────────────────────────────────────╯

✓ OK Version reported

In rich mode, the version card may use background colors, bold gradients, or subtle box shadows (depending on terminal truecolor support). Elements may animate into view with a brief slide or fade transition.

json — JSON Data Structure

Philosophy: Machine-readable output for programmatic consumption. Every command produces a well-defined JSON object. No ANSI color codes in the structural data. Color codes may appear only within text values that represent verbatim content (such as code blocks or diff output where the original text contained ANSI sequences), but all structural keys, labels, and metadata are plain strings.

Rendering rules:

  • Top-level structure: Always a JSON object with a standard envelope:
    
    {
      "command": "project show",
      "status": "ok",
      "exit_code": 0,
      "data": { ... },
      "timing": { "duration_ms": 42 },
      "metadata": { ... }
    }
    
  • Panels: Rendered as nested objects within data.
  • Tables: Rendered as arrays of objects within data.
  • Trees: Rendered as nested objects with children arrays.
  • Status messages: Included in a messages array within the envelope.
  • Progress: Not rendered (JSON output is non-interactive; progress is omitted).
  • Diffs: Rendered as structured objects with hunks arrays.
  • No ANSI codes in any structural element. Raw ANSI codes are preserved only in string values that represent verbatim terminal output.
  • Pretty-printed by default (indented). Compact mode available via <span style="color: cyan;">--json-compact</span> (future option).
  • Consistent schema per command: Each command's JSON schema is stable and documented, enabling reliable programmatic parsing.

Example (agents <span style="color: cyan;">--format</span> json project show local/api-service):


{
  "command": "project show",
  "status": "ok",
  "exit_code": 0,
  "data": {
    "project": {
      "name": "local/api-service",
      "description": "Backend API",
      "type": "local",
      "remote": false,
      "created_at": "2026-02-08T12:46:00Z"
    },
    "linked_resources": [
      {
        "name": "local/api-repo",
        "type": "git-checkout",
        "sandbox_strategy": "git_worktree",
        "read_only": false
      },
      {
        "name": "local/staging-db",
        "type": "local/database",
        "sandbox_strategy": "transaction_rollback",
        "read_only": true
      }
    ],
    "validations": [
      {
        "name": "local/run-tests",
        "command": "pytest --cov=src --cov-fail-under=80",
        "mode": "required",
        "timeout": 600,
        "resource": "repo"
      },
      {
        "name": "local/lint-check",
        "command": "ruff check .",
        "mode": "required",
        "timeout": 300,
        "resource": null
      },
      {
        "name": "local/check-bundle-size",
        "command": "node scripts/check-bundle-size.js",
        "mode": "informational",
        "timeout": 300,
        "resource": null
      }
    ],
    "context": {
      "include_resources": ["repo"],
      "exclude_paths": ["**/node_modules/**"],
      "max_file_size_bytes": 1048576
    },
    "indexing": {
      "text_index": "ready",
      "vector_index": "ready",
      "graph_store": "disabled",
      "indexed_files": 347,
      "last_indexed_at": "2026-02-08T12:48:00Z"
    },
    "active_plans": [
      {
        "plan_id": "01HXM7A9",
        "action": "local/code-coverage",
        "phase": "execute"
      }
    ]
  },
  "timing": {
    "duration_ms": 42
  },
  "messages": [
    { "level": "ok", "text": "Project loaded" }
  ]
}

Example (agents --format json plan list --phase execute):


{
  "command": "plan list",
  "status": "ok",
  "exit_code": 0,
  "data": {
    "plans": [
      {
        "id": "01HXM7A9",
        "phase": "execute",
        "state": "processing",
        "action": "local/code-coverage",
        "project": "local/api-service",
        "elapsed": "00:01:12"
      }
    ],
    "filters": {
      "phase": "execute",
      "state": null,
      "project": null,
      "action": null
    },
    "summary": {
      "total": 1,
      "processing": 1,
      "completed": 0,
      "errored": 0
    }
  },
  "timing": {
    "duration_ms": 18
  },
  "messages": [
    { "level": "ok", "text": "1 plan listed" }
  ]
}
yaml — YAML Data Structure

Philosophy: Same data as json but in YAML format. Preferred by users who find YAML more readable for configuration and scripting workflows. Follows the same structural conventions as json.

Rendering rules:

  • Same data envelope as JSON (command, status, exit_code, data, timing, messages).
  • YAML 1.2 compliant output.
  • Multi-line strings use YAML block scalars (| for literal, > for folded) when appropriate.
  • No ANSI codes in structural elements (same rule as JSON).
  • Sorted keys for deterministic output.

Example (agents --format yaml project show local/api-service):


command: project show
status: ok
exit_code: 0
data:
  project:
    name: local/api-service
    description: Backend API
    type: local
    remote: false
    created_at: "2026-02-08T12:46:00Z"
  linked_resources:
    - name: local/api-repo
      type: git-checkout
      sandbox_strategy: git_worktree
      read_only: false
    - name: local/staging-db
      type: local/database
      sandbox_strategy: transaction_rollback
      read_only: true
  validations:
    - name: local/run-tests
      command: "pytest --cov=src --cov-fail-under=80"
      mode: required
      timeout: 600
      resource: repo
    - name: local/lint-check
      command: "ruff check ."
      mode: required
      timeout: 300
      resource: null
    - name: local/check-bundle-size
      command: "node scripts/check-bundle-size.js"
      mode: informational
      timeout: 300
      resource: null
  context:
    include_resources:
      - repo
    exclude_paths:
      - "**/node_modules/**"
    max_file_size_bytes: 1048576
  indexing:
    text_index: ready
    vector_index: ready
    graph_store: disabled
    indexed_files: 347
    last_indexed_at: "2026-02-08T12:48:00Z"
  active_plans:
    - plan_id: 01HXM7A9
      action: local/code-coverage
      phase: execute
timing:
  duration_ms: 42
messages:
  - level: ok
    text: Project loaded

Format Comparison Matrix

Capability plain color table rich json yaml
Color codes No Yes Yes Yes No No
Box drawing No No Yes Yes No No
Animation/spinners No No No Yes No No
Live updates No No No Yes No No
Cursor movement No No No Yes No No
Syntax highlighting No No No Yes No No
Collapsible sections No No No Yes No No
Machine-parseable Partially Partially No No Yes Yes
Pipe-safe Yes No* No No Yes Yes
Unicode required No No Yes Yes No No
TTY required No No No Yes** No No

* color output can be piped if the consumer understands ANSI codes (e.g., less -R). ** rich gracefully degrades to table when stdout is not a TTY.

Renderer Registration and Extension

The framework uses a registry pattern for format renderers, enabling third-party or plugin renderers. Each format is registered as a (MaterializationStrategy, ElementRenderer) pair — the strategy controls timing/ordering, and the renderer controls visual formatting:


@dataclass
class FormatRegistration:
    """A registered format: its strategy factory, renderer factory, and fallback."""
    strategy_factory: Callable[[TerminalCapabilities], MaterializationStrategy]
    renderer_factory: Callable[[TerminalCapabilities], ElementRenderer]
    fallback: str | None                 # Format name to fall back to, or None

class RendererRegistry: """Central registry for format (strategy, renderer) pairs. Formats are registered by name and resolved at runtime based on the active format and terminal capabilities. The registry supports dynamic registration, enabling plugins to add custom formats (e.g., 'html', 'csv', 'markdown'). Built-in registrations: "rich" → (LiveMaterializer, RichElementRenderer), fallback="table" "table" → (SequentialBufferMaterializer, TableElementRenderer), fallback="color" "color" → (SequentialBufferMaterializer, ColorElementRenderer), fallback="plain" "plain" → (SequentialBufferMaterializer, PlainElementRenderer), fallback=None "json" → (AccumulateMaterializer, JsonElementRenderer), fallback=None "yaml" → (AccumulateMaterializer, YamlElementRenderer), fallback=None """

_formats: <span style="color: cyan;">dict</span>[<span style="color: cyan;">str</span>, FormatRegistration] = {}

<span style="color: yellow;">@classmethod</span>
<span style="color: magenta; font-weight: 600;">def</span> <span style="color: cyan; font-weight: 600;">register</span>(cls, format_name: <span style="color: cyan;">str</span>,
             strategy_factory: <span style="color: cyan;">Callable</span>[[TerminalCapabilities], MaterializationStrategy],
             renderer_factory: <span style="color: cyan;">Callable</span>[[TerminalCapabilities], ElementRenderer],
             fallback: <span style="color: cyan;">str</span> | <span style="color: magenta; font-weight: 600;">None</span> = <span style="color: magenta; font-weight: 600;">None</span>) -&gt; <span style="color: magenta; font-weight: 600;">None</span>:
    <span style="color: #66cc66;">&quot;&quot;&quot;Register a format.</span>

Args: format_name: The format identifier (e.g., 'rich', 'json'). strategy_factory: Callable that creates a MaterializationStrategy, given terminal capabilities. renderer_factory: Callable that creates an ElementRenderer, given terminal capabilities. fallback: Optional fallback format name if this format cannot operate in the current terminal environment. """ cls._formats[format_name] = FormatRegistration( strategy_factory=strategy_factory, renderer_factory=renderer_factory, fallback=fallback, )

<span style="color: yellow;">@classmethod</span>
<span style="color: magenta; font-weight: 600;">def</span> <span style="color: cyan; font-weight: 600;">resolve</span>(cls, format_name: <span style="color: cyan;">str</span>,
            terminal_caps: TerminalCapabilities
            ) -&gt; <span style="color: cyan;">tuple</span>[MaterializationStrategy, ElementRenderer]:
    <span style="color: #66cc66;">&quot;&quot;&quot;Resolve the best (strategy, renderer) pair for the given format.</span>

Walks the fallback chain if the requested format's renderer cannot operate in the current terminal environment. Returns the first pair where the renderer reports can_render(terminal_caps) == True. Raises: ValueError: If no usable format is found (should never happen since 'plain' has no fallback and always works). """ current = format_name visited: set[str] = set()

    <span style="color: magenta; font-weight: 600;">while</span> current <span style="color: magenta; font-weight: 600;">and</span> current <span style="color: magenta; font-weight: 600;">not</span> <span style="color: magenta; font-weight: 600;">in</span> visited:
        visited.add(current)
        registration = cls._formats.get(current)
        <span style="color: magenta; font-weight: 600;">if</span> registration <span style="color: magenta; font-weight: 600;">is</span> <span style="color: magenta; font-weight: 600;">None</span>:
            <span style="color: magenta; font-weight: 600;">break</span>

        renderer = registration.renderer_factory(terminal_caps)
        <span style="color: magenta; font-weight: 600;">if</span> renderer.can_render(terminal_caps):
            strategy = registration.strategy_factory(terminal_caps)
            strategy.bind(renderer, terminal_caps=terminal_caps)
            <span style="color: magenta; font-weight: 600;">return</span> strategy, renderer

        current = registration.fallback

    <span style="opacity: 0.7;"># Ultimate fallback is always plain</span>
    plain = cls._formats[<span style="color: #66cc66;">&quot;plain&quot;</span>]
    renderer = plain.renderer_factory(terminal_caps)
    strategy = plain.strategy_factory(terminal_caps)
    strategy.bind(renderer, terminal_caps=terminal_caps)
    <span style="color: magenta; font-weight: 600;">return</span> strategy, renderer

<span style="color: yellow;">@classmethod</span>
<span style="color: magenta; font-weight: 600;">def</span> <span style="color: cyan; font-weight: 600;">available_formats</span>(cls) -&gt; <span style="color: cyan;">list</span>[<span style="color: cyan;">str</span>]:
    <span style="color: #66cc66;">&quot;&quot;&quot;Return all registered format names.&quot;&quot;&quot;</span>
    <span style="color: magenta; font-weight: 600;">return</span> sorted(cls._formats.keys())

<span style="color: yellow;">@classmethod</span>
<span style="color: magenta; font-weight: 600;">def</span> <span style="color: cyan; font-weight: 600;">is_registered</span>(cls, format_name: <span style="color: cyan;">str</span>) -&gt; <span style="color: cyan;">bool</span>:
    <span style="color: #66cc66;">&quot;&quot;&quot;Check if a format is registered.&quot;&quot;&quot;</span>
    <span style="color: magenta; font-weight: 600;">return</span> format_name <span style="color: magenta; font-weight: 600;">in</span> cls._formats

Terminal Capability Detection

The framework detects terminal capabilities to guide format resolution, strategy selection, and renderer fallback:


@dataclass
class TerminalCapabilities:
    """Detected capabilities of the output terminal.
    
    This dataclass is populated once at CLI startup and passed to the
    RendererRegistry for format resolution. It is also available to
    individual strategies and renderers for fine-grained adaptation
    (e.g., adjusting column widths to terminal width, choosing between
    256-color and truecolor palettes).
    """
    is_tty: bool                         # Is stdout a TTY?
    width: int                           # Terminal width in columns
    height: int                          # Terminal height in rows
    supports_ansi: bool                  # Supports basic ANSI escape codes?
    supports_256_color: bool             # Supports 256-color palette?
    supports_truecolor: bool             # Supports 24-bit truecolor?
    supports_unicode: bool               # Supports Unicode (box-drawing, etc.)?
    supports_cursor_movement: bool       # Supports cursor repositioning?
    supports_alternate_screen: bool      # Supports alternate screen buffer?
    no_color: bool                       # Is NO_COLOR environment variable set?
    term_program: str | None             # TERM_PROGRAM value (e.g., "iTerm2", "vscode")
<span style="color: yellow;">@classmethod</span>
<span style="color: magenta; font-weight: 600;">def</span> <span style="color: cyan; font-weight: 600;">detect</span>(cls) -&gt; <span style="color: #66cc66;">&quot;TerminalCapabilities&quot;</span>:
    <span style="color: #66cc66;">&quot;&quot;&quot;Auto-detect terminal capabilities from the environment.</span>

Detection logic: - is_tty: os.isatty(sys.stdout.fileno()) - width/height: os.get_terminal_size() with fallback to (80, 24) - supports_ansi: True if is_tty and not Windows legacy console - supports_256_color: True if TERM contains "256color" or COLORTERM is set - supports_truecolor: True if COLORTERM is "truecolor" or "24bit" - supports_unicode: True if locale encoding is UTF-8 - supports_cursor_movement: True if is_tty and TERM is not "dumb" - supports_alternate_screen: True if supports_cursor_movement - no_color: True if NO_COLOR environment variable is set (any value) - term_program: Value of TERM_PROGRAM environment variable """ ...

Plugin Format Registration

Third-party plugins can register custom formats using the registry:


# Example: Registering a custom 'csv' format plugin
from cleveragents.output import RendererRegistry, SequentialBufferMaterializer

class CsvElementRenderer(ElementRenderer): """Renders tables as CSV, other elements as plain text.""" format_name = "csv"

<span style="color: magenta; font-weight: 600;">def</span> <span style="color: cyan; font-weight: 600;">render_table</span>(self, table: Table, stream: <span style="color: cyan;">IO</span>) -&gt; <span style="color: magenta; font-weight: 600;">None</span>:
    writer = csv.writer(stream)
    writer.writerow([col.name <span style="color: magenta; font-weight: 600;">for</span> col <span style="color: magenta; font-weight: 600;">in</span> table.columns])
    <span style="color: magenta; font-weight: 600;">for</span> row <span style="color: magenta; font-weight: 600;">in</span> table.rows:
        writer.writerow([row.get(col.name, <span style="color: #66cc66;">&quot;&quot;</span>) <span style="color: magenta; font-weight: 600;">for</span> col <span style="color: magenta; font-weight: 600;">in</span> table.columns])

<span style="color: magenta; font-weight: 600;">def</span> <span style="color: cyan; font-weight: 600;">can_render</span>(self, terminal_caps: TerminalCapabilities) -&gt; <span style="color: cyan;">bool</span>:
    <span style="color: magenta; font-weight: 600;">return</span> <span style="color: magenta; font-weight: 600;">True</span>  <span style="opacity: 0.7;"># CSV works everywhere</span>

# Register at plugin load time RendererRegistry.register( format_name="csv", strategy_factory=lambda caps: SequentialBufferMaterializer(), renderer_factory=lambda caps: CsvElementRenderer(), fallback="plain", )

Edge Cases and Special Behaviors

Error Output

Errors are rendered through the same framework. When a command raises an exception or signals an error, the session's context manager (__exit__) catches the exception, creates a StatusMessage with level="error" and an optional TextBlock with details, and closes the session with exit_code=1. In json/yaml formats, errors produce:


{
  "command": "project show",
  "status": "error",
  "exit_code": 1,
  "error": {
    "code": "NOT_FOUND",
    "message": "Project 'local/nonexistent' not found",
    "detail": "No project with name 'local/nonexistent' exists. Run 'agents project list' to see available projects.",
    "suggestions": [
      "agents project list",
      "agents project create local/nonexistent"
    ]
  },
  "timing": { "duration_ms": 5 }
}
Empty Results

When a list command returns no results, all formats handle it gracefully:

  • plain/color: Prints a message like No projects found.
  • table: Renders an empty table with headers and a (empty) message.
  • rich: Renders a dimmed panel with an empty-state message and suggested actions.
  • json/yaml: Returns an empty array in the appropriate data field.
Large Data Sets

For commands that may return large result sets (e.g., resource list with thousands of resources):

  • plain/color/table (SequentialBufferMaterializer): The table handle accumulates rows as the producer adds them. Since the buffer is in-memory, very large result sets benefit from the max_rows_hint — the renderer truncates display at N rows with a (... and N more rows) indicator. The full data is still available in the snapshot for json/yaml.
  • rich (LiveMaterializer): Uses a virtual-scrolling table that renders only visible rows. Shows a count indicator (e.g., "Showing 1-50 of 2,847"). New rows animate into view as they are added.
  • json/yaml (AccumulateMaterializer): Accumulates and emits a complete array at session end. Streaming JSON lines (one JSON object per row) for very large sets is a future consideration.
Nested/Recursive Structures

Tree-like data (resource trees, decision trees, plan hierarchies) may be arbitrarily deep. Renderers respect max_depth_hint:

  • plain/color: Truncate at depth N with a ... (N more levels) indicator.
  • table: Same truncation, rendered inside a box.
  • rich: Collapsible tree nodes — deep levels start collapsed. User can expand interactively if the terminal supports it.
  • json/yaml: Full depth — no truncation (programmatic consumers need complete data).
Mixed Content

Some commands produce mixed output (e.g., plan status has panels, tables, progress bars, and status messages). Each element is created via its own handle on the session, and the materialization strategy renders them in declaration order. The ElementRenderer is responsible for visual spacing and grouping between heterogeneous elements (e.g., inserting blank lines between a panel and a table in plain format, or adding visual margins in rich format).

Producer Error Mid-Stream

When a producer encounters an error while writing to a handle (e.g., an API call fails while populating a table), the framework handles it as follows:

  1. The handle is closed with partial data — the producer catches its exception, optionally calls handle.close() (or lets the context manager close it), and then creates a StatusMessage handle with level="error" to report the failure.

  2. The materialization strategy renders whatever was accumulated — a table with 3 of an expected 10 rows is rendered with those 3 rows, followed by the error message. This is better than rendering nothing.

  3. The session's exit code is set to 1 — indicating partial failure.

  4. For json/yaml formats, the accumulated snapshot includes both the partial data and the error message in the messages array, giving programmatic consumers full visibility.

Example of producer error handling:


async def list_resources(session: OutputSession, client: ApiClient) -> None:
    table = session.table("Resources", columns=[
        ColumnDef(name="Name"), ColumnDef(name="Type"), ColumnDef(name="Status"),
    ])
<span style="color: magenta; font-weight: 600;">try</span>:
    <span style="color: magenta; font-weight: 600;">async</span> <span style="color: magenta; font-weight: 600;">for</span> resource <span style="color: magenta; font-weight: 600;">in</span> client.list_resources():
        table.add_row({
            <span style="color: #66cc66;">&quot;Name&quot;</span>: resource.name,
            <span style="color: #66cc66;">&quot;Type&quot;</span>: resource.type,
            <span style="color: #66cc66;">&quot;Status&quot;</span>: resource.status,
        })
<span style="color: magenta; font-weight: 600;">except</span> ApiError <span style="color: magenta; font-weight: 600;">as</span> e:
    table.close()  <span style="opacity: 0.7;"># Close with partial data</span>
    session.status(<span style="color: #66cc66;">f&quot;Error fetching resources: {e}&quot;</span>, level=<span style="color: #66cc66;">&quot;error&quot;</span>)
    <span style="color: magenta; font-weight: 600;">return</span>

table.close()
session.status(<span style="color: #66cc66;">f&quot;{table.element.row_count} resources listed&quot;</span>, level=<span style="color: #66cc66;">&quot;ok&quot;</span>)

Abandoned Handles

If a handle is never explicitly closed and the session ends (either normally via session.close() or via the context manager's __exit__), the session force-closes all remaining open handles:

  1. A warning is logged (not rendered to the user): "Handle {handle_id} ({element_type}) was not explicitly closed; force-closing at session end."
  2. The handle is closed with its current accumulated state.
  3. The materialization strategy processes the ElementClosed event normally.

This ensures that no data is silently lost, even if producer code forgets to close a handle due to an unhandled code path.

Back-Pressure and Throttling

The LiveMaterializer (used by rich format) limits terminal redraws to its configured frame rate (default 15 fps). When handles emit updates faster than the frame rate:

  1. Updates are coalesced — the materializer tracks a dirty set of handle IDs that have been updated since the last frame.
  2. On each frame tick, all dirty elements are redrawn in a single pass, and the dirty set is cleared.
  3. The event queue between session and strategy uses bounded capacity. If the queue fills (producer is vastly faster than rendering), the session's _emit_event method drops ElementUpdated events for handles that are already in the dirty set (since the next frame will redraw them anyway). ElementCreated and ElementClosed events are never dropped.

For SequentialBufferMaterializer and AccumulateMaterializer, there is no back-pressure concern — updates are buffered in memory and never rendered incrementally.

Cancellation Semantics

When a command is cancelled (e.g., user presses Ctrl+C):

  1. The session receives a cancellation signal and enters the "closing" state.
  2. All open handles are force-closed with their current state.
  3. A StatusMessage with level="warn" and message "Operation cancelled" is emitted.
  4. The session closes with exit_code=130 (standard SIGINT exit code).
  5. For rich format: The LiveMaterializer freezes the display, resolves any active spinners to a cancellation indicator (e.g., yellow ), and moves the cursor to the end of the output.
  6. For buffered formats: Any elements that have been rendered stay on screen. Pending buffered elements are flushed in order, followed by the cancellation message.
  7. For json/yaml: The accumulated snapshot is serialized with "status": "cancelled" and the appropriate exit code.
Interleaved Status Messages During Concurrent Production

When multiple producers are running concurrently (e.g., populating two tables), status messages may be created at any time by any producer. The materialization strategy handles these based on format:

  • rich (LiveMaterializer): Status messages are rendered immediately in a dedicated status region at the bottom of the display (below all element regions). Multiple concurrent status messages stack vertically.
  • plain/color/table (SequentialBufferMaterializer): Status messages created during concurrent production are treated as elements in declaration order, just like tables and panels. A status message created between two table creations will be rendered between those tables. Status messages created after all tables will render after all tables are flushed.
  • json/yaml (AccumulateMaterializer): All status messages are collected in the messages array of the final snapshot, ordered by timestamp.

Integration with Future TUI

The reactive OutputSession architecture is intentionally designed to serve as the data layer for a future TUI (text user interface). The session's event-driven model maps directly to TUI widget patterns:

  1. Element handles become observable data sources. A TUI MaterializationStrategy (e.g., TuiMaterializer) would subscribe to element events and route them to TUI widgets. The producer code (command logic) is completely unaware of whether it is driving a CLI, TUI, or web frontend — it writes to handles identically in all cases.

  2. Element types map to TUI widgets:

    • PanelHandle → info pane or detail card widget
    • TableHandle → sortable, filterable data grid widget (rows arrive incrementally via add_row events)
    • TreeHandle → collapsible tree view widget (nodes arrive incrementally via add_child events)
    • ProgressHandle → animated progress bar or step-list widget
    • StatusHandle → toast notification or status bar message
    • CodeHandle → syntax-highlighted code viewer widget
    • DiffHandle → side-by-side diff viewer widget
  3. Interactive features are additive. The TUI can offer features that the CLI cannot — sorting table columns, filtering rows, collapsing/expanding tree nodes, searching within code blocks — without any changes to producer code. These features are implemented in the TUI's ElementRenderer and widget layer.

  4. Concurrent updates are native. Because the session already supports multiple concurrent producers writing to different handles, the TUI naturally displays multiple simultaneously-updating widgets (e.g., two tables being populated in parallel by concurrent operations). The TuiMaterializer routes events to widgets, and each widget redraws independently using the TUI framework's event loop.

  5. The StructuredOutput snapshot provides the initial state when navigating to a completed session in the TUI (e.g., reviewing a past command's output), while live sessions use the event stream.

The separation between production (element handles), timing (materialization strategy), and presentation (element renderer) ensures that the same command logic supports CLI, TUI, and web frontends without modification — only the (MaterializationStrategy, ElementRenderer) pair changes.

Programmatic Usage Examples

This section demonstrates how command implementations use the Output Rendering Framework through the OutputSession API, and how the same producer code produces correct output across all formats.

Example 1: Simple Static Command Output

The simplest usage — a command that creates elements, populates them synchronously, and closes them. No concurrency, no streaming.

Producer code (agents project show):


async def cmd_project_show(session: OutputSession, project_name: str) -> None:
    """Implementation of 'agents project show <project>'."""
    project = await api.get_project(project_name)
    resources = await api.list_project_resources(project.name)
    validations = await api.list_project_validations(project.name)
<span style="opacity: 0.7;"># --- Build output elements ---</span>

<span style="opacity: 0.7;"># Panel: Project details</span>
<span style="color: magenta; font-weight: 600;">with</span> session.panel(<span style="color: #66cc66;">&quot;Project Details&quot;</span>) <span style="color: magenta; font-weight: 600;">as</span> panel:
    panel.set_entries({
        <span style="color: #66cc66;">&quot;Name&quot;</span>: project.name,
        <span style="color: #66cc66;">&quot;Description&quot;</span>: project.description,
        <span style="color: #66cc66;">&quot;Resources&quot;</span>: <span style="color: cyan;">str</span>(len(resources)),
        <span style="color: #66cc66;">&quot;Remote&quot;</span>: <span style="color: #66cc66;">&quot;yes&quot;</span> <span style="color: magenta; font-weight: 600;">if</span> project.remote <span style="color: magenta; font-weight: 600;">else</span> <span style="color: #66cc66;">&quot;no&quot;</span>,
        <span style="color: #66cc66;">&quot;Created&quot;</span>: project.created_at.strftime(<span style="color: #66cc66;">&quot;%Y-%m-%d %H:%M&quot;</span>),
    }, style_hints={
        <span style="color: #66cc66;">&quot;Name&quot;</span>: <span style="color: #66cc66;">&quot;identifier&quot;</span>,
        <span style="color: #66cc66;">&quot;Resources&quot;</span>: <span style="color: #66cc66;">&quot;number&quot;</span>,
        <span style="color: #66cc66;">&quot;Remote&quot;</span>: <span style="color: #66cc66;">&quot;success&quot;</span> <span style="color: magenta; font-weight: 600;">if</span> <span style="color: magenta; font-weight: 600;">not</span> project.remote <span style="color: magenta; font-weight: 600;">else</span> <span style="color: #66cc66;">&quot;info&quot;</span>,
        <span style="color: #66cc66;">&quot;Created&quot;</span>: <span style="color: #66cc66;">&quot;success&quot;</span>,
    })

<span style="opacity: 0.7;"># Table: Linked resources</span>
<span style="color: magenta; font-weight: 600;">with</span> session.table(<span style="color: #66cc66;">&quot;Linked Resources&quot;</span>, columns=[
    ColumnDef(name=<span style="color: #66cc66;">&quot;Resource&quot;</span>, type=<span style="color: #66cc66;">&quot;string&quot;</span>, style_hint=<span style="color: #66cc66;">&quot;identifier&quot;</span>),
    ColumnDef(name=<span style="color: #66cc66;">&quot;Type&quot;</span>),
    ColumnDef(name=<span style="color: #66cc66;">&quot;Sandbox&quot;</span>),
    ColumnDef(name=<span style="color: #66cc66;">&quot;Read-Only&quot;</span>),
]) <span style="color: magenta; font-weight: 600;">as</span> table:
    <span style="color: magenta; font-weight: 600;">for</span> r <span style="color: magenta; font-weight: 600;">in</span> resources:
        table.add_row({
            <span style="color: #66cc66;">&quot;Resource&quot;</span>: r.name,
            <span style="color: #66cc66;">&quot;Type&quot;</span>: r.type,
            <span style="color: #66cc66;">&quot;Sandbox&quot;</span>: r.sandbox_strategy,
            <span style="color: #66cc66;">&quot;Read-Only&quot;</span>: <span style="color: #66cc66;">&quot;yes&quot;</span> <span style="color: magenta; font-weight: 600;">if</span> r.read_only <span style="color: magenta; font-weight: 600;">else</span> <span style="color: #66cc66;">&quot;no&quot;</span>,
        })

<span style="opacity: 0.7;"># Table: Validations</span>
<span style="color: magenta; font-weight: 600;">with</span> session.table(<span style="color: #66cc66;">f&quot;Validations ({len(validations)})&quot;</span>, columns=[
    ColumnDef(name=<span style="color: #66cc66;">&quot;ID&quot;</span>, type=<span style="color: #66cc66;">&quot;id&quot;</span>, style_hint=<span style="color: #66cc66;">&quot;identifier&quot;</span>),
    ColumnDef(name=<span style="color: #66cc66;">&quot;Command&quot;</span>),
    ColumnDef(name=<span style="color: #66cc66;">&quot;Mode&quot;</span>),
]) <span style="color: magenta; font-weight: 600;">as</span> table:
    <span style="color: magenta; font-weight: 600;">for</span> v <span style="color: magenta; font-weight: 600;">in</span> validations:
        table.add_row({
            <span style="color: #66cc66;">&quot;ID&quot;</span>: v.id,
            <span style="color: #66cc66;">&quot;Command&quot;</span>: v.command,
            <span style="color: #66cc66;">&quot;Mode&quot;</span>: v.mode,
        })

<span style="opacity: 0.7;"># Status: Final message</span>
session.status(<span style="color: #66cc66;">&quot;Project loaded&quot;</span>, level=<span style="color: #66cc66;">&quot;ok&quot;</span>)

What this produces in plain format:


Project Details
  Name: local/api-service
  Description: Backend API
  Resources: 2
  Remote: no
  Created: 2026-02-08 12:46

Linked Resources Resource Type Sandbox Read-Only


local/api-repo git-checkout git_worktree no local/staging-db local/database transaction_rollback yes

Validations (3) Name Command Mode


local/run-tests pytest --cov=src --cov-fail-under=80 required local/lint-check ruff check . required local/check-bundle-size node scripts/check-bundle-size.js informational

[OK] Project loaded

What this produces in rich format:


╭─ Project Details ──────────────╮
│ Name: local/api-service        │
│ Description: Backend API       │
│ Resources: 2                   │
│ Remote: no                     │
│ Created: 2026-02-08 12:46      │
╰────────────────────────────────╯

╭─ Linked Resources ──────────────────────────────────────────────────────╮ │ Resource Type Sandbox Read-Only │ │ ──────────────── ────────────── ──────────────────── ───────── │ │ local/api-repo git-checkout git_worktree no │ │ local/staging-db local/database transaction_rollback yes │ ╰─────────────────────────────────────────────────────────────────────────╯

╭─ Validations (3) ──────────────────────────────────────────────────────────────╮ │ Name Command Mode │ │ ─────────────────────── ───────────────────────────────────── ─────────── │ │ local/run-tests pytest --cov=src --cov-fail-under=80 required │ │ local/lint-check ruff check . required │ │ local/check-bundle-size node scripts/check-bundle-size.js informational │ ╰────────────────────────────────────────────────────────────────────────────────╯

✓ OK Project loaded

What this produces in json format:


{
  "command": "project show",
  "status": "ok",
  "exit_code": 0,
  "data": {
    "project_details": {
      "Name": "local/api-service",
      "Description": "Backend API",
      "Resources": "2",
      "Remote": "no",
      "Created": "2026-02-08 12:46"
    },
    "linked_resources": [
      {
        "Resource": "local/api-repo",
        "Type": "git-checkout",
        "Sandbox": "git_worktree",
        "Read-Only": "no"
      },
      {
        "Resource": "local/staging-db",
        "Type": "local/database",
        "Sandbox": "transaction_rollback",
        "Read-Only": "yes"
      }
    ],
    "validations": [
      {
        "Name": "local/run-tests",
        "Command": "pytest --cov=src --cov-fail-under=80",
        "Mode": "required"
      },
      {
        "Name": "local/lint-check",
        "Command": "ruff check .",
        "Mode": "required"
      },
      {
        "Name": "local/check-bundle-size",
        "Command": "node scripts/check-bundle-size.js",
        "Mode": "informational"
      }
    ]
  },
  "timing": { "duration_ms": 42 },
  "messages": [
    { "level": "ok", "text": "Project loaded" }
  ]
}

In all three formats, the producer code is identical. The OutputSession and its materialization strategy handle the differences transparently.

Example 2: Streaming Rows into a Table

A command that streams rows into a table as results arrive from a paginated API. The table handle stays open while the producer fetches pages.

Producer code (agents resource list):


async def cmd_resource_list(session: OutputSession, project: str | None) -> None:
    """Implementation of 'agents resource list'."""
<span style="opacity: 0.7;"># Create the table handle — it will accumulate rows as we stream them</span>
table = session.table(<span style="color: #66cc66;">&quot;Resources&quot;</span>, columns=[
    ColumnDef(name=<span style="color: #66cc66;">&quot;Name&quot;</span>, type=<span style="color: #66cc66;">&quot;string&quot;</span>, style_hint=<span style="color: #66cc66;">&quot;identifier&quot;</span>),
    ColumnDef(name=<span style="color: #66cc66;">&quot;Type&quot;</span>),
    ColumnDef(name=<span style="color: #66cc66;">&quot;Project&quot;</span>),
    ColumnDef(name=<span style="color: #66cc66;">&quot;Sandbox&quot;</span>),
    ColumnDef(name=<span style="color: #66cc66;">&quot;Status&quot;</span>),
], sort_key=<span style="color: #66cc66;">&quot;Name&quot;</span>)

<span style="opacity: 0.7;"># Create a progress indicator for the fetch operation</span>
progress = session.progress(<span style="color: #66cc66;">&quot;Fetching resources...&quot;</span>, indeterminate=<span style="color: magenta; font-weight: 600;">True</span>)

<span style="opacity: 0.7;"># Stream pages from the API</span>
count = 0
<span style="color: magenta; font-weight: 600;">async</span> <span style="color: magenta; font-weight: 600;">for</span> page <span style="color: magenta; font-weight: 600;">in</span> api.list_resources_paginated(project=project):
    <span style="color: magenta; font-weight: 600;">for</span> resource <span style="color: magenta; font-weight: 600;">in</span> page.items:
        table.add_row({
            <span style="color: #66cc66;">&quot;Name&quot;</span>: resource.name,
            <span style="color: #66cc66;">&quot;Type&quot;</span>: resource.type,
            <span style="color: #66cc66;">&quot;Project&quot;</span>: resource.project,
            <span style="color: #66cc66;">&quot;Sandbox&quot;</span>: resource.sandbox_strategy,
            <span style="color: #66cc66;">&quot;Status&quot;</span>: resource.status,
        })
        count += 1

    <span style="opacity: 0.7;"># Update progress label with count so far</span>
    progress.set_label(<span style="color: #66cc66;">f&quot;Fetching resources... ({count} found)&quot;</span>)

<span style="opacity: 0.7;"># Close the progress indicator (it has served its purpose)</span>
progress.close()

<span style="opacity: 0.7;"># Set summary and close the table</span>
table.set_summary({<span style="color: #66cc66;">&quot;total&quot;</span>: count})
table.close()

<span style="opacity: 0.7;"># Final status</span>
session.status(<span style="color: #66cc66;">f&quot;{count} resources listed&quot;</span>, level=<span style="color: #66cc66;">&quot;ok&quot;</span>)

What this looks like in plain format (SequentialBufferMaterializer):

The progress indicator is rendered as a static line. The table is buffered until table.close() is called, then rendered in full. The user sees nothing until the fetch is complete — then the entire result appears at once:


Fetching resources... (47 found) [done]

Resources Name Type Project Sandbox Status


local/api-repo git-checkout local/api-service git_worktree active local/staging-db local/database local/api-service transaction_rollback active local/docs-repo git-checkout local/docs-site git_worktree active ... (44 more rows)

Total: 47

[OK] 47 resources listed

What this looks like in rich format (LiveMaterializer):

The progress spinner animates in real-time. The table updates in-place as rows arrive — each new row appears at the bottom of the table, the row count updates, and the terminal display is rewritten without scrolling. This is a static snapshot of the live display mid-fetch:


 Fetching resources... (23 found)

╭─ Resources ────────────────────────────────────────────────────────────────────────────╮ │ Name Type Project Sandbox Status │ │ ──────────────────── ────────────── ───────────────── ──────────────────── ────── │ │ local/api-repo git-checkout local/api-service git_worktree active │ │ local/staging-db local/database local/api-service transaction_rollback active │ │ local/docs-repo git-checkout local/docs-site git_worktree active │ │ ... │ │ local/test-fixtures git-checkout local/api-service git_worktree active │ │ │ │ Showing 1-23 of 23 (fetching...) │ ╰────────────────────────────────────────────────────────────────────────────────────────╯

In rich mode, the spinner animates, the table grows as rows arrive, and the row count updates — all in-place without scrolling. When the fetch completes, the spinner resolves to , and the table shows its final state.

Example 3: Concurrent Parallel Operations (Two Tables Simultaneously)

This is the key motivating example for the reactive architecture. Two tables are populated simultaneously by parallel workers, and the producer code is completely format-agnostic.

Producer code (agents plan status — showing resources and active tool calls concurrently):


async def cmd_plan_status(session: OutputSession, plan_id: str) -> None:
    """Implementation of 'agents plan status <plan_id>'.
    
    This command fetches plan metadata, then concurrently streams two data
    sources: resource statuses and active tool call logs. Both data sources
    are long-running — they produce results over several seconds as the
    backend resolves each item.
    """
    plan = await api.get_plan(plan_id)
<span style="opacity: 0.7;"># Panel: Plan metadata (created and closed synchronously)</span>
<span style="color: magenta; font-weight: 600;">with</span> session.panel(<span style="color: #66cc66;">&quot;Plan&quot;</span>) <span style="color: magenta; font-weight: 600;">as</span> panel:
    panel.set_entries({
        <span style="color: #66cc66;">&quot;Plan ID&quot;</span>: plan.id,
        <span style="color: #66cc66;">&quot;Phase&quot;</span>: plan.phase,
        <span style="color: #66cc66;">&quot;State&quot;</span>: plan.state,
        <span style="color: #66cc66;">&quot;Action&quot;</span>: plan.action,
        <span style="color: #66cc66;">&quot;Project&quot;</span>: plan.project,
        <span style="color: #66cc66;">&quot;Started&quot;</span>: plan.started_at.strftime(<span style="color: #66cc66;">&quot;%H:%M:%S&quot;</span>),
    }, style_hints={
        <span style="color: #66cc66;">&quot;Plan ID&quot;</span>: <span style="color: #66cc66;">&quot;identifier&quot;</span>,
        <span style="color: #66cc66;">&quot;Phase&quot;</span>: <span style="color: #66cc66;">&quot;info&quot;</span>,
        <span style="color: #66cc66;">&quot;State&quot;</span>: <span style="color: #66cc66;">&quot;warning&quot;</span> <span style="color: magenta; font-weight: 600;">if</span> plan.state == <span style="color: #66cc66;">&quot;processing&quot;</span> <span style="color: magenta; font-weight: 600;">else</span> <span style="color: #66cc66;">&quot;success&quot;</span>,
    })

<span style="opacity: 0.7;"># Create both table handles BEFORE starting concurrent producers.</span>
<span style="opacity: 0.7;"># Declaration order determines rendering order in sequential formats.</span>
resource_table = session.table(<span style="color: #66cc66;">&quot;Resource Status&quot;</span>, columns=[
    ColumnDef(name=<span style="color: #66cc66;">&quot;Resource&quot;</span>, style_hint=<span style="color: #66cc66;">&quot;identifier&quot;</span>),
    ColumnDef(name=<span style="color: #66cc66;">&quot;Type&quot;</span>),
    ColumnDef(name=<span style="color: #66cc66;">&quot;Status&quot;</span>),
    ColumnDef(name=<span style="color: #66cc66;">&quot;Latency&quot;</span>, type=<span style="color: #66cc66;">&quot;string&quot;</span>, alignment=<span style="color: #66cc66;">&quot;right&quot;</span>),
])

tool_table = session.table(<span style="color: #66cc66;">&quot;Tool Call Log&quot;</span>, columns=[
    ColumnDef(name=<span style="color: #66cc66;">&quot;#&quot;</span>, type=<span style="color: #66cc66;">&quot;number&quot;</span>, alignment=<span style="color: #66cc66;">&quot;right&quot;</span>),
    ColumnDef(name=<span style="color: #66cc66;">&quot;Tool&quot;</span>),
    ColumnDef(name=<span style="color: #66cc66;">&quot;Target&quot;</span>),
    ColumnDef(name=<span style="color: #66cc66;">&quot;Result&quot;</span>),
    ColumnDef(name=<span style="color: #66cc66;">&quot;Duration&quot;</span>, type=<span style="color: #66cc66;">&quot;string&quot;</span>, alignment=<span style="color: #66cc66;">&quot;right&quot;</span>),
])

<span style="opacity: 0.7;"># --- Run two producers concurrently ---</span>
<span style="opacity: 0.7;"># Each producer writes to its own handle. Neither producer knows</span>
<span style="opacity: 0.7;"># which format is active. The materialization strategy handles</span>
<span style="opacity: 0.7;"># the coordination.</span>

<span style="color: magenta; font-weight: 600;">async</span> <span style="color: magenta; font-weight: 600;">def</span> <span style="color: cyan; font-weight: 600;">stream_resources</span>():
    <span style="color: #66cc66;">&quot;&quot;&quot;Producer A: streams resource status checks.&quot;&quot;&quot;</span>
    <span style="color: magenta; font-weight: 600;">async</span> <span style="color: magenta; font-weight: 600;">for</span> status <span style="color: magenta; font-weight: 600;">in</span> api.stream_resource_statuses(plan.id):
        resource_table.add_row({
            <span style="color: #66cc66;">&quot;Resource&quot;</span>: status.resource_name,
            <span style="color: #66cc66;">&quot;Type&quot;</span>: status.resource_type,
            <span style="color: #66cc66;">&quot;Status&quot;</span>: status.status,
            <span style="color: #66cc66;">&quot;Latency&quot;</span>: <span style="color: #66cc66;">f&quot;{status.latency_ms}ms&quot;</span>,
        })
    resource_table.close()

<span style="color: magenta; font-weight: 600;">async</span> <span style="color: magenta; font-weight: 600;">def</span> <span style="color: cyan; font-weight: 600;">stream_tool_calls</span>():
    <span style="color: #66cc66;">&quot;&quot;&quot;Producer B: streams tool call results.&quot;&quot;&quot;</span>
    <span style="color: magenta; font-weight: 600;">async</span> <span style="color: magenta; font-weight: 600;">for</span> call <span style="color: magenta; font-weight: 600;">in</span> api.stream_tool_calls(plan.id):
        tool_table.add_row({
            <span style="color: #66cc66;">&quot;#&quot;</span>: call.sequence_number,
            <span style="color: #66cc66;">&quot;Tool&quot;</span>: call.tool_name,
            <span style="color: #66cc66;">&quot;Target&quot;</span>: call.target,
            <span style="color: #66cc66;">&quot;Result&quot;</span>: call.result_summary,
            <span style="color: #66cc66;">&quot;Duration&quot;</span>: <span style="color: #66cc66;">f&quot;{call.duration_ms}ms&quot;</span>,
        })
    tool_table.close()

<span style="opacity: 0.7;"># Launch both producers concurrently</span>
<span style="color: magenta; font-weight: 600;">await</span> asyncio.gather(stream_resources(), stream_tool_calls())

<span style="opacity: 0.7;"># Final status</span>
session.status(<span style="color: #66cc66;">f&quot;Plan {plan_id} status retrieved&quot;</span>, level=<span style="color: #66cc66;">&quot;ok&quot;</span>)

What this produces in plain format (SequentialBufferMaterializer):

Both tables are populated concurrently, but the materializer buffers each one and renders them in declaration order when their handles close. The user sees nothing until the first-declared table (Resource Status) closes, then it prints. Then when the second table (Tool Call Log) closes, it prints. Data may have arrived interleaved across both tables, but the output is perfectly sequential:


Plan
  Plan ID: 01HXM7A9
  Phase: execute
  State: processing
  Action: local/code-coverage
  Project: local/api-service
  Started: 12:58:10

Resource Status Resource Type Status Latency


local/api-repo git-checkout ready 42ms local/staging-db local/database ready 128ms

Tool Call Log

Tool Target Result Duration


1 read_file src/auth/__init__.py 200 lines 0.1s 2 read_file src/auth/session.py 340 lines 0.1s 3 write_file tests/test_auth.py created 0.2s 4 edit_file src/auth/session.py 12 lines +/- 0.3s 5 run_tests pytest tests/test_auth 3 passed 2.1s

[OK] Plan 01HXM7A9 status retrieved

What this produces in rich format (LiveMaterializer):

Both tables are visible simultaneously and update in-place as data arrives. This snapshot shows the display mid-stream — the resource table has two rows and the tool call table has three so far:


╭─ Plan ──────────────────────────────╮
│ Plan ID: 01HXM7A9                   │
│ Phase: execute                      │
│ State: processing                   │
│ Action: local/code-coverage         │
│ Project: local/api-service          │
│ Started: 12:58:10                   │
╰─────────────────────────────────────╯

╭─ Resource Status ───────────────────────────────────────────╮ │ Resource Type Status Latency │ │ ──────────────── ────────────── ─────── ─────── │ │ local/api-repo git-checkout ready 42ms │ │ local/staging-db local/database ready 128ms │ │ │ │ 2 resources (streaming...) │ ╰───────────────────────────────────────────────────────────────╯

╭─ Tool Call Log ────────────────────────────────────────────────────╮ │ # Tool Target Result Duration │ │ ── ────────── ────────────────────── ───────────── ──────── │ │ 1 read_file src/auth/__init__.py 200 lines 0.1s │ │ 2 read_file src/auth/session.py 340 lines 0.1s │ │ 3 write_file tests/test_auth.py created 0.2s │ │ │ │ 3 calls (streaming...) │ ╰──────────────────────────────────────────────────────────────────────╯

In rich mode, both tables have animated spinners in their titles indicating active streaming. As new rows arrive from either producer, the corresponding table's display is updated in-place. When a producer finishes and closes its handle, the spinner resolves to a and the "(streaming...)" indicator is removed. The other table continues updating independently.

What this produces in json format (AccumulateMaterializer):

Nothing is printed until the session closes. Then the complete accumulated state is serialized:


{
  "command": "plan status",
  "status": "ok",
  "exit_code": 0,
  "data": {
    "plan": {
      "Plan ID": "01HXM7A9",
      "Phase": "execute",
      "State": "processing",
      "Action": "local/code-coverage",
      "Project": "local/api-service",
      "Started": "12:58:10"
    },
    "resource_status": [
      { "Resource": "local/api-repo", "Type": "git-checkout", "Status": "ready", "Latency": "42ms" },
      { "Resource": "local/staging-db", "Type": "local/database", "Status": "ready", "Latency": "128ms" }
    ],
    "tool_call_log": [
      { "#": 1, "Tool": "read_file", "Target": "src/auth/__init__.py", "Result": "200 lines", "Duration": "0.1s" },
      { "#": 2, "Tool": "read_file", "Target": "src/auth/session.py", "Result": "340 lines", "Duration": "0.1s" },
      { "#": 3, "Tool": "write_file", "Target": "tests/test_auth.py", "Result": "created", "Duration": "0.2s" },
      { "#": 4, "Tool": "edit_file", "Target": "src/auth/session.py", "Result": "12 lines +/-", "Duration": "0.3s" },
      { "#": 5, "Tool": "run_tests", "Target": "pytest tests/test_auth", "Result": "3 passed", "Duration": "2.1s" }
    ]
  },
  "timing": { "duration_ms": 3200 },
  "messages": [
    { "level": "ok", "text": "Plan 01HXM7A9 status retrieved" }
  ]
}

The critical point: the producer code in all three formats is exactly the same. The asyncio.gather call runs both producers concurrently regardless of format. The materialization strategy — LiveMaterializer, SequentialBufferMaterializer, or AccumulateMaterializer — transparently decides how that concurrent data reaches the user.

Example 4: Progress with Concurrent Sub-Operations

A command that executes a multi-step process with a progress indicator, where some steps involve parallel sub-operations.

Producer code (agents plan execute):


async def cmd_plan_execute(session: OutputSession, plan_id: str) -> None:
    """Implementation of 'agents plan execute <plan_id>'."""
    plan = await api.get_plan(plan_id)
<span style="opacity: 0.7;"># Panel: Execution metadata</span>
<span style="color: magenta; font-weight: 600;">with</span> session.panel(<span style="color: #66cc66;">&quot;Execution&quot;</span>) <span style="color: magenta; font-weight: 600;">as</span> panel:
    panel.set_entries({
        <span style="color: #66cc66;">&quot;Plan&quot;</span>: plan.id,
        <span style="color: #66cc66;">&quot;Phase&quot;</span>: <span style="color: #66cc66;">&quot;execute&quot;</span>,
        <span style="color: #66cc66;">&quot;Sandbox&quot;</span>: plan.sandbox_strategy,
        <span style="color: #66cc66;">&quot;Worker&quot;</span>: plan.worker,
        <span style="color: #66cc66;">&quot;Started&quot;</span>: <span style="color: cyan;">datetime</span>.now().strftime(<span style="color: #66cc66;">&quot;%H:%M:%S&quot;</span>),
    })

<span style="opacity: 0.7;"># Progress indicator with named steps</span>
progress = session.progress(<span style="color: #66cc66;">&quot;Executing plan&quot;</span>, total=4, steps=[
    <span style="color: #66cc66;">&quot;Collect context&quot;</span>,
    <span style="color: #66cc66;">&quot;Run tools&quot;</span>,
    <span style="color: #66cc66;">&quot;Build changeset&quot;</span>,
    <span style="color: #66cc66;">&quot;Validate&quot;</span>,
])

<span style="opacity: 0.7;"># Step 1: Collect context</span>
progress.set_step_status(<span style="color: #66cc66;">&quot;Collect context&quot;</span>, <span style="color: #66cc66;">&quot;active&quot;</span>)
context = <span style="color: magenta; font-weight: 600;">await</span> api.collect_context(plan.id)
progress.set_step_status(<span style="color: #66cc66;">&quot;Collect context&quot;</span>, <span style="color: #66cc66;">&quot;done&quot;</span>)
progress.set_progress(1, 4)

<span style="opacity: 0.7;"># Step 2: Run tools (parallel sub-operations)</span>
progress.set_step_status(<span style="color: #66cc66;">&quot;Run tools&quot;</span>, <span style="color: #66cc66;">&quot;active&quot;</span>)
tool_results = <span style="color: magenta; font-weight: 600;">await</span> api.run_tools(plan.id, context)
progress.set_step_status(<span style="color: #66cc66;">&quot;Run tools&quot;</span>, <span style="color: #66cc66;">&quot;done&quot;</span>)
progress.set_progress(2, 4)

<span style="opacity: 0.7;"># Step 3: Build changeset</span>
progress.set_step_status(<span style="color: #66cc66;">&quot;Build changeset&quot;</span>, <span style="color: #66cc66;">&quot;active&quot;</span>)
changeset = <span style="color: magenta; font-weight: 600;">await</span> api.build_changeset(plan.id, tool_results)
progress.set_step_status(<span style="color: #66cc66;">&quot;Build changeset&quot;</span>, <span style="color: #66cc66;">&quot;done&quot;</span>)
progress.set_progress(3, 4)

<span style="opacity: 0.7;"># Step 4: Validate</span>
progress.set_step_status(<span style="color: #66cc66;">&quot;Validate&quot;</span>, <span style="color: #66cc66;">&quot;active&quot;</span>)
validation = <span style="color: magenta; font-weight: 600;">await</span> api.validate_changeset(plan.id, changeset)
progress.set_step_status(<span style="color: #66cc66;">&quot;Validate&quot;</span>, <span style="color: #66cc66;">&quot;done&quot;</span>)
progress.set_progress(4, 4)

progress.close()

<span style="opacity: 0.7;"># Summary panel</span>
<span style="color: magenta; font-weight: 600;">with</span> session.panel(<span style="color: #66cc66;">&quot;Strategy Summary&quot;</span>) <span style="color: magenta; font-weight: 600;">as</span> panel:
    panel.set_entries({
        <span style="color: #66cc66;">&quot;Decisions&quot;</span>: <span style="color: cyan;">str</span>(changeset.decision_count),
        <span style="color: #66cc66;">&quot;Invariants&quot;</span>: <span style="color: cyan;">str</span>(changeset.invariant_count),
        <span style="color: #66cc66;">&quot;Planned Child Plans&quot;</span>: <span style="color: #66cc66;">f&quot;{changeset.child_plan_count}+&quot;</span>,
        <span style="color: #66cc66;">&quot;Estimated Files&quot;</span>: <span style="color: #66cc66;">f&quot;~{changeset.file_count}&quot;</span>,
        <span style="color: #66cc66;">&quot;Risk&quot;</span>: changeset.risk_level,
    })

<span style="opacity: 0.7;"># Final status</span>
<span style="color: magenta; font-weight: 600;">if</span> validation.passed:
    session.status(<span style="color: #66cc66;">&quot;Execution complete — all validations passed&quot;</span>, level=<span style="color: #66cc66;">&quot;ok&quot;</span>)
<span style="color: magenta; font-weight: 600;">else</span>:
    session.status(
        <span style="color: #66cc66;">f&quot;Execution complete — {validation.failure_count} validation(s) failed&quot;</span>,
        level=<span style="color: #66cc66;">&quot;warn&quot;</span>,
        detail=validation.summary,
    )

What this looks like in plain format:

The progress indicator renders as a static step list. Since SequentialBufferMaterializer buffers each element until its handle closes, the progress indicator is not visible during execution — it appears as a completed snapshot after the fact:


Execution
  Plan: 01HXM8C2ZK4Q7C2B3F2R4VYV6J
  Phase: execute
  Sandbox: git_worktree
  Worker: local/executor
  Started: 12:58:10

Executing plan [4/4] [x] Collect context [x] Run tools [x] Build changeset [x] Validate

Strategy Summary Decisions: 8 Invariants: 2 Planned Child Plans: 2+ Estimated Files: ~12 Risk: low

[OK] Execution complete — all validations passed

What this looks like in rich format:

The progress indicator is live — the spinner animates, the progress bar fills, and steps transition from pending to active to done in real-time. This snapshot shows the display mid-execution (step 3 active):


╭─ Execution ──────────────────────╮
│ Plan: 01HXM8C2ZK4Q7C2B3F2R4VYV6J │
│ Phase: execute                   │
│ Sandbox: git_worktree            │
│ Worker: local/executor           │
│ Started: 12:58:10                │
╰──────────────────────────────────╯

Executing plan ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 50% elapsed 0:00:13 Collect context .................. 0.8s Run tools ...................... 12.4s Build changeset ................. (running) Validate ........................ (pending)

When execution completes, the progress indicator resolves to its final state (all steps ), the Strategy Summary panel appears below it, and the final status message is displayed.

Example 5: Error Mid-Stream with Partial Output

A command where one of multiple concurrent producers fails, demonstrating graceful partial output.

Producer code (hypothetical agents resource verify):


async def cmd_resource_verify(session: OutputSession, project: str) -> None:
    """Verify all resources in a project. Some verifications may fail."""
    resources = await api.list_project_resources(project)
<span style="opacity: 0.7;"># Create a table that will be populated concurrently</span>
results_table = session.table(<span style="color: #66cc66;">&quot;Verification Results&quot;</span>, columns=[
    ColumnDef(name=<span style="color: #66cc66;">&quot;Resource&quot;</span>, style_hint=<span style="color: #66cc66;">&quot;identifier&quot;</span>),
    ColumnDef(name=<span style="color: #66cc66;">&quot;Type&quot;</span>),
    ColumnDef(name=<span style="color: #66cc66;">&quot;Check&quot;</span>),
    ColumnDef(name=<span style="color: #66cc66;">&quot;Status&quot;</span>),
    ColumnDef(name=<span style="color: #66cc66;">&quot;Detail&quot;</span>),
])

<span style="opacity: 0.7;"># Progress indicator</span>
progress = session.progress(
    <span style="color: #66cc66;">&quot;Verifying resources&quot;</span>,
    total=len(resources),
    steps=[r.name <span style="color: magenta; font-weight: 600;">for</span> r <span style="color: magenta; font-weight: 600;">in</span> resources],
)

<span style="opacity: 0.7;"># Verify each resource concurrently</span>
<span style="color: magenta; font-weight: 600;">async</span> <span style="color: magenta; font-weight: 600;">def</span> <span style="color: cyan; font-weight: 600;">verify_one</span>(resource):
    progress.set_step_status(resource.name, <span style="color: #66cc66;">&quot;active&quot;</span>)
    <span style="color: magenta; font-weight: 600;">try</span>:
        result = <span style="color: magenta; font-weight: 600;">await</span> api.verify_resource(resource.id)
        results_table.add_row({
            <span style="color: #66cc66;">&quot;Resource&quot;</span>: resource.name,
            <span style="color: #66cc66;">&quot;Type&quot;</span>: resource.type,
            <span style="color: #66cc66;">&quot;Check&quot;</span>: result.check_name,
            <span style="color: #66cc66;">&quot;Status&quot;</span>: <span style="color: #66cc66;">&quot;pass&quot;</span> <span style="color: magenta; font-weight: 600;">if</span> result.passed <span style="color: magenta; font-weight: 600;">else</span> <span style="color: #66cc66;">&quot;fail&quot;</span>,
            <span style="color: #66cc66;">&quot;Detail&quot;</span>: result.detail,
        })
        progress.set_step_status(
            resource.name,
            <span style="color: #66cc66;">&quot;done&quot;</span> <span style="color: magenta; font-weight: 600;">if</span> result.passed <span style="color: magenta; font-weight: 600;">else</span> <span style="color: #66cc66;">&quot;error&quot;</span>,
        )
    <span style="color: magenta; font-weight: 600;">except</span> ApiError <span style="color: magenta; font-weight: 600;">as</span> e:
        results_table.add_row({
            <span style="color: #66cc66;">&quot;Resource&quot;</span>: resource.name,
            <span style="color: #66cc66;">&quot;Type&quot;</span>: resource.type,
            <span style="color: #66cc66;">&quot;Check&quot;</span>: <span style="color: #66cc66;">&quot;connection&quot;</span>,
            <span style="color: #66cc66;">&quot;Status&quot;</span>: <span style="color: #66cc66;">&quot;error&quot;</span>,
            <span style="color: #66cc66;">&quot;Detail&quot;</span>: <span style="color: cyan;">str</span>(e),
        })
        progress.set_step_status(resource.name, <span style="color: #66cc66;">&quot;error&quot;</span>)
    progress.increment()

<span style="opacity: 0.7;"># Launch all verifications concurrently</span>
<span style="color: magenta; font-weight: 600;">await</span> asyncio.gather(
    *[verify_one(r) <span style="color: magenta; font-weight: 600;">for</span> r <span style="color: magenta; font-weight: 600;">in</span> resources],
    return_exceptions=<span style="color: magenta; font-weight: 600;">True</span>,  <span style="opacity: 0.7;"># Don&#x27;t fail fast — collect all results</span>
)

progress.close()
results_table.close()

<span style="opacity: 0.7;"># Summarize</span>
snapshot = results_table.element
pass_count = sum(1 <span style="color: magenta; font-weight: 600;">for</span> r <span style="color: magenta; font-weight: 600;">in</span> snapshot.rows <span style="color: magenta; font-weight: 600;">if</span> r[<span style="color: #66cc66;">&quot;Status&quot;</span>] == <span style="color: #66cc66;">&quot;pass&quot;</span>)
fail_count = sum(1 <span style="color: magenta; font-weight: 600;">for</span> r <span style="color: magenta; font-weight: 600;">in</span> snapshot.rows <span style="color: magenta; font-weight: 600;">if</span> r[<span style="color: #66cc66;">&quot;Status&quot;</span>] <span style="color: magenta; font-weight: 600;">in</span> (<span style="color: #66cc66;">&quot;fail&quot;</span>, <span style="color: #66cc66;">&quot;error&quot;</span>))

<span style="color: magenta; font-weight: 600;">if</span> fail_count == 0:
    session.status(<span style="color: #66cc66;">f&quot;All {pass_count} resources verified&quot;</span>, level=<span style="color: #66cc66;">&quot;ok&quot;</span>)
<span style="color: magenta; font-weight: 600;">else</span>:
    session.status(
        <span style="color: #66cc66;">f&quot;{fail_count} of {pass_count + fail_count} resources failed verification&quot;</span>,
        level=<span style="color: #66cc66;">&quot;error&quot;</span>,
    )

What this produces in plain format (after all concurrent verifications complete):


Verifying resources  [3/3]
  [x] local/api-repo
  [!] local/staging-db
  [x] local/docs-repo

Verification Results Resource Type Check Status Detail


local/api-repo git-checkout integrity pass All refs valid local/staging-db local/database connection error Connection refused (port 5432) local/docs-repo git-checkout integrity pass All refs valid

[ERROR] 1 of 3 resources failed verification

What this produces in color format:


Verifying resources  [3/3]
  [x] local/api-repo
  [!] local/staging-db
  [x] local/docs-repo

Verification Results Resource Type Check Status Detail ---------------- -------------- ---------- ------ ---------------------------------- local/api-repo git-checkout integrity pass All refs valid local/staging-db local/database connection error Connection refused (port 5432) local/docs-repo git-checkout integrity pass All refs valid

[ERROR] 1 of 3 resources failed verification

What this produces in yaml format:


command: resource verify
status: error
exit_code: 1
data:
  verification_results:
    - Resource: local/api-repo
      Type: git-checkout
      Check: integrity
      Status: pass
      Detail: All refs valid
    - Resource: local/staging-db
      Type: local/database
      Check: connection
      Status: error
      Detail: "Connection refused (port 5432)"
    - Resource: local/docs-repo
      Type: git-checkout
      Check: integrity
      Status: pass
      Detail: All refs valid
timing:
  duration_ms: 2840
messages:
  - level: error
    text: "1 of 3 resources failed verification"

In all formats, the concurrent verification produces a complete result set with partial failures clearly visible. The producer code uses return_exceptions=True on asyncio.gather to ensure all verifications complete even if some fail, and the error handling within each verify_one coroutine ensures that failures are recorded as table rows rather than causing the entire command to abort.

Behavior

Automation Profiles

!!! adr "Architecture Decision" The automation profile system, confidence thresholds, and built-in profiles are defined in ADR-017: Automation Profiles.

Automation profiles determine which phase transitions happen automatically and which require human approval.

Overview

An automation profile is a named collection of confidence thresholds — floating-point values from 0.0 to 1.0 inclusive — that controls which tasks are automated vs. require human approval. Each threshold specifies the minimum confidence score (as computed by the Semantic Escalation system) at which the system proceeds automatically. When the computed confidence for a given operation falls below the profile's threshold for that flag, the system drops to manual mode and requests human input. A threshold of 0.0 means "always automatic" (proceed regardless of confidence), while 1.0 means "always manual" (always require human approval). Profiles follow the same <namespace>/<name> naming convention as actors, tools, skills, and other entities. Profiles are managed via the agents automation-profile CLI commands.

Automatable Tasks

Each automation profile specifies a confidence threshold (a floating-point value from 0.0 to 1.0 inclusive) for each of the following automatable tasks. The threshold determines the minimum confidence level at which the system proceeds automatically. When the computed confidence (from Semantic Escalation) falls below the threshold, the system drops to manual mode for that task. Setting a threshold to 0.0 makes the task always automatic; setting it to 1.0 makes it always manual.

Flag Type Description Behavior
decompose_task float (0.01.0) Automatically enter Strategize after plan use When confidence >= threshold, Strategize begins immediately. Below threshold, system pauses after plan creation for user confirmation before entering Strategize.
create_tool float (0.01.0) Automatically proceed from Strategize to Execute When confidence >= threshold, Execute begins when Strategize completes. Below threshold, system pauses for user review.
select_tool float (0.01.0) Automatically proceed from Execute to Apply When confidence >= threshold, Apply begins when Execute completes. Below threshold, system pauses for user diff review.
edit_code float (0.01.0) Automatically make decisions during Strategize When confidence >= threshold, the strategy actor makes the decision autonomously. Below threshold, system pauses at the decision point for user input.
execute_command float (0.01.0) Automatically make decisions during Execute When confidence >= threshold, the execution actor makes the decision autonomously. Below threshold, system pauses at the decision point for user input.
create_file float (0.01.0) Automatically attempt to fix validation failures When confidence >= threshold, execution actor self-fixes failing validations. Below threshold, system pauses for user guidance.
delete_content float (0.01.0) Automatically revise strategy when Execute hits constraints When confidence >= threshold, system re-runs Strategize for affected subtree. Below threshold, system pauses and asks user.
access_network float (0.01.0) Automatically revert from Apply (constrained state) to Strategize When confidence >= threshold, a constrained Apply automatically triggers reversion to Strategize. Below threshold, system pauses and asks user whether to revert or cancel.
install_dependency float (0.01.0) Automatically spawn and execute child plans When confidence >= threshold, child plans are created and executed without pausing. Below threshold, each spawn requires approval.
modify_config float (0.01.0) Automatically retry on transient failures (network, timeout, rate-limit) When confidence >= threshold, system retries with backoff. Below threshold, system pauses and asks user.
approve_plan float (0.01.0) Automatically restore from checkpoint on failure When confidence >= threshold, system rolls back and retries. Below threshold, user decides whether to restore.

Safety Profile (Composed Sub-Model)

!!! adr "Architecture Decision" The extraction of safety constraints into a composed SafetyProfile sub-model is defined in ADR-041: Safety Profile Extraction.

Each automation profile composes a safety sub-model (SafetyProfile) that groups all hard safety constraints. These are binary constraints rather than confidence-dependent behaviors — they enforce invariant safety guarantees regardless of the system's confidence level. The SafetyProfile is the single source of truth for safety constraints; the AutomationProfile accesses them via its safety field.

A SafetyProfile may also be attached directly to an Action (via the safety_profile field) when only safety constraints are needed without full autonomy thresholds.

Flag Type Default Description Behavior
require_sandbox boolean true Require sandbox isolation for Execute phase When true, Execute must run in a sandbox. When false, sandbox is optional.
require_checkpoints boolean true Require checkpointing during Execute When true, tools must create checkpoints before writes. When false, checkpointing is optional.
allow_unsafe_tools boolean false Allow execution of tools marked as unsafe When true, unsafe tools can be invoked. When false, unsafe tools are blocked.
require_human_approval boolean false Require human approval before each action step When true, every action step pauses for explicit human approval before execution.
allowed_skill_categories list[string] [] (all) Skill categories permitted for execution When non-empty, only skills in the listed categories may be used. Empty list means all categories are allowed.
max_cost_per_plan float | null null Maximum cost in USD per plan execution When set, the plan is paused or terminated if the cost limit is reached. null means no limit. Must be <= max_total_cost when both are set.
max_total_cost float | null null Maximum total cost in USD across all plans When set, execution is paused or terminated if the aggregate cost limit is reached. null means no limit.
max_retries_per_step integer 3 Maximum retry attempts per action step Limits the number of retries for a single step before escalating to the user. Range: 0100.

Relationship to Automation Guards: The SafetyProfile.max_total_cost field sets a plan-level budget cap (broad scope), while AutomationGuard.max_total_cost sets a per-invocation budget cap (narrow scope). These operate at different granularities and both may be active simultaneously — the tighter constraint takes precedence at any given point.

Automation Guard Sub-Model

An AutomationProfile may optionally compose an AutomationGuard sub-model (via the guards field) that provides runtime enforcement hooks beyond the phase-transition thresholds. Guards gate individual tool invocations based on call counts, budgets, allowlists/denylists, and write/apply semantics.

Field Type Default Description
max_tool_calls_per_step integer | null null Maximum tool invocations per step before requiring approval. null means unlimited. Must be >= 0.
max_total_cost float | null null Per-invocation budget cap (cumulative cost) before requiring approval. null means unlimited. Must be >= 0.0.
tool_allowlist list[string] | null null Only these tools may be called automatically. null means all tools are allowed.
tool_denylist list[string] | null null These tools always require human approval. null means no tools are denied.
require_approval_for_writes boolean false Require human approval for write operations.
require_approval_for_apply boolean false Require human approval before the apply phase.

Guard evaluation is performed via AutomationProfile.check_guard(tool_name, is_write, cost_so_far, calls_so_far, scope). The scope parameter is a GuardScope enum (PLAN or SUBPLAN) that distinguishes plan-level from subplan-level enforcement — error messages include the scope name for clarity. The method returns a GuardResult with fields allowed (bool), reason (str | None), and requires_approval (bool). Evaluation order: denylist → allowlist → tool call limit → budget cap → write approval → apply approval.

Built-in Automation Profiles

CleverAgents ships with eight built-in automation profiles. Built-in profiles use no namespace prefix.

Threshold values are shown for each flag. A value of 0.0 means always automatic, 1.0 means always manual, and intermediate values (e.g., 0.5, 0.7) mean "automatic when confidence is at or above that level."

Flag manual review supervised cautious trusted auto ci full-auto
decompose_task 1.0 0.0 0.0 0.7 0.0 0.0 0.0 0.0
create_tool 1.0 0.0 1.0 0.7 0.0 0.0 0.0 0.0
select_tool 1.0 1.0 1.0 1.0 1.0 1.0 0.0 0.0
edit_code 1.0 1.0 0.0 0.6 0.0 0.0 0.0 0.0
execute_command 1.0 1.0 1.0 0.8 0.0 0.0 0.0 0.0
create_file 1.0 1.0 1.0 0.7 0.0 0.0 0.0 0.0
delete_content 1.0 1.0 1.0 0.8 1.0 0.0 0.0 0.0
access_network 1.0 1.0 1.0 0.9 1.0 1.0 0.0 0.0
install_dependency 1.0 0.0 1.0 0.7 0.0 0.0 0.0 0.0
modify_config 1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0
approve_plan 1.0 1.0 1.0 0.6 1.0 0.0 0.0 0.0
Safety Profile
safety.require_sandbox true true true true true true true false
safety.require_checkpoints true true true true true true true false
safety.allow_unsafe_tools false false false false false false false true
safety.require_human_approval false false false false false false false false
safety.allowed_skill_categories [] [] [] [] [] [] [] []
safety.max_cost_per_plan null null null null null null null null
safety.max_total_cost null null null null null null null null
safety.max_retries_per_step 3 3 3 3 3 3 3 3

manual: Maximum human control. All thresholds set to 1.0 — every phase transition, every decision, every child plan requires explicit human approval regardless of confidence. Sandbox and checkpoints are mandatory. Unsafe tools are blocked. This is the default starting point. Use for: new users learning the system, critical production systems, first-time exploration of an unfamiliar codebase, sensitive projects, regulatory environments.

review: Phases run automatically (Strategize and Execute proceed without pausing), and most decisions within those phases require human approval (thresholds set to 1.0), except transient retries and child plan spawning which are automatic (thresholds 0.0). Apply is manual. The system does the work but consults you on every choice point. Use for: teams that want to stay in the decision loop without manually triggering each phase, code reviews where architectural choices matter more than execution mechanics.

supervised: Strategize thresholds set to 0.0 (always automatic), but Execute and Apply thresholds set to 1.0 (always manual). The system plans autonomously and retries transient failures automatically, but pauses before Execute for human review of the strategy. Use for: projects where you trust the planning but want to review before execution begins.

cautious: Uses intermediate confidence thresholds (0.60.8) instead of binary 0.0/1.0 values. The system proceeds automatically only when the Semantic Escalation system reports high confidence, and escalates to the user when uncertain. Apply is always manual (1.0). Higher thresholds (0.8) are applied to riskier operations like strategy revision and execution decisions, while lower thresholds (0.6) are used for safer operations like checkpoint restores and strategy decisions. Transient retries are always automatic. Use for: teams adopting automation gradually, projects with mixed complexity where some tasks are routine but others need oversight, situations where you want the system to self-assess rather than follow rigid rules.

trusted: Strategize and Execute thresholds set to 0.0 (always automatic). Validation fixes, child plan spawning, and transient retries proceed automatically. Strategy revision and checkpoint restores remain manual (thresholds 1.0) to preserve human oversight over plan-level course corrections. The system pauses only before Apply (threshold 1.0) for human review of the final diffs. Use for: day-to-day feature development, routine refactoring, test generation.

auto: All thresholds set to 0.0 except Apply (1.0). The system can revise its own strategy, restore from checkpoints, and handle all decisions automatically. Only Apply requires human approval. Use for: well-understood projects, batch operations, tasks with strong invariant coverage.

ci: All thresholds set to 0.0 — complete end-to-end automation including Apply — but sandbox and checkpoints remain mandatory and unsafe tools are blocked. Designed for non-interactive environments where full automation is needed but safety nets must be preserved. Use for: CI/CD pipelines, automated testing workflows, scheduled batch jobs, any headless execution where rollback capability is essential.

full-auto: All thresholds set to 0.0 — complete end-to-end automation including Apply. No sandbox or checkpoint requirements. Unsafe tools are allowed. Use for: low-risk routine tasks (dependency updates, documentation generation, formatting), trusted batch operations with rollback capabilities, environments where external safety mechanisms exist.

Profile Precedence

Automation profiles are determined using this precedence (highest to lowest):

  1. Plan-level: Explicitly set via --automation-profile on agents plan use
  2. Action-level: Set on the action via --automation-profile on agents action create
  3. Project-level: Set via agents config set core.automation-profile <PROFILE> --project <PROJECT>
  4. Global-level: Set via agents config set core.automation-profile <PROFILE>

The effective profile for a plan is resolved at the moment of agents plan use. Once resolved, the profile is locked to that plan — subsequent changes to project or global profiles do not affect running plans.

Child Plan Profile Inheritance

Child plans inherit the parent plan's effective automation profile. If the parent's profile is changed explicitly after creation, new child plans use the new profile while already-running child plans retain their original profile.

Custom Automation Profiles

Custom profiles are created via YAML configuration files and registered with agents automation-profile add. Each automatable task flag takes a confidence threshold (0.01.0) instead of a boolean. A threshold of 0.0 means "always automatic" and 1.0 means "always manual." Intermediate values (e.g., 0.5, 0.7) enable fine-grained control where the system proceeds automatically only when the Semantic Escalation confidence score meets or exceeds the threshold:


# File: profiles/careful-auto.yaml
name: local/careful-auto
description: "Autonomous execution with mandatory sandbox and manual apply"

# Confidence thresholds (0.0 = always auto, 1.0 = always manual) decompose_task: 0.0 create_tool: 0.0 select_tool: 1.0 edit_code: 0.0 execute_command: 0.0 create_file: 0.0 delete_content: 1.0 access_network: 1.0 install_dependency: 0.0 modify_config: 0.0 approve_plan: 0.0

# Safety profile (composed sub-model) safety: require_sandbox: true require_checkpoints: true allow_unsafe_tools: false

A profile with intermediate thresholds provides nuanced control. For example, execute_command: 0.7 means decisions during Execute proceed automatically when confidence >= 0.7, but drop to manual review when confidence is below 0.7:


# File: profiles/nuanced-auto.yaml
name: local/nuanced-auto
description: "Nuanced automation with graduated confidence thresholds"

decompose_task: 0.0 # Always auto-strategize create_tool: 0.5 # Auto-execute when confidence >= 0.5 select_tool: 1.0 # Always require manual apply edit_code: 0.3 # Low bar for strategy decisions execute_command: 0.7 # Higher bar for execution decisions create_file: 0.5 # Auto-fix when reasonably confident delete_content: 0.8 # High confidence needed for auto-revision access_network: 0.9 # Very high bar for late-stage reversion install_dependency: 0.5 # Auto-spawn when moderately confident modify_config: 0.0 # Always auto-retry transient errors approve_plan: 0.6 # Auto-restore when fairly confident

# Safety profile (composed sub-model) safety: require_sandbox: true require_checkpoints: true allow_unsafe_tools: false


agents automation-profile add --config ./profiles/careful-auto.yaml

Semantic Escalation

Semantic Escalation is the system that computes a confidence score (0.01.0) for each operation, and it is the mechanism through which automation profile thresholds take effect. The confidence score reflects the system's assessment of how likely an autonomous action is to succeed without human guidance. This score is then compared against the profile's threshold for the relevant flag to determine whether to proceed automatically or escalate to the user.

The confidence score is computed from multiple factors:


class AutonomyController:
    def should_proceed_automatically(self, decision, context, profile):
        """Determine whether to proceed automatically or escalate to user."""
        factors = {
            'past_success_rate': self.get_historical_success(decision.type),
            'codebase_familiarity': self.get_familiarity_score(context.project),
            'risk_assessment': self.evaluate_risk(decision),
            'invariant_complexity': self.analyze_invariants(decision)
        }
    confidence = self.compute_confidence(factors)  <span style="opacity: 0.7;"># Returns 0.01.0</span>
    threshold = profile.get_threshold(decision.flag)  <span style="opacity: 0.7;"># e.g., execute_command</span>

    <span style="color: magenta; font-weight: 600;">if</span> confidence &gt;= threshold:
        <span style="opacity: 0.7;"># Confidence meets or exceeds the profile threshold — proceed</span>
        <span style="color: magenta; font-weight: 600;">return</span> ProceedAutonomously(decision, confidence)
    <span style="color: magenta; font-weight: 600;">else</span>:
        <span style="opacity: 0.7;"># Confidence below threshold — escalate to human</span>
        <span style="color: magenta; font-weight: 600;">return</span> RequestHumanGuidance(decision, confidence, threshold, factors)

How thresholds and confidence interact: A profile with execute_command: 0.7 means the system will make execution decisions automatically when confidence >= 0.7, but will pause for human input when confidence < 0.7. This is more nuanced than a binary on/off: a profile can set a high bar (0.9) for risky operations while being permissive (0.2) for low-risk ones.

Special cases: A threshold of 0.0 means "always automatic" — even zero confidence passes the threshold. A threshold of 1.0 means "always manual" — no confidence level (which tops out below 1.0 in practice) is high enough to pass. This means the old boolean behavior is a strict subset: true maps to 0.0 and false maps to 1.0.

Progressive Trust Building

New users typically follow this progression:

  1. Start with manual to understand system behavior
  2. Move to review to let phases run automatically while staying in the decision loop
  3. Adopt supervised as confidence in the planning phase builds
  4. Try cautious for confidence-gated automation that escalates only when uncertain
  5. Adopt trusted for routine development tasks
  6. Enable auto for well-understood projects with strong invariant coverage
  7. Use ci for headless CI/CD pipelines with safety nets intact
  8. Use full-auto for low-risk batch operations where external safety mechanisms exist

Guardrails

!!! adr "Architecture Decision" System-level guardrails, pre-flight checks, and runtime limits are defined in ADR-018: Semantic Error Prevention.

System-level guardrails are pre-flight checks and runtime limits that prevent plans from executing in invalid or unsafe conditions. These are distinct from Validations (the Tool subtype described in Core Concepts > Validation) — guardrails operate at the system/infrastructure level before and during plan execution, whereas Validations verify the quality of the work produced by a plan at the end of the Execute phase.

Plan Generation Guardrails

Before a plan begins execution, the system performs pre-flight validation to ensure the plan is well-formed and its dependencies are satisfiable. These checks must be implemented:

  • Action schema validation: Verify the action referenced by the plan exists, is well-formed, and its configuration conforms to the expected schema.
  • Actor availability: Confirm that all actors required by the plan (strategy actor, execution actor, estimation actor, invariant reconciliation actor) are registered and reachable. For remote actors, verify network connectivity.
  • Skill and tool existence: Verify that all skills and tools referenced by the action's actor configuration exist in the registry. This includes both named tools and tools referenced via skills (transitively resolved).
  • Automation policy: Verify that the automation profile allows the requested level of autonomy for the target project, resources, and actions.
  • Rollback feasibility: If require_checkpoints is enabled on the plan, verify that all tools in the actor's skill set have checkpointable: true. Reject plans that would use non-checkpointable tools under a checkpoint-required policy.
  • Resource accessibility: Verify that the project's linked resources are accessible — git repos are cloneable, databases are connectable, file system paths exist, etc. This is a shallow connectivity check, not a full resource health assessment.
  • Validation attachment resolution: Pre-resolve the validations that will apply to this plan (from resource-direct, project, and plan attachment scopes) and verify they are all registered and their tool definitions are valid. This ensures validation failures during Execute are due to the work product, not misconfigured validations.

These pre-flight checks prevent "plan runs with fake providers," missing tool errors mid-execution, and other surprises that waste compute and user time. If any pre-flight check fails, the plan is rejected before entering the Strategize phase, with a clear error message identifying the failing check.

Cost and Rate Limits

Runtime guardrails to prevent runaway resource consumption:

  • Per-plan budgets: Maximum API token spend, maximum wall-clock time, maximum number of tool invocations per plan. When a budget is exceeded, the plan pauses and requests user approval to continue or is terminated.
  • Per-session budgets: Aggregate limits across all plans in a session. Prevents a single interactive session from consuming excessive resources.
  • Per-org budgets: Server-enforced limits for multi-user deployments. Administrators set organization-wide spend caps that cannot be overridden by individual users.
  • Per-actor limits: Maximum tool calls per actor invocation, maximum retries per tool failure, maximum tokens per LLM call. Prevents individual actors from looping indefinitely.
  • Validation retry limits: Maximum number of fix-then-revalidate iterations when a required Validation fails during Execute. This is a specific application of per-actor limits — the default is 3 attempts, configurable per plan or in the automation profile.

Cost and rate limits are future concerns that require integration with LLM provider billing APIs and internal metering. The system should define configuration surfaces for these limits but may initially implement only the per-plan and per-actor limits, with per-session, per-org, and billing integration added later.

Correcting Plans (Core Feature)

!!! adr "Architecture Decision" Plan correction, decision revision, and the edit-replay model are defined in ADR-007: Decision Tree and Correction.

Correcting plans is where CleverAgents becomes more than "a fancy prompt runner."

The Goal

When a plan makes a wrong decision early, we want to:

  • correct the decision,
  • recompute only the affected subtree,
  • preserve unaffected work.

This is explicitly described: "redo everything below that decision, not the entire code base."

Decision Tree Representation

Every plan records (see Decision Data Model section):

  • decisions (choice points) - created during Strategize, including invariant_enforced, subplan_spawn, and subplan_parallel_spawn decisions
  • dependencies (which later work depended on that decision)
  • child plans spawned because of that decision - populated during Execute
  • artifacts generated under that branch

This makes plan runs auditable and correctable.

Two Correction Modes

=== "Revert (--mode=revert)"

!!! warning "Potentially Expensive"
    Find the decision point in the tree, roll back all changes (code and non-code) to that point, and re-run from that decision point forward. Old execution artifacts are kept for comparison.

- [x] Roll back all changes to the decision point
- [x] Re-run from that decision point forward
- [x] Keep old execution artifacts for comparison
- [ ] ==Potentially expensive== if high up in the tree

=== "Append (--mode=append)"

!!! success "Safer and Cheaper"
    Leave history intact and append a new plan at the end that fixes the outcome. Does not rewrite history.

- [x] Leave history intact
- [x] Append a new corrective plan at the end
- [x] Cheaper and safer in many cases
- [x] Does ==not rewrite history==

Correction Flow (Revert Mode)

!!! adr "Architecture Decision" The detailed rollback and replay mechanics, including mid-phase correction flows and cross-sandbox coordination, are defined in ADR-035: Decision Tree Rollback and Replay.

Correction operates on two independent rollback dimensions:

Dimension What It Restores Mechanism Applicable Phase
Resource rollback Sandbox contents (files, database state, filesystem) Checkpoint restoration via sandbox-strategy-specific handlers Execute only
Reasoning rollback Actor's conversation history, intermediate reasoning, graph state LangGraph checkpoint restoration from actor_state_ref Strategize and Execute
Mid-Strategize Correction

When the target decision was created during Strategize (e.g., a strategy_choice or subplan_spawn), only reasoning rollback is needed because Strategize is read-only — no resource modifications exist to undo:

  1. Restore actor state: Load the target decision's context_snapshot.actor_state_ref (LangGraph checkpoint). This restores the strategy actor to the exact reasoning state it was in when the decision was made.
  2. Supersede affected subtree: Mark the target decision and all its descendants as superseded (cascading via ADR-034). Decisions above the target remain active and unchanged.
  3. Inject guidance: Create a user_intervention decision containing the user's --guidance text.
  4. Resume strategy actor: The actor continues reasoning from the restored state, informed by the correction guidance. New decisions replace the superseded ones, with is_correction: true, corrects_decision_id: <target>.
  5. Record correction: Create a correction_attempts record linking old and new decisions.

If the plan was already in Execute when this correction is requested, all Execute-phase work is rolled back (via checkpoint groups — see below) and the plan reverts to Strategize at the corrected decision point.

Mid-Execute Correction

When the target decision was created during Execute (e.g., an implementation_choice or error_recovery), both resource and reasoning rollback are needed:

  1. Load checkpoint group: Every Execute-phase decision has a corresponding checkpoint group — one per-resource checkpoint for each sandbox resource that had been modified at the time the decision was recorded. These are stored in checkpoint_metadata linked by decision_id.

  2. Classify resources:

    • Rollbackable: resources with sandbox strategies that support checkpointing (git_worktree, filesystem_copy, overlay, transaction_rollback)
    • Non-rollbackable: resources with sandbox.strategy: none (tracked with marker records)
    • Unaffected: resources not yet modified at the decision point (no checkpoint row — no rollback needed)
  3. Warn about limitations: If non-rollbackable resources exist, the user is warned and must confirm. If downstream decisions triggered tools with irreversible side_effects, the user is warned that external effects cannot be undone.

  4. Resource rollback: For each rollbackable checkpoint, dispatch to the strategy-specific handler:

    Strategy Rollback Operation
    git_worktree git reset --hard <commit_sha> in the worktree
    filesystem_copy Restore from archived snapshot
    overlay Discard overlay writes since checkpoint
    transaction_rollback ROLLBACK TO SAVEPOINT <savepoint_name>

    If any individual resource rollback fails, the correction is marked 'failed' and the plan enters errored state.

  5. Reasoning rollback: Restore the execution actor's LangGraph state from context_snapshot.actor_state_ref.

  6. Supersede affected subtree: Cascade superseding through the target and all descendants.

  7. Inject guidance and resume: The execution actor continues from the restored state.

!!! note "Decision-Aligned Checkpointing" The 1:1 mapping between Execute-phase decisions and resource checkpoints is created automatically by the record_decision tool (see Decision Recording Protocol in the Core Concepts section). Every record_decision call during Execute triggers a coordinated checkpoint group across all modified sandbox resources. This is what enables fine-grained rollback to any individual Execute-phase decision rather than reverting the entire Execute phase.

Affected Subtree Computation

The "affected subtree" — all decisions and child plans invalidated by a correction — is computed by walking the influence DAG (not just the structural tree):


def compute_affected_subtree(target_decision_id):
    affected_decisions = {target_decision_id}
    affected_plans = set()
    queue = [target_decision_id]
<span style="color: magenta; font-weight: 600;">while</span> queue:
    current = queue.pop(<span style="color: yellow;">0</span>)

    <span style="opacity: 0.7;"># Follow structural tree children</span>
    children = query(<span style="color: #66cc66;">"SELECT decision_id FROM decisions "</span>
                     <span style="color: #66cc66;">"WHERE parent_decision_id = :current AND superseded_by IS NULL"</span>)

    <span style="opacity: 0.7;"># Follow influence DAG dependents</span>
    dependents = query(<span style="color: #66cc66;">"SELECT downstream_ref FROM decision_dependencies "</span>
                       <span style="color: #66cc66;">"WHERE upstream_decision_id = :current AND dependency_type = 'decision'"</span>)

    <span style="color: magenta; font-weight: 600;">for</span> d <span style="color: magenta; font-weight: 600;">in</span> children | dependents:
        <span style="color: magenta; font-weight: 600;">if</span> d <span style="color: magenta; font-weight: 600;">not in</span> affected_decisions:
            affected_decisions.add(d)
            queue.append(d)

    <span style="opacity: 0.7;"># Collect affected child plans</span>
    child_plans = query(<span style="color: #66cc66;">"SELECT downstream_ref FROM decision_dependencies "</span>
                        <span style="color: #66cc66;">"WHERE upstream_decision_id = :current AND dependency_type = 'plan'"</span>)
    affected_plans.update(child_plans)

<span style="color: magenta; font-weight: 600;">return</span> affected_decisions, affected_plans

Cross-Plan Correction Cascading

When the affected subtree includes child plans (via subplan_spawn or subplan_parallel_spawn decisions), the child plan's state determines the behavior:

Child Plan State Action
Not yet started Cancel the child plan
In progress (Strategize or Execute) Cancel the child plan, roll back its sandbox
Completed but not applied Cancel the child plan, roll back its sandbox
Already applied Correction rejected — applied changes cannot be unilaterally reverted; correct the child plan independently or use --mode=append on the parent
Rollback Tiers

The system's rollback capability depends on the plan's checkpoint and sandbox configuration:

Tier Prerequisites Capability
Full decision-level Checkpointing enabled + sandboxable resources Roll back to any individual Execute-phase decision
Phase-level Checkpointing disabled or unsupported resources Revert entire Execute phase → Strategize; re-execute from scratch
No rollback sandbox.strategy: none + checkpointing disabled Only --mode=append corrections available

The system detects the available tier automatically and informs the user when a requested correction exceeds the available capability.

Phase Boundary Interaction

agents plan correct is orthogonal to phase reversion (Execute → Strategize):

  • Phase reversion is actor-triggered when execution constraints are too tight — the actor signals that the strategy needs adjustment.
  • Decision correction is user-triggered via plan correct — the user identifies a wrong decision.

They can interact: correcting a Strategize-phase decision while the plan is in Execute causes both resource rollback (of Execute-phase work) and phase reversion to Strategize. Correcting an Execute-phase decision while still in Execute performs resource rollback within Execute without a phase change.

Long-Running Database Transactions

The transaction_rollback sandbox strategy uses SAVEPOINTs for decision-aligned checkpoints within a single transaction spanning Execute. For plans with extended execution times, this may exceed database transaction timeouts. Recommended mitigations:

  • Decompose into child plans: Each child plan gets a shorter transaction (aligns with hierarchical decomposition).
  • Use none strategy with --mode=append: Accept non-rollbackable database changes; fix issues via append corrections.
  • Implement a custom snapshot-based strategy: A custom database-snapshot resource type using point-in-time snapshots instead of long transactions.

History Cleanup

History can only be flagged for cleanup after a plan reaches the applied state.

Once a plan is applied:

  • It can no longer be rolled back
  • Old correction artifacts can be archived or deleted based on retention policy
  • The decision tree is preserved for audit purposes (superseded decisions are never deleted, per ADR-034)

CLI Commands for Correction


# View decision tree
agents plan tree <plan_id>
agents --format=json plan tree <plan_id>  # For visualization tools

# View decision tree including superseded branches agents plan tree --show-superseded <plan_id>

# Inspect a specific decision agents plan explain <decision_id> # Shows: question, chosen option, alternatives, rationale, downstream impact

# Correct via revert-and-replay agents plan correct <decision_id> --mode=revert --guidance "<what the decision should be>" # Re-executes from that point with the new guidance

# Correct via append (add fix at end) agents plan correct <decision_id> --mode=append --guidance "<description of the fix>" # Creates a new child plan to fix the outcome without rewriting history

# Compare old vs new after correction agents plan diff --correction <correction_attempt_id>

Correction Safety

!!! success "Safety Guarantees" Corrections always:

- [x] Create a new attempt revision (increment `plan.attempt`)
- [x] Preserve old artifacts for diff/compare
- [x] Run execute in sandbox again
- [x] Require apply gating again
- [x] ==Never modify already-applied changes==
- [x] Warn about irreversible side effects before proceeding
- [x] Reject correction of decisions whose affected subtree includes applied child plans

This keeps history reproducible and prevents accidental destructive edits.

Human-in-the-Loop Collaboration

!!! adr "Architecture Decision" Human-in-the-loop controls, interruptibility, and approval gates are defined in ADR-017: Automation Profiles.

Even though the direction is "more autonomous," the transcript explicitly recognizes that real workflows require engineers to collaborate with the system, editing code while it works, and using better UX integration (TUI/web/IDE).

!!! tip "Design Principles for Human-in-the-Loop" | Principle | Description | | :-------- | :---------- | | Visibility | What is the system doing ==right now==? | | Interruptibility | Pause / cancel / retry at any point | | Editability | Allow user to modify strategy before execute | | Reconciliation | Detect if user changed sandbox files mid-run and handle it |

UI / Interaction Model

!!! adr "Architecture Decision" The CLI-first interaction model, TUI, and output rendering are defined in ADR-021: CLI and Output Rendering.

CLI-first + TUI + Web App + IDE

!!! abstract "Multi-Frontend Architecture" The system is ==CLI-first==, with a single UI codebase powering multiple frontends:

- [x] **CLI** — Primary interface for all operations
- [x] **TUI** — Built using Textual for rich terminal experiences
- [x] **Web App** — Generated from TUI "for free"
- [ ] **IDE Plugin** — Future: embeds the TUI in the IDE

Plan Tree Visualization

The TUI should show:

  • plan list
  • plan details
  • plan tree (ASCII)
  • diff view
  • approvals

And should later allow exporting the tree as image (PNG) or JSON for other visualization tools.

TUI

TUI Architecture Overview

!!! adr "Architecture Decision" The TUI architecture, framework selection (Textual >= 1.0), screen hierarchy, TuiMaterializer integration, and A2A event subscription model are defined in ADR-044: TUI Architecture and Framework.

The TUI (Terminal User Interface) is the second Presentation-layer surface for CleverAgents, built on Textual (>= 1.0). It provides real-time plan monitoring, multi-session management, interactive decision tree navigation, and rich conversation with actors — capabilities impractical in the stateless CLI.

The TUI communicates with the Application layer exclusively through A2A (ADR-026). It subscribes to A2A events for real-time updates and uses the existing Output Rendering Framework (ADR-021) via a TuiMaterializer that maps ElementHandle events to Textual widget operations — enabling all CLI command producers to render in the TUI without modification.

Key architectural principles:

  • Direct-to-chat launch — no launcher screen; opens directly to the main chat interface
  • Right-side collapsible sidebar — three states cycled by shift+tab: hidden → visible → fullscreen
  • Multi-session tabs — independent sessions with separate personas, conversations, and A2A bindings
  • Escape-cascading navigationescape always moves toward the main screen from any state
  • Keyboard-first — every operation achievable without a mouse; mouse is supplementary
  • Single UI codebase — the same Textual widget tree serves standalone TUI, Web (via Textual Web), and IDE plugin

MainScreen Layout — Sidebar Hidden

When the sidebar is hidden, the conversation takes the full terminal width:

┌──────────────────────────────────────────────────────────────────────────────────────────────┐
│═══════════════════════════════════════════ ◆ ═══════════════════════════════════════════     │
│ Session 1Session 2Session 3                                                          │
│              ┗━━━━━━━━━━━━┛                                                                  │
├──────────────────────────────────────────────────────────────────────────────────────────────┤
│                                                                                              │
│  You                                                          14:01                         │
│  Can you review the auth module in the                                                      │
│  API service?                                                                               │
│  @project:api-service:src/auth/handler.py                                                   │
│                                                                                             │
│                                                                                             │
│  Actor                                                       14:02                          │
│  I'll review the authentication handler.                                                    │
│  Let me analyze the code structure and                                                      │
│  identify potential issues.                                                                 │
│                                                                                             │
│   🔧 local/code-analysis                                                                  │
│                                                                                             │
│  Found 3 issues in the auth module:                                                         │
│                                                                                             │
│  1. **Missing rate limiting**                                                               │
│  2. **JWT token** not validated                                                             │
│  3. **Session cleanup** missing                                                             │
│                                                                                             │
│   🔧 local/file-read                                                                      │
│   ┌─ src/auth/handler.py ─────────────┐                                                     │
│   │  45 │ def login(self, req):      │                                                      │
│   │  46 │     creds = extract(req)   │                                                      │
│   │  47 │     return auth(creds)     │                                                      │
│   └──────────────────────────────────┘                                                      │
│                                                                                              │
│──────────────────────────────────────────────────────────────────────────────────────────────│
│ Agent connected                                                                              │
│──────────────────────────────────────────────────────────────────────────────────────────────│
│ ┌─────────────────────────────────────────────────────────────────────────────────────┐      │
│ │  What would you like to do?                                                        │      │
│ │   ▌@▐ refs  ▌/▐ commands  ▌!▐ shell                                                 │      │
│ └─────────────────────────────────────────────────────────────────────────────────────┘      │
│ feature-devclaude-4-sonnetthink: high2 projects │ $0.12                             │
├──────────────────────────────────────────────────────────────────────────────────────────────┤
│ F1 Help │ shift+tab Sidebar │ tab Persona │ ctrl+tab Preset │ ctrl+s Sessions │ ctrl+q Quit  │
└──────────────────────────────────────────────────────────────────────────────────────────────┘

Key elements in this layout:

  • Throbber (top edge): rainbow gradient bar, visible only when the actor is processing. Collapses to zero height when idle.
  • Session Tabs (below throbber): tab bar with underline indicator on the active session. Hidden when only one session exists. Session labels show state icons: (actor working), (awaiting input), plain (idle).
  • Conversation (center): scrollable message stream with block cursor navigation.
  • Prompt (bottom): PromptTextArea with mode-dependent symbol ( normal, $ shell, ☰ multi-line), overlays for @ references and / commands, and the PersonaBar.
  • Footer: context-sensitive hotkey reference, always visible.

MainScreen Layout — Sidebar Visible

When the sidebar is visible (shift+tab from hidden), it docks to the right at 32-40 chars wide:

┌──────────────────────────────────────────────────────────────────────────────────────────────┐
│═══════════════════════════════════════════ ◆ ═══════════════════════════════════════════     │
│ Session 1Session 2Session 3                                                          │
│              ┗━━━━━━━━━━━━┛                                                                  │
├─────────────────────────────────────────────────────────────┬────────────────────────────────┤
│                                                             │ ▼ PLANS                        │
│  You                                  14:01                │ ┌────────────────────────────┐ │
│  Can you review the auth module in the                     │ │  fix-auth-bug             │ │
│  API service?                                              │ │   Phase: Execute ●●●      │ │
│  @project:api-service:src/auth/handler.py                  │ │   Profile: trusted         │ │
│                                                            │ │   Actor: claude-4-sonnet   │ │
│                                                            │ │   Cost: $0.08              │ │
│  Actor                                  14:02              │ │                            │ │
│  I'll review the authentication handler.                   │ │  refactor-models          │ │
│  Let me analyze the code structure and                     │ │   Phase: Strategize ○○○   │ │
│  identify potential issues.                                │ │   Profile: cautious        │ │
│                                                            │ │   Depth: 2 (3 subplans)    │ │
│   🔧 local/code-analysis                                 │ │   Actor: gpt-4o            │ │
│                                                            │ │   ├─ users   (execute)     │ │
│  Found 3 issues in the auth module:                        │ │   ├─ orders  (strategize)  │ │
│                                                            │ │   └─ auth    (pending)     │ │
│  1. **Missing rate limiting**                              │ │                            │ │
│  2. **JWT token** not validated                            │ │  update-deps              │ │
│  3. **Session cleanup** missing                            │ │   Phase: Idle              │ │
│                                                            │ │   Profile: auto            │ │
│   🔧 local/file-read                                     │ └────────────────────────────┘ │
│   ┌─ src/auth/handler.py ─────────────┐                    │                                │
│   │  45 │ def login(self, req):       │                     │ ▼ PROJECTS                     │
│   │  46 │     creds = extract(req)    │                     │ ┌────────────────────────────┐ │
│   │  47 │     return auth(creds)      │                     │ │ ◆ cleveragents        [2p] │ │
│   └───────────────────────────────────┘                     │ │ ◆ api-service         [1p] │ │
│                                                             │ │   frontend-app        [0p] │ │
│─────────────────────────────────────────────────────────────│ │   infra-terraform     [0p] │ │
│ Agent connected                                             │ └────────────────────────────┘ │
│─────────────────────────────────────────────────────────────│                                │
│ ┌────────────────────────────────────────────────────────┐  │                                │
│ │  What would you like to do?                           │  │                                │
│ │   ▌@▐ refs  ▌/▐ commands  ▌!▐ shell                    │  │                                │
│ └────────────────────────────────────────────────────────┘  │                                │
│ feature-devclaude-4-sonnetthink: med2 proj │ $0.12 │                                │
├─────────────────────────────────────────────────────────────┴────────────────────────────────┤
│ F1 Help │ shift+tab Sidebar │ tab Persona │ ctrl+tab Preset │ ctrl+s Sessions │ ctrl+q Quit  │
└──────────────────────────────────────────────────────────────────────────────────────────────┘

Key elements in this layout:

  • Throbber (top edge): rainbow gradient bar, visible only when the actor is processing. Collapses to zero height when idle.
  • Session Tabs (below throbber): tab bar with underline indicator on the active session. Hidden when only one session exists. Session labels show state icons: (actor working), (awaiting input), plain (idle).
  • Conversation (center): scrollable message stream with block cursor navigation.
  • Sidebar (right, 32-40 chars wide): Plans panel and Projects panel in collapsible containers.
  • Prompt (bottom of left panel): same prompt components as hidden mode, constrained to the conversation column width.
  • PersonaBar (below prompt): shows persona name, actor, preset, scope, and cost.

MainScreen Layout — Sidebar Fullscreen

Fullscreen mode (shift+tab from visible) covers the entire terminal for plan/project/persona management:

┌──────────────────────────────────────────────────────────────────────────────────────────────┐
│ PLANS & PROJECTS BROWSER                                                                     │
│──────────────────────────────────────────────────────────────────────────────────────────────│
│ PLANSPROJECTS                                     │
│ ┌───────────────────────────────────────────┐ │ ┌──────────────────────────────────────────┐ │
│ │                                           │ │ │                                          │ │
│ │ [x] ► fix-auth-bug                        │ │ │ [x] cleveragents                         │ │
│ │     Phase: Execute ●●●                   │ │ │     Namespace: local/                    │ │
│ │     State: in_progress                    │ │ │     Resources: 5                         │ │
│ │     Profile: trusted                      │ │ │     Plans: 2 active                      │ │
│ │     Actor: anthropic/claude-4-sonnet      │ │ │     Invariants: 3                        │ │
│ │     Started: 2m ago    Cost: $0.08        │ │ │     Validations: 2                       │ │
│ │     Decisions: 6 (4 done, 2 pending)      │ │ │                                          │ │
│ │                                           │ │ │ [ ] api-service                          │ │
│ │ [ ] refactor-models                       │ │ │     Namespace: local/                    │ │
│ │     Phase: Strategize ○○○                │ │ │     Resources: 3                         │ │
│ │     State: in_progress                    │ │ │     Plans: 1 active                      │ │
│ │     Profile: cautious                     │ │ │     Invariants: 1                        │ │
│ │     Actor: openai/gpt-4o                  │ │ │     Validations: 4                       │ │
│ │     Started: 5m ago    Cost: $0.03        │ │ │                                          │ │
│ │     Decisions: 3 (2 done, 1 active)       │ │ │ [ ] frontend-app                         │ │
│ │     Subplans: 3 (depth 2)                 │ │ │     Namespace: local/                    │ │
│ │       ├─ users    Execute ●●●            │ │ │     Resources: 2                         │ │
│ │       ├─ orders   Strategize ○○○         │ │ │     Plans: 0                             │ │
│ │       └─ auth     Pending                 │ │ │                                          │ │
│ │                                           │ │ │ [ ] infra-terraform                      │ │
│ │ [ ] update-deps                           │ │ │     Namespace: local/                    │ │
│ │     Phase: Idle                           │ │ │     Resources: 8                         │ │
│ │     Profile: auto                         │ │ │     Plans: 0                             │ │
│ │                                           │ │ │                                          │ │
│ └───────────────────────────────────────────┘ │ └──────────────────────────────────────────┘ │
│──────────────────────────────────────────────────────────────────────────────────────────────│
│ PERSONA CYCLE LIST (tab order)                                                               │
│ ┌──────────────────────────────────────────────────────────────────────────────────────┐     │
│ │  1. feature-dev (claude-4-sonnet, 2 projects)                                        │     │
│ │  2. reviewer (gpt-4o, 1 project)                                                     │     │
│ │  3. infra-admin (claude-4-opus, 1 project)                                           │     │
│ │  [+] Add current selection as persona...                                             │     │
│ └──────────────────────────────────────────────────────────────────────────────────────┘     │
│──────────────────────────────────────────────────────────────────────────────────────────────│
│ Selected: 1 plan, 1 project                                                                  │
│ space Select │ enter Details │ ctrl+p Create Persona │ / Search │ d Delete │ esc Back        │
└──────────────────────────────────────────────────────────────────────────────────────────────┘

In fullscreen mode:

  • Selection mode: space toggles selection on the highlighted plan or project. Selected items show [x], deselected show [ ].
  • Persona creation: ctrl+p opens the PersonaEditorModal with the selected plans and projects pre-populated as the scope.
  • Detail inspection: enter on a plan opens the PlanDetailModal; enter on a project opens the ProjectDetailModal.
  • Persona cycle list: The bottom panel shows the ordered list of personas that tab cycles through. Users can reorder (ctrl+up/ctrl+down), remove (d), or add new personas from the current selection.

Sidebar State Transitions

State Layout Input Focus Content
Hidden Sidebar display: none; conversation full width Prompt retains focus No sidebar content visible
Visible Sidebar docked right, 32-40 chars wide Prompt retains focus; ctrl+b focuses sidebar Plans and Projects panels in collapsible containers
Fullscreen Covers entire screen Sidebar takes input focus Extended details, selection mode, persona management

State transitions:

Hidden ──shift+tab──► Visible ──shift+tab──► Fullscreen
  ▲                                              │
  └──────────────── escape (×1-2) ───────────────┘

Persona System

!!! adr "Architecture Decision" The persona abstraction, argument preset cycling, scope behavior, and persona lifecycle are defined in ADR-045: TUI Persona System.

A persona is a TUI-only abstraction that bundles:

  1. Actor reference — a namespaced actor name (e.g., anthropic/claude-4-sonnet)
  2. Base arguments — default arguments passed to the actor on every invocation
  3. Scoped projects — projects always included in the session's context
  4. Scoped plans — plans always included in the session's context
  5. Argument presets — named argument overrides cycled with ctrl+tab
  6. Display metadata — short name, accent color, description

Personas are stored as YAML files in ~/.config/cleveragents/personas/ and are strictly a Presentation-layer concept — they never appear in the domain model, A2A protocol, or database schema.

PersonaBar

The PersonaBar always shows the current state:

┌──────────────────────────────────────────────────────────────────────┐
│  What would you like to do?                                         │
│   ▌@▐ refs  ▌/▐ commands  ▌!▐ shell                                  │
└──────────────────────────────────────────────────────────────────────┘
  feature-devclaude-4-sonnetthink: high2 projects       │ $0.12
  ─────────── ─ ─────────────── ─ ─────────── ─ ──────────────── ─ ─────
  persona       actor             preset        scope              cost
PersonaBar Segment Style Updates When
Persona name $text-primary on $primary 10% bg tab cycle
Actor name $text-secondary tab cycle
Preset label $text-warning (non-default) / $text-muted (default) ctrl+tab cycle
Scope indicator $text-muted Persona change or /scope:add/remove
Session cost $secondary 70%, right-aligned After each actor response

Persona Cycling

tab cycles through personas in the configured cycle list. ctrl+tab cycles through the current persona's argument presets:

tab:       persona_1 → persona_2 → persona_3 → persona_1 → ...
ctrl+tab:  default → think: high → think: max → quick → default → ...

When cycling, the PersonaBar updates immediately. The actor binding for the session is updated — subsequent prompts use the new persona's actor and scope.

First-Run Experience

On first launch (no personas configured), a centered overlay guides actor selection:

┌──────────────────────────────────────────────────────────────────────┐
│ Welcome to CleverAgents                                              │
│                                                                      │
│ Select an actor to get started:                                      │
│                                                                      │
│    anthropic/claude-4-sonnet   (recommended)                        │
│     anthropic/claude-4-opus                                          │
│     openai/gpt-4o                                                    │
│     openai/o3                                                        │
│     google/gemini-2                                                  │
│     / to search...                                                   │
│                                                                      │
│ A default persona will be created with this actor.                   │
│ You can add more actors and personas later.                          │
│                                                                      │
│──────────────────────────────────────────────────────────────────────│
│ enter Select │ j/k Navigate │ / Search                               │
└──────────────────────────────────────────────────────────────────────┘

Selection creates a "default" persona with the chosen actor and auto-generated argument presets (thinking effort levels are auto-detected from the actor's argument schema). Subsequent launches restore the last active persona.

Reference and Command System

!!! adr "Architecture Decision" The @ reference notation grammar, fuzzy search algorithm, / command system, ! shell mode, and ACMS integration are defined in ADR-046: TUI Reference and Command System.

The TUI prompt supports three input modes, each activated by a distinct prefix:

First Character Mode Prompt Symbol Sent To Overlay
(any other) Normal Actor via A2A
@ (inline) Normal + Reference Actor (with CRP directives) ReferencePickerOverlay
/ Command / TUI command processor SlashCommandOverlay
! Shell $ Host OS subprocess

Reference Picker (@)

Typing @ anywhere in a normal-mode prompt activates the Reference Picker — a fuzzy-search overlay for projects, plans, and resources:

┌─ Reference Picker ───────────────────────────────────────────────────┐
│ @hand                                                                │
│ ──────────────────────────────────────────────────────────────────── │
│  PROJECT  api-service:src/auth/handler.py                            │
│           local/api-service • Python • 245 lines                     │
│                                                                      │
│  PROJECT  cleveragents:git_dir/src/cli/commands/handler.py           │
│           local/cleveragents • Python • 189 lines                    │
│                                                                      │
│  PROJECT  api-service:src/auth/middleware/handler_base.py            │
│           local/api-service • Python • 67 lines                      │
│                                                                      │
│  PLAN     fix-auth-handler (01HXM8C2...)                             │
│           Phase: Execute • Actor: claude-4-sonnet                    │
│                                                                      │
│ ──────────────────────────────────────────────────────────────────── │
│ enter Select │ tab Tree │ ctrl+p Projects │ ctrl+l Plans             │
└──────────────────────────────────────────────────────────────────────┘

Resolved references use a canonical notation: @project:local/api-service:src/auth/handler.py. Fuzzy input like @handler.py auto-expands to the canonical form on selection.

Resolved @ references are translated into CRP (Context Request Protocol) directives before the prompt reaches the actor — they semantically direct the ACMS to prioritize referenced resources in context assembly.

Reference Picker — Tree Browser Mode

Pressing tab in the Reference Picker switches to tree browser mode for hierarchical navigation:

┌─ Reference Picker (Tree) ────────────────────────────────────────────┐
│                                                                      │
│  ▼ Projects                                                          │
│    ▼ local/cleveragents                                              │
│      ▼ git_dir (git-checkout)                                        │
│        ▸ src/                                                        │
│        ▸ tests/                                                      │
│        ▸ docs/                                                       │
│        ─ pyproject.toml                                              │
│        ─ README.md                                                   │
│      ▸ database (sqlite-db)                                          │
│    ▸ local/api-service                                               │
│    ▸ local/frontend-app                                              │
│  ▼ Plans                                                             │
│    ▸ fix-auth-bug (01HXM8C2)                                         │
│    ▸ refactor-models (01HXM9D3)                                      │
│                                                                      │
│ enter Select │ tab Search │ space Expand │ / Filter                  │
└──────────────────────────────────────────────────────────────────────┘

Slash Command Overlay (/)

Typing / as the first character activates command mode. Commands are TUI operations executed locally — they are not sent to the actor.

Slash Command Overlay
┌─ Commands ─────────────────────────────────────────────────────────────┐
│ /se                                                                    │
│ ───────────────────────────────────────────────────────────────────────│
│  /session:create         Create a new session tab                      │
│  /session:list           Show all sessions                             │
│  /session:show           Show session details                          │
│  /session:switch         Switch to session by ID                       │
│  /session:close          Close current session                         │
│  /session:delete         Delete a saved session                        │
│  /session:rename         Rename current session                        │
│  /session:export         Export session to file                        │
│  /session:import         Import session from file                      │
│  /settings               Open settings screen                          │
│                                                                        │
│ enter Execute │ tab Complete │ escape Dismiss                          │
└────────────────────────────────────────────────────────────────────────┘

TUI slash commands mirror CLI command patterns where applicable. The CLI uses agents <noun> <verb>; the TUI uses /<noun>:<verb>. Commands that exist in both CLI and TUI use the same verb names. TUI-only commands (persona, scope, TUI utilities) have no CLI equivalent.

Complete Command Reference

Session Commandsmirrors CLI agents session <verb>

Command Arguments Description
/session:create [--persona <name>] Create a new session tab
/session:list Display all sessions
/session:show <id> <id> Show session details
/session:switch <id> <id> Switch to session by ID or tab index
/session:close [--force] Close the current session tab
/session:delete <id> <id>, [--yes/-y] Delete a saved session
/session:rename <name> <name> Rename the current session
/session:export [path] [path] Export session to JSON
/session:import <path> <path> Import session from JSON

Persona CommandsTUI-only

Command Arguments Description
/persona:list Display all personas
/persona:set <name> <name> Switch to persona
/persona:create Open PersonaEditorModal
/persona:edit [name] [name] Edit persona
/persona:delete <name> <name> Delete persona
/persona:export <name> <name> Export persona YAML
/persona:import <path> <path> Import persona YAML

Scope CommandsTUI-only

Command Arguments Description
/scope:add <ref> <ref> Add project/plan to session scope
/scope:remove <ref> <ref> Remove from session scope
/scope:clear Clear session-level scope additions
/scope:show Show effective scope

Plan Commandsmirrors CLI agents plan <verb>

Command Arguments Description
/plan:use <action> <action>, [projects...], [--arg/-a <key=value>] Start a new plan
/plan:list [--phase] [--state] [--project] [--action] List plans
/plan:status [id] [id] Show plan status
/plan:tree <id> <id>, [--show-superseded] [--depth] Show decision tree
/plan:execute [id] [id] Execute a plan
/plan:apply [id] [id] Apply a completed plan
/plan:cancel <id> <id>, [--reason/-r] Cancel a plan
/plan:diff <id> <id>, [--correction] Show plan diff
/plan:correct <did> <did>, --mode, --guidance Correct a decision
/plan:resume <id> <id>, [--dry-run] Resume a plan
/plan:revert <id> <id>, [--to-phase] Revert a plan
/plan:rollback <id> <cp> both required Rollback to checkpoint
/plan:explain <did> <did>, [--show-context] Explain a decision
/plan:errors <id> <id> Show plan errors
/plan:artifacts <id> <id> Show plan artifacts
/plan:inspect [id] [id] Open PlanDetailModal

Project Commandsmirrors CLI agents project <verb>

Command Arguments Description
/project:list [--namespace/-n] List all projects
/project:create <name> <name>, [--description/-d] [--resource/-r] Create project
/project:show <name> <name> Show project details
/project:delete <name> <name>, [--yes/-y] Delete project
/project:inspect <name> <name> Open ProjectDetailModal
/project:context:show <name> <name> Show project context config

Actor, Resource, Config, Tool, Skill, Invariant, Profile Commandsmirrors CLI

Command Description
/actor:list, /actor:show, /actor:set-default Actor management
/resource:list, /resource:show, /resource:tree, /resource:inspect Resource management
/config:list, /config:get, /config:set Configuration management
/tool:list, /tool:show Tool management
/skill:list, /skill:show Skill management
/invariant:list, /invariant:add, /invariant:remove Invariant management
/profile:list, /profile:show Automation profile management
/context:inspect, /context:set, /context:simulate Context management (inspect state, set policy, simulate assembly)

TUI Utility CommandsTUI-only

Command Arguments Description
/clear Clear conversation display
/theme [name] [name] Switch color theme
/settings Open SettingsScreen
/help [command] [command] Show help
/about Show version and system info
/debug Toggle debug mode

Shell Mode (!)

Shell mode provides direct OS command execution from the TUI prompt. Typing ! as the first character changes the prompt symbol to $ and enables shell syntax highlighting, file/directory tab completion, and separate shell history.

┌────────────────────────────────────────────────────────────────────┐
│ $ git status                                                       │
│ ┌────────────────────────────────────────────────────────────────┐ │
│ │ On branch main                                                 │ │
│ │ Your branch is up to date with 'origin/main'.                  │ │
│ │                                                                │ │
│ │ Changes not staged for commit:                                 │ │
│ │   modified:   src/auth/handler.py                              │ │
│ │   modified:   src/auth/middleware.py                           │ │
│ └────────────────────────────────────────────────────────────────┘ │
│ (exit code: 0)                                                     │
└────────────────────────────────────────────────────────────────────┘

Shell results appear as ShellResult blocks in the conversation stream with a $primary left border and $foreground 4% background. Long-running commands stream output in real-time; ctrl+c sends SIGINT.

Plan Detail Modal

The PlanDetailModal shows a plan's decision tree alongside decision details:

┌───────────────────────────────────── Plan: fix-auth-bug ─────────────────────────────────────┐
│ ID: 01HXM8C2ABCD     Phase: Execute       State: in_progress                                 │
│ Profile: trusted      Actor: claude-4-sonnet   Started: 2m ago    Cost: $0.08                │
│ Projects: local/api-service                                                                  │
│──────────────────────────────────────────────────────────────────────────────────────────────│
│ DECISION TREEDECISION DETAIL                                   │
│ ┌──────────────────────────────────────┐ │ ┌────────────────────────────────────────┐        │
│ │                                      │ │ │ Decision: D3                           │        │
│ │ ▼ D1 strategy_choice                │ │ │ Type: implementation_choice            │        │
│ │   ├─ D2 invariant_enforced          │ │ │ Phase: Execute                         │        │
│ │   ├─▶D3 implementation_choice      │ │ │                                        │        │
│ │   │   ├─ D4 tool_invocation         │ │ │ Question:                              │        │
│ │   │   ├─ D5 tool_invocation        │ │ │ How should rate limiting be            │        │
│ │   │   └─ D6 tool_invocation        │ │ │ implemented on the login endpoint?     │        │
│ │   ├─ D7 resource_selection         │ │ │                                        │        │
│ │   └─ D8 subplan_spawn              │ │ │ Chosen: Token bucket algorithm         │        │
│ │                                      │ │ │ with Redis backing store               │        │
│ │ / Search decisions...                │ │ │                                        │        │
│ │                                      │ │ │ Alternatives:                          │        │
│ └──────────────────────────────────────┘ │ │ • Sliding window counter               │        │
│                                          │ │ • Fixed window counter                 │        │
│ Legend:                                  │ │ • IP-based blocking                    │        │
│   completed   active                  │ │                                        │        │
│   pending    failed                   │ │ Confidence: 0.85                       │        │
│   superseded                            │ │ Rationale: Token bucket provides       │        │
│                                          │ │ fine-grained control with burst        │        │
│ ───────────────────────────────────────  │ │ allowance and Redis enables            │        │
│ Phase: ●●●                              │ │ distributed rate limiting...           │        │
│ Decisions: 8 total                       │ │                                        │        │
│  4 completed, 1 active, 3 pending        │ │ Dependencies:                          │        │
│                                          │ │  ← D1 (strategy_choice)                │        │
│                                          │ │  → D4, D5, D6 (tool_invocations)       │        │
│                                          │ │                                        │        │
│                                          │ │ Context Snapshot: 2.3 KB               │        │
│                                          │ └────────────────────────────────────────┘        │
│──────────────────────────────────────────────────────────────────────────────────────────────│
│ j/k Nav │ enter Inspect │ c Correct │ d Diff │ h History │ t Toggle │ / Search               │
│ s Superseded │ x Expand all │ z Collapse all │ escape Close                                  │
└──────────────────────────────────────────────────────────────────────────────────────────────┘

Decision Tree Interaction

Key Action
j / down Move to next decision
k / up Move to previous decision
enter Select decision → show detail in right panel
space Expand/collapse subtree
c Correct selected decision (opens correction dialog)
d Show diff for selected decision
h Show decision history / superseded chain
t Toggle detail panel visibility
x Expand all nodes
z Collapse all nodes
s Show superseded decisions (hidden by default)
/ Search decisions by type, content, or ID
escape Close modal

Project Detail Modal

The ProjectDetailModal shows a project's resource DAG, invariants, and running plans:

┌───────────────────────────────── Project: local/api-service ─────────────────────────────────┐
│ Namespace: local/        Resources: 3          Plans: 1 active                               │
│ Invariants: 1            Validations: 4        Automation: trusted                           │
│──────────────────────────────────────────────────────────────────────────────────────────────│
│ RESOURCESDETAIL                                            │
│ ┌──────────────────────────────────────┐ │ ┌────────────────────────────────────────┐        │
│ │                                      │ │ │ Resource: api-repo                     │        │
│ │ ▼ api-repo (git-checkout)            │ │ │ Type: git-checkout                     │        │
│ │   ├─ src/ (fs-directory)             │ │ │ ID: 01HXYZ...                          │        │
│ │   │   ├─ auth/                       │ │ │                                        │        │
│ │   │   │   ├─ handler.py              │ │ │ URL: git@github.com:org/api.git        │        │
│ │   │   │   ├─ middleware.py           │ │ │ Branch: main                           │        │
│ │   │   │   └─ limiter.py              │ │ │ Commit: a1b2c3d (2h ago)               │        │
│ │   │   ├─ models/                     │ │ │                                        │        │
│ │   │   └─ routes/                     │ │ │ Sandbox: git_worktree                  │        │
│ │   ├─ tests/ (fs-directory)           │ │ │ Checkpoint: enabled                    │        │
│ │   └─ pyproject.toml                  │ │ │                                        │        │
│ │ ─ api-db (container-instance)        │ │ │ Children: 2 (src/, tests/)             │        │
│ │ ─ api-cache (container-instance)     │ │ │ Validations: 2 attached                │        │
│ │                                      │ │ │                                        │        │
│ └──────────────────────────────────────┘ │ └────────────────────────────────────────┘        │
│                                          │                                                   │
│ INVARIANTSRUNNING PLANS                                     │
│ ┌──────────────────────────────────────┐ │ ┌────────────────────────────────────────┐        │
│ │ 1. All API endpoints must have       │ │ │ fix-auth-bug  Execute ●●●  $0.08      │        │
│ │    rate limiting (project-level)     │ │ │                                        │        │
│ └──────────────────────────────────────┘ │ └────────────────────────────────────────┘        │
│──────────────────────────────────────────────────────────────────────────────────────────────│
│ j/k Nav │ enter Drill │ i Invariants │ v Validations │ p Plans │ / Search │ esc Close        │
└──────────────────────────────────────────────────────────────────────────────────────────────┘

Persona Editor Modal

The PersonaEditorModal is used for creating and editing personas:

┌─────────────────────────────────────── Create Persona ───────────────────────────────────────┐
│                                                                                              │
│ Name:        ┌──────────────────────────────────────┐                                        │
│              │ feature-dev                          │                                        │
│              └──────────────────────────────────────┘                                        │
│                                                                                              │
│ Description: ┌────────────────────────────────────────────────────────┐                      │
│              │ Feature development with Claude on main projects       │                      │
│              └────────────────────────────────────────────────────────┘                      │
│                                                                                              │
│ Actor:       ┌──────────────────────────────────────────┐                                    │
│              │  anthropic/claude-4-sonnet              │ ▼                                  │
│              └──────────────────────────────────────────┘                                    │
│                                                                                              │
│ Color:   ● $primary   ○ $secondary   ○ $success   ○ $warning   ○ $error                      │
│                                                                                              │
│ ── Base Arguments (from actor schema) ──────────────────────────────────────                 │
│ thinking_effort:  ┌──────────┐   temperature:  ┌──────────┐                                  │
│                   │ medium   │ ▼               │ 0.7      │                                  │
│                   └──────────┘                 └──────────┘                                  │
│ max_tokens:       ┌──────────┐                                                               │
│                   │ 16384    │                                                               │
│                   └──────────┘                                                               │
│                                                                                              │
│ ── Scoped Projects ─────────────────────────────────────────────────────────                 │
│ [x] local/cleveragents        [x] local/api-service                                          │
│ [ ] local/frontend-app        [ ] local/infra-terraform                                      │
│                                                                                              │
│ ── Scoped Plans ────────────────────────────────────────────────────────────                 │
│ [ ] fix-auth-bug (01HXM8C2)   [ ] refactor-models (01HXM9D3)                                 │
│                                                                                              │
│ ── Argument Presets (ctrl+tab cycling) ─────────────────────────────────────                 │
│ ┌────────────────────────────────────────────────────────────────────────────────────┐       │
│ │  1. default      │ (base arguments)                                                │       │
│ │  2. think: high  │ thinking_effort=high                                            │       │
│ │  3. think: max   │ thinking_effort=max, temperature=0.3                            │       │
│ │  4. quick        │ thinking_effort=low, max_tokens=4096                            │       │
│ │  [+] Add preset                                                                    │       │
│ └────────────────────────────────────────────────────────────────────────────────────┘       │
│                                                                                              │
│ Cycle Order: ┌──────┐  (0 = not in tab cycle)                                                │
│              │  1   │                                                                        │
│              └──────┘                                                                        │
│                                                                                              │
│──────────────────────────────────────────────────────────────────────────────────────────────│
│ tab Next field │ enter Save │ escape Cancel                                                  │
└──────────────────────────────────────────────────────────────────────────────────────────────┘

Permissions Screen

When a tool requests permission to modify resources, the PermissionsScreen shows the requested changes with a diff view:

┌───────────────────────────────────── Permission Request ─────────────────────────────────────┐
│                                                                                              │
│ local/file-write wants to modify 3 files in local/api-service:                               │
│                                                                                              │
│ ┌─────────────────────────────┐ ┌──────────────────────────────────────────────────────┐     │
│ │Files (3 changes):           │ │src/auth/handler.py                                   │     │
│ │                             │ │──────────────────────────────────────────────────────│     │
│ │ src/auth/handler.py [M]    │ │ @@ -45,6 +45,12 @@                                   │     │
│ │  src/auth/middleware.py [M] │ │  def login(self, request):                           │     │
│ │  src/auth/limiter.py   [A]  │ │ +    @rate_limit(max_calls=5, period=60)             │     │
│ │                             │ │      credentials = self.extract(request)             │     │
│ │                             │ │ -    return self.authenticate(credentials)           │     │
│ │                             │ │ +    result = self.authenticate(credentials)         │     │
│ │                             │ │ +    self.log_attempt(request, result)               │     │
│ │                             │ │ +    return result                                   │     │
│ │                             │ │                                                      │     │
│ │                             │ │ @@ -78,3 +84,8 @@                                    │     │
│ │                             │ │  def logout(self, request):                          │     │
│ │                             │ │ +    self.cleanup_session(request)                   │     │
│ │                             │ │      return Response(status=200)                     │     │
│ │                             │ │                                                      │     │
│ └─────────────────────────────┘ └──────────────────────────────────────────────────────┘     │
│──────────────────────────────────────────────────────────────────────────────────────────────│
│ [M] Modified  [A] Added  [D] Deleted                                                         │
│ a Allow │ A Allow Always │ r Reject │ R Reject Always │ j/k Nav │ d Diff │ esc               │
└──────────────────────────────────────────────────────────────────────────────────────────────┘

The diff view supports three display modes, toggled with d:

Mode Behavior
Unified Standard unified diff with +/- lines (default)
Side-by-side Two-column view with old content left, new content right
Context Shows only changed lines with surrounding context (3 lines default)

Inline Permission Question Widget

For single-file permission requests, the TUI renders a PermissionQuestionWidget inline in the conversation stream rather than pushing the full PermissionsScreen. This avoids interrupting the conversation flow for simple allow/reject decisions.

The widget displays:

  • The actor name and requested operation type
  • The target file path
  • Inline diff content (when available)
  • Four decision options with keyboard shortcuts
Key Decision Scope
a Allow once This operation only
A Allow always All operations this session
r Reject once This operation only
R Reject always All operations this session

Navigate options with up/down and confirm with enter, or use the single-key shortcuts directly. Press v to open the full PermissionsScreen for multi-file diff review.

Permission request types: file_write, file_delete, file_read, shell_exec, network.

Routing logic:

  • Single-file operations → PermissionQuestionWidget inline in conversation stream
  • Multi-file operations → PermissionsScreen pushed directly (no inline widget)

The widget emits a PermissionDecisionEvent when a decision is made, which the TUI processes to either allow or reject the pending tool operation.

Domain models:

  • InlinePermissionQuestion — carries file_path, request_type (PermissionRequestType), diff_content, and actor_name
  • PermissionRequestType enum: FILE_WRITE, FILE_DELETE, FILE_READ, SHELL_EXEC, NETWORK
  • PermissionDecision enum: ALLOW_ONCE, ALLOW_ALWAYS, REJECT_ONCE, REJECT_ALWAYS

Additional UI Components

Throbber

A rainbow gradient animated bar spans the full width at the top, visible only when the actor is processing:

┌───────────────────────────────────────────────────────────────────────────┐
│ ════════════════════════════════════ ◆ ═══════════════════════════════════│
│ (actor processing...)                                                     │
│                                                                           │
│ (idle — throbber collapses to zero height)                                │
└───────────────────────────────────────────────────────────────────────────┘

The gradient cycles through 12 colors at 15fps: #881177#aa3355#cc6666#ee9944#eedd00#99dd55#44dd88#22ccbb#00bbcc#0099cc#3366bb#663399

Session Tabs

┌────────────────────────────────────────────────────────────────────────────┐
│ Session 1Session 2Session 3⌛ Session 4                       │
│              ┗━━━━━━━━━━━┛                                                 │
│                                                                            │
│ ctrl+[ Previous │ ctrl+] Next │ ctrl+n New │ ctrl+w Close                  │
│ ctrl+s Sessions screen │ 1-9 Jump to tab                                   │
│                                                                            │
│ Indicators:   actor working  │   awaiting input  │  (plain) idle        │
│              ━━━ active tab underline                                      │
└────────────────────────────────────────────────────────────────────────────┘

Notifications (Flash)

Notifications appear as a single-line Flash bar above the prompt, auto-dismissing after a configurable timeout (default 5s):

┌────────────────────────────────────────┐
│ ✔ Plan fix-auth-bug completed          │
│ (auto-dismiss in 5s)                   │
│                                        │
│ ✗ Plan refactor-models failed          │
└────────────────────────────────────────┘

Help Panel (F1)

F1 opens a context-sensitive help overlay showing available hotkeys for the current screen/widget:

┌─ Help: Main Screen ─────────────────────────────────────────────────────┐
│                                                                         │
│ Navigation                                                              │
│  shift+tab     Cycle sidebar: hidden → visible → fullscreen             │
│  tab           Cycle to next persona                                    │
│  ctrl+tab      Cycle to next argument preset                            │
│  ctrl+[/]      Switch session tab                                       │
│  escape        Close overlay / sidebar / return to prompt               │
│                                                                         │
│ Prompt                                                                  │
└─────────────────────────────────────────────────────────────────────────┘

Escape Cascade

The escape key always cascades toward the main screen:

┌───────────────────────────────────────────────────────────────────────┐
│ Modal (PlanDetail, ProjectDetail, PersonaEditor, ...)                 │
│   ↓ escape                                                            │
│ Overlay (ReferencePicker, SlashCommand)                               │
│   ↓ escape                                                            │
│ Sidebar Fullscreen                                                    │
│   ↓ escape                                                            │
│ Sidebar Visible                                                       │
└───────────────────────────────────────────────────────────────────────┘

Multiple escape presses from any state eventually reach the main chat prompt. No modal or screen may trap the user without an escape path.

Conversation Stream

The conversation displays a chronological stream of typed message blocks:

Block Type Visual Treatment Source
Welcome ASCII art + instructions in $text-success App startup (first message)
UserInput Left border $secondary, 15% background tint, Markdown User prompt submission
ActorResponse Streaming Markdown with syntax-highlighted code fences session.message events
ActorThought $primary-muted 20% bg, max 10 lines (expandable), italic Actor reasoning
ToolCall Expandable: icon + status pill header, collapsible content tool.invoked / tool.completed
PlanProgress Grid layout with status icons per step Plan phase changes
DiffView Unified or side-by-side diff, syntax highlighting Tool results with diffs
TerminalEmbed Bordered terminal, $primary 50% border, green/red tint Shell or tool terminal output
ShellResult Left border $primary, 4% foreground bg User shell command (!) output
Note Semantic: info ($primary), warning ($warning), error ($error) System notifications

The conversation uses a 2-column grid: a 1-character cursor column (left, navigable via alt+up/alt+down) and the content stream (right). The block cursor provides keyboard-driven message navigation — enter or space expands/collapses a block.

Theme and Styling

The TUI uses Textual's CSS (TCSS) system with semantic color tokens:

Token Usage
$primary Active elements, sidebar borders, plan status
$secondary User input highlights, prompt accent
$success Completed states, passed validations
$error Failed states, error notifications
$warning Caution states, cost warnings
$text, $text-muted Primary and secondary text

Default theme is Dracula. All 17+ Textual built-in themes are supported and switchable via /theme <name>. The TCSS file hierarchy follows the screen/widget module structure.

Block Cursor and Context Menu

The conversation uses a 2-column grid: a 1-character-wide cursor column on the left and the content stream on the right. The block cursor (a gutter indicator) is navigated with alt+up/alt+down and provides keyboard-driven interaction with message blocks.

Cursor Navigation

Key Action
alt+up Move cursor to previous block
alt+down Move cursor to next block
space Expand or collapse the focused block (if expandable)
enter Open context menu for the focused block
escape Clear cursor selection, return focus to prompt
Click on block Position cursor on clicked block

Blocks that implement the ExpandProtocol (ToolCall, ActorThought, DiffView) can be toggled with space. The indicator (collapsed) changes to (expanded) on toggle.

Context Menu

Pressing enter with the block cursor on any conversation block opens a floating context menu:

┌─ Block Actions ──────────────────────────────────────────────────────┐
│                                                                      │
│  c   Copy to clipboard                                               │
│  p   Copy to prompt                                                  │
│  e   Export as Markdown                                              │
│  v   Export as SVG (opens browser)                                   │
│  m   Maximize / restore block                                        │
│  r   Retry (ActorResponse only)                                      │
│  d   Show raw data                                                   │
│                                                                      │
│──────────────────────────────────────────────────────────────────────│
│ escape Dismiss                                                       │
└──────────────────────────────────────────────────────────────────────┘
Action Key Applies To Description
Copy to clipboard c All blocks Copies the block's text content (Markdown source for ActorResponse, plain text for others) to the system clipboard
Copy to prompt p All blocks Inserts the block's text content into the prompt TextArea at the cursor position
Export as Markdown e All blocks Exports the block's content to a .md file in the current working directory
Export as SVG v All blocks Renders the block to an SVG file and opens it in the default browser
Maximize / restore m ActorThought, ToolCall, DiffView Toggles between collapsed/default height and full-screen height for the block
Retry r ActorResponse Re-sends the preceding user prompt to the actor, replacing this response
Show raw data d All blocks Shows the raw A2A message data for debugging

Conversation Block Details

Tool Call States

Tool calls progress through pending → completed/failed states. The expand behavior is controlled by the tools.expand setting:

┌────────────────────────────────────────────────────────────────────────┐
│  Pending:                                                              │
│   🔧 local/code-analysis                                            │
│                                                                        │
│  Completed (collapsed):                                                │
│   🔧 local/code-analysis                                             │
│                                                                        │
│  Completed (expanded):                                                 │
│   🔧 local/code-analysis                                             │
│   ┌─ src/auth/handler.py ────────────────────────────────────────────┐ │
│   │  45 │ def login(self, req):                                      │ │
│   │  46 │     creds = extract(req)                                   │ │
│   │  47 │     return auth(creds)                                     │ │
│   └──────────────────────────────────────────────────────────────────┘ │
│                                                                        │
│  Failed:                                                               │
│   🔧 local/code-analysis ✗ failed                                     │
└────────────────────────────────────────────────────────────────────────┘
tools.expand Setting Behavior
never All tool calls start collapsed
always All tool calls auto-expand on completion
success Only successful tool calls auto-expand
fail (default) Only failed tool calls auto-expand
both Both successful and failed auto-expand

Tool calls with kind: "read" (e.g., file reads, directory listings) never auto-expand regardless of setting, as they produce verbose output that would dominate the conversation.

Actor Thought Block

Actor thoughts (reasoning traces) display in a muted container with configurable visibility:

┌──────────────────────────────────────────────────────────────────────┐
│  Collapsed (default, max 10 lines):                                  │
│  ┌──────────────────────────────────────────────────────────────┐    │
│   I need to analyze the authentication flow. The handler           │
│   uses a custom JWT implementation that may have...                │
│                      (8 more lines — space to expand)              │
│  └──────────────────────────────────────────────────────────────┘    │
│                                                                      │
│  Expanded (space or m to maximize):                                  │
│  ┌──────────────────────────────────────────────────────────────┐    │
│   I need to analyze the authentication flow. The handler           │
│   uses a custom JWT implementation that may have timing            │
│   vulnerabilities. Let me check the token validation...            │
│   ...(full content shown)...                                       │
│  └──────────────────────────────────────────────────────────────┘    │
└──────────────────────────────────────────────────────────────────────┘
Property Value
Default height Max 10 lines (CSS max-height: 10)
Expanded height Full content (CSS max-height: 100vh)
Background $primary-muted 20%
Text style Italic
Toggle visibility agent.thoughts setting or ctrl+shift+t
Expand/collapse space with block cursor, or m in context menu

Permission Question Widget

When an actor requests permission for a single-file operation, a Question widget replaces the prompt area temporarily. For multi-file operations (diffs), the full PermissionsScreen is pushed instead.

┌──────────────────────────────────────────────────────────────────────┐
│ Permission Required                                                  │
│                                                                      │
│ The actor wants to write to:                                         │
│   src/auth/handler.py                                                │
│                                                                      │
│    a  Allow once                                                    │
│     A  Allow always (this session)                                   │
│     r  Reject once                                                   │
│     R  Reject always (this session)                                  │
│                                                                      │
│ For multi-file diffs, press v to open full PermissionsScreen         │
└──────────────────────────────────────────────────────────────────────┘
Key Action
a Allow this operation once
A Allow all operations of this type for the remainder of the session
r Reject this operation once
R Reject all operations of this type for the remainder of the session
v Open full PermissionsScreen with diff view (for file writes)
up / down Navigate between options
enter Select the highlighted option

The Question widget uses a blinking caret animation on the selected option to draw attention. Multiple permission requests are queued and presented sequentially — the next question appears immediately after the current one is answered.

Prompt Architecture Detail

History Navigation

The prompt maintains two independent JSONL history files per project:

History File Location Trigger
Prompt history ~/.local/state/cleveragents/history/<project-hash>/prompt.jsonl up/down arrows when cursor is at first/last row of the TextArea
Shell history ~/.local/state/cleveragents/history/<project-hash>/shell.jsonl up/down arrows in shell mode (!/$ prompt)

History navigation rules:

  • up at row 0, column 0: replaces TextArea content with the previous history entry
  • down at the last row: replaces TextArea content with the next history entry (or clears to the original input if at the end)
  • History entries are stored as-is (including multi-line prompts)
  • History files are capped at 10,000 entries; oldest entries are pruned on write
  • Each prompt mode (normal, shell) has independent history and independent cursor position within that history

Key Passthrough

When the conversation Window widget has focus (e.g., during block cursor navigation), any printable character typed is forwarded to the prompt TextArea. This ensures the user can always start typing without explicitly focusing the prompt — the focus shifts automatically on the first printable keypress.

Multiline Detection

The prompt auto-detects multiline input and switches the prompt symbol to :

Condition Result
Input contains \n (newline character) Switch to multiline mode
Input contains triple backticks (```) Switch to multiline mode
shift+enter or ctrl+j pressed Insert newline, switch to multiline
All newlines removed Revert to single-line mode

In multiline mode, enter inserts a newline; ctrl+enter submits. In single-line mode, enter submits.

Shell Danger Detection

When shell mode is active (!/$ prefix), the prompt performs heuristic analysis of the command to detect potentially destructive operations. Dangerous commands are highlighted with $error styling and a warning indicator appears below the prompt:

Pattern Risk Level Example
rm -rf / rm -r High rm -rf /
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)

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.

Notification System

The TUI uses a multi-tier notification system to keep users informed across different attention contexts:

Flash Messages (In-App)

Flash messages are single-line notifications displayed between the conversation and the prompt. They auto-dismiss after the configured ui.flash_duration (default: 5s).

Style CSS Class Use Case Example
Default -default Informational Press ctrl+f to focus terminal
Success -success Positive outcomes ✔ Plan fix-auth-bug completed ($0.08)
Warning -warning Caution ⚠ Plan approaching cost limit ($4.50/$5.00)
Error -error Failures ✗ Plan refactor-models failed: validation error

Desktop Notifications

Desktop notifications alert the user when the TUI is not in the foreground. They are delivered via the OS notification system (using notifypy on Linux/macOS, native win10toast on Windows).

Setting Value Behavior
never Desktop notifications are disabled entirely
on-blur (default) Notifications are sent only when the terminal window does not have focus
always Notifications are sent regardless of focus state

Events that trigger desktop notifications:

  • Actor turn completed (response finished)
  • Permission requested (actor wants to perform a gated action)
  • Plan phase transition (idle → strategize, execute → completed, etc.)
  • Plan error or failure
  • Session cost threshold exceeded (configurable)

Each notification includes: the CleverAgents icon, a title (event type), and a body (session name + brief detail). Low-severity events (informational, routine phase transitions) are suppressed by default (notifications.hide_low_severity: true).

Terminal Title Management

The TUI updates the terminal window title via VT escape sequences (\033]0;...\007) to provide at-a-glance status:

State Title Format Example
Idle ◆ CleverAgents — <session_name> ◆ CleverAgents — Session 2
Actor working ⌛ CleverAgents — <session_name> ⌛ CleverAgents — Session 2
Awaiting input Alternates between and 👉 at 0.5s interval Blinking title draws attention
Permission requested Alternates between and 🔐 at 0.5s interval Urgent attention for permission

Title blinking is controlled by the notifications.blink_title setting (default: true). When disabled, the title updates but does not blink.

Sound Effects

Optional sound effects play on significant events (controlled by notifications.enable_sounds setting, default: true):

  • turn_complete.wav — Actor finished a response
  • permission_required.wav — Permission requested
  • plan_complete.wav — Plan reached terminal state
  • error.wav — Error or failure occurred

Sound files are bundled in src/cleveragents/tui/data/sounds/. The notification system gracefully degrades if audio playback is unavailable (e.g., headless server).

Clipboard Operations

The TUI provides clipboard integration for copying content from the conversation and other widgets:

Operation Trigger Behavior
Auto-copy on selection Mouse text selection When ui.auto_copy is true (default), any text selected with the mouse is automatically copied to the system clipboard
Block copy c in block context menu Copies the focused block's text content to the clipboard
Block copy to prompt p in block context menu Inserts the focused block's text into the prompt TextArea at the cursor
Manual copy OS-native selection + copy (e.g., ctrl+shift+c in most terminals) Standard terminal copy behavior

Clipboard access uses pyperclip when available, with fallback to OSC 52 escape sequences for terminal-native clipboard access (works over SSH and in tmux/screen). If neither is available, copy operations display a flash message with the content for manual copying.

Session Persistence and Resume

TUI sessions are persisted to enable resumption across TUI restarts:

Storage

Sessions are stored in an SQLite database at ~/.local/state/cleveragents/tui.db:

Column Type Description
id TEXT (PK) UUID matching the domain Session entity ID
persona_name TEXT Name of the persona active when the session was last used
actor_identity TEXT Namespaced actor identity (e.g., anthropic/claude-4-sonnet)
title TEXT User-assigned or auto-generated session title
prompt_count INTEGER Number of user prompts sent in this session
total_cost REAL Cumulative cost in USD for this session
created_at TEXT (ISO 8601) Session creation timestamp
last_used TEXT (ISO 8601) Last interaction timestamp
project_path TEXT Working directory at session creation (nullable)
meta_json TEXT JSON blob for extensible metadata (preset, sidebar state, scroll position)

Resume Workflow

  1. User presses ctrl+r (from SessionsScreen) or uses /session:import
  2. The TUI reads the session record from SQLite
  3. A new A2A Task is initiated with the stored session ID — the Application layer recognizes this as a resume request and loads the existing Session entity
  4. The actor receives the session's conversation history via A2A's multi-turn interaction pattern
  5. The conversation stream is populated with historical messages (rendered from stored A2A message data)
  6. The persona is restored to the one stored in the session record

Not all actors support session resume — actors that do not support it will start a fresh conversation. A flash warning is shown: ⚠ This actor does not support session resume. Starting fresh conversation.

Sessions Screen

The Sessions screen (ctrl+s) is a modal overlay showing all active and saved sessions:

┌─ Sessions ─────────────────────────────────────────────────────────────────────────────┐
│                                                                                        │
│   Session 2   feature-dev   claude-4-sonnet    awaiting input     14:02           │
│    Last: Can you review the auth module...                                             │
│                                                                                        │
│    Session 1   reviewer      gpt-4o            ⌛ working              13:58        │
│    Last: Analyze the test coverage for...                                              │
│                                                                                        │
│    Session 3   infra-admin    claude-4-opus     idle                    13:45       │
│    Last: Update the terraform modules...                                               │
│                                                                                        │
│────────────────────────────────────────────────────────────────────────────────────────│
│  Saved Sessions (from previous runs)                                                   │
│                                                                                        │
│    debug-session   feature-dev   claude-4-sonnet   23 prompts   Mar 10 09:15       │
│    refactor-v2    reviewer      gpt-4o            8 prompts    Mar 9 16:30         │
│                                                                                        │
│────────────────────────────────────────────────────────────────────────────────────────│
│ enter Switch │ ctrl+r Resume saved │ ctrl+n New │ d Delete │ r Rename │ esc Back       │
└────────────────────────────────────────────────────────────────────────────────────────┘

The screen is divided into two sections:

  • Active Sessions (top): Currently open session tabs with their state indicators, persona, actor, and last prompt preview. The current session is highlighted with .
  • Saved Sessions (bottom): Previously persisted sessions from the SQLite database, showing prompt count and last-used timestamp. These can be resumed with ctrl+r or enter.

Time formatting follows human-readable relative time: just now, 2m ago, 1h ago, or full date for >24h.

Settings Screen

The Settings screen (F2 or ctrl+,) is a modal overlay with search-driven navigation through all TUI settings:

┌─ Settings ─────────────────────────────────────────────────────────────────────────┐
│  🔍 Search: flash                                                                  │
│────────────────────────────────────────────────────────────────────────────────────│
│                                                                                    │
│  ▼ UI                                                                              │
│    Theme                   [ dracula          ]  17 themes available               │
│    Column width            [ 100              ]  Fixed column width (40-300)       │
│    Scrollbar               [ normal           ]  normal / thin / hidden            │
│    Flash duration           [ 5.0              ]  Seconds (0.5-30)                 │
│    Auto-copy on select     [                 ]  Copy selected text to clipboard   │
│                                                                                    │
│  ▼ Notifications                                                                   │
│    Desktop notifications   [ on-blur          ]  never / on-blur / always          │
│    Blink terminal title    [                 ]  Blink when input needed           │
│    Sound effects           [                 ]  Play sounds on events             │
│                                                                                    │
│────────────────────────────────────────────────────────────────────────────────────│
│ enter Edit │ tab Next │ ctrl+s Focus search │ esc Back (auto-saves)                │
└────────────────────────────────────────────────────────────────────────────────────┘

Settings are schema-driven: each setting has a type, default value, validation rules, and description. The schema is used to auto-generate the settings UI — new settings added to the schema automatically appear in the screen.

Settings are persisted as JSON in ~/.config/cleveragents/tui-settings.json and saved automatically when the settings screen is dismissed.

Complete Settings Schema

UI Settings (ui.*)

Setting Type Default Range / Choices Description
ui.theme choices dracula 17+ Textual themes Color theme for the entire TUI
ui.column boolean true Enable fixed column width for conversation content
ui.column_width integer 100 40300 Maximum column width in characters (when column is true)
ui.scrollbar choices normal normal / thin / hidden Scrollbar appearance for all scrollable widgets
ui.compact_input boolean false Remove prompt border and margin for minimal appearance
ui.sessions_bar choices multiple always / multiple / never When to show the session tab bar (multiple = only when ≥2 sessions)
ui.footer boolean true Show the hotkey footer bar at the bottom
ui.info_bar boolean true Show the info bar below the prompt (PersonaBar)
ui.status_line boolean true Show token count and cost in the info bar
ui.flash_duration number 5.0 0.530.0 Seconds before flash messages auto-dismiss
ui.auto_copy boolean true Automatically copy mouse-selected text to clipboard
ui.prune_low_mark integer 1500 10010000 Target conversation line count after pruning
ui.prune_excess integer 1000 1005000 Lines over prune_low_mark that trigger pruning
ui.throbber choices rainbow rainbow / quotes Throbber style: animated gradient or rotating text quotes

Notification Settings (notifications.*)

Setting Type Default Range / Choices Description
notifications.desktop choices on-blur never / on-blur / always When to send desktop notifications
notifications.blink_title boolean true Blink terminal window title when input is needed
notifications.enable_sounds boolean true Play sound effects on events
notifications.turn_complete boolean true Notify when actor finishes a response
notifications.hide_low_severity boolean true Suppress desktop notifications for low-severity events

Sidebar Settings (sidebar.*)

Setting Type Default Range / Choices Description
sidebar.auto_hide boolean false Auto-hide sidebar when it loses focus (slides back on ctrl+b)
sidebar.width integer 36 2460 Sidebar width in characters when in visible state

Agent Settings (agent.*)

Setting Type Default Range / Choices Description
agent.thoughts boolean true Show actor thinking/reasoning blocks in conversation

Tool Settings (tools.*)

Setting Type Default Range / Choices Description
tools.expand choices fail never / always / success / fail / both When to auto-expand tool call results

Shell Settings (shell.*)

Setting Type Default Range / Choices Description
shell.command string /bin/sh Shell executable for ! mode
shell.startup_commands text PS1="" Commands run at shell startup (newline-separated)
shell.warn_dangerous boolean true Highlight potentially dangerous commands
shell.allow_commands text git,ls,cat,... Commands recognized as shell (auto-switch to $ mode)
shell.directory_commands text cd,rmdir Commands whose arguments are directories (for tab completion)
shell.file_commands text cat,less,head Commands whose arguments are files (for tab completion)

Diff Settings (diff.*)

Setting Type Default Range / Choices Description
diff.view choices auto unified / split / auto Diff display mode (auto selects based on available width)

Conversation Content Pruning

Long conversations accumulate widgets that consume memory and degrade rendering performance. The TUI implements automatic content pruning to maintain responsiveness:

Parameter Setting Default Description
Target line count ui.prune_low_mark 1500 After pruning, the conversation is trimmed to approximately this many lines
Trigger threshold ui.prune_excess 1000 Pruning activates when the conversation exceeds prune_low_mark + prune_excess lines

Pruning behavior:

  1. When the conversation stream exceeds 2500 lines (1500 + 1000), pruning activates
  2. The oldest message blocks are removed from the DOM (not just hidden) until the line count drops to approximately prune_low_mark
  3. A Note block is inserted at the top: (Earlier messages pruned — see session history for full conversation)
  4. Pruning never removes the most recent 500 lines regardless of settings
  5. Pruned messages remain in the session history (SQLite) and can be viewed via /session:export
  6. Welcome blocks, system notes, and the current actor response are never pruned

Safety Behaviors

Double-Tap Quit

ctrl+c does not immediately quit the TUI. Instead:

  1. First press: If an actor is processing, sends an interrupt/cancel signal to the actor. If idle, shows a flash message: Press ctrl+c again within 5s to quit
  2. Second press within 5s: Quits the TUI (after saving session state)
  3. Second press after 5s: Resets — treated as a new first press

ctrl+q always quits immediately (after saving session state) without requiring a double-tap.

Double-Tap Escape for Terminal

When a terminal widget (TerminalEmbed or ShellTerminal) has focus, a single escape press is ambiguous — the terminal itself may use escape sequences. The TUI requires a double-tap within 400ms:

  1. First escape: Starts a 400ms timer
  2. Second escape within 400ms: Exits terminal focus, returns focus to the prompt
  3. No second escape within 400ms: The escape keypress is forwarded to the terminal as a normal escape sequence

A flash hint appears on the first escape: Press escape again to exit terminal.

Loading States

The TUI supports two loading indicator styles, configurable via ui.throbber:

Style Setting Value Description
Rainbow gradient rainbow (default) Animated horizontal bar cycling through 12 colors at 15fps (see Throbber diagram)
Rotating quotes quotes Displays shuffled quotes from a curated collection, rotating every 3 seconds

The quotes collection contains ~200 entries drawn from science fiction (curated for relevance to AI/coding themes). Example quotes:

  • "I'm sorry, Dave. I'm afraid I can't do that." — HAL 9000
  • "The only way to do great work is to love what you do." — Steve Jobs
  • "Any sufficiently advanced technology is indistinguishable from magic." — Arthur C. Clarke

Both styles occupy 1 row at the top of the screen and collapse to zero height when idle.

Conversation Export

Conversations can be exported in multiple formats:

Format Command / Trigger Output
JSON /session:export [path] Full session data including A2A messages, metadata, timestamps
Markdown /session:export --format md [path] Human-readable conversation transcript with Markdown formatting
SVG Context menu → v (per block) Single block rendered as SVG image

The JSON export format is the canonical format for /session:import — it contains all data needed to fully restore a session. The Markdown format is a lossy export intended for sharing and documentation.

Hotkey Reference

Hotkeys vary by the current screen and focused widget. The help panel (F1) always reflects the currently applicable hotkeys.

Global Hotkeys (Available Everywhere)

Key Action
ctrl+q Quit TUI immediately (saves session state)
ctrl+c Interrupt actor / double-tap to quit (5s window)
F1 Toggle help panel
F2 / ctrl+, Open settings
ctrl+s Open Sessions screen
ctrl+n Create new session tab
ctrl+w Close current session tab
ctrl+[ / ctrl+] Switch to previous / next session tab
shift+tab Cycle sidebar: hidden → visible → fullscreen
escape Close current overlay / modal / sidebar toward main screen

MainScreen — Prompt Focused

Key Action
enter Submit prompt (single-line mode) / insert newline (multi-line mode)
ctrl+enter Submit prompt (multi-line mode)
shift+enter / ctrl+j Insert newline (switch to multi-line mode)
tab Cycle to next persona
ctrl+tab Cycle to next argument preset
up (at row 0) Previous prompt history entry
down (at last row) Next prompt history entry
@ Open Reference Picker overlay
/ (at position 0) Open Slash Command overlay
! or $ (at position 0) Activate shell mode
backspace (at position 0 in shell mode) Deactivate shell mode, return to normal
tab (in shell mode) Tab-complete file/directory path
ctrl+b Focus sidebar (when visible)

MainScreen — Conversation / Block Cursor

Key Action
alt+up Move block cursor to previous block
alt+down Move block cursor to next block
space Expand / collapse focused block
enter Open context menu for focused block
escape Clear block cursor, return focus to prompt
Any printable character Forward to prompt (key passthrough)

MainScreen — Block Context Menu

Key Action
c Copy block content to clipboard
p Copy block content to prompt
e Export block as Markdown file
v Export block as SVG (opens browser)
m Maximize / restore block height
r Retry prompt (ActorResponse blocks only)
d Show raw A2A message data
escape Dismiss menu

MainScreen — Terminal Focused

Key Action
escape escape (within 400ms) Exit terminal focus, return to prompt
ctrl+c Send SIGINT to terminal process
All other keys Forwarded to terminal PTY

Sidebar — Visible (Partial)

Key Action
tab / shift+tab (within sidebar) Navigate between Plans and Projects panels
enter Expand / collapse the highlighted collapsible section
up / down Navigate items within a panel
escape Dismiss sidebar, return focus to prompt
shift+tab (global) Transition to fullscreen sidebar

Sidebar — Fullscreen

Key Action
space Toggle selection on highlighted plan/project
enter Open detail modal (PlanDetailModal or ProjectDetailModal)
up / down Navigate items
left / right Switch between Plans and Projects columns
ctrl+p Create persona from current selection
ctrl+up / ctrl+down Reorder personas in the cycle list
d Delete highlighted persona from cycle list
/ Open search/filter within sidebar
escape Return to visible sidebar state

Reference Picker Overlay

Key Action
typing Fuzzy search across projects, plans, and resources
enter Insert selected reference into prompt
up / down Navigate results
tab Switch between fuzzy search and tree browser modes
ctrl+p Filter to projects only
ctrl+l Filter to plans only
escape Dismiss picker, remove @ trigger character

Reference Picker — Tree Browser Mode

Key Action
enter Select highlighted item as reference
space Expand / collapse tree node
up / down Navigate tree
/ Open inline filter
tab Switch back to fuzzy search mode
escape Dismiss picker

Slash Command Overlay

Key Action
typing Fuzzy search commands
enter Execute selected command
tab Auto-complete command name
up / down Navigate command list
escape Dismiss overlay

Plan Detail Modal

Key Action
up / down Navigate decision tree nodes
enter Select decision node (show detail in right panel)
space Expand / collapse subtree
/ Search within decision tree
c Correct the selected decision (opens correction dialog)
a Approve pending decision
r Reject pending decision
d Show diff for the selected decision
e Explain decision (show reasoning)
x Cancel the plan
escape Close modal

Project Detail Modal

Key Action
up / down Navigate resource tree
enter View resource details (right panel)
space Expand / collapse resource tree node
/ Search within resource tree
i Show invariants for selected resource
v Show validations for selected resource
escape Close modal

Persona Editor Modal

Key Action
tab / shift+tab Navigate form fields
enter Edit the focused field / confirm selection
ctrl+a Add a new argument preset
ctrl+d Delete the selected argument preset
ctrl+s Save persona and close editor
escape Cancel and close editor (prompts to save if modified)

Permissions Screen

Key Action
j / down Next file diff
k / up Previous file diff
tab / shift+tab Cycle focus: file list → diff view → action buttons
a Allow once
A Allow always (this session)
r Reject once
R Reject always (this session)
d Toggle diff view mode: unified → split → auto
escape Reject and close (equivalent to r)

Settings Screen

Key Action
ctrl+s Focus search input
tab / shift+tab Navigate settings fields
enter Edit the focused setting
escape Close settings (auto-saves)

Sessions Screen

Key Action
up / down Navigate session list
enter Switch to highlighted active session / resume saved session
ctrl+r Resume a saved (historical) session
ctrl+n Create new session
d Delete highlighted session
r Rename highlighted session
escape Close sessions screen

Question / Permission Widget (Inline)

Key Action
a Allow once
A Allow always (this session)
r Reject once
R Reject always (this session)
v Open full Permissions Screen (for file writes)
up / down Navigate options
enter Select highlighted option

Help Panel (F1)

Key Action
F1 Close help panel
escape Close help panel
up / down Scroll help content

Configuration

!!! adr "Architecture Decision" The configuration system design principles, YAML-first approach, and config file conventions are defined in ADR-024: Configuration System.

This section provides a complete reference for the YAML configuration files used to define the major configurable objects in CleverAgents: Actors, Skills, Tools, Actions, Resource Types, Context Views, and Automation Profiles. Projects are created via CLI commands rather than standalone configuration files, but their context views are configured through YAML and are documented here as well. It also documents the global configuration keys that control system-wide behavior.

Global Configuration Keys

!!! adr "Architecture Decision" Global configuration keys, hierarchical key structure, and config precedence are defined in ADR-024: Configuration System.

Global configuration keys control system-wide defaults and behavior. They are stored in the global configuration file (default: ~/.cleveragents/config.toml) and managed via the agents config set, agents config get, and agents config list commands.

Hierarchical Key Structure

Configuration keys use dot-separated hierarchical names, similar to git config. Each dot introduces a level of nesting, grouping related keys under a common parent. For example:


core.format              # top-level "core" group, "format" key
core.log.level           # "core" group, "log" subgroup, "level" key
index.text.backend       # "index" group, "text" subgroup, "backend" key

The key plan.concurrency is not the same as a top-level key concurrency — the dot path is the full identity of the key. This means you can have plan.concurrency and session.concurrency as distinct keys without conflict.

On the CLI, the full dot path is always used:


$ agents config set core.format table
$ agents config get plan.budget.per-plan
$ agents config list plan.*

The agents config list command accepts an optional glob pattern to filter keys by subtree. For example, agents config list index.* lists all keys under the index group, and agents config list provider.* lists all provider credential keys.

In the TOML configuration file, the hierarchy maps directly to TOML's native table structure. The dot-path key index.text.backend can be expressed equivalently as:


# Inline dot notation (convenient for single keys)
index.text.backend = "tantivy"

# Or as nested TOML tables (better for groups) [index.text] backend = "tantivy"

[index.vector] backend = "faiss"

Both forms are valid and can be mixed. The TOML parser normalizes them to the same internal representation.

!!! note "Configuration Precedence (highest to lowest)" 1. ==CLI flag== (--format, --data-dir, etc.) 2. ==Environment variable== (CLEVERAGENTS_FORMAT, etc.) 3. ==Project-scoped config== (per-project overrides) 4. ==Global config file== (~/.cleveragents/config.toml) 5. ==Built-in default==

Key Reference

Keys are organized by their top-level group. Within each group, the full dot-path, type, default, corresponding environment variable, and description are provided.

core.* — Core System Settings
Key Type Default Env Variable Description
core.data-dir string ~/.cleveragents CLEVERAGENTS_DATA_DIR Root directory for all CleverAgents persistent state: database, logs, cache, sessions, checkpoints, and backups. All other paths are derived relative to this directory unless individually overridden. Overridden per-invocation by the --data-dir CLI flag.
core.config-path string <core.data-dir>/config.toml CLEVERAGENTS_CONFIG_PATH Path to the global configuration file itself. Typically only set via environment variable or CLI flag to bootstrap an alternate configuration. Overridden per-invocation by the --config-path CLI flag.
core.format string rich CLEVERAGENTS_FORMAT Default output rendering format for all CLI commands. Accepted values: rich, color, table, plain, json, yaml. Overridden per-invocation by the --format CLI flag.
core.namespace string local CLEVERAGENTS_NAMESPACE The default namespace prefix applied when creating entities without an explicit namespace. For local-only usage, this is local. In server mode, typically the user's username or organization name.
core.automation-profile string supervised CLEVERAGENTS_AUTOMATION_PROFILE The default automation profile applied to new plans when no profile is specified at the plan, action, or project level. Accepts any built-in profile name (manual, review, supervised, cautious, trusted, auto, ci, full-auto) or a custom profile name in <namespace>/<name> format. Project-scopable.
core.log.level string FATAL CLEVERAGENTS_LOG_LEVEL Base logging verbosity level. Accepted values (from least to most verbose): FATAL, ERROR, WARN, INFO, DEBUG, TRACE. The default FATAL means only fatal errors (where the application exits) produce log output; all other log messages are suppressed. The -v CLI flag can be repeated (-v through -vvvvv) to raise the effective level for a single invocation: -v = ERROR, -vv = WARN, -vvv = INFO, -vvvv = DEBUG, -vvvvv = TRACE. The effective level applies to both log file output and terminal log output (subject to core.log.terminal and core.log.file-enabled settings).
core.log.dir string <core.data-dir>/logs CLEVERAGENTS_LOG_DIR Directory where log files are written. Can be set to an alternative path to redirect log storage.
core.log.file-enabled boolean true CLEVERAGENTS_LOG_FILE_ENABLED Whether log output is written to log files. When true, log messages at or above the effective verbosity level are written to the log directory. When false, log file output is suppressed entirely (useful when log output should only go to the terminal).
core.log.terminal string auto CLEVERAGENTS_LOG_TERMINAL Controls whether log output raised by -v is also displayed on the terminal. Accepted values: auto (show on terminal when -v is used, suppress otherwise), always (always show log output on terminal at the effective level), never (suppress terminal log output entirely — log only to file). This keeps log output separate from normal program output, which goes to stdout.
core.log.terminal-stream string stderr CLEVERAGENTS_LOG_TERMINAL_STREAM The stream used for terminal log output. Accepted values: stderr (default, keeps logs separate from normal stdout output), stdout (merge with normal output), or a file path / device path (e.g., /dev/null, /dev/pts/1, or a file path) to redirect terminal log output to an alternative destination.
core.log.retention-days integer 30 CLEVERAGENTS_LOG_RETENTION_DAYS Number of days to retain log files before automatic cleanup.
core.backup.dir string <core.data-dir>/backups CLEVERAGENTS_BACKUP_DIR Directory where backup snapshots are stored.
core.backup.retention-days integer 7 CLEVERAGENTS_BACKUP_RETENTION_DAYS Number of days to retain backup snapshots created during project deletion, agents init resets, and plan correction history cleanup. Backups older than this are automatically purged.
core.cache.dir string <core.data-dir>/cache CLEVERAGENTS_CACHE_DIR Directory for transient caches (downloaded models, tool artifacts, temporary files).
server.* — Server Mode
Key Type Default Env Variable Description
server.url string (not set) CLEVERAGENTS_SERVER_URL URL of the CleverAgents server for multi-user collaborative mode. When set, the CLI operates in server mode, syncing namespaced entities (actors, actions, skills, projects) with the remote server. When unset, the CLI operates in local-only mode.
server.token string (not set) CLEVERAGENTS_SERVER_TOKEN Authentication token for server mode. Required when server.url is set. Obtained via server registration or team invite flow. Prefer the environment variable for CI environments.
server.sync.auto boolean true CLEVERAGENTS_SERVER_SYNC_AUTO Whether to automatically sync entity definitions (actors, actions, skills) with the server on startup and after registration changes. When false, sync must be triggered manually.
server.sync.interval integer 300 CLEVERAGENTS_SERVER_SYNC_INTERVAL Interval in seconds between automatic background syncs with the server. Only applies when server.sync.auto is true.
actor.* — Actor Defaults
Key Type Default Env Variable Description
actor.default.strategy string (not set) CLEVERAGENTS_DEFAULT_STRATEGY_ACTOR Default strategy actor used for plans when not specified on the action or plan use invocation. Must reference a registered actor in <namespace>/<name> format. When unset, the action's strategy_actor field is required.
actor.default.execution string (not set) CLEVERAGENTS_DEFAULT_EXECUTION_ACTOR Default execution actor used for plans when not specified on the action or plan use invocation. Must reference a registered actor. When unset, the action's execution_actor field is required.
actor.default.estimation string (not set) CLEVERAGENTS_DEFAULT_ESTIMATION_ACTOR Default estimation actor used for cost and effort estimation. Estimation is optional; when unset, plans skip the estimation step.
actor.default.invariant string (not set) CLEVERAGENTS_DEFAULT_INVARIANT_ACTOR Default Invariant Reconciliation Actor used globally. When a plan enters Strategize and neither the plan, action, nor project specifies an invariant actor, this actor reconciles invariant conflicts across scopes. Must reference a registered actor.
actor.default.orchestrator string (not set) CLEVERAGENTS_DEFAULT_ORCHESTRATOR Default orchestrator actor for new sessions created without an explicit --actor flag.
plan.* — Plan Execution
Key Type Default Env Variable Description
plan.concurrency integer 4 CLEVERAGENTS_PLAN_CONCURRENCY Maximum number of plans that can execute concurrently. When this limit is reached, new plan execute commands queue until a slot opens. Child plans count toward the parent's concurrency allocation, not toward the global limit. Project-scopable.
plan.max-child-depth integer 5 CLEVERAGENTS_PLAN_MAX_CHILD_DEPTH Maximum nesting depth for child plan spawning. Prevents runaway recursive plan decomposition. A value of 1 means no child plans; 5 means up to 5 levels of parent-child nesting. Project-scopable.
plan.budget.per-plan float (not set) CLEVERAGENTS_PLAN_BUDGET Maximum estimated API cost (in USD) allowed per plan. When the running cost estimate exceeds this budget, the plan pauses and requests human approval to continue. When unset, no per-plan cost limit is enforced. Project-scopable.
plan.budget.per-session float (not set) CLEVERAGENTS_SESSION_BUDGET Maximum estimated API cost (in USD) allowed per session. When cumulative session cost exceeds this budget, all plan operations in the session pause. When unset, no per-session cost limit is enforced.
plan.budget.warn-threshold float 0.8 CLEVERAGENTS_PLAN_BUDGET_WARN Fraction of the per-plan budget at which a warning is emitted. For example, 0.8 means a warning fires at 80% of the budget. Only effective when plan.budget.per-plan is set.
plan.tool.max-calls-per-step integer 25 CLEVERAGENTS_TOOL_MAX_CALLS Maximum number of tool invocations allowed in a single actor step (one LLM turn). Prevents runaway tool loops. When the limit is reached, the actor step is interrupted and the plan pauses for human review.
plan.tool.max-retries integer 3 CLEVERAGENTS_TOOL_MAX_RETRIES Maximum number of retry attempts for a single failing tool invocation before escalating. Applies to transient errors (network timeouts, rate limits). Non-transient errors fail immediately.
plan.tool.retry-backoff string exponential CLEVERAGENTS_TOOL_RETRY_BACKOFF Retry backoff strategy for transient tool failures. Accepted values: exponential (doubling delay starting at 1s), linear (fixed 2s delay), none (immediate retry).
sandbox.* — Sandbox and Checkpointing
Key Type Default Env Variable Description
sandbox.strategy string git_worktree CLEVERAGENTS_SANDBOX_STRATEGY Default sandbox isolation strategy for the Execute phase. Accepted values: git_worktree (create a git worktree for isolation), filesystem_copy (copy the project directory), transaction_rollback (for database resources), none (no isolation — requires require_sandbox: false in the automation profile). Per-resource-type strategies override this default. Project-scopable.
sandbox.cleanup string on_apply CLEVERAGENTS_SANDBOX_CLEANUP When to clean up sandbox working directories. Accepted values: on_apply (clean up after successful apply), on_terminal (clean up when plan reaches any terminal state — applied, cancelled, or failed), manual (never auto-clean; user must delete).
sandbox.checkpoint.enabled boolean true CLEVERAGENTS_CHECKPOINT_ENABLED Whether checkpointing is enabled globally. When true, the Execute phase creates checkpoints before write operations, enabling rollback. When false, checkpointing is skipped (requires require_checkpoints: false in the automation profile). Project-scopable.
sandbox.checkpoint.dir string <core.data-dir>/checkpoints CLEVERAGENTS_CHECKPOINT_DIR Directory where plan execution checkpoints are stored. Each plan gets a subdirectory named by its ULID.
sandbox.checkpoint.max-per-plan integer 50 CLEVERAGENTS_CHECKPOINT_MAX Maximum number of checkpoints retained per plan. When exceeded, the oldest checkpoints are pruned (keeping the first and most recent).
sandbox.checkpoint.auto-create-on list ["on_tool_write", "on_tool_write_complete", "on_subplan_spawn", "on_error"] CLEVERAGENTS_CHECKPOINT_AUTO_CREATE_ON List of automatic checkpoint triggers. on_tool_write fires before each write-tool execution, on_tool_write_complete fires after, on_subplan_spawn fires before first subplan execution attempt, on_error fires when the Execute phase fails. Set to [] to disable all automatic checkpoints.
audit.* — Audit Logging
Key Type Default Env Variable Description
audit.retention-days integer 0 CLEVERAGENTS_AUDIT_RETENTION_DAYS Days to retain audit log entries before pruning. 0 means keep indefinitely (default for compliance).
audit.async boolean true CLEVERAGENTS_AUDIT_ASYNC When true (default), audit entries are written asynchronously via a write-behind queue on a background daemon thread, so that AuditService.record() does not block the calling domain operation. Set to false to restore synchronous behaviour (useful for debugging or when strict ordering is required).
audit.queue-maxsize integer 10000 CLEVERAGENTS_AUDIT_QUEUE_MAXSIZE Maximum number of pending audit entries in the write-behind queue. When the queue is full, record() blocks until space is available, providing back-pressure. Increase for very high-throughput workloads. Only effective when audit.async is true.
index.* — Code Intelligence and Indexing
Key Type Default Env Variable Description
index.text.backend string tantivy CLEVERAGENTS_INDEX_TEXT_BACKEND Backend for full-text search indexing of project resources. Accepted values: tantivy (recommended, high-performance Rust-based engine), sqlite_fts (built-in SQLite FTS5, no additional dependencies).
index.text.dir string <core.data-dir>/index/text CLEVERAGENTS_INDEX_TEXT_DIR Directory where the full-text search index is stored.
index.vector.backend string faiss CLEVERAGENTS_INDEX_VECTOR_BACKEND Backend for vector similarity search (semantic code search). Accepted values: faiss (Facebook AI Similarity Search, CPU or GPU), qdrant (requires a running Qdrant server), none (disable vector search).
index.vector.dir string <core.data-dir>/index/vector CLEVERAGENTS_INDEX_VECTOR_DIR Directory where the vector index is stored (for local backends).
index.vector.qdrant-url string (not set) CLEVERAGENTS_QDRANT_URL URL of the Qdrant server. Required when index.vector.backend is qdrant.
index.graph.backend string none CLEVERAGENTS_INDEX_GRAPH_BACKEND Backend for the knowledge graph store (structural code relationships, RDF triples). Accepted values: neo4j (requires a running Neo4j server), rdflib (in-process Python RDF library), none (disable graph queries).
index.graph.neo4j-url string (not set) CLEVERAGENTS_NEO4J_URL URL of the Neo4j server. Required when index.graph.backend is neo4j.
index.graph.neo4j-auth string (not set) CLEVERAGENTS_NEO4J_AUTH Authentication for Neo4j in user:password format. Required when index.graph.backend is neo4j.
index.embedding.provider string openai CLEVERAGENTS_EMBEDDING_PROVIDER Provider used for generating vector embeddings. Accepted values: openai, anthropic, local (uses a local sentence-transformers model). When local, no API key is required for embedding generation.
index.embedding.model string text-embedding-3-small CLEVERAGENTS_EMBEDDING_MODEL Model identifier used for generating vector embeddings when indexing project resources. Must be a valid model for the configured index.embedding.provider.
index.embedding.dimensions integer (provider default) CLEVERAGENTS_EMBEDDING_DIMENSIONS Number of dimensions for generated embeddings. When unset, the provider's default dimensionality for the chosen model is used. Can be reduced for faster search at the cost of accuracy.
index.auto-reindex boolean true CLEVERAGENTS_AUTO_REINDEX Whether to automatically re-index project resources when files change on disk. When true, indexes are updated on resource add and before each Strategize phase. When false, re-indexing must be triggered manually.
context.* — Context Tier Defaults

These keys set the default context policy for all projects. Project-level context policies (set via agents project context set) override these defaults. All keys in this group are project-scopable.

Key Type Default Env Variable Description
context.hot.max-tokens integer 16000 CLEVERAGENTS_CTX_HOT_TOKENS Default maximum token budget for hot context (the current working set loaded into the LLM context window). This is a soft cap; the actor's model context window is the hard limit.
context.warm.max-decisions integer 100 CLEVERAGENTS_CTX_WARM_DECISIONS Default maximum number of recent decisions retained in warm context (available for retrieval but not loaded by default).
context.cold.max-decisions integer 500 CLEVERAGENTS_CTX_COLD_DECISIONS Default maximum number of historical decisions retained in cold context (archived for audit and correction).
context.query.limit integer 20 CLEVERAGENTS_CTX_QUERY_LIMIT Default maximum number of retrieval results returned per context query during Strategize and Execute.
context.query.min-relevance float 0.3 CLEVERAGENTS_CTX_QUERY_MIN_RELEVANCE Minimum relevance score (0.01.0) for retrieval results to be included in context. Results below this threshold are discarded even if the query limit is not reached.
context.file.max-size integer 1048576 CLEVERAGENTS_CTX_MAX_FILE_SIZE Default maximum file size (in bytes) for files included in context. Files exceeding this size are summarized or excluded. Default is 1 MB (1,048,576 bytes).
context.file.max-total-size integer 52428800 CLEVERAGENTS_CTX_MAX_TOTAL_SIZE Default maximum total size (in bytes) across all files included in context. Default is 50 MB.
context.summarize.enabled boolean true CLEVERAGENTS_CTX_SUMMARIZE Whether to summarize large context segments that exceed token limits rather than truncating them. When true, an LLM summarization pass produces a condensed version. When false, content is hard-truncated.
context.summarize.max-tokens integer 1000 CLEVERAGENTS_CTX_SUMMARY_TOKENS Maximum number of tokens in a generated context summary. Controls the verbosity of summaries produced when context.summarize.enabled is true.
context.summarize.model string (uses actor's model) CLEVERAGENTS_CTX_SUMMARY_MODEL Model to use for context summarization. When unset, the active actor's own model is used. Set this to a faster/cheaper model (e.g., a smaller variant) to reduce summarization cost.
context.strategies.enabled list ["simple-keyword", "semantic-embedding", "breadth-depth-navigator"] CLEVERAGENTS_CTX_STRATEGIES Ordered list of ACMS context strategies enabled globally. Strategies are run in parallel and their results fused by the Context Assembly Pipeline.
context.strategies.custom.* object (none) Registration block for custom context strategies. Each key under custom names a strategy; the value specifies its module, class, max_quality, and optional config.
context.strategies.arce.model string (uses actor's model) CLEVERAGENTS_CTX_ARCE_MODEL Model used by the ARCE (Autonomous Reasoning Context Extraction) strategy for its internal reasoning loop.
context.strategies.arce.max-rounds integer 3 CLEVERAGENTS_CTX_ARCE_ROUNDS Maximum number of search-refine rounds the ARCE strategy performs before returning results.
context.strategies.breadth-depth-navigator.max-hops integer 4 CLEVERAGENTS_CTX_BDN_HOPS Maximum graph traversal depth for the breadth-depth-navigator strategy.
context.budget.response-reserve-tokens integer 4096 CLEVERAGENTS_CTX_RESPONSE_RESERVE Number of tokens reserved from the model's context window for the response. Subtracted before computing the context budget.
context.budget.tool-definition-estimate integer (auto-computed) CLEVERAGENTS_CTX_TOOL_ESTIMATE Estimated tokens consumed by tool definitions in the prompt. When unset, computed from the active skill set.
context.budget.skeleton-ratio float 0.15 CLEVERAGENTS_CTX_SKELETON_RATIO Default fraction of the context budget reserved for inherited plan skeleton context.
context.budget.refresh-threshold float 0.30 CLEVERAGENTS_CTX_REFRESH_THRESHOLD Fractional change in available budget that triggers automatic context re-assembly. E.g., 0.30 means re-assemble when budget changes by more than 30%.
context.budget.min-useful-budget integer 500 CLEVERAGENTS_CTX_MIN_BUDGET Minimum useful context budget in tokens. If the computed budget falls below this, context assembly is skipped and a warning is emitted.
context.tiers.warm.retention-hours integer 24 CLEVERAGENTS_CTX_WARM_HOURS Number of hours warm-tier context entries are retained before demotion to cold tier.
context.tiers.cold.retention-days integer 90 CLEVERAGENTS_CTX_COLD_DAYS Number of days cold-tier context entries are retained before archival/expiry.
context.uko.default-analyzers list ["python", "typescript", "rust", "java", "markdown", "json-schema"] CLEVERAGENTS_CTX_UKO_ANALYZERS List of UKO analyzers enabled by default for resource indexing. Each analyzer extracts language-specific UKO nodes from resources.
context.uko.analyzers.custom.* object (none) Registration block for custom UKO analyzers. Each key under custom names an analyzer; the value specifies its module, class, languages, and optional config.
context.pipeline.strategy-selector string "builtin:ConfidenceWeightedSelector" CLEVERAGENTS_CTX_PIPELINE_SELECTOR Implementation class for the StrategySelector pipeline component. Decides which strategies to invoke and with what confidence. Value is "module:ClassName".
context.pipeline.budget-allocator string "builtin:ProportionalBudgetAllocator" CLEVERAGENTS_CTX_PIPELINE_ALLOCATOR Implementation class for the BudgetAllocator pipeline component. Distributes token budget across selected strategies.
context.pipeline.strategy-executor string "builtin:ParallelStrategyExecutor" CLEVERAGENTS_CTX_PIPELINE_EXECUTOR Implementation class for the StrategyExecutor pipeline component. Controls parallelism, timeouts, and circuit breaking for strategy invocation.
context.pipeline.fragment-deduplicator string "builtin:ContentHashDeduplicator" CLEVERAGENTS_CTX_PIPELINE_DEDUP Implementation class for the FragmentDeduplicator pipeline component. Removes duplicate fragments via content-hash, UKO-identity, or semantic similarity.
context.pipeline.detail-depth-resolver string "builtin:MaxDepthResolver" CLEVERAGENTS_CTX_PIPELINE_DEPTH Implementation class for the DetailDepthResolver pipeline component. Resolves conflicts when the same UKO node appears at different detail depths.
context.pipeline.fragment-scorer string "builtin:WeightedCompositeScorer" CLEVERAGENTS_CTX_PIPELINE_SCORER Implementation class for the FragmentScorer pipeline component. Computes composite relevance scores for ranking during budget packing.
context.pipeline.budget-packer string "builtin:GreedyKnapsackPacker" CLEVERAGENTS_CTX_PIPELINE_PACKER Implementation class for the BudgetPacker pipeline component. Fits scored fragments into the token budget with depth fallback support.
context.pipeline.fragment-orderer string "builtin:RelevanceCoherenceOrderer" CLEVERAGENTS_CTX_PIPELINE_ORDERER Implementation class for the FragmentOrderer pipeline component. Orders packed fragments for optimal coherence in the context window.
context.pipeline.preamble-generator string "builtin:ProvenancePreambleGenerator" CLEVERAGENTS_CTX_PIPELINE_PREAMBLE Implementation class for the PreambleGenerator pipeline component. Generates provenance summaries prepended to assembled context.
context.pipeline.skeleton-compressor string "builtin:DepthReductionCompressor" CLEVERAGENTS_CTX_PIPELINE_COMPRESSOR Implementation class for the SkeletonCompressor pipeline component. Compresses parent context into skeleton for child plan inheritance.
context.pipeline.strategy-executor.timeout-seconds integer 30 CLEVERAGENTS_CTX_PIPELINE_EXEC_TIMEOUT Timeout in seconds for each strategy execution within the StrategyExecutor.
context.pipeline.strategy-executor.max-workers integer 4 CLEVERAGENTS_CTX_PIPELINE_EXEC_WORKERS Maximum number of parallel workers for concurrent strategy execution.
context.pipeline.strategy-executor.circuit-breaker-threshold integer 3 CLEVERAGENTS_CTX_PIPELINE_EXEC_CB Number of consecutive failures before the circuit breaker opens for a strategy.
context.pipeline.fragment-scorer.relevance-weight float 0.4 CLEVERAGENTS_CTX_PIPELINE_SCORER_REL Weight of relevance score in composite fragment scoring.
context.pipeline.fragment-scorer.hierarchy-weight float 0.3 CLEVERAGENTS_CTX_PIPELINE_SCORER_HIER Weight of hierarchy position in composite fragment scoring.
context.pipeline.fragment-scorer.quality-weight float 0.2 CLEVERAGENTS_CTX_PIPELINE_SCORER_QUAL Weight of strategy quality in composite fragment scoring.
context.pipeline.fragment-scorer.recency-weight float 0.1 CLEVERAGENTS_CTX_PIPELINE_SCORER_REC Weight of recency bonus in composite fragment scoring.
context.pipeline.budget-packer.depth-fallback-steps list [9, 4, 2, 0] CLEVERAGENTS_CTX_PIPELINE_PACKER_STEPS Ordered list of detail depths to try during depth fallback when a fragment doesn't fit at its current depth.
context.pipeline.budget-packer.min-fragment-tokens integer 10 CLEVERAGENTS_CTX_PIPELINE_PACKER_MIN Minimum token size for a fragment to be included. Fragments below this threshold are excluded.
context.pipeline.preamble-generator.enabled boolean true CLEVERAGENTS_CTX_PIPELINE_PREAMBLE_ON Whether the PreambleGenerator produces a preamble. When false, no preamble is prepended to assembled context.
context.pipeline.preamble-generator.max-tokens integer 200 CLEVERAGENTS_CTX_PIPELINE_PREAMBLE_TOK Maximum tokens allocated to the context preamble.
provider.* — LLM Provider Credentials

Provider credential keys follow the pattern provider.<name>.<field>. Each provider has an api-key field and may have additional fields for endpoint configuration. Environment variables for provider keys use standard naming conventions compatible with existing tooling (e.g., OPENAI_API_KEY).

Key Type Default Env Variable Description
provider.openai.api-key string (not set) OPENAI_API_KEY API key for OpenAI models. Required when using OpenAI-based actors or the default embedding provider. The standard OPENAI_API_KEY environment variable is checked for compatibility with existing tooling.
provider.openai.org-id string (not set) OPENAI_ORG_ID OpenAI organization ID. Optional; used when the API key belongs to multiple organizations.
provider.openai.base-url string (not set) OPENAI_BASE_URL Custom base URL for OpenAI-compatible APIs (e.g., a local proxy or alternative provider). When unset, the official OpenAI API URL is used.
provider.anthropic.api-key string (not set) ANTHROPIC_API_KEY API key for Anthropic models. Required when using Anthropic-based actors.
provider.google.api-key string (not set) GOOGLE_API_KEY API key for Google AI models. Required when using Google-based actors.
provider.azure.endpoint string (not set) AZURE_OPENAI_ENDPOINT Azure OpenAI endpoint URL. Required when using Azure-hosted OpenAI models.
provider.azure.api-key string (not set) AZURE_OPENAI_API_KEY API key for Azure OpenAI. Required alongside provider.azure.endpoint.
provider.azure.api-version string 2024-02-01 AZURE_OPENAI_API_VERSION Azure OpenAI API version string.
provider.google.gemini-api-key string (not set) GEMINI_API_KEY API key for Google Gemini models. Required when using Gemini-based actors. The standard GEMINI_API_KEY environment variable is checked for compatibility with Google's tooling.
provider.huggingface.token string (not set) HF_TOKEN Access token for Hugging Face Inference API. Required when using Hugging Face-hosted models. The standard HF_TOKEN environment variable is checked for compatibility with Hugging Face tooling.
provider.openrouter.api-key string (not set) OPENROUTER_API_KEY API key for OpenRouter. Required when using OpenRouter as a provider.

Configuration Scoping

Several keys support project-scoped values in addition to the global default. When a project-scoped value is set, it applies only to plans targeting that project. Keys marked Project-scopable in the reference above support this feature.

The full dot path is used identically in both global and project-scoped contexts:


# Set a global default
$ agents config set core.automation-profile trusted

# Override for a specific project — same key, scoped to the project $ agents config set core.automation-profile manual --project local/production-api

# Project-scoped keys under any group work the same way $ agents config set plan.budget.per-plan 2.00 --project local/production-api $ agents config set sandbox.strategy git_worktree --project local/production-api $ agents config set context.hot.max-tokens 32000 --project local/large-codebase

The full list of project-scopable keys: core.automation-profile, plan.concurrency, plan.max-child-depth, plan.budget.per-plan, sandbox.strategy, sandbox.checkpoint.enabled, and all keys under context.*.

Resolution Order

When resolving the effective value of a configuration key, the following precedence applies (highest to lowest):

  1. CLI flag: Per-invocation overrides (e.g., --format maps to core.format, --data-dir maps to core.data-dir). Highest priority.
  2. Environment variable: The mapped CLEVERAGENTS_* variable or provider-specific variable (e.g., OPENAI_API_KEY for provider.openai.api-key).
  3. Project-scoped config: For project-scopable keys, the value set via agents config set <key> <value> --project <PROJECT>. Only applies in contexts where a project is known (e.g., during plan execution).
  4. Global config file: The value in the config file at the path specified by core.config-path.
  5. Built-in default: The hardcoded default value.

The agents config get <key> command displays the full resolution chain, showing which level provided the winning value. Listing subtrees is supported:


# Show the full resolution chain for a single key
$ agents config get sandbox.checkpoint.enabled

# List all keys in a group $ agents config list index.*

# List all keys with their current effective values $ agents config list

Configuration File Format

The global configuration file uses TOML format. The hierarchical key structure maps naturally to TOML tables. A typical config.toml after initial setup:


# ~/.cleveragents/config.toml

[core] data-dir = "/home/alex/.cleveragents" format = "rich" namespace = "local" automation-profile = "supervised"

[core.log] level = "FATAL" terminal = "auto" terminal-stream = "stderr" file-enabled = true retention-days = 30

[core.backup] retention-days = 7

[server] # url = "https://agents.example.com" # token = "tok_01HXR..."

[actor.default] strategy = "local/strategist" execution = "local/executor" invariant = "local/invariant-resolver" # estimation = "local/estimator" # orchestrator = "local/orchestrator"

[plan] concurrency = 4 max-child-depth = 5

[plan.budget] per-plan = 5.00 per-session = 25.00 warn-threshold = 0.8

[plan.tool] max-calls-per-step = 25 max-retries = 3 retry-backoff = "exponential"

[sandbox] strategy = "git_worktree" cleanup = "on_apply"

[sandbox.checkpoint] enabled = true max-per-plan = 50

[index.text] backend = "tantivy"

[index.vector] backend = "faiss"

[index.graph] backend = "none"

[index.embedding] provider = "openai" model = "text-embedding-3-small"

[context.hot] max-tokens = 16000

[context.warm] max-decisions = 100

[context.cold] max-decisions = 500

[context.query] limit = 20 min-relevance = 0.3

[context.file] max-size = 1048576 max-total-size = 52428800

[context.summarize] enabled = true max-tokens = 1000

# Provider keys — prefer environment variables for secrets [provider.openai] # api-key = "sk-..."

[provider.anthropic] # api-key = "sk-ant-..."

# Project-scoped overrides [project."local/production-api"] "core.automation-profile" = "manual" "plan.budget.per-plan" = 2.00 "sandbox.strategy" = "git_worktree" "sandbox.checkpoint.enabled" = true

[project."local/docs"] "core.automation-profile" = "auto" "plan.budget.per-plan" = 10.00 "context.hot.max-tokens" = 32000

Design Principles

All CleverAgents configuration files share a consistent set of design principles:

  1. YAML-first: All configuration files use YAML syntax. JSON is also accepted where a configuration file is loaded (since valid JSON is valid YAML), but YAML is the canonical format for human-authored configurations.

  2. Namespace/Name convention: Every configurable entity follows the <namespace>/<name> naming convention. The local/ namespace is reserved for local-only items. User namespaces (<username>/) and organization namespaces (<orgname>/) are stored on the server. Built-in entities use provider namespaces (e.g., openai/, anthropic/).

  3. Config-as-complete-definition pattern: For entity registration commands (actor add, skill add, tool add, validation add, resource type add, action create, automation-profile add), the YAML configuration file is the sole source of truth — the --config file fully defines the entity. For runtime commands that reference existing entities (e.g., plan use), CLI options may override entity defaults to customize behavior per-invocation. Exception: validation add accepts --required/--informational flags that override the YAML mode field, as described in the Validation Mode section under Core Concepts.

  4. Environment variable interpolation: All configuration files support ${ENV_VAR} and ${ENV_VAR:default_value} syntax for environment variable interpolation. If a variable is not set and no default is provided, an error is raised. Boolean strings (true/false) and numeric strings are automatically converted to their native types.

  5. Jinja2 template support: Actor configuration files support Jinja2 template syntax within string values (particularly system_prompt fields). Template placeholders like {{ context.project_name }} are preserved during YAML parsing and rendered at runtime.

  6. Idempotent registration: Adding an entity that already exists fails unless --update is provided. This prevents accidental overwrites while allowing intentional updates.

  7. Declarative structure: Configuration files are declarative — they describe what should exist, not how to create it. The system handles all lifecycle management.

  8. Schema version: Configuration files may include a top-level cleveragents.version field to indicate which schema version they target (currently "3.0"). When omitted, the latest schema version is assumed.


Actor Configuration Files

!!! adr "Architecture Decision" The actor configuration schema and actor registration model are defined in ADR-010: Actor and Agent Architecture.

Actor configuration files define intelligent agents — anything conversational, from a single LLM to an entire graph of interconnected actors and tools. Actors are registered via agents actor add --config <file>. For behavioral documentation including Jinja2 template preprocessing, actor composition, multi-actor workflows, and field-level descriptions, see the Actor section. For the Jinja2 preprocessing pipeline specifically, see Jinja2 Template Preprocessing and ADR-032.

JSON Schema

The following is the formal JSON Schema definition for actor configuration files. Since YAML is a superset of JSON, this schema can be used directly to validate actor YAML files using any JSON Schema validator.


{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://cleveragents.dev/schemas/actor-config.json",
  "title": "CleverAgents Actor Configuration",
  "description": "Configuration file schema for defining CleverAgents actors — intelligent agents composed of LLMs, tools, and graph topologies.",
  "type": "object",
  "properties": {
    "name": {
      "type": "string",
      "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
      "description": "Namespaced actor name in <namespace>/<name> format (e.g., 'local/reviewer', 'myorg/deploy-agent'). Required. When the actor is added via the CLI, this value is used as the registered name."
    },
    "cleveragents": {
      "type": "object",
      "description": "Metadata block containing schema version, logging, template engine, and safety settings.",
      "properties": {
        "version": {
          "type": "string",
          "description": "Schema version for this configuration file.",
          "default": "3.0"
        },
        "logging": {
          "type": "object",
          "properties": {
            "level": {
              "type": "string",
              "enum": ["DEBUG", "INFO", "WARNING", "ERROR"],
              "default": "INFO",
              "description": "Logging level."
            }
          },
          "additionalProperties": false
        },
        "template_engine": {
          "type": "string",
          "enum": ["JINJA2", "NONE"],
          "default": "JINJA2",
          "description": "Template engine for string interpolation."
        },
        "unsafe": {
          "type": "boolean",
          "default": false,
          "description": "When true, allows actors to perform operations flagged as unsafe. Requires --unsafe CLI flag."
        },
        "default_actor": {
          "type": "string",
          "description": "Name of the default actor when multiple actors are defined."
        }
      },
      "additionalProperties": false
    },
    "actors": {
      "$ref": "#/$defs/actorMap"
    },
    "agents": {
      "$ref": "#/$defs/actorMap",
      "description": "Alias for 'actors'. Both keys are accepted; use one or the other."
    },
    "routes": {
      "type": "object",
      "description": "Map of route names to their definitions. Routes connect actors via stream or graph topologies.",
      "additionalProperties": {
        "$ref": "#/$defs/route"
      }
    },
    "merges": {
      "type": "array",
      "description": "Stream merge operations combining multiple streams into one.",
      "items": {
        "type": "object",
        "properties": {
          "sources": {
            "type": "array",
            "items": { "type": "string" },
            "description": "Source stream names to merge."
          },
          "target": {
            "type": "string",
            "description": "Target stream name for merged output."
          }
        },
        "required": ["sources", "target"],
        "additionalProperties": false
      }
    },
    "splits": {
      "type": "array",
      "description": "Stream split operations dividing one stream into multiple.",
      "items": {
        "type": "object",
        "properties": {
          "source": {
            "type": "string",
            "description": "Source stream name to split."
          },
          "targets": {
            "type": "array",
            "items": { "type": "string" },
            "description": "Target stream names for split output."
          }
        },
        "required": ["source", "targets"],
        "additionalProperties": false
      }
    },
    "templates": {
      "type": "object",
      "description": "Reusable template definitions for Jinja2 template inheritance.",
      "additionalProperties": true
    },
    "instances": {
      "type": "object",
      "description": "Instantiated templates with bound parameters.",
      "additionalProperties": true
    },
    "global_context": {
      "type": "object",
      "description": "Key-value pairs available to all actors via {{ context.key }} in templates.",
      "additionalProperties": true
    },
    "prompts": {
      "type": "object",
      "description": "Named prompt templates that can be referenced by actors.",
      "additionalProperties": { "type": "string" }
    },
    "pipelines": {
      "type": "object",
      "description": "Hybrid pipeline definitions combining stream and graph stages.",
      "additionalProperties": {
        "type": "object",
        "properties": {
          "stages": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "name": { "type": "string" },
                "type": { "type": "string" },
                "config": { "type": "object", "additionalProperties": true }
              },
              "required": ["name", "type"]
            }
          },
          "metadata": { "type": "object", "additionalProperties": true }
        },
        "required": ["stages"]
      }
    }
  },
  "required": ["name"],
  "oneOf": [
    { "required": ["actors"] },
    { "required": ["agents"] }
  ],
  "additionalProperties": false,
  "$defs": {
    "actorMap": {
      "type": "object",
      "description": "Map of actor names to their definitions.",
      "additionalProperties": {
        "$ref": "#/$defs/actorDefinition"
      }
    },
    "actorDefinition": {
      "type": "object",
      "description": "A single actor definition.",
      "properties": {
        "type": {
          "type": "string",
          "enum": ["llm", "tool"],
          "description": "Actor type: 'llm' for language model actors, 'tool' for tool-based actors."
        },
        "config": {
          "type": "object",
          "description": "Actor configuration. Fields depend on actor type.",
          "properties": {
            "provider": {
              "type": "string",
              "description": "LLM provider identifier: openai, anthropic, google, azure, openrouter, etc."
            },
            "model": {
              "type": "string",
              "description": "Model identifier within the provider: gpt-4, claude-3.5-sonnet, gemini-pro, etc."
            },
            "actor": {
              "type": "string",
              "description": "Combined provider/model format (e.g., 'anthropic/claude-3.5-sonnet'). Alternative to specifying provider and model separately."
            },
            "system_prompt": {
              "type": "string",
              "description": "System prompt text. Supports Jinja2 template syntax for dynamic content."
            },
            "temperature": {
              "type": "number",
              "minimum": 0.0,
              "maximum": 2.0,
              "description": "Sampling temperature. Lower values are more deterministic."
            },
            "max_tokens": {
              "type": "integer",
              "minimum": 1,
              "description": "Maximum number of tokens in the generated response."
            },
            "memory_enabled": {
              "type": "boolean",
              "default": false,
              "description": "Enable conversation memory for multi-turn interactions."
            },
            "max_history": {
              "type": "integer",
              "default": 50,
              "minimum": 1,
              "description": "Maximum number of conversation turns retained in memory."
            },
            "unsafe": {
              "type": "boolean",
              "default": false,
              "description": "Allow this specific actor to perform unsafe operations."
            },
            "options": {
              "type": "object",
              "description": "Provider-specific options passed through to the underlying LLM API.",
              "additionalProperties": true
            },
            "tools": {
              "type": "array",
              "description": "List of inline tool definitions for tool-type actors.",
              "items": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string",
                    "description": "Tool name."
                  },
                  "code": {
                    "type": "string",
                    "description": "Inline Python code defining the tool's behavior."
                  }
                },
                "required": ["name", "code"],
                "additionalProperties": false
              }
            },
            "response_format": {
              "type": "object",
              "description": "JSON schema for structured output from the LLM. When set, the model is constrained to produce output matching this schema.",
              "additionalProperties": true
            }
          },
          "additionalProperties": false
        },
        "skills": {
          "type": "array",
          "description": "List of skill names this actor can use. Each entry is a namespaced skill name (e.g., 'local/file-ops'). Skills provide tool capabilities to the actor.",
          "items": { "type": "string", "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$" }
        },
        "lsp": {
          "description": "LSP server bindings for language intelligence. Can be a list of server names (explicit), an object with languages (language-based), or an object with auto: true (resource-auto).",
          "oneOf": [
            {
              "type": "array",
              "items": { "type": "string", "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$" },
              "description": "Explicit binding: list of namespaced LSP server names."
            },
            {
              "type": "object",
              "properties": {
                "languages": {
                  "type": "array",
                  "items": { "type": "string" },
                  "description": "Language-based binding: resolve LSP servers for these languages from the registry."
                },
                "auto": {
                  "type": "boolean",
                  "description": "Resource-auto binding: discover languages from project resources and resolve servers automatically."
                }
              },
              "additionalProperties": false
            }
          ]
        },
        "lsp_capabilities": {
          "description": "Controls which LSP capabilities are exposed as tools. When 'all' or omitted, all capabilities are available.",
          "oneOf": [
            {
              "type": "string",
              "enum": ["all"]
            },
            {
              "type": "array",
              "items": {
                "type": "string",
                "enum": ["diagnostics", "hover", "completions", "definitions", "references", "rename", "code_actions", "formatting", "signature_help", "document_symbols", "workspace_symbols"]
              }
            }
          ]
        },
        "lsp_context_enrichment": {
          "type": "object",
          "description": "Controls automatic LSP context enrichment (diagnostic and type info injection into ACMS context).",
          "properties": {
            "diagnostics": { "type": "boolean", "default": true, "description": "Auto-inject LSP diagnostics into context." },
            "type_annotations": { "type": "boolean", "default": false, "description": "Auto-inject type information into context." },
            "max_diagnostics_per_file": { "type": "integer", "default": 50, "minimum": 1, "description": "Maximum diagnostics per file to avoid context bloat." }
          },
          "additionalProperties": false
        }
      },
      "required": ["type", "config"],
      "additionalProperties": false
    },
    "route": {
      "type": "object",
      "description": "A route definition — either a stream or a graph topology.",
      "properties": {
        "type": {
          "type": "string",
          "enum": ["stream", "graph"],
          "description": "Route type."
        },
        "stream_type": {
          "type": "string",
          "enum": ["cold", "hot", "replay"],
          "default": "cold",
          "description": "Stream type (stream routes only)."
        },
        "operators": {
          "type": "array",
          "description": "Processing operators (stream routes).",
          "items": {
            "type": "object",
            "properties": {
              "type": {
                "type": "string",
                "enum": ["map", "graph_execute"],
                "description": "Operator type."
              },
              "params": {
                "type": "object",
                "properties": {
                  "agent": { "type": "string", "description": "Actor name for map operators." },
                  "graph": { "type": "string", "description": "Graph route name for graph_execute operators." }
                },
                "additionalProperties": true
              }
            },
            "required": ["type"]
          }
        },
        "subscriptions": {
          "type": "array",
          "items": { "type": "string" },
          "description": "Input stream subscriptions."
        },
        "publications": {
          "type": "array",
          "items": { "type": "string" },
          "description": "Output stream publications."
        },
        "agents": {
          "type": "array",
          "items": { "type": "string" },
          "description": "Actor names used by this route."
        },
        "initial_value": {
          "description": "Initial stream value."
        },
        "buffer_size": {
          "type": "integer",
          "default": 10,
          "minimum": 1,
          "description": "Stream buffer size."
        },
        "template_config": {
          "type": "object",
          "description": "Template-specific configuration.",
          "additionalProperties": true
        },
        "bridge": {
          "type": "object",
          "description": "Bridge configuration for stream-to-graph upgrades.",
          "properties": {
            "upgrade_conditions": { "type": "object", "additionalProperties": true },
            "downgrade_conditions": { "type": "object", "additionalProperties": true },
            "state_extractor": { "type": "string" },
            "state_flattener": { "type": "string" },
            "preserve_subscriptions": { "type": "boolean", "default": true },
            "preserve_checkpointing": { "type": "boolean", "default": true }
          },
          "additionalProperties": false
        },
        "metadata": {
          "type": "object",
          "additionalProperties": true
        },
        "nodes": {
          "type": "object",
          "description": "Graph nodes (required for graph routes).",
          "additionalProperties": {
            "$ref": "#/$defs/graphNode"
          }
        },
        "edges": {
          "type": "array",
          "description": "Graph edges (required for graph routes).",
          "items": {
            "$ref": "#/$defs/graphEdge"
          }
        },
        "entry_point": {
          "type": "string",
          "description": "Entry point node name (required for graph routes)."
        },
        "checkpointing": {
          "type": "boolean",
          "default": false,
          "description": "Enable graph checkpointing."
        },
        "checkpoint_dir": {
          "type": "string",
          "description": "Directory for checkpoint storage."
        },
        "enable_time_travel": {
          "type": "boolean",
          "default": false,
          "description": "Enable time travel debugging."
        },
        "parallel_execution": {
          "type": "boolean",
          "default": false,
          "description": "Allow parallel node execution."
        },
        "state_class": {
          "type": "string",
          "description": "Custom state class name."
        }
      },
      "required": ["type"],
      "additionalProperties": false
    },
    "graphNode": {
      "type": "object",
      "description": "A graph node definition.",
      "properties": {
        "type": {
          "type": "string",
          "enum": ["agent", "function", "tool", "conditional", "subgraph", "start", "end", "message_router"],
          "description": "Node type."
        },
        "agent": { "type": "string", "description": "Actor name for agent nodes." },
        "function": { "type": "string", "description": "Function name for function nodes." },
        "tools": {
          "type": "array",
          "items": { "type": "string" },
          "description": "Tool references for tool nodes."
        },
        "condition": { "type": "object", "additionalProperties": true, "description": "Condition for conditional nodes." },
        "subgraph": { "type": "string", "description": "Route name for subgraph nodes." },
        "retry_policy": { "type": "object", "additionalProperties": true, "description": "Retry configuration." },
        "timeout": { "type": "integer", "minimum": 1, "description": "Timeout in seconds." },
        "parallel": { "type": "boolean", "default": false, "description": "Allow parallel execution." },
        "metadata": { "type": "object", "additionalProperties": true }
      },
      "required": ["type"],
      "additionalProperties": false
    },
    "graphEdge": {
      "type": "object",
      "description": "A graph edge connecting two nodes.",
      "properties": {
        "source": { "type": "string", "description": "Source node name." },
        "target": { "type": "string", "description": "Target node name." },
        "condition": { "type": "object", "additionalProperties": true, "description": "Edge condition for conditional routing." },
        "metadata": { "type": "object", "additionalProperties": true }
      },
      "required": ["source", "target"],
      "additionalProperties": false
    }
  }
}

Informal YAML Schema

The following annotated YAML provides an easier-to-read overview of the same schema defined formally above. It is not a validatable schema — it uses informal annotations (comments, <placeholder> syntax, and | alternatives) to describe the structure. Refer to the JSON Schema above for the authoritative, machine-validatable definition.


# ─── Name ───────────────────────────────────────────────────────────
name: <namespace>/<actor-name>       # Namespaced actor name (required). Used as the registered name.
                                    # Must follow <namespace>/<name> format (e.g., local/reviewer).

# ─── Metadata ─────────────────────────────────────────────────────── cleveragents: version: "3.0" # Schema version (optional, default: latest) logging: level: "INFO" # Log level: DEBUG, INFO, WARNING, ERROR (optional) template_engine: "JINJA2" # Template engine: JINJA2 or NONE (optional, default: JINJA2) unsafe: false # Allow unsafe operations (optional, default: false) default_actor: <actor_name> # Default actor to use when multiple are defined (optional)

# ─── Actor Definitions ────────────────────────────────────────────── # The top-level key can be either "actors" or "agents" (both accepted). actors: <actor_name>: type: llm | tool # Actor type (required) config: # ── For type: llm ────────────────────────────────────────── provider: <string> # Provider identifier: openai, anthropic, google, azure, openrouter, etc. (required for LLM) model: <string> # Model identifier: gpt-4, claude-3.5-sonnet, etc. (required for LLM) # OR use the combined format: actor: "<provider>/<model>" # Combined provider/model (alternative to provider + model)

  <span style="color: cyan; font-weight: 600;">system_prompt</span>: |<span style="opacity: 0.7;">              # System prompt text, supports Jinja2 templates (optional)</span>
    You are a helpful assistant.
    <span style="color: cyan; font-weight: 600;">Project</span>: {{ context.project_name }}
  <span style="color: cyan; font-weight: 600;">temperature</span>: <span style="color: yellow;">0.7</span><span style="opacity: 0.7;">              # Sampling temperature 0.0-2.0 (optional, default: provider default)</span>
  <span style="color: cyan; font-weight: 600;">max_tokens</span>: <span style="color: yellow;">4096</span><span style="opacity: 0.7;">              # Maximum output tokens (optional, default: provider default)</span>
  <span style="color: cyan; font-weight: 600;">memory_enabled</span>: <span style="color: magenta; font-weight: 600;">true</span><span style="opacity: 0.7;">          # Enable conversation memory (optional, default: false)</span>
  <span style="color: cyan; font-weight: 600;">max_history</span>: <span style="color: yellow;">50</span><span style="opacity: 0.7;">               # Maximum conversation turns to retain (optional, default: 50)</span>
  <span style="color: cyan; font-weight: 600;">unsafe</span>: <span style="color: magenta; font-weight: 600;">false</span><span style="opacity: 0.7;">                 # Allow unsafe operations for this actor (optional, default: false)</span>
  <span style="color: cyan; font-weight: 600;">options</span>:                      # Additional provider-specific options (optional)
    <span style="color: cyan; font-weight: 600;">top_p</span>: <span style="color: yellow;">1.0</span>
    <span style="color: cyan; font-weight: 600;">frequency_penalty</span>: <span style="color: yellow;">0.0</span>
    <span style="color: cyan; font-weight: 600;">presence_penalty</span>: <span style="color: yellow;">0.0</span>
    <span style="color: cyan; font-weight: 600;">stop_sequences</span>: [<span style="color: #66cc66;">&quot;END&quot;</span>]
    <span style="color: cyan; font-weight: 600;">seed</span>: <span style="color: yellow;">42</span>

# ── For type: tool ───────────────────────────────────────── tools: - name: <tool_name> # Tool name (required per tool) code: | # Inline Python code (required per tool) def run(input_data): return {"result": input_data["query"]}

  <span style="color: cyan; font-weight: 600;">response_format</span>: {}<span style="opacity: 0.7;">            # JSON schema for structured LLM output (optional, LLM only)</span>

# ── Skills (both LLM and tool actors) ──────────────────────── skills: # Skill references providing tool capabilities (optional) - <namespace>/<skill-name> # e.g., local/file-ops, local/git-ops

# ── LSP Binding (language intelligence for actor nodes) ───── # Three binding modes — pick one:

# Mode 1: Explicit — list specific registered LSP servers lsp: # LSP server binding (optional) - <namespace>/<server-name> # e.g., local/pyright, local/typescript-language-server

# Mode 2: Language-based — runtime resolves servers from registry # lsp: # languages: [python, typescript]

# Mode 3: Auto-discovery — detect languages from project resources # lsp: # auto: true

<span style="color: cyan; font-weight: 600;">lsp_capabilities</span>: <span style="color: magenta;">all</span><span style="opacity: 0.7;">              # Which LSP capabilities to expose (optional, default: all)</span>

# "all" | list of: diagnostics, hover, completions, references, # definitions, symbols, formatting, code_actions, rename

<span style="color: cyan; font-weight: 600;">lsp_context_enrichment</span>:            # ACMS context enrichment from LSP (optional)
  <span style="color: cyan; font-weight: 600;">diagnostics</span>: <span style="color: magenta;">true</span><span style="opacity: 0.7;">               # Auto-inject diagnostics into code context windows</span>
  <span style="color: cyan; font-weight: 600;">type_annotations</span>: <span style="color: magenta;">true</span><span style="opacity: 0.7;">          # Auto-inject inferred types into code context windows</span>
  <span style="color: cyan; font-weight: 600;">max_diagnostics_per_file</span>: <span style="color: magenta;">50</span><span style="opacity: 0.7;">   # Limit diagnostics per file (default: 50)</span>

# ─── Routes ───────────────────────────────────────────────────────── routes: <route_name>: type: stream | graph # Route type (required)

# ── Stream route fields ────────────────────────────────────── stream_type: cold | hot | replay # Stream type (optional, default: cold) operators: # Processing operators (optional) - type: map | graph_execute # Operator type params: # Operator parameters agent: <actor_name> # Actor to use (for map) graph: <route_name> # Graph to execute (for graph_execute) subscriptions: # Input subscriptions (optional) - <stream_name> publications: # Output publications (optional) - <stream_name> agents: # Actors used by this route (optional) - <actor_name> initial_value: <any> # Initial stream value (optional) buffer_size: 10 # Stream buffer size (optional, default: 10) template_config: {} # Template-specific configuration (optional) bridge: # Bridge configuration for stream↔graph upgrades (optional) upgrade_conditions: {} # Conditions to upgrade from stream to graph downgrade_conditions: {} # Conditions to downgrade from graph to stream state_extractor: <string> # Function to extract state during upgrade state_flattener: <string> # Function to flatten state during downgrade preserve_subscriptions: true # Keep subscriptions during transition (optional) preserve_checkpointing: true # Keep checkpoints during transition (optional) metadata: {} # Arbitrary metadata (optional)

# ── Graph route fields ─────────────────────────────────────── nodes: # Graph nodes (required for graph routes) <node_name>: type: agent | function | tool | conditional | subgraph | start | end | message_router agent: <actor_name> # Actor for agent nodes function: <function_name> # Function for function nodes tools: # Tools for tool nodes - <tool_ref> condition: {} # Condition for conditional nodes subgraph: <route_name> # Subgraph for subgraph nodes retry_policy: {} # Retry configuration (optional) timeout: 30 # Timeout in seconds (optional) parallel: false # Allow parallel execution (optional) metadata: {} # Arbitrary metadata (optional) edges: # Graph edges (required for graph routes) - source: <node_name> # Source node (required) target: <node_name> # Target node (required) condition: {} # Edge condition (optional) metadata: {} # Arbitrary metadata (optional) entry_point: <node_name> # Entry point node (required for graph routes) checkpointing: false # Enable graph checkpointing (optional, default: false) checkpoint_dir: <path> # Checkpoint directory (optional) enable_time_travel: false # Enable time travel debugging (optional, default: false) parallel_execution: false # Allow parallel node execution (optional, default: false) state_class: <string> # Custom state class name (optional) metadata: {} # Arbitrary metadata (optional)

# ─── Merges & Splits ──────────────────────────────────────────────── merges: # Stream merge definitions (optional)

  • sources: # Source streams to merge
    • <stream_name> target: <stream_name> # Target stream for merged output

splits: # Stream split definitions (optional)

  • source: <stream_name> # Source stream to split targets: # Target streams for split output
    • <stream_name>

# ─── Templates & Context ──────────────────────────────────────────── templates: {} # Reusable template definitions (optional) instances: {} # Template instances (optional) global_context: {} # Global context available to all actors (optional) prompts: {} # Named prompt templates (optional)

# ─── Pipelines ────────────────────────────────────────────────────── pipelines: # Hybrid pipeline definitions (optional) <pipeline_name>: stages: - name: <stage_name> type: <stage_type> config: {} metadata: {}

Structure Reference

Top-Level Keys

Key Type Required Description
cleveragents object No Metadata block containing version, logging, template engine, and safety settings.
actors (or agents) object Yes Map of actor names to their definitions. Both actors and agents are accepted as the key name.
routes object No Map of route names to their definitions. Routes connect actors via stream or graph topologies.
merges list No Stream merge operations combining multiple streams into one.
splits list No Stream split operations dividing one stream into multiple.
templates object No Reusable template definitions for Jinja2 template inheritance.
instances object No Instantiated templates with bound parameters.
global_context object No Key-value pairs available to all actors via {{ context.key }} in templates.
prompts object No Named prompt templates that can be referenced by actors.
pipelines object No Hybrid pipelines combining stream and graph stages into sequential workflows.

cleveragents Block

Field Type Default Description
version string "3.0" Schema version for this configuration file.
logging.level string "INFO" Logging level: DEBUG, INFO, WARNING, ERROR.
template_engine string "JINJA2" Template engine for string interpolation: JINJA2 or NONE.
unsafe boolean false When true, allows actors to perform operations flagged as unsafe. Requires --unsafe CLI flag.
default_actor string (first actor) Name of the default actor when multiple actors are defined.

Actor Definition Fields

Field Type Required Description
type string Yes Actor type. llm for language model actors, tool for tool-based actors.
config.provider string Yes (LLM) LLM provider identifier: openai, anthropic, google, azure, openrouter, etc.
config.model string Yes (LLM) Model identifier within the provider: gpt-4, claude-3.5-sonnet, gemini-pro, etc.
config.actor string No Combined provider/model format. Alternative to specifying provider and model separately.
config.system_prompt string No System prompt text. Supports Jinja2 template syntax for dynamic content.
config.temperature float Provider default Sampling temperature (0.0 to 2.0). Lower values are more deterministic.
config.max_tokens integer Provider default Maximum number of tokens in the generated response.
config.memory_enabled boolean false Enable conversation memory for multi-turn interactions.
config.max_history integer 50 Maximum number of conversation turns retained in memory.
config.unsafe boolean false Allow this specific actor to perform unsafe operations.
config.options object {} Provider-specific options passed through to the underlying LLM API.
config.tools list Yes (tool) List of inline tool definitions for tool-type actors. Each tool has name and code.
config.response_format object No JSON schema for structured output from the LLM. Constrains model output to match the schema.
skills list No List of namespaced skill names (e.g., local/file-ops) providing tool capabilities to the actor.
lsp list or object No LSP server binding. List of namespaced server names (explicit mode), object with languages list (language-based mode), or object with auto: true (auto-discovery mode).
lsp_capabilities string or list No Which LSP capabilities to expose. "all" (default) or a list from: diagnostics, hover, completions, references, definitions, symbols, formatting, code_actions, rename.
lsp_context_enrichment object No ACMS context enrichment from LSP. Fields: diagnostics (bool), type_annotations (bool), max_diagnostics_per_file (int, default 50).

Node Types (Graph Routes)

Type Description
agent A node backed by an actor. Requires agent field pointing to an actor name.
function A node backed by a Python function. Requires function field.
tool A node that invokes one or more tools. Requires tools list.
conditional A branching node that routes based on conditions. Requires condition field.
subgraph A node that delegates to another graph route. Requires subgraph field.
start The explicit start node (alternative to entry_point).
end A terminal node that ends graph execution.
message_router Routes messages to different nodes based on message content or metadata.

Examples

Example 1: Minimal Chat Actor (Simple)

A bare-minimum actor configuration for a simple conversational agent:


# minimal-chat.yaml
# Register: agents actor add --config minimal-chat.yaml

name: local/chat

actors: chat: type: llm config: provider: anthropic model: claude-3.5-sonnet system_prompt: "You are a helpful assistant."

routes: main: type: stream operators: - type: map params: agent: chat publications: - output

merges:

  • sources: [output] target: final

This is the simplest possible actor: one LLM actor, one stream route, one merge. No graph complexity, no tools, no templates.


Example 2: Code Reviewer with Tools (Medium)

An actor that can read files and search code to perform code reviews:


# code-reviewer.yaml
# Register: agents actor add --config code-reviewer.yaml

name: local/reviewer

cleveragents: version: "3.0" logging: level: "INFO"

actors: reviewer: type: llm config: provider: openai model: gpt-4 temperature: 0.2 max_tokens: 4096 system_prompt: | You are a senior code reviewer. Analyze code for: - Security vulnerabilities - Performance issues - Code style violations - Missing error handling Provide specific, actionable feedback with file and line references.

file_reader: type: tool config: tools: - name: read_file code: | def run(input_data): path = input_data.get("path", "") with open(path, "r") as f: return {"content": f.read(), "path": path}

routes: review_graph: type: graph nodes: analyze: type: agent agent: reviewer read: type: tool tools: [read_file] report: type: agent agent: reviewer edges: - source: analyze target: read - source: read target: report - source: analyze target: report condition: no_files_needed: true entry_point: analyze checkpointing: false

output_stream: type: stream operators: - type: graph_execute params: graph: review_graph publications: - review_output

merges:

  • sources: [review_output] target: final

This actor defines an LLM reviewer and a file-reading tool, connected via a graph route with conditional edges.


Example 3: Multi-Agent Research Pipeline with Templates (Complex)

A complex actor configuration with multiple LLM actors, Jinja2 templates, environment variable interpolation, memory, and hybrid routing:


# research-pipeline.yaml
# Register: agents actor add --config research-pipeline.yaml --unsafe

name: local/research

cleveragents: version: "3.0" logging: level: "DEBUG" template_engine: "JINJA2" unsafe: true default_actor: orchestrator

actors: orchestrator: type: llm config: provider: anthropic model: claude-3.5-sonnet temperature: 0.7 max_tokens: 8192 memory_enabled: true max_history: 100 system_prompt: | You are an orchestrator managing a research pipeline for {{ context.project_name }}. Topic: {{ context.research_topic }}

    Your job is to:
    1. Break down the research question into sub-questions
    2. Delegate to specialist researchers
    3. Synthesize findings into a coherent report

    {% if context.deadline %}
    <span style="color: cyan; font-weight: 600;">Deadline</span>: {{ context.deadline }}. Prioritize breadth over depth.
    {% endif %}

researcher: type: llm config: provider: openai model: gpt-4 temperature: 0.3 max_tokens: 4096 system_prompt: | You are a domain expert researcher. Provide thorough, factual analysis. Always cite sources and note confidence levels.

synthesizer: type: llm config: provider: anthropic model: claude-3.5-sonnet temperature: 0.5 max_tokens: 16384 system_prompt: | You are an expert at synthesizing multiple research reports into coherent narratives. Resolve contradictions, highlight consensus, and note gaps.

web_searcher: type: tool config: tools: - name: search_web code: | import os, json def run(input_data): api_key = os.environ.get("SEARCH_API_KEY", "") query = input_data.get("query", "") # Simulated web search return {"results": [], "query": query}

    - <span style="color: cyan; font-weight: 600;">name</span>: fetch_url
      <span style="color: cyan; font-weight: 600;">code</span>: |
        def run(input_data):
            url = input_data.get(&quot;url&quot;, &quot;&quot;)
            return {&quot;content&quot;: f&quot;Content from {url}&quot;, &quot;url&quot;: url}

routes: research_graph: type: graph nodes: plan: type: agent agent: orchestrator research_parallel: type: agent agent: researcher parallel: true search: type: tool tools: [search_web, fetch_url] synthesize: type: agent agent: synthesizer review: type: agent agent: orchestrator edges: - source: plan target: research_parallel - source: plan target: search - source: research_parallel target: synthesize - source: search target: synthesize - source: synthesize target: review - source: review target: plan condition: needs_more_research: true entry_point: plan checkpointing: true checkpoint_dir: "${CHECKPOINT_DIR:/tmp/research_checkpoints}" enable_time_travel: true parallel_execution: true

progress_stream: type: stream stream_type: hot operators: - type: map params: agent: orchestrator subscriptions: - research_updates publications: - progress_output buffer_size: 50

output_stream: type: stream operators: - type: graph_execute params: graph: research_graph publications: - research_output

merges:

  • sources: [progress_output, research_output] target: final

global_context: project_name: "AI Safety Research" research_topic: "Alignment techniques in large language models" deadline: "2026-03-01"

prompts: deep_dive: "Provide an in-depth analysis of {topic} with at least 5 sources." summary: "Summarize the following research in 500 words: {content}"

templates: research_section: template: | ## {{ section_title }} {{ section_content }} Confidence: {{ confidence_level }} Sources: {{ sources | join(', ') }}

This configuration demonstrates multi-actor orchestration, graph routing with parallel execution, hot stream monitoring, Jinja2 templates with conditionals, environment variables, checkpointing, and global context.


Example 4: Simple Echo/Tool Actor (Simple)

A non-LLM actor that processes input with pure Python code:


# echo-actor.yaml
# Register: agents actor add --config echo-actor.yaml

name: local/echo

cleveragents: version: "3.0"

actors: echo: type: tool config: tools: - name: echo_tool code: | def run(input_data): message = input_data.get("content", "") return {"response": f"Echo: {message}"}

routes: main: type: stream operators: - type: map params: agent: echo publications: - output

merges:

  • sources: [output] target: final

Example 5: Graph Actor with Conditional Routing (Medium)

An actor that routes between different specialists based on input classification:


# classifier-router.yaml
# Register: agents actor add --config classifier-router.yaml

name: local/smart-router

actors: classifier: type: llm config: provider: openai model: gpt-4 temperature: 0.0 max_tokens: 100 system_prompt: | Classify the user's request into exactly one category: CODING, WRITING, ANALYSIS, or GENERAL. Respond with only the category name.

coding_expert: type: llm config: provider: anthropic model: claude-3.5-sonnet temperature: 0.2 system_prompt: "You are an expert software engineer. Write clean, well-tested code."

writing_expert: type: llm config: provider: openai model: gpt-4 temperature: 0.7 system_prompt: "You are a professional writer. Produce clear, engaging prose."

analyst: type: llm config: provider: anthropic model: claude-3.5-sonnet temperature: 0.3 system_prompt: "You are a data analyst. Provide thorough, evidence-based analysis."

generalist: type: llm config: provider: openai model: gpt-4 temperature: 0.5 system_prompt: "You are a helpful general-purpose assistant."

routes: router_graph: type: graph nodes: classify: type: agent agent: classifier code: type: agent agent: coding_expert write: type: agent agent: writing_expert analyze: type: agent agent: analyst general: type: agent agent: generalist edges: - source: classify target: code condition: category: "CODING" - source: classify target: write condition: category: "WRITING" - source: classify target: analyze condition: category: "ANALYSIS" - source: classify target: general condition: category: "GENERAL" entry_point: classify

main: type: stream operators: - type: graph_execute params: graph: router_graph publications: - output

merges:

  • sources: [output] target: final

Example 6: Environment-Aware Actor with Jinja2 Conditionals (Simple Jinja2)

A single-actor configuration demonstrating the most common Jinja2 preprocessing features: {{ variable }} interpolation, {% if %} conditionals, the | default filter, and ${VAR:default} environment variable substitution. See ADR-032 for the full Jinja2 preprocessing specification.


# support-bot.yaml
# Register: agents actor add --config support-bot.yaml
#
# Jinja2 preprocessing (Phase 1) resolves {{ }} and {% %} at load time.
# Phase 2: env var interpolation resolves ${VAR} after parsing.

name: local/support-bot

cleveragents: version: "3.0" template_engine: "JINJA2"

actors: support: type: llm config: # Phase 2: environment variables with defaults and type coercion provider: ${LLM_PROVIDER:anthropic} model: ${LLM_MODEL:claude-3.5-sonnet} temperature: 0.4 max_tokens: ${MAX_TOKENS:4096} # coerced to int automatically memory_enabled: ${ENABLE_MEMORY:true} # coerced to bool automatically system_prompt: | You are a {{ context.role }} for {{ context.company }}.

    {% if context.tier == "enterprise" %}
    This is an enterprise customer. Provide priority support with
    detailed technical explanations and offer to escalate issues
    to the engineering team when needed.
    {% else %}
    Provide friendly, concise support. Direct complex issues to
    the documentation at {{ context.docs_url }}.
    {% endif %}

    Always respond in {{ context.language | default("English") }}.

routes: main: type: stream operators: - type: map params: agent: support publications: - output

merges:

  • sources: [output] target: final

# global_context populates the {{ context.* }} namespace at load time global_context: company: "Acme Corp" role: "technical support specialist" tier: "enterprise" docs_url: "https://docs.acme.example.com"

This example shows the three most common Jinja2 preprocessing patterns: variable interpolation ({{ context.company }}), conditional blocks ({% if context.tier == "enterprise" %}), and the | default filter for safe fallbacks. Environment variables (${LLM_PROVIDER:anthropic}) are resolved in Phase 2 after YAML parsing, with automatic type coercion converting "4096" to int and "true" to bool.


Example 7: Multi-Actor Paper Writer with Advanced Jinja2 (Advanced Jinja2)

A comprehensive multi-actor configuration exercising the full Jinja2 preprocessing feature set. Crucially, this example places {% for %} and {% if %} directives outside any YAML value — directly at the structural level where YAML keys and list items would normally appear. The raw file is not valid YAML until after Jinja2 Phase 1 preprocessing renders it into static YAML text. Features demonstrated include: structural {% for %} loops that generate actor definitions, graph nodes, and graph edges; structural {% if %} conditionals that include or exclude entire actor blocks, route definitions, and merge sources; {% if %} nested inside {% for %} at the structural level; loop.index; Jinja2 type tests (is mapping); .get() with defaults; string slicing ([:200]); ternary expressions; arithmetic; | tojson / | length / | upper / | join filters; {# comment #} blocks; and deeply nested global_context. See ADR-032 for full details.


# paper-writer.yaml
# Register: agents actor add --config paper-writer.yaml
#
# IMPORTANT: This file is NOT valid YAML as written. Jinja2 directives
# ({% for %}, {% if %}, {% endif %}, {% endfor %}) appear at the structural
# level — where YAML keys and list items would normally be — making the
# raw file unparseable by any YAML parser. Phase 1 (Jinja2 preprocessing)
# renders these directives into static YAML text BEFORE the YAML parser
# ever sees the file. Jinja2 syntax inside system_prompt fields is
# preserved for deferred runtime rendering.

name: local/paper-writer

cleveragents: version: "3.0" template_engine: "JINJA2" logging: level: "${LOG_LEVEL:INFO}" unsafe: ${ALLOW_UNSAFE:false} # Phase 2: coerced to bool default_actor: orchestrator

{# ─── Template comment: stripped from output, never reaches YAML parser ─── #}

actors: # ── Orchestrator: uses deferred Jinja2 in system_prompt ───────────── orchestrator: type: llm config: provider: ${LLM_PROVIDER:anthropic} model: ${PRIMARY_MODEL:claude-3.5-sonnet} temperature: 0.7 max_tokens: ${MAX_TOKENS:8192} memory_enabled: true max_history: ${MAX_HISTORY:100} system_prompt: | You are the lead orchestrator for a research paper. Topic: {{ context.paper_details.topic | tojson }} Audience: {{ context.paper_details.audience | tojson }} Max length: {{ context.paper_details.length | tojson }} words

    {# ── Vetted sources: for-loop with type test, .get(), slicing ── #}
    {% if context.vetted_sources and context.vetted_sources|length &gt; 0 %}
    The following {{ context.vetted_sources|length }} vetted sources:
    {% for source in context.vetted_sources %}
    {{ loop.index }}.
    {% if source is mapping %}
      {{ source.get('citation', 'Untitled') }}
      {% if source.get('summary') %}
      — {{ source.get('summary')[:200] }}
      {% if source.get('summary')|length &gt; 200 %}
      ...
      {% endif %}
      {% endif %}
    {% else %}
      {{ source }}
    {% endif %}
    {% endfor %}
    {% else %}
    No vetted sources are available yet. Begin with the discovery phase.
    {% endif %}

    {% if context.deadline %}
    DEADLINE: {{ context.deadline }}. Prioritize accordingly.
    {% endif %}

    {# ── Section plan: ternary expression highlights current section ── #}
    Section plan:
    {% for section in context.sections %}
    {% set m = "&gt;&gt;&gt; " if section == context.current_section else "    " %}
    {{ m }}{{ loop.index }}. {{ section }}
    {% endfor %}

    {# ── Arithmetic in expressions ── #}
    Progress: section {{ context.current_section_index + 1 }}
    of {{ context.sections|length }}.

# ── Writer: deferred templates with nested conditionals ───────────── writer: type: llm config: provider: ${LLM_PROVIDER:anthropic} model: ${PRIMARY_MODEL:claude-3.5-sonnet} temperature: 0.5 max_tokens: 16384 system_prompt: | You are writing section "{{ context.current_section }}" of a paper on {{ context.paper_details.topic }}.

    {% if context.section_content %}
    Previous draft:
    {% set sec = context.current_section %}
    {{ context.section_content.get(sec, 'No prior draft.') }}
    {% endif %}

    {# ── Nested: loop inside conditional ── #}
    {% if context.review_feedback and context.review_feedback|length &gt; 0 %}
    Reviewer feedback to address:
    {% for fb in context.review_feedback %}
    [{{ fb.reviewer }}] ({{ fb.severity }}): {{ fb.comment }}
    {% endfor %}
    {% endif %}

    Format: {{ context.paper_details.get('format', 'markdown') | upper }}

# ── STRUCTURAL {% if %}: the entire assembler actor definition — its YAML # ── key and all nested content — is conditionally included. The {% if %} # ── and {% endif %} lines occupy positions where YAML keys would be, # ── making this raw text invalid YAML. After Phase 1 rendering, either # ── the full assembler: block appears or nothing does. {% if context.enable_assembly %} assembler: type: llm config: actor: anthropic/claude-3.5-sonnet temperature: 0.3 max_tokens: 32768 system_prompt: | Assemble the final paper from these completed sections: {% for path in context.sections %} --- {{ path }} --- {{ context.section_content.get(path, '[MISSING]') }} {% endfor %}

    Total sections: {{ context.sections|length }}
    Target length: {{ context.paper_details.length }} words

    {% if context.latex_errors %}
    Previous compilation errors (last 2000 chars):
    {{ context.latex_errors[-2000:] }}
    {% endif %}

{% endif %}

# ── STRUCTURAL {% for %}: GENERATE one reviewer actor per entry in # ── context.reviewers. The {% for %} line sits where a YAML key would # ── be — not inside any string value. A YAML parser would reject this. # This {% for %} runs at Phase 1 and produces static YAML actor definitions. # With 3 reviewers in global_context, the rendered YAML contains 3 actors: # reviewer_methods, reviewer_domain, reviewer_style. {% for reviewer in context.reviewers %} reviewer_{{ reviewer.id }}: type: llm config: provider: {{ reviewer.get('provider', 'openai') }} model: {{ reviewer.get('model', 'gpt-4') }} temperature: 0.2 max_tokens: 4096 system_prompt: | You are {{ reviewer.name }}, an expert reviewer specializing in {{ reviewer.specialty }}.

    Evaluate the paper section for:
    {# ── Nested loop: iterate criteria inside reviewer loop ── #}
    {% for criterion in reviewer.criteria %}
    - {{ criterion }}
    {% endfor %}

    Severity ratings: Critical, Major, Minor, Suggestion.

    {% if context.review_mode == &quot;strict&quot; %}
    Apply strict academic standards. Flag all unsupported claims.
    {% else %}
    Focus on substantive issues. Ignore minor style preferences.
    {% endif %}

{% endfor %}

# ── Routes: load-time loop generates graph nodes and edges ────────── routes: writing_graph: type: graph nodes: plan: type: agent agent: orchestrator draft: type: agent agent: writer # STRUCTURAL {% for %}: generates review_methods, review_domain, # review_style as concrete YAML keys — invalid YAML until rendered {% for reviewer in context.reviewers %} review_{{ reviewer.id }}: type: agent agent: reviewer_{{ reviewer.id }} {% endfor %} # STRUCTURAL {% if %}: the assemble node only exists when assembly # is enabled — matches the conditional assembler actor above {% if context.enable_assembly %} assemble: type: agent agent: assembler {% endif %} edges: - source: plan target: draft # STRUCTURAL {% for %}: generates edge sets for each reviewer. # Contains a NESTED STRUCTURAL {% if %} — the assemble edge only # appears when enable_assembly is true. Both directives sit where # YAML list items would be — completely invalid YAML until rendered. {% for reviewer in context.reviewers %} - source: draft target: review_{{ reviewer.id }} - source: review_{{ reviewer.id }} target: draft condition: has_critical_feedback: true # {% if %} NESTED inside {% for %}: each reviewer gets an edge # to assemble only when the assembler exists {% if context.enable_assembly %} - source: review_{{ reviewer.id }} target: assemble condition: review_passed: true {% endif %} {% endfor %} entry_point: plan checkpointing: true checkpoint_dir: "${CHECKPOINT_DIR:/tmp/paper_checkpoints}" parallel_execution: true

output_stream: type: stream operators: - type: graph_execute params: graph: writing_graph publications: - paper_output

# ── STRUCTURAL {% if %}: this entire route definition — the YAML key # ── "progress_stream:" and all its children — only exists in the # ── rendered output when enable_monitoring is true. A YAML parser # ── would choke on the bare {% if %} line sitting where it expects # ── a mapping key. {% if context.enable_monitoring %} progress_stream: type: stream stream_type: hot operators: - type: map params: agent: orchestrator subscriptions: - writing_updates publications: - progress_output buffer_size: 50 {% endif %}

merges:

  • sources:
    • paper_output # STRUCTURAL {% if %} inside a YAML list: this list item only # appears in the rendered YAML when the condition is true. # The raw file has a {% if %} line where a "- value" is expected. {% if context.enable_monitoring %}
    • progress_output {% endif %} target: final

# ── global_context: deeply nested structures drive all template rendering ── global_context: paper_details: topic: "Alignment techniques in large language models" audience: "ML researchers" length: 8000 publication: "NeurIPS 2026" format: "latex" sections: - "Abstract" - "Introduction" - "Related Work" - "Methodology" - "Experiments" - "Results > Quantitative" - "Results > Qualitative" - "Discussion" - "Conclusion" current_section: "Introduction" current_section_index: 1 deadline: "2026-06-01" review_mode: "strict" # These flags drive the structural {% if %} conditionals above. # Set to false to exclude the assembler actor and monitoring route entirely. enable_assembly: true enable_monitoring: true # The reviewers list drives the structural {% for %} loops that generate # actor definitions, graph nodes, and graph edges at load time. reviewers: - id: methods name: "Dr. Methods" specialty: "research methodology" provider: "openai" model: "gpt-4" criteria: - "Statistical validity" - "Reproducibility of experiments" - "Clarity of methodology description" - id: domain name: "Dr. Domain" specialty: "AI alignment" provider: "anthropic" model: "claude-3.5-sonnet" criteria: - "Technical accuracy" - "Completeness of literature review" - "Novelty of contributions" - id: style name: "Prof. Style" specialty: "academic writing" criteria: - "Clarity and readability" - "Logical flow between sections" - "Proper citation format"

This configuration demonstrates every major Jinja2 preprocessing feature:

Feature Where Used
Structural {% if %} wrapping entire YAML blocks Assembler actor def, assemble graph node, progress_stream route, merge source list item
Structural {% for %} generating YAML keys/items Reviewer actor defs, graph nodes, graph edges
{% if %} nested inside {% for %} at structural level Reviewer→assemble edge (conditional per-reviewer)
{{ context.X.Y }} — nested variable access Orchestrator, writer, assembler system_prompt
{{ X | tojson }} — safe serialization filter Orchestrator prompt: topic, audience, length
{{ X | length }} — collection length Source count, section count
{{ X | upper }} — string transformation Writer prompt: format output
{{ X | default("Y") }} — fallback defaults Reviewer .get('provider', 'openai')
{% if X and X|length > 0 %} — compound conditions Vetted sources, review feedback, deadline
{% for X in Y %} / loop.index — iteration Sources list, sections list, reviewers list
{# comment #} — template comments Stripped from rendered output
source is mapping — Jinja2 type test Vetted sources loop
.get('key', 'default') — safe dict access Section content, paper format
[:200] / [-2000:] — string/list slicing Source summaries, LaTeX errors
"X" if cond else "Y" — ternary expressions Section plan current-section marker
{{ index + 1 }} — arithmetic Section progress counter
Nested {% for %} inside {% for %} Reviewer criteria inside reviewer loop
${VAR:default} — env var with type coercion Provider, model, tokens, unsafe flag
global_context with nested dicts/lists paper_details, sections, reviewers
Deferred rendering in system_prompt All system_prompt fields preserve {{ }} for runtime

Why the raw file is not valid YAML: The {% for %}, {% endfor %}, {% if %}, and {% endif %} directives in this file appear at positions where the YAML parser expects mapping keys or list items — for example, {% if context.enable_assembly %} sits at the same indentation level as assembler: under the actors: mapping, and {% if context.enable_monitoring %} sits where a route name key would be under routes:. A YAML parser would reject these lines as syntax errors. Jinja2 Phase 1 preprocessing resolves all directives into plain text before the YAML parser ever runs, producing a valid static YAML document.

Load-time vs. runtime rendering: The structural {% for %} and {% if %} directives in the actors, routes, and merges sections run at load time (Phase 1) and produce static YAML — the three reviewer entries expand into three concrete actor definitions (reviewer_methods, reviewer_domain, reviewer_style), their corresponding graph nodes and edges, and the assembler and monitoring route conditionally appear or disappear. In contrast, Jinja2 syntax inside system_prompt fields is preserved through the load-parse cycle (via the template protection mechanism) and evaluated at runtime when the actor's execution context is available.


Skill Configuration Files

!!! adr "Architecture Decision" The skill configuration schema, tool references, and skill inclusion are defined in ADR-012: Skill System.

Skill configuration files define reusable, namespaced collections of tools. Skills assemble tools by referencing named tools from the Tool Registry, defining anonymous inline tools, and including other skills. Skills are registered via agents skill add --config <file>.

JSON Schema

The following is the formal JSON Schema definition for skill configuration files.


{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://cleveragents.dev/schemas/skill-config.json",
  "title": "CleverAgents Skill Configuration",
  "description": "Configuration file schema for defining CleverAgents skills — reusable, namespaced collections of tools.",
  "type": "object",
  "properties": {
    "name": {
      "type": "string",
      "pattern": "^[a-zA-Z0-9_-]+/[a-zA-Z0-9_-]+$",
      "description": "Fully qualified skill name in <namespace>/<name> format."
    },
    "description": {
      "type": "string",
      "description": "Human-readable description of what this skill provides."
    },
    "tools": {
      "type": "array",
      "description": "References to named tools from the Tool Registry.",
      "items": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "pattern": "^[a-zA-Z0-9_-]+/[a-zA-Z0-9_-]+$",
            "description": "Fully qualified name of a tool registered in the Tool Registry."
          },
          "description": {
            "type": "string",
            "description": "Override the tool's registered description within this skill context."
          },
          "writes": {
            "type": "boolean",
            "description": "Override the tool's writes capability flag."
          },
          "checkpointable": {
            "type": "boolean",
            "description": "Override the tool's checkpointable capability flag."
          }
        },
        "required": ["name"],
        "additionalProperties": false
      }
    },
    "inline_tools": {
      "type": "array",
      "description": "Anonymous tool definitions that exist only within this skill. Not registered in the Tool Registry.",
      "items": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "description": "Tool name, unique within this skill."
          },
          "description": {
            "type": "string",
            "description": "Human-readable description of the tool's purpose."
          },
          "source": {
            "type": "string",
            "const": "custom",
            "description": "Source type. Always 'custom' for inline tools."
          },
          "code": {
            "type": "string",
            "description": "Python code defining the tool's behavior. Must contain a run(input_data) function."
          },
          "input_schema": {
            "$ref": "https://json-schema.org/draft/2020-12/schema",
            "description": "JSON Schema describing the tool's input parameters."
          },
          "writes": {
            "type": "boolean",
            "default": false,
            "description": "Whether this tool performs write operations."
          },
          "checkpointable": {
            "type": "boolean",
            "default": false,
            "description": "Whether this tool supports checkpointing."
          },
          "side_effects": {
            "type": "array",
            "items": { "type": "string" },
            "default": [],
            "description": "Descriptions of side effects (e.g., 'network_call', 'schema_mutation')."
          }
        },
        "required": ["name", "source", "code"],
        "additionalProperties": false
      }
    },
    "includes": {
      "type": "array",
      "description": "Other skills whose tools are merged into this skill.",
      "items": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "pattern": "^[a-zA-Z0-9_-]+/[a-zA-Z0-9_-]+$",
            "description": "Fully qualified skill name to include."
          },
          "description": {
            "type": "string",
            "description": "Override the included skill's description."
          }
        },
        "required": ["name"],
        "additionalProperties": false
      }
    },
    "mcp_servers": {
      "type": "array",
      "description": "MCP server specifications for exposing remote tools.",
      "items": {
        "$ref": "#/$defs/mcpServer"
      }
    },
    "agent_skill_folders": {
      "type": "array",
      "description": "Agent Skills Standard folders to include.",
      "items": {
        "type": "object",
        "properties": {
          "path": {
            "type": "string",
            "description": "Path to the folder containing SKILL.md."
          },
          "name": {
            "type": "string",
            "description": "Override the skill bundle name."
          }
        },
        "required": ["path"],
        "additionalProperties": false
      }
    }
  },
  "required": ["name"],
  "additionalProperties": false,
  "$defs": {
    "mcpServer": {
      "type": "object",
      "description": "An MCP server specification.",
      "properties": {
        "name": {
          "type": "string",
          "description": "Identifier for the MCP server."
        },
        "transport": {
          "type": "string",
          "enum": ["stdio", "sse", "streamable-http"],
          "description": "Transport protocol."
        },
        "command": {
          "type": "string",
          "description": "Command to start the server (required for stdio transport)."
        },
        "args": {
          "type": "array",
          "items": { "type": "string" },
          "description": "Command-line arguments for the server command."
        },
        "env": {
          "type": "object",
          "additionalProperties": { "type": "string" },
          "description": "Environment variables to set when starting the server."
        },
        "url": {
          "type": "string",
          "format": "uri",
          "description": "Server URL (required for sse and streamable-http transports)."
        },
        "headers": {
          "type": "object",
          "additionalProperties": { "type": "string" },
          "description": "HTTP headers for remote server connections."
        },
        "tool_filter": {
          "type": "object",
          "properties": {
            "include": {
              "type": "array",
              "items": { "type": "string" },
              "description": "Whitelist of tool names to expose."
            },
            "exclude": {
              "type": "array",
              "items": { "type": "string" },
              "description": "Blacklist of tool names to hide."
            }
          },
          "additionalProperties": false
        }
      },
      "required": ["name", "transport"],
      "additionalProperties": false
    }
  }
}

Informal YAML Schema

The following annotated YAML provides an easier-to-read overview of the same schema defined formally above. It is not a validatable schema — it uses informal annotations to describe the structure. Refer to the JSON Schema above for the authoritative, machine-validatable definition.


# ─── Skill Metadata ─────────────────────────────────────────────────
name: <namespace>/<name>            # Fully qualified skill name (required)
description: <string>               # Human-readable description (optional)

# ─── Tool References ──────────────────────────────────────────────── # Named tools from the Tool Registry, referenced by fully-qualified name. tools:

  • name: <namespace>/<tool_name> # Reference to a registered tool (required) description: <string> # Override the tool's description (optional) writes: <boolean> # Override the tool's writes flag (optional) checkpointable: <boolean> # Override the tool's checkpointable flag (optional)

# ─── Inline (Anonymous) Tools ─────────────────────────────────────── # Tools defined directly within this skill. These are NOT registered # in the Tool Registry and cannot be reused outside this skill. inline_tools:

  • name: <string> # Tool name (unique within this skill, required) description: <string> # Tool description (optional) source: custom # Source type (required, always "custom" for inline) code: | # Inline Python code (required) def run(input_data): return {"result": "value"} input_schema: # JSON Schema for tool inputs (optional) type: object properties: param_name: type: string description: "Parameter description" required: ["param_name"] writes: false # Whether this tool writes (optional, default: false) checkpointable: false # Whether this tool supports checkpointing (optional, default: false) side_effects: [] # List of side effect descriptions (optional)

# ─── Included Skills ──────────────────────────────────────────────── # Other skills whose tools are merged into this skill. includes:

  • name: <namespace>/<skill_name> # Fully qualified skill name to include (required) description: <string> # Override the included skill's description (optional)

# ─── MCP Server Specifications ────────────────────────────────────── # MCP servers whose tools are exposed through this skill. mcp_servers:

  • name: <string> # Server name for identification (required) transport: stdio | sse | streamable-http # Transport protocol (required) command: <string> # Server command (required for stdio) args: # Command arguments (optional)
    • <string> env: # Environment variables for the server (optional) KEY: "value" url: <string> # Server URL (required for sse/streamable-http) headers: {} # HTTP headers (optional, for sse/streamable-http) tool_filter: # Filter which tools to expose (optional) include: # Include only these tools (optional)
      • <tool_name> exclude: # Exclude these tools (optional)
      • <tool_name>

# ─── Agent Skills Standard Folders ────────────────────────────────── # References to Agent Skills Standard (SKILL.md-based) tool bundles. agent_skill_folders:

  • path: <string> # Path to the folder containing SKILL.md (required) name: <string> # Override the skill bundle name (optional)

Structure Reference

Top-Level Fields

Field Type Required Description
name string Yes Fully qualified skill name in <namespace>/<name> format. This value is used as the skill's registered name.
description string No Human-readable description of what this skill provides.
tools list No References to named tools from the Tool Registry.
inline_tools list No Anonymous tool definitions that exist only within this skill.
includes list No Other skills to include (their tools are merged in).
mcp_servers list No MCP server specifications for exposing remote tools.
agent_skill_folders list No Agent Skills Standard folders to include.

Tool Reference Fields (tools[])

Field Type Required Description
name string Yes Fully qualified name of a tool registered in the Tool Registry.
description string No Override the tool's registered description within this skill context.
writes boolean No Override the tool's writes capability flag.
checkpointable boolean No Override the tool's checkpointable capability flag.

Inline Tool Fields (inline_tools[])

Field Type Required Description
name string Yes Tool name, unique within this skill.
description string No Human-readable description of the tool's purpose.
source string Yes Always custom for inline tools.
code string Yes Python code defining the tool's behavior. Must contain a run(input_data) function.
input_schema object No JSON Schema describing the tool's input parameters.
writes boolean No Whether this tool performs write operations. Default: false.
checkpointable boolean No Whether this tool supports checkpointing. Default: false.
side_effects list No Descriptions of any side effects (e.g., ["network_call"], ["schema_mutation"]).

MCP Server Fields (mcp_servers[])

Field Type Required Description
name string Yes Identifier for the MCP server.
transport string Yes Transport protocol: stdio, sse, or streamable-http.
command string Conditional Command to start the server (required for stdio transport).
args list No Command-line arguments for the server command.
env object No Environment variables to set when starting the server.
url string Conditional Server URL (required for sse and streamable-http transports).
headers object No HTTP headers for remote server connections.
tool_filter.include list No Whitelist of tool names to expose. When set, only these tools are available.
tool_filter.exclude list No Blacklist of tool names to hide. Applied after include.

Examples

Example 1: Minimal Read-Only Skill (Simple)

A skill that bundles a few built-in file operations:


# file-reader-skill.yaml
# Register: agents skill add --config file-reader-skill.yaml

name: local/file-reader description: "Basic file reading operations"

tools:

  • name: builtin/read_file
  • name: builtin/list_directory
  • name: builtin/search_files

Example 2: Git Operations Skill with MCP (Medium)

A skill that combines built-in git tools with GitHub tools via an MCP server:


# git-and-github-skill.yaml
# Register: agents skill add --config git-and-github-skill.yaml

name: local/git-github description: "Git operations and GitHub integration"

tools:

  • name: builtin/git_status
  • name: builtin/git_diff
  • name: builtin/git_log
  • name: builtin/git_blame

includes:

  • name: local/file-reader

mcp_servers:

  • name: github transport: stdio command: npx args:
    • "-y"
    • "@modelcontextprotocol/server-github" env: GITHUB_PERSONAL_ACCESS_TOKEN: "${GITHUB_TOKEN}" tool_filter: include:
      • create_issue
      • create_pull_request
      • list_repos
      • get_file_contents

This skill includes another skill (local/file-reader) and adds GitHub tools from an MCP server, filtered to only expose four specific tools.


Example 3: Full DevOps Toolkit (Complex)

A comprehensive skill with multiple includes, MCP servers, inline tools, and agent skill folders:


# devops-toolkit.yaml
# Register: agents skill add --config devops-toolkit.yaml

name: local/devops-toolkit description: "Full-stack development and operations toolkit"

tools:

  • name: builtin/shell_execute description: "Execute shell commands in the project sandbox"
  • name: local/validate-api-compat description: "Check API backward compatibility"

includes:

  • name: local/file-reader
  • name: local/git-github
  • name: local/docker-tools

inline_tools:

  • name: run_migrations description: "Run database migrations with rollback support" source: custom code: | import subprocess def run(input_data): direction = input_data.get("direction", "up") count = input_data.get("count", 1) result = subprocess.run( ["alembic", direction, str(count)], capture_output=True, text=True ) return { "success": result.returncode == 0, "stdout": result.stdout, "stderr": result.stderr } input_schema: type: object properties: direction: type: string enum: ["up", "down"] description: "Migration direction" count: type: integer default: 1 description: "Number of migrations to run" required: ["direction"] writes: true checkpointable: true side_effects: ["schema_mutation"]

  • name: health_check description: "Check service health endpoints" source: custom code: | import urllib.request def run(input_data): url = input_data.get("url", "http://localhost:8000/health") try: resp = urllib.request.urlopen(url, timeout=10) return {"status": resp.status, "healthy": resp.status == 200} except Exception as e: return {"status": 0, "healthy": False, "error": str(e)} input_schema: type: object properties: url: type: string description: "Health check URL" writes: false

mcp_servers:

  • name: linear transport: stdio command: npx args: ["-y", "@modelcontextprotocol/server-linear"] env: LINEAR_API_KEY: "${LINEAR_API_KEY}"

agent_skill_folders:

  • path: ./skills/deploy-to-staging name: deploy-staging
  • path: ./skills/code-review-bundle name: code-review

Example 4: Skill with Only Inline Tools (Simple)

A self-contained skill that defines everything inline, with no external dependencies:


# text-processing-skill.yaml
# Register: agents skill add --config text-processing-skill.yaml

name: local/text-processing description: "Simple text transformation utilities"

inline_tools:

  • name: word_count description: "Count words in text" source: custom code: | def run(input_data): text = input_data.get("text", "") return {"count": len(text.split())} input_schema: type: object properties: text: type: string description: "Text to count words in" required: ["text"] writes: false

  • name: to_uppercase description: "Convert text to uppercase" source: custom code: | def run(input_data): return {"result": input_data.get("text", "").upper()} input_schema: type: object properties: text: type: string required: ["text"] writes: false

  • name: extract_urls description: "Extract URLs from text" source: custom code: | import re def run(input_data): text = input_data.get("text", "") urls = re.findall(r'https?://[^\s<>"{}|\^`[]]+', text) return {"urls": urls, "count": len(urls)} writes: false


Action Configuration Files

!!! adr "Architecture Decision" The action configuration schema and action-to-plan lifecycle are defined in ADR-006: Plan Lifecycle.

Action configuration files define reusable plan templates — complete specifications for work that can be applied to projects. Actions are created via agents action create --config <file>.

JSON Schema

The following is the formal JSON Schema definition for action configuration files.


{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://cleveragents.dev/schemas/action-config.json",
  "title": "CleverAgents Action Configuration",
  "description": "Configuration file schema for defining CleverAgents actions — reusable plan templates that specify work to be applied to projects.",
  "type": "object",
  "properties": {
    "name": {
      "type": "string",
      "pattern": "^[a-zA-Z0-9_-]+/[a-zA-Z0-9_-]+$",
      "description": "Fully qualified action name in <namespace>/<name> format."
    },
    "description": {
      "type": "string",
      "description": "Short (one-line) description of the action."
    },
    "long_description": {
      "type": "string",
      "description": "Detailed multi-line description explaining purpose, usage, and expected outcomes."
    },
    "strategy_actor": {
      "type": "string",
      "pattern": "^[a-zA-Z0-9_-]+/[a-zA-Z0-9_-]+$",
      "description": "Actor to use during the Strategize phase. Must reference a registered actor."
    },
    "execution_actor": {
      "type": "string",
      "pattern": "^[a-zA-Z0-9_-]+/[a-zA-Z0-9_-]+$",
      "description": "Actor to use during the Execute phase. Must reference a registered actor."
    },
    "estimation_actor": {
      "type": "string",
      "pattern": "^[a-zA-Z0-9_-]+/[a-zA-Z0-9_-]+$",
      "description": "Actor for effort and cost estimation before execution."
    },
    "review_actor": {
      "type": "string",
      "pattern": "^[a-zA-Z0-9_-]+/[a-zA-Z0-9_-]+$",
      "description": "Actor for reviewing execution results."
    },
    "apply_actor": {
      "type": "string",
      "pattern": "^[a-zA-Z0-9_-]+/[a-zA-Z0-9_-]+$",
      "description": "Actor to use during the Apply phase."
    },
    "invariant_actor": {
      "type": "string",
      "pattern": "^[a-zA-Z0-9_-]+/[a-zA-Z0-9_-]+$",
      "description": "Actor for reconciling conflicting invariants across scopes."
    },
    "definition_of_done": {
      "type": "string",
      "description": "Clear, measurable criteria that define when the action's work is complete."
    },
    "reusable": {
      "type": "boolean",
      "default": true,
      "description": "Whether the action persists after being used."
    },
    "read_only": {
      "type": "boolean",
      "default": false,
      "description": "Whether the action is restricted to read-only operations."
    },
    "state": {
      "type": "string",
      "enum": ["available", "archived"],
      "default": "available",
      "description": "State of the action."
    },
    "arguments": {
      "type": "array",
      "description": "Typed parameters that users supply when using the action via agents plan use --arg name=value.",
      "items": {
        "$ref": "#/$defs/argument"
      }
    },
    "automation_profile": {
      "type": "string",
      "description": "Default automation profile for plans created from this action."
    },
    "invariants": {
      "type": "array",
      "items": { "type": "string" },
      "description": "Constraints carried forward as plan-level invariants when the action is used."
    }
  },
  "required": ["name", "description", "strategy_actor", "execution_actor", "definition_of_done"],
  "additionalProperties": false,
  "$defs": {
    "argument": {
      "type": "object",
      "description": "A typed parameter for the action.",
      "properties": {
        "name": {
          "type": "string",
          "description": "Argument name. Used as the key in --arg name=value."
        },
        "type": {
          "type": "string",
          "enum": ["string", "integer", "float", "boolean", "list"],
          "description": "Data type of the argument."
        },
        "required": {
          "type": "boolean",
          "default": false,
          "description": "Whether the argument must be provided."
        },
        "description": {
          "type": "string",
          "description": "Human-readable description shown in help text."
        },
        "default": {
          "description": "Default value when the argument is not provided. Type must match the 'type' field."
        },
        "validation_pattern": {
          "type": "string",
          "description": "Regex pattern for validating string arguments."
        },
        "min_value": {
          "type": "number",
          "description": "Minimum acceptable value for integer and float arguments."
        },
        "max_value": {
          "type": "number",
          "description": "Maximum acceptable value for integer and float arguments."
        }
      },
      "required": ["name", "type"],
      "additionalProperties": false
    }
  }
}

Informal YAML Schema

The following annotated YAML provides an easier-to-read overview of the same schema defined formally above. It is not a validatable schema — it uses informal annotations to describe the structure. Refer to the JSON Schema above for the authoritative, machine-validatable definition.


# ─── Action Identity ────────────────────────────────────────────────
name: <namespace>/<name>            # Fully qualified action name (required)
description: <string>               # Short description (required)
long_description: |                 # Detailed description (optional)
  Multi-line detailed explanation of what this action does,
  when to use it, and what outcomes to expect.

# ─── Lifecycle Actors ─────────────────────────────────────────────── strategy_actor: <namespace>/<name> # Actor for the Strategize phase (required) execution_actor: <namespace>/<name> # Actor for the Execute phase (required) estimation_actor: <namespace>/<name> # Actor for effort/cost estimation (optional) review_actor: <namespace>/<name> # Actor for reviewing results (optional) apply_actor: <namespace>/<name> # Actor for the Apply phase (optional) invariant_actor: <namespace>/<name> # Invariant Reconciliation Actor (optional)

# ─── Completion Criteria ──────────────────────────────────────────── definition_of_done: | # Criteria for when the action is complete (required) Clear, measurable criteria that define success. Multiple criteria can be listed.

# ─── Action Properties ────────────────────────────────────────────── reusable: true # Keep action after use (optional, default: true) read_only: false # Restrict to read-only operations (optional, default: false) state: available # State of the action: available or archived (optional, default: available) # ─── Arguments ────────────────────────────────────────────────────── # Arguments are typed parameters that must be supplied when using # the action via agents plan use --arg name=value. arguments:

  • name: <string> # Argument name (required) type: string | integer | float | boolean | list # Argument type (required) required: true # Whether the argument must be provided (optional, default: false) description: <string> # Human-readable description (optional) default: <value> # Default value when not provided (optional) validation_pattern: <regex> # Regex pattern for string validation (optional) min_value: <number> # Minimum value for numeric types (optional) max_value: <number> # Maximum value for numeric types (optional)

# ─── Automation ───────────────────────────────────────────────────── automation_profile: <string> # Default automation profile name (optional)

# ─── Invariants ───────────────────────────────────────────────────── # Invariants carried forward as plan-level invariants when this action is used. invariants:

  • <string> # Invariant text

Structure Reference

Top-Level Fields

Field Type Required Description
name string Yes Fully qualified action name in <namespace>/<name> format. This value is used as the action's registered name.
description string Yes Short (one-line) description of the action.
long_description string No Detailed multi-line description explaining purpose, usage, and expected outcomes.
strategy_actor string Yes Actor to use during the Strategize phase. Must reference a registered actor.
execution_actor string Yes Actor to use during the Execute phase. Must reference a registered actor.
estimation_actor string No Actor to use for effort and cost estimation before execution.
review_actor string No Actor to use for reviewing execution results.
apply_actor string No Actor to use during the Apply phase.
invariant_actor string No Actor for reconciling conflicting invariants across scopes. Carried forward to plans.
definition_of_done string Yes Clear, measurable criteria that define when the action's work is complete.
reusable boolean No Whether the action persists after being used. Default: true.
read_only boolean No Whether the action is restricted to read-only operations. Default: false.
state string No State of the action: available or archived. Default: available.
arguments list No Typed parameters that users supply when using the action.
automation_profile string No Default automation profile for plans created from this action.
invariants list No Constraints carried forward as plan-level invariants when the action is used.

Argument Fields (arguments[])

Field Type Required Description
name string Yes Argument name. Used as the key in --arg name=value.
type string Yes Data type: string, integer, float, boolean, list.
required boolean No Whether the argument must be provided. Default: false.
description string No Human-readable description shown in help text and agents action show.
default any No Default value when the argument is not provided. Type must match type.
validation_pattern string No Regex pattern for validating string arguments.
min_value number No Minimum acceptable value for integer and float arguments.
max_value number No Maximum acceptable value for integer and float arguments.

Examples

Example 1: Simple Linting Action (Simple)

A minimal action for running linters on a project:


# lint-check.yaml
# Create: agents action create --config lint-check.yaml

name: local/lint-check description: "Run linting checks on the project"

strategy_actor: local/strategist execution_actor: local/executor

definition_of_done: | All linting checks pass with zero errors.

reusable: true read_only: true state: available

Usage: agents plan use local/lint-check local/api-service


Example 2: Code Coverage Action with Arguments (Medium)

An action with typed arguments, invariants, and a specific automation profile:


# code-coverage.yaml
# Create: agents action create --config code-coverage.yaml

name: local/code-coverage description: "Increase test coverage to a target percentage" long_description: | Analyzes the current test coverage of a project, identifies modules with low coverage, and generates comprehensive test suites to meet the target coverage percentage.

The action prioritizes:

  1. Business-critical modules (auth, payments)
  2. Recently modified code
  3. Error-prone areas based on git history

strategy_actor: local/strategist execution_actor: local/executor estimation_actor: local/estimator

definition_of_done: | Test coverage reaches the target_coverage_percent threshold across all specified modules. All generated tests pass. No existing tests are broken by the changes.

reusable: true read_only: false arguments:

  • name: target_coverage_percent type: integer required: true description: "Target test coverage percentage (1-100)" min_value: 1 max_value: 100

  • name: test_command type: string required: false description: "Test framework command to use" default: "pytest --cov"

  • name: exclude_patterns type: list required: false description: "File patterns to exclude from coverage analysis" default: ["/migrations/", "**/conftest.py"]

  • name: focus_modules type: list required: false description: "Specific modules to prioritize for coverage"

automation_profile: trusted

invariants:

  • "Generated tests must not import production secrets or credentials"
  • "Test files must follow the project's existing test naming conventions"
  • "All database interactions in tests must use mocks or fixtures"

Usage: agents plan use local/code-coverage local/api-service --arg target_coverage_percent=85


Example 3: Security Audit Action (Complex)

A comprehensive security audit action with multiple actors, extensive arguments, and strong invariants:


# security-audit.yaml
# Create: agents action create --config security-audit.yaml

name: local/security-audit description: "Comprehensive security audit of a project" long_description: | Performs a thorough security audit covering:

  • Dependency vulnerability scanning (CVEs)
  • Static Application Security Testing (SAST)
  • Authentication and authorization review
  • Input validation and injection prevention
  • Secrets detection and credential scanning
  • API security (rate limiting, CORS, headers)
  • Data handling and privacy compliance

Generates a detailed report with severity ratings (Critical, High, Medium, Low, Informational) and remediation guidance. Optionally creates fix plans for identified issues.

strategy_actor: local/security-strategist execution_actor: local/security-scanner estimation_actor: local/estimator review_actor: local/security-reviewer invariant_actor: local/invariant-resolver

definition_of_done: | All critical and high severity findings have been identified. A complete security report has been generated with:

  • Severity classification for each finding
  • Remediation steps for each finding
  • Risk score for the overall project If auto_fix is enabled, all critical findings have remediation plans created as child plans.

reusable: true read_only: false state: available

arguments:

  • name: severity_threshold type: string required: false description: "Minimum severity to include in report" default: "low" validation_pattern: "^(critical|high|medium|low|informational)$"

  • name: auto_fix type: boolean required: false description: "Automatically create fix plans for critical findings" default: false

  • name: scan_dependencies type: boolean required: false description: "Include dependency vulnerability scanning" default: true

  • name: compliance_frameworks type: list required: false description: "Compliance frameworks to check against" default: ["owasp-top-10"]

  • name: max_findings type: integer required: false description: "Maximum number of findings to report" default: 100 min_value: 1 max_value: 1000

  • name: ignore_paths type: list required: false description: "Paths to exclude from scanning" default: ["/node_modules/", "/vendor/", "/.git/"]

automation_profile: supervised

invariants:

  • "Never modify production database schemas during audit"
  • "Never execute discovered exploit code against live systems"
  • "All findings must include reproducible steps"
  • "Secrets found during scanning must be redacted in reports"
  • "Remediation fixes must not break existing tests"

Usage: agents plan use local/security-audit local/api-service local/web-app --arg auto_fix=true --arg severity_threshold=medium


Example 4: Database Migration Action (Medium)

An action for managing database schema migrations:


# db-migrate.yaml
# Create: agents action create --config db-migrate.yaml

name: local/db-migrate description: "Plan and execute database schema migrations"

strategy_actor: local/db-strategist execution_actor: local/db-executor

definition_of_done: | All migration scripts are generated and pass dry-run validation. Rollback scripts are generated for each migration. The migration can be applied without data loss.

reusable: true read_only: false

arguments:

  • name: migration_tool type: string required: false description: "Migration tool to use" default: "alembic" validation_pattern: "^(alembic|flyway|django|knex)$"

  • name: dry_run type: boolean required: false description: "Only generate and validate, do not apply" default: true

  • name: target_schema type: string required: false description: "Target schema version identifier"

automation_profile: supervised

invariants:

  • "All migrations must include corresponding rollback scripts"
  • "Data migrations must preserve existing data integrity"
  • "Schema changes must maintain backward compatibility for 24 hours"

Tool Configuration Files

!!! adr "Architecture Decision" The tool configuration schema, source types, and capability metadata are defined in ADR-011: Tool System.

Tool configuration files define namespaced, independently registered, callable operations. Tools are the atomic unit of execution — each has a name, input/output schemas, capability metadata, and an implementation. Tools are registered via agents tool add --config <file> and can then be referenced by name in skills and actor graphs.

JSON Schema

The following is the formal JSON Schema definition for tool configuration files.


{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://cleveragents.dev/schemas/tool-config.json",
  "title": "CleverAgents Tool Configuration",
  "description": "Configuration file schema for defining CleverAgents tools — namespaced, independently registered, callable operations.",
  "type": "object",
  "properties": {
    "name": {
      "type": "string",
      "pattern": "^[a-zA-Z0-9_-]+/[a-zA-Z0-9_-]+$",
      "description": "Fully qualified tool name in <namespace>/<name> format."
    },
    "description": {
      "type": "string",
      "description": "Human-readable description of the tool's purpose and behavior."
    },
    "source": {
      "type": "string",
      "enum": ["custom", "mcp", "agent_skill", "builtin"],
      "description": "Tool source type. Determines which implementation fields are required."
    },
    "code": {
      "type": "string",
      "description": "Python code implementing the tool. Required when source is 'custom'. Must define a run(input_data) function."
    },
    "mcp_server": {
      "type": "string",
      "description": "Name of the MCP server exposing this tool. Required when source is 'mcp'."
    },
    "mcp_tool_name": {
      "type": "string",
      "description": "Name of the tool on the MCP server. Required when source is 'mcp'."
    },
    "agent_skill_path": {
      "type": "string",
      "description": "Path to the Agent Skills Standard folder containing SKILL.md. Required when source is 'agent_skill'."
    },
    "input_schema": {
      "$ref": "https://json-schema.org/draft/2020-12/schema",
      "description": "JSON Schema describing the tool's input parameters."
    },
    "output_schema": {
      "$ref": "https://json-schema.org/draft/2020-12/schema",
      "description": "JSON Schema describing the tool's output format."
    },
    "writes": {
      "type": "boolean",
      "default": false,
      "description": "Whether the tool performs any write operations."
    },
    "write_scope": {
      "type": "string",
      "description": "Scope of write operations (e.g., 'filesystem', 'database:migrations', 'api:github')."
    },
    "checkpointable": {
      "type": "boolean",
      "default": false,
      "description": "Whether the tool supports checkpointing — saving state before execution and restoring on failure."
    },
    "checkpoint_scope": {
      "type": "string",
      "enum": ["file", "transaction", "snapshot", "composite"],
      "description": "The checkpointing strategy."
    },
    "side_effects": {
      "type": "array",
      "items": { "type": "string" },
      "default": [],
      "description": "Side effects that cannot be undone by checkpointing alone (e.g., 'network_call', 'schema_mutation', 'email_sent')."
    },
    "idempotent": {
      "type": "boolean",
      "default": false,
      "description": "Whether the tool is safe to retry — calling it multiple times with the same input produces the same result."
    },
    "read_only": {
      "type": "boolean",
      "default": false,
      "description": "Whether the tool only reads data without modifying anything."
    },
    "unsafe": {
      "type": "boolean",
      "default": false,
      "description": "Whether the tool is flagged as unsafe. Requires allow_unsafe_tools: true in the automation profile."
    },
    "timeout": {
      "type": "integer",
      "default": 300,
      "minimum": 1,
      "description": "Default execution timeout in seconds."
    },
    "resource_slots": {
      "type": "array",
      "description": "Declared resource dependencies for this tool.",
      "items": {
        "$ref": "#/$defs/resourceSlot"
      }
    },
    "lifecycle": {
      "type": "object",
      "description": "Lifecycle hook implementations.",
      "properties": {
        "discover": {
          "type": "string",
          "description": "Python code or function name run during tool discovery."
        },
        "activate": {
          "type": "string",
          "description": "Python code or function name run when the tool is activated."
        },
        "deactivate": {
          "type": "string",
          "description": "Python code or function name run when the tool is deactivated."
        }
      },
      "additionalProperties": false
    },
  },
  "required": ["name", "description", "source"],
  "allOf": [
    {
      "if": { "properties": { "source": { "const": "custom" } } },
      "then": { "required": ["code"] }
    },
    {
      "if": { "properties": { "source": { "const": "mcp" } } },
      "then": { "required": ["mcp_server", "mcp_tool_name"] }
    },
    {
      "if": { "properties": { "source": { "const": "agent_skill" } } },
      "then": { "required": ["agent_skill_path"] }
    }
  ],
  "additionalProperties": false,
  "$defs": {
    "resourceSlot": {
      "type": "object",
      "description": "A declared resource dependency slot.",
      "properties": {
        "name": {
          "type": "string",
          "description": "Identifier for this resource slot."
        },
        "resource_type": {
          "type": "string",
          "description": "The resource type this slot requires (e.g., 'git-checkout', 'fs-directory')."
        },
        "access": {
          "type": "string",
          "enum": ["read_only", "read_write"],
          "description": "Access level needed."
        },
        "description": {
          "type": "string",
          "description": "Human-readable description of how the tool uses this resource."
        },
        "binding": {
          "type": "string",
          "enum": ["contextual", "static", "parameter"],
          "default": "contextual",
          "description": "How the slot is resolved at runtime."
        },
        "static_resource": {
          "type": "string",
          "pattern": "^[a-zA-Z0-9_-]+/[a-zA-Z0-9_-]+$",
          "description": "Fully qualified name of a specific resource. Required when binding is 'static'."
        }
      },
      "required": ["name", "resource_type", "access"],
      "if": { "properties": { "binding": { "const": "static" } } },
      "then": { "required": ["name", "resource_type", "access", "static_resource"] },
      "additionalProperties": false
    }
  }
}

Informal YAML Schema

The following annotated YAML provides an easier-to-read overview of the same schema defined formally above. It is not a validatable schema — it uses informal annotations to describe the structure. Refer to the JSON Schema above for the authoritative, machine-validatable definition.


# ─── Tool Identity ──────────────────────────────────────────────────
name: <namespace>/<name>            # Fully qualified tool name (required)
description: <string>               # Human-readable description (required)

# ─── Source and Implementation ────────────────────────────────────── source: custom | mcp | agent_skill | builtin # Tool source type (required)

# For source: custom — inline Python implementation code: | # Python code with a run(input_data) function (required for custom) def run(input_data): param = input_data.get("param_name", "default") # ... tool logic ... return {"result": "value"}

# For source: mcp — tool exposed by an MCP server mcp_server: <string> # MCP server name (required for mcp) mcp_tool_name: <string> # Tool name on the MCP server (required for mcp)

# For source: agent_skill — tool from an Agent Skills Standard folder agent_skill_path: <string> # Path to the SKILL.md folder (required for agent_skill)

# ─── Input/Output Schema ──────────────────────────────────────────── input_schema: # JSON Schema for tool inputs (optional but recommended) type: object properties: param_name: type: string description: "Parameter description" enum: ["value1", "value2"] # Enumerated valid values (optional) default: "value1" # Default value (optional) numeric_param: type: integer description: "A numeric parameter" minimum: 0 # Minimum value (optional) maximum: 100 # Maximum value (optional) required: ["param_name"] # Required parameters

output_schema: # JSON Schema for tool outputs (optional) type: object properties: result: type: string success: type: boolean

# ─── Capability Metadata ──────────────────────────────────────────── writes: false # Whether the tool performs write operations (optional, default: false) write_scope: <string> # Scope of writes, e.g. "filesystem", "database:migrations" (optional) checkpointable: false # Whether the tool supports checkpointing (optional, default: false) checkpoint_scope: <string> # Checkpointing strategy: "file", "transaction", "snapshot", "composite" (optional) side_effects: # List of side effect types (optional)

  • <string> # e.g. "network_call", "schema_mutation", "process_spawn" idempotent: false # Whether the tool is safe to retry (optional, default: false) read_only: false # Whether the tool only reads (optional, default: false) unsafe: false # Whether the tool is flagged as unsafe (optional, default: false) timeout: 300 # Default execution timeout in seconds (optional, default: 300)

# ─── Resource Bindings ────────────────────────────────────────────── # Declare which resources this tool operates on. resource_slots:

  • name: <string> # Slot name for reference (required) resource_type: <string> # Required resource type, e.g. "git-checkout", "fs-directory" (required) access: read_only | read_write # Access level needed (required) description: <string> # Description of how the resource is used (optional) binding: contextual | static | parameter # How the slot is resolved (optional, default: contextual) static_resource: <namespace>/<name> # Specific resource (required when binding is static)

# ─── Lifecycle Hooks ──────────────────────────────────────────────── lifecycle: discover: <string> # Python function or code for tool discovery (optional) activate: <string> # Python function or code run when tool is activated (optional) deactivate: <string> # Python function or code run when tool is deactivated (optional)

Structure Reference

Top-Level Fields

Field Type Required Description
name string Yes Fully qualified tool name in <namespace>/<name> format. This value is used as the tool's registered name.
description string Yes Human-readable description of the tool's purpose and behavior.
source string Yes Tool source type. Determines which implementation fields are required: custom (inline Python), mcp (MCP server), agent_skill (SKILL.md folder), builtin (first-party operations).
code string Conditional Python code implementing the tool. Required when source is custom. Must define a run(input_data) function that accepts a dict and returns a dict.
mcp_server string Conditional Name of the MCP server exposing this tool. Required when source is mcp.
mcp_tool_name string Conditional Name of the tool on the MCP server. Required when source is mcp.
agent_skill_path string Conditional Path to the Agent Skills Standard folder containing SKILL.md. Required when source is agent_skill.
input_schema object No JSON Schema describing the tool's input parameters. Strongly recommended for all tools.
output_schema object No JSON Schema describing the tool's output format.
Capability Metadata Fields
Field Type Default Description
writes boolean false Whether the tool performs any write operations (file writes, database mutations, API calls with side effects).
write_scope string Describes the scope of write operations, e.g. "filesystem", "database:migrations", "api:github". Used for access checks and audit logging.
checkpointable boolean false Whether the tool supports checkpointing — the ability to save state before execution and restore it on failure.
checkpoint_scope string The checkpointing strategy: file (file-level backup), transaction (database transaction), snapshot (full state snapshot), composite (multi-resource checkpoint).
side_effects list [] Descriptions of side effects that cannot be undone by checkpointing alone, e.g. "network_call", "schema_mutation", "email_sent", "process_spawn".
idempotent boolean false Whether the tool is safe to retry — calling it multiple times with the same input produces the same result.
read_only boolean false Whether the tool only reads data without modifying anything. When true, writes must be false.
unsafe boolean false Whether the tool is flagged as unsafe. Unsafe tools require allow_unsafe_tools: true in the automation profile.
timeout integer 300 Default execution timeout in seconds. Can be overridden per invocation.

Resource Slot Fields (resource_slots[])

Field Type Required Description
name string Yes Identifier for this resource slot, used for reference in code and logs.
resource_type string Yes The resource type this slot requires, e.g. git-checkout, fs-directory, fs-file.
access string Yes Access level: read_only or read_write.
description string No Human-readable description of how the tool uses this resource.
binding string No How the slot is resolved at runtime. contextual (default): resolved from the plan's project. static: hardcoded to a specific resource. parameter: passed as a tool argument at invocation time.
static_resource string Conditional Fully qualified name of a specific resource. Required when binding is static.

Lifecycle Hook Fields (lifecycle)

Field Type Required Description
discover string No Code or function name run during tool discovery to check prerequisites and report capabilities.
activate string No Code or function name run when the tool is activated (e.g., establish connections, validate credentials).
deactivate string No Code or function name run when the tool is deactivated (e.g., close connections, release resources).

Examples

Example 1: Simple Read-Only Tool (Simple)

A tool that reads a file and returns its contents:


# read-config-file.yaml
# Register: agents tool add --config read-config-file.yaml

name: local/read-config description: "Read and parse a configuration file (JSON, YAML, or TOML)"

source: custom code: | import json, os def run(input_data): path = input_data["path"] if not os.path.exists(path): return {"error": f"File not found: {path}", "success": False} with open(path, "r") as f: content = f.read() ext = os.path.splitext(path)[1].lower() if ext == ".json": parsed = json.loads(content) elif ext in (".yaml", ".yml"): import yaml parsed = yaml.safe_load(content) else: parsed = None return {"content": content, "parsed": parsed, "path": path, "success": True}

input_schema: type: object properties: path: type: string description: "Path to the configuration file" required: ["path"]

output_schema: type: object properties: content: type: string parsed: type: object success: type: boolean

writes: false read_only: true idempotent: true


Example 2: Database Migration Tool (Medium)

A tool that runs database migrations with rollback support:


# run-migrations.yaml
# Register: agents tool add --config run-migrations.yaml

name: local/run-migrations description: "Run database migrations with direction control and rollback support"

source: custom code: | import subprocess def run(input_data): direction = input_data.get("direction", "up") count = input_data.get("count", 1) dry_run = input_data.get("dry_run", False)

  cmd = [&quot;alembic&quot;]
  if dry_run:
      cmd.append(&quot;<span style="color: cyan;">--sql</span>&quot;)
  cmd.extend([direction, str(count)])

  result = subprocess.run(cmd, capture_output=True, text=True, timeout=120)
  return {
      <span style="color: cyan; font-weight: 600;">&quot;success&quot;</span>: result.returncode == 0,
      <span style="color: cyan; font-weight: 600;">&quot;stdout&quot;</span>: result.stdout,
      <span style="color: cyan; font-weight: 600;">&quot;stderr&quot;</span>: result.stderr,
      <span style="color: cyan; font-weight: 600;">&quot;direction&quot;</span>: direction,
      <span style="color: cyan; font-weight: 600;">&quot;count&quot;</span>: count,
      <span style="color: cyan; font-weight: 600;">&quot;dry_run&quot;</span>: dry_run,
      <span style="color: cyan; font-weight: 600;">&quot;return_code&quot;</span>: result.returncode
  }

input_schema: type: object properties: direction: type: string enum: ["up", "down"] description: "Migration direction: 'up' to apply, 'down' to rollback" count: type: integer default: 1 minimum: 1 maximum: 50 description: "Number of migrations to run" dry_run: type: boolean default: false description: "Generate SQL without executing" required: ["direction"]

writes: true write_scope: "database:migrations" checkpointable: true checkpoint_scope: "transaction" side_effects:

  • "schema_mutation" idempotent: false timeout: 120

resource_slots:

  • name: database resource_type: "local/database" access: read_write description: "The database to run migrations against" binding: contextual

Example 3: MCP-Backed GitHub Tool (Simple)

A tool that delegates to a GitHub MCP server:


# github-create-issue.yaml
# Register: agents tool add --config github-create-issue.yaml

name: local/github-create-issue description: "Create a GitHub issue via the GitHub MCP server"

source: mcp mcp_server: github mcp_tool_name: create_issue

input_schema: type: object properties: owner: type: string description: "Repository owner" repo: type: string description: "Repository name" title: type: string description: "Issue title" body: type: string description: "Issue body (Markdown)" labels: type: array items: type: string description: "Labels to apply" required: ["owner", "repo", "title"]

writes: true write_scope: "api:github" checkpointable: false side_effects:

  • "network_call" idempotent: false

Example 4: Deployment Tool with Resource Bindings (Complex)

A comprehensive deployment tool with multiple resource slots, lifecycle hooks, and safety metadata:


# deploy-staging.yaml
# Register: agents tool add --config deploy-staging.yaml

name: local/deploy-staging description: "Deploy the current build to the staging environment with health checks"

source: custom code: | import subprocess, time, urllib.request, json

def run(input_data): service = input_data["service"] version = input_data.get("version", "latest") wait_healthy = input_data.get("wait_healthy", True) health_timeout = input_data.get("health_timeout", 120)

# Build the deployment command cmd = ["kubectl", "set", "image", f"deployment/{service}", f"{service}={service}:{version}", "--namespace=staging"] result = subprocess.run(cmd, capture_output=True, text=True, timeout=60) if result.returncode != 0: return {"success": False, "error": result.stderr, "phase": "deploy"}

# Wait for rollout rollout = subprocess.run( ["kubectl", "rollout", "status", f"deployment/{service}", "--namespace=staging", f"--timeout={health_timeout}s"], capture_output=True, text=True, timeout=health_timeout + 10 ) if rollout.returncode != 0: return {"success": False, "error": rollout.stderr, "phase": "rollout"}

# Health check if wait_healthy: health_url = f"http://{service}.staging.svc.cluster.local/health" start = time.time() while time.time() - start < health_timeout: try: resp = urllib.request.urlopen(health_url, timeout=5) if resp.status == 200: body = json.loads(resp.read()) if body.get("status") == "healthy": return { "success": True, "service": service, "version": version, "health": body } except Exception: pass time.sleep(5) return {"success": False, "error": "Health check timeout", "phase": "health"}

  return {&quot;success&quot;: True, &quot;service&quot;: service, &quot;version&quot;: version}

input_schema: type: object properties: service: type: string description: "Name of the service to deploy" version: type: string default: "latest" description: "Docker image version tag" wait_healthy: type: boolean default: true description: "Wait for health check to pass" health_timeout: type: integer default: 120 minimum: 10 maximum: 600 description: "Health check timeout in seconds" required: ["service"]

output_schema: type: object properties: success: type: boolean service: type: string version: type: string error: type: string phase: type: string

writes: true write_scope: "infrastructure:kubernetes" checkpointable: true checkpoint_scope: "composite" side_effects:

  • "network_call"
  • "process_spawn"
  • "infrastructure_mutation" idempotent: false unsafe: true timeout: 300

resource_slots:

  • name: repo resource_type: git-checkout access: read_only description: "Source repository for build artifacts" binding: contextual
  • name: cluster_config resource_type: fs-file access: read_only description: "Kubernetes cluster configuration (kubeconfig)" binding: static static_resource: local/staging-kubeconfig

lifecycle: activate: | def activate(context): import subprocess result = subprocess.run(["kubectl", "cluster-info"], capture_output=True, text=True) if result.returncode != 0: raise RuntimeError("Cannot connect to Kubernetes cluster") return {"cluster": "connected"} deactivate: | def deactivate(context): pass # No cleanup needed


Example 5: API Compatibility Checker (Medium)

A tool that validates API backward compatibility:


# validate-api-compat.yaml
# Register: agents tool add --config validate-api-compat.yaml

name: local/validate-api-compat description: "Validate that API changes maintain backward compatibility with existing clients"

source: custom code: | import subprocess, json

def run(input_data): spec_path = input_data.get("spec_path", "openapi.yaml") base_branch = input_data.get("base_branch", "main")

# Get the base spec from the main branch base_result = subprocess.run( ["git", "show", f"{base_branch}:{spec_path}"], capture_output=True, text=True ) if base_result.returncode != 0: return {"compatible": True, "reason": "No base spec found (new API)", "changes": []}

# Run OpenAPI diff diff_result = subprocess.run( ["oasdiff", "breaking", "--format", "json", "--base", "/dev/stdin", "--revision", spec_path], input=base_result.stdout, capture_output=True, text=True )

  if diff_result.returncode == 0:
      return {&quot;compatible&quot;: True, &quot;changes&quot;: [], &quot;reason&quot;: &quot;No breaking changes&quot;}

  <span style="color: cyan; font-weight: 600;">try</span>:
      breaking = json.loads(diff_result.stdout)
  except json.JSONDecodeError:
      breaking = [{&quot;description&quot;: diff_result.stdout}]

  return {
      <span style="color: cyan; font-weight: 600;">&quot;compatible&quot;</span>: False,
      <span style="color: cyan; font-weight: 600;">&quot;changes&quot;</span>: breaking,
      <span style="color: cyan; font-weight: 600;">&quot;reason&quot;</span>: f&quot;Found {len(breaking)} breaking change(s)&quot;
  }

input_schema: type: object properties: spec_path: type: string default: "openapi.yaml" description: "Path to the OpenAPI specification file" base_branch: type: string default: "main" description: "Branch to compare against for backward compatibility" required: []

writes: false read_only: true checkpointable: false idempotent: true timeout: 60

resource_slots:

  • name: repo resource_type: git-checkout access: read_only description: "Git repository containing the API spec" binding: contextual

Validation Configuration Files

!!! adr "Architecture Decision" The validation configuration schema and read-only enforcement are defined in ADR-013: Validation Abstraction.

Validation configuration files define Validations — a specialized subtype of Tool. A Validation YAML file uses the same format as a Tool YAML file, with an additional validation block containing the validation-specific metadata. Because Validation extends Tool, all tool fields (name, description, source, code, input_schema, output_schema, capability metadata, resource bindings, lifecycle hooks) are available and behave identically. Validations are registered via agents validation add --config <file>.

Read-only enforcement: Validations are always read-only. The writes and checkpointable fields are always forced to false regardless of what the YAML file specifies — if a validation YAML includes writes: true or checkpointable: true, these values are silently overridden to false at registration time. Because this is enforced, it is idiomatic to omit read_only, writes, and checkpointable from validation YAML files entirely (they are unnecessary). The examples below include read_only: true for documentation clarity, but it is not required.

Shared namespace: The name field in a validation YAML occupies the same namespace as tools. Registration will fail if a tool with the same name already exists.

Validation-Specific Fields

In addition to all fields from the Tool Configuration schema, a Validation YAML adds:

Field Type Default Description
validation.mode string "required" "required" or "informational". Required validations must pass for execution to proceed; informational validations report results without blocking.
wraps string (none) Name of an existing registered Tool to wrap. When set, the Validation delegates execution to the wrapped Tool and passes its output through the transform function. Mutually exclusive with source and code.
transform string (none) Python function that converts the wrapped Tool's output to the Validation return format. Required when wraps is set (unless the wrapped Tool already returns validation-format output). The function receives the Tool's output as its argument and must return { "passed": bool, ... }.
argument_mapping object (none) Dictionary mapping the wrapped Tool's input parameter names to either a Validation input parameter name (string) or a fixed literal value. When present, only mapped arguments are forwarded to the wrapped Tool. When absent, the Validation's input arguments are passed through unchanged. Only valid when wraps is set.

When wraps is used, the source and code fields must be omitted — the Validation's implementation is the wrapped Tool plus the transform. The Validation inherits the wrapped Tool's input_schema, resource_slots, and timeout unless explicitly overridden. When the Validation defines a custom input_schema, an argument_mapping should be provided to specify how the Validation's inputs map to the wrapped Tool's expected inputs. See the Tool Wrapping subsection under Core Concepts > Validation for full semantics.

The output of a validation tool must conform to a structured JSON format:

Field Type Required Description
passed boolean Yes Whether the validation passed (true) or failed (false).
message string No Human-readable summary of the validation result.
data object No Arbitrary structured data in any format the validation chooses to convey informational output about the result.

Examples

Example 1: Unit Test Validation (Required)


# validations/run-tests.yaml
# Register: agents validation add --config validations/run-tests.yaml

name: local/run-tests description: "Run unit tests with coverage and report pass/fail"

source: custom code: | import subprocess, json def run(input_data): threshold = input_data.get("coverage_threshold", 80) result = subprocess.run( ["pytest", "--cov=src", f"--cov-fail-under={threshold}", "--tb=short", "-q"], capture_output=True, text=True ) passed = result.returncode == 0 return { "passed": passed, "message": "All tests passed" if passed else f"Tests failed (exit code {result.returncode})", "data": { "stdout": result.stdout, "stderr": result.stderr, "returncode": result.returncode, "coverage_threshold": threshold } }

validation: mode: required

input_schema: type: object properties: coverage_threshold: type: integer default: 80 description: "Minimum coverage percentage required"

read_only: true idempotent: true timeout: 600

resource_slots:

  • name: repo resource_type: git-checkout access: read_only binding: contextual

Example 2: Lint Check Validation (Required, Simple)


# validations/lint-check.yaml

name: local/lint-check description: "Run linter and report pass/fail"

source: custom code: | import subprocess def run(input_data): result = subprocess.run(["ruff", "check", "."], capture_output=True, text=True) passed = result.returncode == 0 return { "passed": passed, "message": "Lint clean" if passed else f"Lint errors found", "data": {"stdout": result.stdout, "stderr": result.stderr} }

validation: mode: required

read_only: true idempotent: true timeout: 300


Example 3: Bundle Size Check (Informational)


# validations/check-bundle-size.yaml

name: local/check-bundle-size description: "Check bundle size (advisory — does not block execution)"

source: custom code: | import subprocess, json def run(input_data): result = subprocess.run(["node", "scripts/check-bundle-size.js"], capture_output=True, text=True) try: size_data = json.loads(result.stdout) except json.JSONDecodeError: size_data = {"raw_output": result.stdout} passed = result.returncode == 0 return { "passed": passed, "message": "Bundle size within limits" if passed else "Bundle size exceeds advisory threshold", "data": size_data }

validation: mode: informational

read_only: true timeout: 120


Example 4: MCP-Backed Validation

A validation that uses an MCP server to perform security scanning:


# validations/security-scan.yaml

name: local/security-scan description: "Run security vulnerability scan via MCP"

source: mcp mcp_server: "npx @security/mcp-scanner" mcp_tool_name: scan_vulnerabilities

validation: mode: required

read_only: true timeout: 300


Example 5: Wrapping an Existing Tool

A validation that wraps an existing local/run-tests tool, reusing its implementation and interpreting its output as pass/fail. No test-running logic is duplicated — only the pass/fail interpretation is defined.


# validations/tests-pass.yaml
# Assumes local/run-tests is already registered as a Tool that runs
# the test suite and returns {returncode, tests_run, tests_passed, ...}

name: local/tests-pass description: "Validate that all unit tests pass (wraps local/run-tests)"

wraps: local/run-tests

transform: | def transform(tool_output): passed = tool_output.get("returncode") == 0 tests_run = tool_output.get("tests_run", 0) tests_passed = tool_output.get("tests_passed", 0) return { "passed": passed, "message": f"{tests_passed}/{tests_run} tests passed" if passed else f"{tests_run - tests_passed} tests failed", "data": tool_output }

validation: mode: required

timeout: 600

Note: source, code, input_schema, and resource_slots are omitted — they are inherited from the wrapped local/run-tests tool. Only the transform, validation.mode, and optional overrides (like timeout) are needed. If the Validation defines a custom input_schema, an argument_mapping should also be provided to map the Validation's inputs to the wrapped Tool's expected inputs.


Example 6: Wrapping with Different Interpretation (Informational)

A second validation wrapping the same tool but extracting a different signal — coverage threshold — as an advisory check:


# validations/coverage-check.yaml

name: local/coverage-check description: "Check test coverage exceeds threshold (advisory)"

wraps: local/run-tests

transform: | def transform(tool_output): coverage = tool_output.get("coverage_percent", 0) threshold = 80 return { "passed": coverage >= threshold, "message": f"Coverage: {coverage:.1f}% (threshold: {threshold}%)", "data": { "coverage_percent": coverage, "threshold": threshold, "above_threshold": coverage >= threshold } }

validation: mode: informational


Resource Type Configuration Files

!!! adr "Architecture Decision" The resource type system, custom type registration, and type hierarchy are defined in ADR-008: Resource System.

Resource type configuration files define custom resource type schemas that extend the built-in resource types (git-checkout, git, fs-mount, fs-directory, etc.). A resource type defines what CLI arguments agents resource add <type> accepts, whether instances are physical or virtual, allowed child/parent types, auto-discovery behavior, sandbox strategy, and handler implementation. Custom types are registered via agents resource type add --config <file>.

JSON Schema

The following is the formal JSON Schema definition for resource type configuration files.


{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://cleveragents.dev/schemas/resource-type-config.json",
  "title": "CleverAgents Resource Type Configuration",
  "description": "Configuration file schema for defining custom resource types that extend the built-in resource types.",
  "type": "object",
  "properties": {
    "name": {
      "type": "string",
      "pattern": "^[a-zA-Z0-9_-]+/[a-zA-Z0-9_-]+$",
      "description": "Fully qualified resource type name in <namespace>/<name> format."
    },
    "description": {
      "type": "string",
      "description": "Human-readable description of what this resource type represents."
    },
    "physical": {
      "type": "boolean",
      "description": "true for physical types (concrete manifestations), false for virtual types (abstract identity linking equivalent physical resources)."
    },
    "user_addable": {
      "type": "boolean",
      "default": true,
      "description": "Whether users can create instances directly via 'agents resource add <type>'."
    },
    "cli_args": {
      "type": "array",
      "description": "Arguments accepted by 'agents resource add <type>'.",
      "items": {
        "$ref": "#/$defs/cliArg"
      }
    },
    "child_types": {
      "type": "array",
      "description": "Resource types that can be children of this type.",
      "items": {
        "$ref": "#/$defs/childType"
      }
    },
    "parent_types": {
      "type": "array",
      "description": "Resource types that can be parents of this type. If omitted, the resource can be top-level.",
      "items": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "description": "Parent resource type name."
          },
          "description": {
            "type": "string",
            "description": "Description of the parent-child relationship from the child's perspective."
          }
        },
        "required": ["type"],
        "additionalProperties": false
      }
    },
    "sandbox_strategy": {
      "type": "string",
      "enum": ["git_worktree", "copy_on_write", "transaction_rollback", "snapshot", "none"],
      "description": "Sandbox strategy for instances of this type."
    },
    "handler": {
      "type": "object",
      "description": "The Python handler class that implements resource operations.",
      "properties": {
        "class": {
          "type": "string",
          "description": "Python class name that implements the resource handler interface."
        },
        "module": {
          "type": "string",
          "description": "Python module path where the handler class is defined."
        },
        "config": {
          "type": "object",
          "description": "Arbitrary configuration passed to the handler constructor.",
          "additionalProperties": true
        }
      },
      "required": ["class", "module"],
      "additionalProperties": false
    },
    "auto_discovery": {
      "type": "object",
      "description": "Controls automatic child resource discovery when a resource of this type is created.",
      "properties": {
        "enabled": {
          "type": "boolean",
          "default": true,
          "description": "Whether auto-discovery runs when a resource of this type is created."
        },
        "scan_depth": {
          "type": "integer",
          "default": 3,
          "minimum": 1,
          "description": "Maximum recursion depth for scanning."
        },
        "include_patterns": {
          "type": "array",
          "items": { "type": "string" },
          "description": "Glob patterns for resources to discover."
        },
        "exclude_patterns": {
          "type": "array",
          "items": { "type": "string" },
          "description": "Glob patterns for resources to skip during discovery."
        }
      },
      "additionalProperties": false
    },
    "equivalence": {
      "type": "object",
      "description": "Equivalence criteria for virtual types. Only applicable when physical is false.",
      "properties": {
        "criteria": {
          "type": "array",
          "items": { "type": "string" },
          "description": "Fields used to determine equivalence (e.g., 'content_hash', 'filename', 'permissions', 'url')."
        },
        "description": {
          "type": "string",
          "description": "Human-readable description of the equivalence rule."
        }
      },
      "required": ["criteria"],
      "additionalProperties": false
    },
  },
  "required": ["name", "description", "physical", "sandbox_strategy", "handler"],
  "if": {
    "properties": { "physical": { "const": false } }
  },
  "then": {
    "required": ["name", "description", "physical", "sandbox_strategy", "handler", "equivalence"]
  },
  "additionalProperties": false,
  "$defs": {
    "cliArg": {
      "type": "object",
      "description": "A CLI argument definition for 'agents resource add <type>'.",
      "properties": {
        "name": {
          "type": "string",
          "description": "Argument name. Becomes --<name> on the CLI."
        },
        "type": {
          "type": "string",
          "enum": ["string", "path", "integer", "boolean", "url"],
          "description": "Argument type. 'path' validates that the path exists; 'url' validates URL format."
        },
        "required": {
          "type": "boolean",
          "default": false,
          "description": "Whether the argument must be provided."
        },
        "description": {
          "type": "string",
          "description": "Description shown in --help."
        },
        "default": {
          "description": "Default value when the argument is not provided."
        },
        "validation_pattern": {
          "type": "string",
          "description": "Regex pattern for validating string and url type arguments."
        }
      },
      "required": ["name", "type"],
      "additionalProperties": false
    },
    "childType": {
      "type": "object",
      "description": "An allowed child resource type relationship.",
      "properties": {
        "type": {
          "type": "string",
          "description": "Child resource type name."
        },
        "auto_discover": {
          "type": "boolean",
          "default": false,
          "description": "Whether children of this type are automatically created when the parent resource is registered."
        },
        "manual_link": {
          "type": "boolean",
          "default": true,
          "description": "Whether manual 'agents resource link-child' is allowed for this relationship."
        },
        "description": {
          "type": "string",
          "description": "Description of the parent-child relationship."
        },
        "max_count": {
          "type": ["integer", "null"],
          "minimum": 1,
          "description": "Maximum number of children of this type. null or omitted means unlimited."
        }
      },
      "required": ["type"],
      "additionalProperties": false
    }
  }
}

Informal YAML Schema

The following annotated YAML provides an easier-to-read overview of the same schema defined formally above. It is not a validatable schema — it uses informal annotations to describe the structure. Refer to the JSON Schema above for the authoritative, machine-validatable definition.


# ─── Resource Type Identity ─────────────────────────────────────────
name: <namespace>/<name>            # Fully qualified resource type name (required)
description: <string>               # Human-readable description (required)

# ─── Type Classification ──────────────────────────────────────────── physical: true # Whether instances are physical or virtual (required) # Physical: a specific, concrete manifestation (this file at this path) # Virtual: an abstract identity linking equivalent physical resources user_addable: true # Whether users can create instances directly (optional, default: true)

# ─── Type Inheritance ─────────────────────────────────────────────── inherits: <parent-type-name> # Parent resource type to inherit from (optional) # Subtypes inherit all fields from the parent type. # Only fields that differ from or extend the parent need to be declared. # See ADR-042 for full inheritance semantics.

# ─── CLI Arguments ────────────────────────────────────────────────── # Define the arguments accepted by agents resource add &lt;type&gt;. cli_args:

  • name: <string> # Argument name (becomes --<name> on CLI) (required) type: string | path | integer | boolean | url # Argument type (required) required: true # Whether the argument is required (optional, default: false) description: <string> # Description shown in help text (optional) default: <value> # Default value (optional) validation_pattern: <regex> # Regex validation for string/url types (optional)

# ─── Parent/Child Type Relationships ──────────────────────────────── # Define which resource types can be children of this type. child_types:

  • type: <string> # Child resource type name (required) auto_discover: true # Automatically create children when parent is created (optional, default: false) manual_link: true # Allow manual parent-child linking (optional, default: true) description: <string> # Description of the relationship (optional) max_count: <integer> # Maximum number of children of this type (optional, null = unlimited)

# Define which resource types can be parents of this type. parent_types:

  • type: <string> # Parent resource type name (required) description: <string> # Description of the relationship (optional) # If parent_types is omitted, the resource can be top-level (no parent required).

# ─── Sandbox Strategy ────────────────────────────────────────────── sandbox_strategy: <string> # Sandbox strategy for instances of this type (required) # Built-in strategies: "git_worktree", "copy_on_write", # "transaction_rollback", "snapshot", "none"

# ─── Handler Implementation ──────────────────────────────────────── handler: class: <string> # Python class implementing the resource handler (required) module: <string> # Python module path (required) config: {} # Handler-specific configuration (optional)

# ─── Auto-Discovery Configuration ────────────────────────────────── auto_discovery: enabled: true # Whether child auto-discovery runs on creation (optional, default: true) scan_depth: <integer> # Max depth for recursive scanning (optional, default: 3) include_patterns: # Glob patterns for resources to auto-discover (optional) - <string> exclude_patterns: # Glob patterns to exclude from auto-discovery (optional) - <string>

# ─── Virtual Type Configuration ───────────────────────────────────── # Only applicable when physical: false (virtual types). equivalence: criteria: # Fields used to determine equivalence (required for virtual) - <string> # e.g. "content_hash", "filename", "permissions", "url" description: <string> # Human-readable description of the equivalence rule (optional)

Structure Reference

Top-Level Fields

Field Type Required Description
name string Yes Fully qualified resource type name in <namespace>/<name> format. This value is used as the resource type's registered name.
description string Yes Human-readable description of what this resource type represents.
physical boolean Yes true for physical types (concrete manifestations), false for virtual types (abstract identity linking equivalent physical resources).
user_addable boolean No Whether users can create instances directly via agents resource add <type>. Default: true. When false, instances are only created via auto-discovery as children of other resources.
inherits string No Parent resource type name to inherit from. Subtypes inherit all properties, capabilities, child types, sandbox strategy, and handler behavior from the parent. Only fields that differ from or extend the parent need to be declared. Collection fields (cli_args, child_types, parent_types) use additive merging by default; use <field>_replace: true to replace entirely. Single inheritance only; maximum chain depth of 5. See ADR-042.
sandbox_strategy string Yes (unless inherited) The sandbox strategy used when executing within this resource type. Inherited from parent type if inherits is set and field is omitted.
handler object Yes (unless inherited) The Python handler class that implements resource operations. Inherited from parent type if inherits is set and field is omitted; subtypes typically override with a subclass handler.

CLI Argument Fields (cli_args[])

Field Type Required Description
name string Yes Argument name. Becomes --<name> on the CLI.
type string Yes Argument type: string, path, integer, boolean, url. The path type validates that the path exists; url type validates URL format.
required boolean No Whether the argument must be provided. Default: false.
description string No Description shown in --help and agents resource type show.
default any No Default value when the argument is not provided.
validation_pattern string No Regex pattern for validating string and url type arguments.

Child Type Fields (child_types[])

Field Type Required Description
type string Yes Name of the allowed child resource type (e.g., fs-directory, svn-file).
auto_discover boolean No Whether children of this type are automatically created when the parent resource is registered. Default: false.
manual_link boolean No Whether manual agents resource link-child is allowed for this relationship. Default: true.
description string No Description of the parent-child relationship.
max_count integer No Maximum number of children of this type. null or omitted means unlimited.

Parent Type Fields (parent_types[])

Field Type Required Description
type string Yes Name of the allowed parent resource type.
description string No Description of the parent-child relationship from the child's perspective.

Handler Fields (handler)

Field Type Required Description
class string Yes Python class name that implements the resource handler interface.
module string Yes Python module path where the handler class is defined.
config object No Arbitrary configuration passed to the handler constructor.

Auto-Discovery Fields (auto_discovery)

Field Type Required Description
enabled boolean No Whether auto-discovery runs when a resource of this type is created. Default: true.
scan_depth integer No Maximum recursion depth for scanning. Default: 3.
include_patterns list No Glob patterns for resources to discover.
exclude_patterns list No Glob patterns for resources to skip during discovery.

Equivalence Fields (equivalence) — Virtual Types Only

Field Type Required Description
criteria list Yes (virtual) Fields used to determine whether two physical resources are equivalent. Common criteria: content_hash, filename, permissions, url, commit_hash, tree_hash.
description string No Human-readable description of the equivalence rule.

Built-in Sandbox Strategies

Strategy Description
git_worktree Creates a git worktree for isolated execution. Changes are committed to a branch and merged on apply.
copy_on_write Creates a CoW copy of the resource. Changes are applied by replacing the original.
transaction_rollback Wraps operations in a database transaction. Rolled back on failure, committed on apply.
snapshot Takes a full snapshot before execution. Restored on rollback.
none No sandbox. Changes are applied directly. Use only for read-only or idempotent operations.

Examples

Example 1: Simple SVN Repository Type (Simple)

A custom resource type for Subversion repositories:


# svn-type.yaml
# Register: agents resource type add --config svn-type.yaml

name: local/svn description: "A Subversion (SVN) repository checkout"

physical: true user_addable: true

cli_args:

  • name: url type: url required: true description: "SVN repository URL" validation_pattern: "^svn(\+ssh)?://.|^https?://."
  • name: checkout-path type: path required: true description: "Local checkout directory"
  • name: revision type: string required: false description: "Specific revision to checkout" default: "HEAD"

child_types:

  • type: fs-directory auto_discover: true description: "Working copy directory tree"
  • type: local/svn-revision auto_discover: true description: "SVN revision history"

sandbox_strategy: copy_on_write

handler: class: SVNHandler module: cleveragents.resource.handlers.svn config: svn_binary: "svn" trust_server_cert: true

auto_discovery: enabled: true scan_depth: 2 exclude_patterns: - "/.svn/"

Usage: agents resource add local/svn local/legacy-repo --url svn://svn.example.com/trunk --checkout-path /repos/legacy


Example 2: S3 Bucket Resource Type (Medium)

A resource type for Amazon S3 buckets with prefix-based child discovery:


# s3-bucket-type.yaml
# Register: agents resource type add --config s3-bucket-type.yaml

name: local/s3-bucket description: "An Amazon S3 bucket with prefix-based object organization"

physical: true user_addable: true

cli_args:

  • name: bucket type: string required: true description: "S3 bucket name" validation_pattern: "^[a-z0-9][a-z0-9.-]{1,61}[a-z0-9]$"
  • name: region type: string required: false description: "AWS region" default: "us-east-1"
  • name: prefix type: string required: false description: "Key prefix to scope resource to a 'subdirectory'" default: ""
  • name: profile type: string required: false description: "AWS CLI profile name" default: "default"

child_types:

  • type: local/s3-prefix auto_discover: true description: "S3 key prefixes (virtual directories)" max_count: 1000
  • type: local/s3-object auto_discover: true description: "S3 objects (files)"

sandbox_strategy: copy_on_write

handler: class: S3BucketHandler module: cleveragents.resource.handlers.s3 config: max_object_size: 104857600 # 100 MB default_acl: "private"

auto_discovery: enabled: true scan_depth: 3 include_patterns: - "/*.json" - "/.yaml" - "**/.yml" - "/*.py" - "/.sql" exclude_patterns: - "**/.log" - "/*.tmp" - "/node_modules/**"


Example 3: Database Resource Type (Medium)

A resource type for relational databases:


# database-type.yaml
# Register: agents resource type add --config database-type.yaml

name: local/database description: "A relational database (PostgreSQL, MySQL, SQLite)"

physical: true user_addable: true

cli_args:

  • name: connection-string type: string required: true description: "Database connection string (e.g., postgresql://user:pass@host/db)"
  • name: engine type: string required: false description: "Database engine" default: "postgresql" validation_pattern: "^(postgresql|mysql|sqlite|mssql)$"
  • name: read-only type: boolean required: false description: "Connect in read-only mode" default: false
  • name: schema type: string required: false description: "Database schema to scope to" default: "public"

child_types:

  • type: local/db-table auto_discover: true description: "Database tables"
  • type: local/db-view auto_discover: true description: "Database views"
  • type: local/db-migration auto_discover: false manual_link: true description: "Migration scripts linked to this database"

parent_types:

  • type: git-checkout description: "Repository containing the application that owns this database"

sandbox_strategy: transaction_rollback

handler: class: DatabaseHandler module: cleveragents.resource.handlers.database config: connection_pool_size: 5 statement_timeout: 30000 # 30 seconds log_queries: true

auto_discovery: enabled: true scan_depth: 1


Example 4: Virtual File Type (Simple)

A virtual resource type that links equivalent physical files across repositories:


# virtual-config-file-type.yaml
# Register: agents resource type add --config virtual-config-file-type.yaml

name: local/config-file description: "Virtual type linking equivalent configuration files across repositories"

physical: false user_addable: false

child_types:

  • type: fs-file auto_discover: false manual_link: true description: "Physical file instances"
  • type: git-tree-entry auto_discover: false manual_link: true description: "Git tree entries representing the same config file"

sandbox_strategy: none

handler: class: VirtualConfigFileHandler module: cleveragents.resource.handlers.virtual_config config: track_content_drift: true

equivalence: criteria: - content_hash - filename description: "Two physical resources represent the same config file when they share the same filename and content hash"


Example 5: Docker Registry Resource Type (Complex)

A resource type for Docker container registries with full auto-discovery and lifecycle hooks:


# docker-registry-type.yaml
# Register: agents resource type add --config docker-registry-type.yaml

name: local/docker-registry description: "A Docker container registry with image and tag discovery"

physical: true user_addable: true

cli_args:

  • name: registry-url type: url required: true description: "Docker registry URL (e.g., registry.example.com, ghcr.io/org)" validation_pattern: "^[a-zA-Z0-9][a-zA-Z0-9.-]+(:[0-9]+)?(/[a-zA-Z0-9._-]+)*$"
  • name: username type: string required: false description: "Registry username for authentication"
  • name: password-env type: string required: false description: "Environment variable name containing the registry password"
  • name: namespace type: string required: false description: "Image namespace or organization filter"

child_types:

  • type: local/docker-image auto_discover: true description: "Docker images in the registry" max_count: 500
  • type: local/docker-tag auto_discover: true description: "Image tags"

sandbox_strategy: none

handler: class: DockerRegistryHandler module: cleveragents.resource.handlers.docker config: api_version: "v2" page_size: 100 cache_ttl: 300

auto_discovery: enabled: true scan_depth: 2 include_patterns: - "/latest" - "/main" - "/release-*" exclude_patterns: - "/sha256:" - "**/-dirty"

Example 6: Devcontainer Instance Resource Type (Inherited)

This example demonstrates the devcontainer-instance type, which inherits from container-instance via the resource type inheritance mechanism (ADR-042). Fields not shown here are inherited from the parent type. Only the additional fields and overrides specific to devcontainer semantics are defined.

# devcontainer-instance inherits from container-instance (ADR-039)
# See ADR-042 for inheritance rules, ADR-043 for devcontainer lifecycle

name: "devcontainer-instance" inherits: "container-instance" description: "A container instance defined by a devcontainer.json configuration file. Auto-discovered as a child of git-checkout resources containing a .devcontainer/ directory. Supports lazy activation — detected at discovery time but built only on first access." physical_virtual: "physical"

fields: # Inherited from container-instance: image, engine, ports, environment, volumes # Additional fields specific to devcontainer semantics: devcontainer_json_path: type: "string" required: true description: "Relative path to devcontainer.json from the parent resource root (e.g., .devcontainer/devcontainer.json)" workspace_folder: type: "string" required: false default: "/workspaces/${localWorkspaceFolderBasename}" description: "Container-side workspace path, parsed from devcontainer.json workspaceFolder field" features: type: "map<string, object>" required: false description: "Dev Container Features to install, parsed from devcontainer.json features field" post_create_command: type: "string | list<string>" required: false description: "Command(s) to run after container creation, from devcontainer.json postCreateCommand" post_start_command: type: "string | list<string>" required: false description: "Command(s) to run after container start, from devcontainer.json postStartCommand" activation_state: type: "enum(detected, building, running, stopping, stopped, failed)" required: true default: "detected" description: "Lifecycle state. Starts as 'detected' (lazy); transitions to 'building' then 'running' on first access. The 'stopping' state tracks in-progress shutdown before reaching 'stopped'."

handler: "DevcontainerHandler"

sandbox_strategy: "container_snapshot" # Overrides the parent's sandbox_strategy; devcontainers use container snapshots # for checkpoint/rollback rather than the generic container strategy.

capabilities: readable: true writable: true sandboxable: true checkpointable: true executable: true

auto_discovery: enabled: true parent_types: - "git-checkout" detection: scan_paths: - ".devcontainer/devcontainer.json" - ".devcontainer.json" activation: "lazy" # Container is NOT built at discovery time. # State remains "detected" until the execution environment router # selects this devcontainer for tool execution.


Context View Configuration

!!! adr "Architecture Decision" The context view configuration, resource filtering, and phase-specific context assembly are defined in ADR-014: Context Management (ACMS).

Context views control how project resources are filtered and presented to actors during different plan phases. They are managed via agents project context set and its associated CLI flags, but can also be defined declaratively in YAML for portability and version control.

JSON Schema

The following is the formal JSON Schema definition for context view configuration files.


{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://cleveragents.dev/schemas/context-view-config.json",
  "title": "CleverAgents Context View Configuration",
  "description": "Configuration file schema for defining context views that control how project resources are filtered and presented to actors during plan phases.",
  "type": "object",
  "properties": {
    "project": {
      "type": "string",
      "pattern": "^[a-zA-Z0-9_-]+/[a-zA-Z0-9_-]+$",
      "description": "Fully qualified project name this view applies to."
    },
    "view": {
      "type": "string",
      "enum": ["default", "strategize", "execute", "apply"],
      "description": "Phase view. 'default' is the fallback for all phases; phase-specific views override 'default'."
    },
    "include_resources": {
      "type": "array",
      "items": { "type": "string" },
      "description": "Whitelist of resource names to include. When specified, only these resources provide context."
    },
    "exclude_resources": {
      "type": "array",
      "items": { "type": "string" },
      "description": "Blacklist of resource names to exclude. Applied after include_resources."
    },
    "include_paths": {
      "type": "array",
      "items": { "type": "string" },
      "description": "Glob patterns for file paths to include. When specified, only matching files are included."
    },
    "exclude_paths": {
      "type": "array",
      "items": { "type": "string" },
      "description": "Glob patterns for file paths to exclude. Applied after include_paths."
    },
    "hot_max_tokens": {
      "type": ["integer", "null"],
      "minimum": 1,
      "description": "Soft cap on the number of tokens in hot (immediate) context. null means no soft cap."
    },
    "warm_max_decisions": {
      "type": "integer",
      "minimum": 1,
      "description": "Maximum number of decisions retained in warm context."
    },
    "cold_max_decisions": {
      "type": "integer",
      "minimum": 1,
      "description": "Maximum number of decisions retained in cold context."
    },
    "query_limit": {
      "type": "integer",
      "default": 20,
      "minimum": 1,
      "description": "Maximum number of retrieval results per query against the cold tier."
    },
    "max_file_size": {
      "type": "integer",
      "default": 1048576,
      "minimum": 1,
      "description": "Maximum individual file size in bytes that will be included in context. Default: 1 MB."
    },
    "max_total_size": {
      "type": "integer",
      "default": 52428800,
      "minimum": 1,
      "description": "Maximum aggregate size in bytes across all included files. Default: 50 MB."
    },
    "summarize": {
      "type": "boolean",
      "default": false,
      "description": "When true, large context segments are automatically summarized rather than excluded."
    },
    "summary_max_tokens": {
      "type": "integer",
      "minimum": 1,
      "description": "Maximum tokens for each generated summary. Only applies when summarize is true."
    },
    "strategy": {
      "type": "array",
      "items": { "type": "string" },
      "description": "Ordered list of ACMS context strategies to use for this view. Overrides the global strategy list. Valid built-in values: simple-keyword, semantic-embedding, breadth-depth-navigator, arce, temporal-archaeology, plan-decision-context."
    },
    "default_breadth": {
      "type": "integer",
      "minimum": 0,
      "default": 2,
      "description": "Default number of hops from focus nodes in the UKO graph for context expansion. 0 = focus nodes only."
    },
    "default_depth": {
      "oneOf": [
        { "type": "integer", "minimum": 0 },
        { "type": "string", "pattern": "^[A-Z][A-Z0-9_]*$" }
      ],
      "default": 3,
      "description": "Default detail depth for context fragments. May be a non-negative integer or a named level string from the active domain's DetailLevelMap (e.g., 'SIGNATURES', 'FULL_SOURCE'). Named levels are resolved to integers via the DetailLevelMap inheritance chain. Default: 3."
    },
    "depth_gradient": {
      "type": "object",
      "additionalProperties": {
        "oneOf": [
          { "type": "integer", "minimum": 0 },
          { "type": "string", "pattern": "^[A-Z][A-Z0-9_]*$" }
        ]
      },
      "description": "Per-hop detail depth overrides. Key is the hop distance (0 = focus node), value is an integer depth or named level string. Hops not listed use default_depth."
    },
    "skeleton_ratio": {
      "type": "number",
      "minimum": 0.0,
      "maximum": 1.0,
      "default": 0.15,
      "description": "Fraction of the context budget reserved for inherited plan skeleton context. 0.0 = no skeleton inheritance; 1.0 = all budget to skeleton."
    },
    "temporal_scope": {
      "type": "string",
      "enum": ["current", "recent", "all"],
      "default": "current",
      "description": "Temporal scope for UKO node resolution. 'current' = only isCurrent nodes; 'recent' = current + nodes valid within warm retention window; 'all' = include historical versions."
    },
    "auto_refresh": {
      "type": "boolean",
      "default": true,
      "description": "When true, the ACMS automatically re-assembles context when the available budget changes by more than the refresh threshold (default 30%). When false, context is only assembled on explicit request."
    }
  },
  "required": ["project", "view"],
  "additionalProperties": false
}

Informal YAML Schema

The following annotated YAML provides an easier-to-read overview of the same schema defined formally above. It is not a validatable schema — it uses informal annotations to describe the structure. Refer to the JSON Schema above for the authoritative, machine-validatable definition.


# ─── Context View Identity ──────────────────────────────────────────
project: <namespace>/<name>         # Project this context view applies to (required)
view: default | strategize | execute | apply  # Which phase view (required)

# ─── Resource Filtering ───────────────────────────────────────────── include_resources: # Resources to include (whitelist, optional)

  • <resource_name> exclude_resources: # Resources to exclude (blacklist, optional)
  • <resource_name>

# ─── Path Filtering ───────────────────────────────────────────────── include_paths: # Glob patterns for files to include (optional)

  • "src/**/*.py"
  • "tests/**/*.py" exclude_paths: # Glob patterns for files to exclude (optional)
  • "/node_modules/"
  • "/pycache/"
  • "/.git/"
  • "/dist/"

# ─── Token and Size Budgets ───────────────────────────────────────── hot_max_tokens: <integer> # Soft cap on hot context tokens (optional, null = no limit) warm_max_decisions: <integer> # Max decisions in warm context (optional) cold_max_decisions: <integer> # Max decisions in cold context (optional) query_limit: <integer> # Max retrieval results per query (optional, default: 20) max_file_size: <integer> # Max file size in bytes to include (optional, default: 1048576) max_total_size: <integer> # Max total size across all included files (optional, default: 52428800)

# ─── Summarization ────────────────────────────────────────────────── summarize: true # Enable summarization for large context segments (optional) summary_max_tokens: <integer> # Token limit for generated summaries (optional)

# ─── ACMS Strategy & Context Assembly ─────────────────────────────── strategy: # ACMS strategies to use (optional, overrides global list)

  • simple-keyword
  • semantic-embedding
  • breadth-depth-navigator default_breadth: <integer> # Default hop count for UKO graph expansion (optional, default: 2) default_depth: 3 # Default detail depth — integer or named level (optional, default: 3) depth_gradient: # Per-hop detail depth overrides (optional) 0: 9 # Focus nodes get depth 9 (FULL_SOURCE for code) 1: 4 # 1-hop neighbors get depth 4 (SIGNATURES for code) 2: 0 # 2-hop neighbors get depth 0 (MODULE_LISTING for code) skeleton_ratio: <float> # Fraction of budget for inherited plan skeleton (optional, default: 0.15) temporal_scope: current | recent | all # Temporal scope for UKO node resolution (optional, default: current) auto_refresh: true # Auto re-assemble context on budget change (optional, default: true)

Structure Reference

Top-Level Fields

Field Type Required Description
project string Yes Fully qualified project name this view applies to.
view string Yes Phase view: default (fallback for all phases), strategize, execute, or apply. Phase-specific views override the default view.
include_resources list No Whitelist of resource names to include. When specified, only these resources provide context.
exclude_resources list No Blacklist of resource names to exclude. Applied after include_resources.
include_paths list No Glob patterns for file paths to include. When specified, only matching files are included.
exclude_paths list No Glob patterns for file paths to exclude. Applied after include_paths.
hot_max_tokens integer No Soft cap on the number of tokens in hot (immediate) context. The effective limit is the lesser of this value and the actor's hard context window limit. When null, no soft cap is applied.
warm_max_decisions integer No Maximum number of decisions retained in warm context (recent decisions available for reference).
cold_max_decisions integer No Maximum number of decisions retained in cold context (older decisions queryable via search).
query_limit integer No Maximum number of retrieval results per query against the cold tier. Default: 20.
max_file_size integer No Maximum individual file size in bytes that will be included in context. Files exceeding this are either excluded or summarized. Default: 1 MB (1,048,576).
max_total_size integer No Maximum aggregate size in bytes across all included files. Default: 50 MB (52,428,800).
summarize boolean No When true, large context segments are automatically summarized rather than excluded. Default: false.
summary_max_tokens integer No Maximum tokens for each generated summary. Only applies when summarize is true.
strategy list No Ordered list of ACMS context strategies to use for this view. Overrides the global context.strategies.enabled list. Built-in strategies: simple-keyword, semantic-embedding, breadth-depth-navigator, arce, temporal-archaeology, plan-decision-context.
default_breadth integer No Default number of hops from focus nodes in the UKO graph for context expansion. 0 = focus nodes only. Default: 2.
default_depth integer or string No Default detail depth for context fragments. Accepts a non-negative integer or a named level string from the active domain's DetailLevelMap (e.g., SIGNATURES, FULL_SOURCE). Default: 3.
depth_gradient object No Per-hop detail depth overrides. Keys are hop distances (0 = focus node), values are integers or named level strings. Hops not listed use default_depth.
skeleton_ratio number No Fraction of context budget reserved for inherited plan skeleton context. Range: 0.01.0. Default: 0.15.
temporal_scope string No Temporal scope for UKO node resolution: current (only isCurrent nodes), recent (current + warm retention window), all (include historical versions). Default: current.
auto_refresh boolean No When true, ACMS automatically re-assembles context when the available budget changes by more than the refresh threshold. Default: true.

Context Tiers

CleverAgents uses a three-tier context architecture:

Tier Description Controlled By
Hot Immediate context sent in the LLM prompt. Includes active files, recent changes, and current task description. hot_max_tokens, include_paths, exclude_paths
Warm Recent decisions and intermediate results. Available for reference but not always in the prompt. warm_max_decisions
Cold Historical decisions, full project index, and archived artifacts. Queryable via retrieval. cold_max_decisions, query_limit

View Resolution Order

When a plan enters a phase, the context view is resolved as follows:

  1. Check for a phase-specific view (e.g., strategize).
  2. Fall back to the default view.
  3. Fall back to system defaults (include all resources, no path filtering, default budgets).

Examples

Example 1: Basic Default View (Simple)

A default context view with minimal filtering:


# context-default.yaml
# Apply: agents project context set --view default --include-path "src/**" \
#        --exclude-path "**/node_modules/**" local/api-service
# Or import this file and apply via the CLI.

project: local/api-service view: default

exclude_paths:

  • "/node_modules/"
  • "/pycache/"
  • "/.git/"

max_file_size: 1048576 # 1 MB


Example 2: Strategize-Optimized View (Medium)

A focused view for the Strategize phase that limits context to essential architecture files:


# context-strategize.yaml

project: local/api-service view: strategize

include_resources:

  • local/api-repo

exclude_resources:

  • local/staging-db

include_paths:

  • "src/**/*.py"
  • "docs/architecture/**"
  • "README.md"
  • "pyproject.toml"

exclude_paths:

  • "/node_modules/"
  • "/test_fixtures/"
  • "/pycache/"
  • "/migrations/"

hot_max_tokens: 12000 warm_max_decisions: 50 cold_max_decisions: 200 query_limit: 20

max_file_size: 524288 # 512 KB max_total_size: 10485760 # 10 MB

summarize: true summary_max_tokens: 800

This view keeps the Strategize phase focused on source code and architecture docs, limits hot context to 12K tokens, and enables summarization for files that exceed the size limits.


Example 3: Execute-Phase View with Full Resources (Medium)

An execution view with broader access for implementing changes:


# context-execute.yaml

project: local/api-service view: execute

include_resources:

  • local/api-repo
  • local/staging-db

include_paths:

  • "src/**"
  • "tests/**"
  • "config/**"
  • "scripts/**"
  • "Makefile"
  • "pyproject.toml"
  • "requirements*.txt"

exclude_paths:

  • "/node_modules/"
  • "/pycache/"
  • "/dist/"
  • "**/*.pyc"

hot_max_tokens: 24000 warm_max_decisions: 100 cold_max_decisions: 500 query_limit: 30

max_file_size: 2097152 # 2 MB max_total_size: 104857600 # 100 MB

summarize: true summary_max_tokens: 1200


Example 4: Apply-Phase Minimal View (Simple)

A minimal view for the Apply phase, focused only on changed files and validation:


# context-apply.yaml

project: local/api-service view: apply

include_paths:

  • "src/**"
  • "tests/**"

exclude_paths:

  • "/pycache/"

hot_max_tokens: 8000 warm_max_decisions: 20 cold_max_decisions: 50

summarize: false


Example 5: Large Monorepo View with Aggressive Filtering (Complex)

A context view designed for a large monorepo where careful filtering is essential:


# context-monorepo-strategize.yaml

project: local/platform view: strategize

include_resources:

  • local/platform-repo

include_paths:

  • "packages/auth/**/*.ts"
  • "packages/auth/**/*.tsx"
  • "packages/shared/**/*.ts"
  • "packages/api-gateway/**/*.ts"
  • "docs/architecture/**"
  • "package.json"
  • "tsconfig.json"
  • "lerna.json"

exclude_paths:

  • "/node_modules/"
  • "/dist/"
  • "/coverage/"
  • "**/*.test.ts"
  • "**/*.spec.ts"
  • "**/*.stories.tsx"
  • "/fixtures/"
  • "/snapshots/"
  • "/generated/"

hot_max_tokens: 16000 warm_max_decisions: 30 cold_max_decisions: 150 query_limit: 15

max_file_size: 262144 # 256 KB - aggressive for a monorepo max_total_size: 5242880 # 5 MB

summarize: true summary_max_tokens: 600

This configuration is tailored for a TypeScript monorepo, focusing on specific packages and excluding generated code, tests, and build artifacts to keep context within manageable bounds.


Automation Profile Configuration Files

!!! adr "Architecture Decision" The automation profile configuration schema, threshold model, and built-in profiles are defined in ADR-017: Automation Profiles.

Automation profile configuration files define named collections of confidence thresholds (floating-point values from 0.0 to 1.0) and boolean safety flags controlling which operations are automated vs. requiring human approval. Each threshold specifies the minimum confidence score at which the system proceeds automatically; below the threshold, the system drops to manual mode. A threshold of 0.0 means "always automatic" and 1.0 means "always manual." Built-in profiles (manual, review, supervised, cautious, trusted, auto, ci, full-auto) are always available. Custom profiles are registered via agents automation-profile add --config <file>.

JSON Schema

The following is the formal JSON Schema definition for automation profile configuration files.


{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://cleveragents.dev/schemas/automation-profile-config.json",
  "title": "CleverAgents Automation Profile Configuration",
  "description": "Configuration file schema for defining automation profiles — named collections of confidence thresholds (0.0-1.0) and boolean safety flags controlling which operations are automated vs. requiring human approval.",
  "type": "object",
  "properties": {
    "name": {
      "type": "string",
      "pattern": "^[a-zA-Z0-9_-]+/[a-zA-Z0-9_-]+$",
      "description": "Fully qualified profile name in <namespace>/<name> format."
    },
    "description": {
      "type": "string",
      "description": "Human-readable description of the profile's purpose and behavior."
    },
    "decompose_task": {
      "type": "number",
      "minimum": 0.0,
      "maximum": 1.0,
      "description": "Confidence threshold (0.0-1.0) for automatically transitioning from Action to Strategize. 0.0 = always automatic, 1.0 = always manual."
    },
    "create_tool": {
      "type": "number",
      "minimum": 0.0,
      "maximum": 1.0,
      "description": "Confidence threshold (0.0-1.0) for automatically transitioning from Strategize to Execute. 0.0 = always automatic, 1.0 = always manual."
    },
    "select_tool": {
      "type": "number",
      "minimum": 0.0,
      "maximum": 1.0,
      "description": "Confidence threshold (0.0-1.0) for automatically applying changes after execution completes. 0.0 = always automatic, 1.0 = always manual."
    },
    "edit_code": {
      "type": "number",
      "minimum": 0.0,
      "maximum": 1.0,
      "description": "Confidence threshold (0.0-1.0) for autonomously making decisions during Strategize. 0.0 = always automatic, 1.0 = always manual."
    },
    "execute_command": {
      "type": "number",
      "minimum": 0.0,
      "maximum": 1.0,
      "description": "Confidence threshold (0.0-1.0) for autonomously making decisions during Execute. 0.0 = always automatic, 1.0 = always manual."
    },
    "create_file": {
      "type": "number",
      "minimum": 0.0,
      "maximum": 1.0,
      "description": "Confidence threshold (0.0-1.0) for automatically attempting to fix validation failures. 0.0 = always automatic, 1.0 = always manual."
    },
    "delete_content": {
      "type": "number",
      "minimum": 0.0,
      "maximum": 1.0,
      "description": "Confidence threshold (0.0-1.0) for automatically revising the strategy when execution reveals issues. 0.0 = always automatic, 1.0 = always manual."
    },
    "access_network": {
      "type": "number",
      "minimum": 0.0,
      "maximum": 1.0,
      "description": "Confidence threshold (0.0-1.0) for automatically reverting from a constrained Apply phase to Strategize. 0.0 = always automatic, 1.0 = always manual."
    },
    "modify_config": {
      "type": "number",
      "minimum": 0.0,
      "maximum": 1.0,
      "description": "Confidence threshold (0.0-1.0) for automatically retrying operations that fail due to transient errors. 0.0 = always automatic, 1.0 = always manual."
    },
    "approve_plan": {
      "type": "number",
      "minimum": 0.0,
      "maximum": 1.0,
      "description": "Confidence threshold (0.0-1.0) for automatically restoring from the most recent checkpoint on failure. 0.0 = always automatic, 1.0 = always manual."
    },
    "install_dependency": {
      "type": "number",
      "minimum": 0.0,
      "maximum": 1.0,
      "description": "Confidence threshold (0.0-1.0) for automatically spawning child plans decided during Strategize. 0.0 = always automatic, 1.0 = always manual."
    },
    "require_sandbox": {
      "type": "boolean",
      "description": "When true, all write operations must execute within a sandbox. Execution fails if no sandbox strategy is available."
    },
    "require_checkpoints": {
      "type": "boolean",
      "description": "When true, checkpoints must be created before any write operation, enabling rollback."
    },
    "allow_unsafe_tools": {
      "type": "boolean",
      "description": "When true, tools flagged as unsafe can be invoked. When false, unsafe tool invocations are blocked."
    }
  },
  "required": [
    "name",
    "description",
    "decompose_task",
    "create_tool",
    "select_tool",
    "edit_code",
    "execute_command",
    "create_file",
    "delete_content",
    "access_network",
    "modify_config",
    "approve_plan",
    "install_dependency",
    "require_sandbox",
    "require_checkpoints",
    "allow_unsafe_tools"
  ],
  "additionalProperties": false
}

Informal YAML Schema

The following annotated YAML provides an easier-to-read overview of the same schema defined formally above. It is not a validatable schema — it uses informal annotations to describe the structure. Refer to the JSON Schema above for the authoritative, machine-validatable definition.


# ─── Profile Identity ───────────────────────────────────────────────
name: <namespace>/<name>            # Fully qualified profile name (required)
description: <string>               # Human-readable description (required)

# ─── Phase Transition Thresholds ──────────────────────────────────── # Confidence thresholds controlling phase transitions. # 0.0 = always automatic, 1.0 = always manual. decompose_task: <float: 0.01.0> # Confidence threshold for Action → Strategize (required, 0.0=auto, 1.0=manual) create_tool: <float: 0.01.0> # Confidence threshold for Strategize → Execute (required, 0.0=auto, 1.0=manual) select_tool: <float: 0.01.0> # Confidence threshold for Execute → Apply (required, 0.0=auto, 1.0=manual)

# ─── Decision Thresholds ──────────────────────────────────────────── # Confidence thresholds controlling decisions within each phase. # 0.0 = always automatic, 1.0 = always manual. edit_code: <float: 0.01.0> # Confidence threshold for decisions during Strategize (required, 0.0=auto, 1.0=manual) execute_command: <float: 0.01.0> # Confidence threshold for decisions during Execute (required, 0.0=auto, 1.0=manual)

# ─── Self-Repair Thresholds ───────────────────────────────────────── create_file: <float: 0.01.0> # Confidence threshold for auto-fixing validation failures (required, 0.0=auto, 1.0=manual) delete_content: <float: 0.01.0> # Confidence threshold for auto-revising strategy when Execute hits constraints (required, 0.0=auto, 1.0=manual) access_network: <float: 0.01.0> # Confidence threshold for auto-reverting from constrained Apply to Strategize (required, 0.0=auto, 1.0=manual) modify_config: <float: 0.01.0> # Confidence threshold for auto-retrying transient errors (required, 0.0=auto, 1.0=manual) approve_plan: <float: 0.01.0> # Confidence threshold for auto-restoring from checkpoints (required, 0.0=auto, 1.0=manual)

# ─── Execution Control Thresholds ────────────────────────────────── install_dependency: <float: 0.01.0> # Confidence threshold for auto-spawning child plans (required, 0.0=auto, 1.0=manual)

# ─── Safety Requirements ──────────────────────────────────────────── require_sandbox: <boolean> # Require sandbox for all write operations (required) require_checkpoints: <boolean> # Require checkpoint creation before write operations (required) allow_unsafe_tools: <boolean> # Allow tools flagged as unsafe (required)

Structure Reference

Top-Level Fields

Field Type Required Description
name string Yes Fully qualified profile name. This value is used as the profile's registered name.
description string Yes Human-readable description of the profile's purpose and behavior.

Phase Transition Thresholds

Field Type Required Description
decompose_task number (0.01.0) Yes Confidence threshold for transitioning from Action to Strategize. When computed confidence >= threshold, Strategize begins automatically. 0.0 = always automatic, 1.0 = always manual.
create_tool number (0.01.0) Yes Confidence threshold for transitioning from Strategize to Execute. When computed confidence >= threshold, Execute begins automatically. 0.0 = always automatic, 1.0 = always manual.
select_tool number (0.01.0) Yes Confidence threshold for transitioning from Execute to Apply. When computed confidence >= threshold, Apply begins automatically. 0.0 = always automatic, 1.0 = always manual. A threshold of 0.0 means changes are committed without human review.

Decision Automation Thresholds

Field Type Required Description
edit_code number (0.01.0) Yes Confidence threshold for autonomous decision-making during Strategize. When computed confidence >= threshold, the strategy actor decides autonomously. 0.0 = always automatic, 1.0 = always manual.
execute_command number (0.01.0) Yes Confidence threshold for autonomous decision-making during Execute. When computed confidence >= threshold, the execution actor decides autonomously. 0.0 = always automatic, 1.0 = always manual.

Self-Repair Thresholds

Field Type Required Description
create_file number (0.01.0) Yes Confidence threshold for automatically fixing validation failures (e.g., failing tests, lint errors). When computed confidence >= threshold, fix is attempted automatically. 0.0 = always automatic, 1.0 = always manual.
delete_content number (0.01.0) Yes Confidence threshold for automatically revising the strategy when execution reveals issues. When computed confidence >= threshold, revision proceeds automatically. 0.0 = always automatic, 1.0 = always manual.
access_network number (0.01.0) Yes Confidence threshold for automatically reverting from a constrained Apply phase to Strategize. When computed confidence >= threshold, reversion proceeds automatically. 0.0 = always automatic, 1.0 = always manual.
modify_config number (0.01.0) Yes Confidence threshold for automatically retrying operations that fail due to transient errors (network timeouts, rate limits). When computed confidence >= threshold, retry proceeds automatically. 0.0 = always automatic, 1.0 = always manual.
approve_plan number (0.01.0) Yes Confidence threshold for automatically restoring from the most recent checkpoint on failure. When computed confidence >= threshold, restore proceeds automatically. 0.0 = always automatic, 1.0 = always manual.

Execution Control Thresholds

Field Type Required Description
install_dependency number (0.01.0) Yes Confidence threshold for automatically spawning child plans decided during Strategize. When computed confidence >= threshold, child plans spawn automatically during Execute. 0.0 = always automatic, 1.0 = always manual.

Safety Flags

Field Type Required Description
require_sandbox boolean Yes When true, all write operations must execute within a sandbox (git worktree, copy-on-write, transaction). Execution fails if no sandbox strategy is available.
require_checkpoints boolean Yes When true, checkpoints must be created before any write operation. This enables rollback.
allow_unsafe_tools boolean Yes When true, tools flagged as unsafe can be invoked. When false, unsafe tool invocations are blocked.

Built-in Profile Reference

Threshold manual review supervised cautious trusted auto ci full-auto
auto_strat 1.0 0.0 0.0 0.7 0.0 0.0 0.0 0.0
auto_exec 1.0 0.0 1.0 0.7 0.0 0.0 0.0 0.0
select_tool 1.0 1.0 1.0 1.0 1.0 1.0 0.0 0.0
auto_dec_strat 1.0 1.0 0.0 0.6 0.0 0.0 0.0 0.0
auto_dec_exec 1.0 1.0 1.0 0.8 0.0 0.0 0.0 0.0
auto_val_fix 1.0 1.0 1.0 0.7 0.0 0.0 0.0 0.0
auto_strat_rev 1.0 1.0 1.0 0.8 1.0 0.0 0.0 0.0
auto_rev_apply 1.0 1.0 1.0 0.9 1.0 1.0 0.0 0.0
auto_child 1.0 0.0 1.0 0.7 0.0 0.0 0.0 0.0
require_sandbox true true true true true true true false
allow_unsafe false false false false false false false true

Examples

Example 1: Careful Autonomous Profile (Simple)

A profile for autonomous execution with mandatory sandboxing and manual apply:


# careful-auto.yaml
# Register: agents automation-profile add --config careful-auto.yaml

name: local/careful-auto description: "Autonomous execution with mandatory sandbox and manual apply"

decompose_task: 0.0 create_tool: 0.0 select_tool: 1.0

edit_code: 0.0 execute_command: 0.0

create_file: 0.0 delete_content: 1.0 access_network: 1.0 modify_config: 0.0 approve_plan: 0.0

install_dependency: 0.0

require_sandbox: true require_checkpoints: true allow_unsafe_tools: false

This is essentially the auto built-in profile but with approve_plan set to 0.0 (always automatic) for more aggressive self-repair.


Example 2: CI/CD Pipeline Profile (Medium)

A profile designed for automated CI/CD pipelines where everything runs autonomously:


# ci-pipeline.yaml
# Register: agents automation-profile add --config ci-pipeline.yaml

name: local/ci-pipeline description: "Full automation for CI/CD pipelines. All phases automated, sandbox required."

decompose_task: 0.0 create_tool: 0.0 select_tool: 0.0

edit_code: 0.0 execute_command: 0.0

create_file: 0.0 delete_content: 0.0 access_network: 0.0 modify_config: 0.0 approve_plan: 0.0

install_dependency: 0.0

require_sandbox: true require_checkpoints: true allow_unsafe_tools: false

This profile is similar to full-auto but with mandatory sandboxing and checkpoints enabled — suitable for CI/CD environments where you want full automation but with safety nets.


Example 3: Review-Heavy Profile (Medium)

A profile for regulated environments where every decision must be reviewed:


# review-heavy.yaml
# Register: agents automation-profile add --config review-heavy.yaml

name: local/review-heavy description: "Maximum human oversight. Every decision and phase requires approval."

decompose_task: 0.0 create_tool: 1.0 select_tool: 1.0

edit_code: 1.0 execute_command: 1.0

create_file: 1.0 delete_content: 1.0 access_network: 1.0 modify_config: 1.0 approve_plan: 1.0

install_dependency: 1.0

require_sandbox: true require_checkpoints: true allow_unsafe_tools: false

This profile auto-starts strategize but requires human approval for every decision, every phase transition, and every child plan spawn. Useful in regulated industries (finance, healthcare) where audit trails and human review are mandatory.


Example 4: Development Sandbox Profile (Simple)

A fast-iteration profile for local development where safety is relaxed:


# dev-sandbox.yaml
# Register: agents automation-profile add --config dev-sandbox.yaml

name: local/dev-sandbox description: "Fast iteration for local development. Relaxed safety, auto execution."

decompose_task: 0.0 create_tool: 0.0 select_tool: 1.0

edit_code: 0.0 execute_command: 0.0

create_file: 0.0 delete_content: 0.0 access_network: 1.0 modify_config: 0.0 approve_plan: 0.0

install_dependency: 0.0

require_sandbox: false require_checkpoints: false allow_unsafe_tools: true

This profile removes sandbox and checkpoint requirements for speed during local development. It still requires manual apply to prevent accidental commits. Unsafe tools are allowed for full flexibility.


Example 5: Production Deployment Profile (Complex)

A profile for production deployments combining autonomous execution with maximum safety:


# production-deploy.yaml
# Register: agents automation-profile add --config production-deploy.yaml

name: local/production-deploy description: | Production deployment profile. Fully autonomous execution with maximum safety guarantees. All phases require sandbox and checkpoint. Apply requires manual approval. Strategy revision is enabled to adapt to deployment issues.

decompose_task: 0.0 create_tool: 0.0 select_tool: 1.0

edit_code: 0.0 execute_command: 0.0

create_file: 0.0 delete_content: 0.0 access_network: 1.0 modify_config: 0.0 approve_plan: 0.0

install_dependency: 0.0

require_sandbox: true require_checkpoints: true allow_unsafe_tools: false

LSP Server Configuration Files

!!! adr "Architecture Decision" The LSP Registry, actor LSP binding, capability exposure, and server lifecycle are defined in ADR-027: Language Server Protocol (LSP) Integration.

LSP server configuration files define Language Server Protocol servers that provide language intelligence to actors. LSP servers are Infrastructure-layer components — they have nothing to do with IDE integration. Each server has a namespaced name, a launch command, supported languages, initialization options, and capability declarations. LSP servers are registered via agents lsp add --config <file> and can then be referenced by actors in their lsp: configuration field.

JSON Schema

The following is the formal JSON Schema definition for LSP server configuration files.


{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://cleveragents.dev/schemas/lsp-server-config.json",
  "title": "CleverAgents LSP Server Configuration",
  "description": "Configuration file schema for defining LSP servers registered in the LSP Registry and attached to actors for language intelligence.",
  "type": "object",
  "properties": {
    "name": {
      "type": "string",
      "pattern": "^([a-zA-Z0-9_-]+:)?[a-zA-Z0-9_-]+/[a-zA-Z0-9_-]+$",
      "description": "Fully qualified LSP server name in [[server:]namespace/]name format."
    },
    "description": {
      "type": "string",
      "description": "Human-readable description of the LSP server's purpose."
    },
    "languages": {
      "type": "array",
      "items": { "type": "string" },
      "minItems": 1,
      "description": "Programming languages this server supports. Used for language-based and auto-discovery binding resolution."
    },
    "command": {
      "type": "string",
      "description": "Shell command to launch the LSP server process. Must support --stdio or equivalent for stdin/stdout JSON-RPC communication."
    },
    "args": {
      "type": "array",
      "items": { "type": "string" },
      "default": [],
      "description": "Additional arguments appended to the command."
    },
    "env": {
      "type": "object",
      "additionalProperties": { "type": "string" },
      "default": {},
      "description": "Environment variables set when launching the server process."
    },
    "root_path": {
      "type": "string",
      "default": "{{ project.root }}",
      "description": "Workspace root path sent in the LSP initialize request. Supports Jinja2 template variables."
    },
    "init_options": {
      "type": "object",
      "additionalProperties": true,
      "default": {},
      "description": "Initialization options sent in the LSP initialize request. Server-specific; passed through verbatim."
    },
    "capabilities": {
      "type": "array",
      "items": {
        "type": "string",
        "enum": ["diagnostics", "hover", "completions", "references", "definitions", "symbols", "formatting", "code_actions", "rename"]
      },
      "default": ["diagnostics", "hover", "completions", "references", "definitions", "symbols", "formatting", "code_actions", "rename"],
      "description": "LSP capabilities this server advertises. The runtime uses this to determine which tool adapters and context enrichment features are available. Defaults to all capabilities; restrict to a subset if the server does not support certain features."
    },
    "health_check": {
      "type": "object",
      "properties": {
        "enabled": {
          "type": "boolean",
          "default": true,
          "description": "Whether to perform periodic health checks on the running server process."
        },
        "interval_seconds": {
          "type": "integer",
          "default": 60,
          "minimum": 10,
          "description": "Seconds between health check probes."
        },
        "restart_on_failure": {
          "type": "boolean",
          "default": true,
          "description": "Whether to automatically restart the server if health checks fail."
        },
        "max_restarts": {
          "type": "integer",
          "default": 3,
          "minimum": 0,
          "description": "Maximum number of automatic restarts before marking the server as failed."
        }
      },
      "additionalProperties": false,
      "description": "Health check configuration for the running server process."
    }
  },
  "required": ["name", "languages", "command"],
  "additionalProperties": false
}

Informal YAML Schema


# ─── LSP Server Configuration ────────────────────────────────────────
# Registered via: agents lsp add --config <this-file>
# Referenced by actors via: lsp: [<namespace>/<name>]

name: <namespace>/<server-name> # Namespaced identifier (required) description: "..." # Human-readable description (optional)

languages: # Supported languages (required, min 1)

  • python
  • pyi # e.g., Python stub files

command: pyright-langserver # Launch command (required) args: # Additional arguments (optional)

  • --stdio

env: # Environment variables (optional) PYTHONPATH: /app/src

root_path: "{{ project.root }}" # Workspace root, Jinja2 supported (optional, default: {{ project.root }})

init_options: # LSP initialize request options (optional) python.analysis.typeCheckingMode: standard python.analysis.autoSearchPaths: true python.analysis.diagnosticSeverityOverrides: reportMissingImports: warning reportUnusedVariable: information

capabilities: # Advertised capabilities (optional, default: all)

  • diagnostics
  • hover
  • completions
  • references
  • definitions
  • symbols
  • formatting
  • code_actions
  • rename

health_check: # Health monitoring (optional) enabled: true # Periodic health probes (default: true) interval_seconds: 60 # Probe interval (default: 60, min: 10) restart_on_failure: true # Auto-restart on failure (default: true) max_restarts: 3 # Max auto-restarts before marking failed (default: 3)

Structure Reference

LSP Server Configuration Fields

Field Type Required Description
name string Yes Fully qualified LSP server name in [[server:]namespace/]name format.
description string No Human-readable description of the server's purpose.
languages list Yes Programming languages this server supports (min 1). Used for language-based and auto-discovery binding resolution.
command string Yes Shell command to launch the LSP server process. Must support stdio JSON-RPC communication.
args list No Additional arguments appended to the command.
env object No Environment variables set when launching the server process.
root_path string No Workspace root path sent in the LSP initialize request. Supports Jinja2 template variables. Default: {{ project.root }}.
init_options object No Initialization options sent in the LSP initialize request. Server-specific; passed through verbatim.
capabilities list No LSP capabilities this server advertises: diagnostics, hover, completions, references, definitions, symbols, formatting, code_actions, rename. Default: all.
health_check object No Health monitoring configuration.
health_check.enabled boolean No Whether to perform periodic health checks. Default: true.
health_check.interval_seconds integer No Seconds between health check probes. Default: 60, min: 10.
health_check.restart_on_failure boolean No Auto-restart server on health check failure. Default: true.
health_check.max_restarts integer No Maximum automatic restarts before marking server as failed. Default: 3, min: 0.

Examples

Example 1: Pyright (Python type checker and language server)


name: local/pyright
description: "Pyright language server for Python type checking and intelligence"
languages:
  - python
  - pyi
command: pyright-langserver
args: ["--stdio"]
init_options:
  python.analysis.typeCheckingMode: standard
  python.analysis.autoSearchPaths: true
  python.analysis.useLibraryCodeForTypes: true

Example 2: TypeScript Language Server


name: local/ts-server
description: "TypeScript/JavaScript language server"
languages:
  - typescript
  - javascript
  - tsx
  - jsx
command: typescript-language-server
args: ["--stdio"]
init_options:
  preferences:
    includeInlayParameterNameHints: all
    includeInlayVariableTypeHints: true

Example 3: gopls (Go language server) with restricted capabilities


name: local/gopls
description: "Go language server (diagnostics and navigation only)"
languages:
  - go
command: gopls
args: ["serve"]
capabilities:
  - diagnostics
  - hover
  - references
  - definitions
  - symbols
health_check:
  interval_seconds: 120
  max_restarts: 5

Example 4: Rust Analyzer with custom environment


name: local/rust-analyzer
description: "Rust Analyzer language server"
languages:
  - rust
command: rust-analyzer
env:
  CARGO_HOME: /home/user/.cargo
  RUSTUP_HOME: /home/user/.rustup
init_options:
  cargo:
    allFeatures: true
  checkOnSave:
    command: clippy

Workflow Examples

This section presents complete, end-to-end workflow examples showing how CleverAgents is used in practice. Each example starts from initial setup and walks through every command and decision point. The examples progress from simple single-action workflows to complex multi-project, multi-actor orchestrations.

Example 1: Hello World — Fix a Single Bug

Scenario: A developer finds a bug in their Python web API where the /health endpoint returns a 500 error instead of 200 when the database connection is unavailable. They want CleverAgents to fix it with full human oversight.

Complexity: Beginner. Single project, single action, manual automation profile.

Step 1: Initialize Environment and Register Resources


# Initialize CleverAgents (first-time setup)
$ agents init --yes

╭─ Initialized ────────────────────────────────────────────╮ │ Data Dir: /home/dev/.cleveragents (created) │ │ Config: /home/dev/.cleveragents/config.toml │ │ Database: initialized (schema v3) │ │ Directories: logs, cache, sessions, contexts │ ╰──────────────────────────────────────────────────────────╯

✓ OK Initialized (non-interactive)


# Register the git checkout as a resource
$ agents resource add git-checkout local/api-repo \
    --path /home/dev/projects/api-service \
    --branch main

╭─ Resource ──────────────────────────────────────────╮ │ Name: local/api-repo │ │ ID: 01HXR1A1B2C3D4E5F6G7H8J9K0 │ │ Type: git-checkout │ │ Physical/Virtual: physical │ │ Path: /home/dev/projects/api-service │ │ Branch: main │ │ Created: 2026-02-11 09:15 │ ╰─────────────────────────────────────────────────────╯

╭─ Auto-discovered Children ─────────────────────────────────────────╮ │ ID Type Status │ │ ─────────────── ────────────── ───────────────── │ │ 01HXR1A1B2C3… git created │ │ 01HXR1A1B2C4… git-remote created │ │ 01HXR1A1B2C5… git-branch created │ │ 01HXR1A1B2C6… fs-directory created │ │ + 32 git-commit resources │ │ + 187 git-tree-entry resources │ │ + 5 fs-directory + 41 fs-file │ ╰────────────────────────────────────────────────────────────────────╯

╭─ Capabilities ─────────────────╮ │ Readable: yes │ │ Writable: yes │ │ Sandboxable: yes │ │ Checkpointable: yes │ │ Sandbox Strategy: git_worktree │ ╰────────────────────────────────╯

✓ OK Resource registered (270 child resources discovered)


# Create the project and link the resource
$ agents project create \
    --description "REST API service" \
    --resource local/api-repo \
    local/api-service

╭─ Project Created ──────────────────────────╮ │ Name: local/api-service │ │ Description: REST API service │ │ Type: local │ │ Created: 2026-02-11 09:15 │ ╰────────────────────────────────────────────╯

╭─ Linked Resources ───────────────────────────────────────╮ │ Name Type Read-Only │ │ ───────────────── ────────────── ───────── │ │ local/api-repo git-checkout no │ ╰──────────────────────────────────────────────────────────╯

╭─ Defaults ──────────────────────────────╮ │ Sandbox: git_worktree │ │ Validations: 0 │ │ Context Filters: none │ │ Automation Profile: (inherits global) │ ╰─────────────────────────────────────────╯

✓ OK Project created


# Register a required validation (tests must pass)
$ agents validation add \
    --config validations/unit-tests.yaml \
    --required

╭─ Validation Registered ─────────────────────────────────────╮ │ Name: local/unit-tests │ │ Command: pytest tests/ │ │ Description: Unit tests │ │ Mode: required │ │ Timeout: 300s (default) │ ╰─────────────────────────────────────────────────────────────╯

✓ OK Validation registered

# Attach the validation to the project $ agents validation attach --project local/api-service local/unit-tests

✓ OK Validation attached to project local/api-service

Step 2: Create an Action

Create a file actions/fix-bug.yaml:


name: local/fix-bug
description: "Fix a reported bug in the codebase"
long_description: |
  Analyze the bug description, locate the relevant code,
  implement a fix, and add or update tests to cover the fix.
definition_of_done: |
  - The described bug no longer reproduces
  - All existing tests pass
  - At least one new test covers the fixed behavior
  - No unrelated code is modified
strategy_actor: anthropic/claude-3.5-sonnet
execution_actor: anthropic/claude-3.5-sonnet
reusable: true
state: available
args:
  - name: bug_description
    type: string
    required: true
    description: "Description of the bug to fix"
  - name: affected_file
    type: string
    required: false
    description: "File path where the bug is suspected (optional hint)"

# Register the action
$ agents action create --config ./actions/fix-bug.yaml

╭─ Action Created ─────────────────────────────╮ │ Name: local/fix-bug │ │ ID: 01HXR1B1C2D3E4F5G6H7I8J9K0 │ │ State: available │ │ Strategy Actor: anthropic/claude-3.5-sonnet │ │ Execution Actor: anthropic/claude-3.5-sonnet │ │ Reusable: yes │ │ Read Only: no │ │ Created: 2026-02-11 09:16 │ ╰──────────────────────────────────────────────╯

╭─ Definition of Done ──────────────────────────────────╮ │ - The described bug no longer reproduces │ │ - All existing tests pass │ │ - At least one new test covers the fixed behavior │ │ - No unrelated code is modified │ ╰───────────────────────────────────────────────────────╯

╭─ Arguments ───────────────────────────────────────────────────────────────╮ │ Name Type Required Description │ │ ─────────────── ────── ──────── ────────────────────────────────────── │ │ bug_description string yes Description of the bug to fix │ │ affected_file string no File path where the bug is suspected │ ╰───────────────────────────────────────────────────────────────────────────╯

╭─ Automation ──────────────────────────╮ │ Profile: (inherits global) │ │ Source: default │ ╰───────────────────────────────────────╯

╭─ Usage ──────────────────────────────────────────────────────────────────╮ │ agents plan use local/fix-bug local/api-service │ │ --arg bug_description="..." │ ╰──────────────────────────────────────────────────────────────────────────╯

✓ OK Action created

Step 3: Create and Execute a Plan


# Bind the action to the project with the manual profile for full oversight
$ agents plan use \
    --automation-profile manual \
    --arg bug_description="The /health endpoint returns HTTP 500 when the database \
connection is unavailable. It should return HTTP 200 with {\"status\": \"degraded\", \
\"database\": \"unavailable\"} instead of crashing." \
    --arg affected_file="src/routes/health.py" \
    local/fix-bug local/api-service

╭─ Plan Created ──────────────────────────╮ │ Plan ID: 01HXR1C1D2E3F4G5H6I7J8K9L0 │ │ Phase: strategize │ │ Action: local/fix-bug │ │ Project: local/api-service │ │ Automation: manual │ │ Attempt: 1 │ ╰─────────────────────────────────────────╯

╭─ Inputs ──────────────────────────────────────────────────────────────╮ │ - bug_description="The /health endpoint returns HTTP 500 when the │ │ database connection is unavailable. It should return HTTP 200 with │ │ {"status": "degraded", "database": "unavailable"} instead │ │ of crashing." │ │ - affected_file="src/routes/health.py" │ │ - automation_profile=manual │ ╰───────────────────────────────────────────────────────────────────────╯

╭─ Actors ──────────────────────────────────────╮ │ Strategy: anthropic/claude-3.5-sonnet │ │ Execution: anthropic/claude-3.5-sonnet │ │ Estimation: (none) │ ╰───────────────────────────────────────────────╯

╭─ Automation ──────────────────────────╮ │ Profile: manual │ │ Source: plan override │ │ Read-Only: no │ ╰───────────────────────────────────────╯

╭─ Context ─────────────────────────╮ │ Resources: 1 (git-checkout) │ │ Indexed Files: 41 │ │ View: strategize │ │ Hot Token Budget: 8,000 │ ╰───────────────────────────────────╯

╭─ Next Steps ───────────────────────────────────────╮ │ - agents plan execute 01HXR1C1D2E3F4G5H6I7J8K9L0 │ │ - agents plan status 01HXR1C1D2E3F4G5H6I7J8K9L0 │ │ - agents plan tree 01HXR1C1D2E3F4G5H6I7J8K9L0 │ ╰────────────────────────────────────────────────────╯

✓ OK Plan created

Because the automation profile is manual, the plan stops and waits after creation. The developer must explicitly trigger each phase.


# Manually trigger Strategize → the strategy actor analyzes the codebase
$ agents plan execute 01HXR1C1D2E3F4G5H6I7J8K9L0

╭─ Execution ─────────────────────────────╮ │ Plan: 01HXR1C1D2E3F4G5H6I7J8K9L0 │ │ Phase: strategize │ │ Sandbox: git_worktree │ │ Worker: anthropic/claude-3.5-sonnet │ │ Started: 09:17:02 │ │ Attempt: 1 │ ╰─────────────────────────────────────────╯

╭─ Sandbox ───────────────────────────────────────────────────────╮ │ Strategy: git_worktree │ │ Path: /home/dev/projects/api-service/.worktrees/plan-01HXR1C1 │ │ Branch: cleveragents/plan-01HXR1C1D2 │ │ Status: active │ ╰─────────────────────────────────────────────────────────────────╯

╭─ Strategy Summary ────────────────╮ │ Decisions: 4 │ │ Invariants: 0 │ │ Planned Child Plans: 0 │ │ Estimated Files: ~2 │ │ Risk: low │ ╰───────────────────────────────────╯

╭─ Progress ────────╮ │ Collect context │ │ Build strategy │ │ Awaiting review │ ╰───────────────────╯

✓ OK Strategize complete — awaiting manual approval

The strategy actor has read the codebase, identified the relevant files, and produced decisions. Since edit_code is 1.0 (manual), the plan pauses for review:


# Review the decision tree
$ agents plan tree 01HXR1C1D2E3F4G5H6I7J8K9L0

╭─ Decision Tree ───────────────────────────────────────────────────────────────────────────╮ │ Plan: 01HXR1C1D2E3F4G5H6I7J8K9L0 │ │ ├─ [prompt_definition] "Fix /health endpoint to return 200 when DB unavailable" │ │ ├─ [strategy_choice] "Wrap DB call in try/except, return degraded status" (conf: 0.95) │ │ ├─ [strategy_choice] "Modify src/routes/health.py only" (conf: 0.91) │ │ └─ [strategy_choice] "Add test_health_db_unavailable to tests/test_health.py" (conf: 0.93)│ ╰───────────────────────────────────────────────────────────────────────────────────────────╯

╭─ Tree Summary ─────────────╮ │ Nodes: 4 │ │ Depth: 1 │ │ Child Plans: 0 │ │ Invariants: 0 │ │ Superseded: 0 (hidden) │ ╰────────────────────────────╯

╭─ Decision IDs (for correction) ──────────────────╮ │ Root: 01HXR1D1A2B3C4D5E6F7G8H9 │ │ Strategy 1: 01HXR1D2A2B3C4D5E6F7G8H9 │ │ Strategy 2: 01HXR1D3A2B3C4D5E6F7G8H9 │ │ Strategy 3: 01HXR1D4A2B3C4D5E6F7G8H9 │ ╰──────────────────────────────────────────────────╯

✓ OK Decision tree rendered


# Inspect the main strategy decision in detail
$ agents plan explain 01HXR1D2A2B3C4D5E6F7G8H9

╭─ Decision ──────────────────────────────────────────────────────────────╮ │ ID: 01HXR1D2A2B3C4D5E6F7G8H9 │ │ Type: strategy_choice │ │ Question: How should the /health endpoint handle database failures? │ │ Chosen: Wrap DB call in try/except, return degraded status │ │ Confidence: 0.95 │ │ Plan: 01HXR1C1D2E3F4G5H6I7J8K9L0 │ │ Sequence: 1 of 3 │ │ Created: 2026-02-11 09:17 │ ╰─────────────────────────────────────────────────────────────────────────╯

╭─ Alternatives Considered ──────────────────────────────────────────────────╮ │ 1. Wrap DB call in try/except, return degraded status (chosen) │ │ 2. Add a circuit breaker middleware for all DB-dependent routes │ │ 3. Return HTTP 503 Service Unavailable with retry-after header │ ╰────────────────────────────────────────────────────────────────────────────╯

╭─ Impact ──────────────────────╮ │ Downstream Decisions: 2 │ │ Downstream Child Plans: 0 │ │ Artifacts Produced: 2 │ │ Correction Impact: low │ ╰───────────────────────────────╯

╭─ Rationale ──────────────────────────────────────────────────╮ │ The bug description explicitly requires HTTP 200 with a │ │ degraded status JSON body. A try/except around the DB call │ │ in the health endpoint is the minimal, targeted fix. A │ │ circuit breaker would be over-engineering for a single │ │ endpoint. HTTP 503 contradicts the requested behavior. │ ╰──────────────────────────────────────────────────────────────╯

╭─ Correction ─────────────────────────────────────────────────╮ │ agents plan correct 01HXR1D2A2B3C4D5E6F7G8H9 │ │ --mode revert --guidance "Use a different approach..." │ ╰──────────────────────────────────────────────────────────────╯

✓ OK Decision explained


# Approve and proceed to Execute (manual profile requires explicit trigger)
$ agents plan execute 01HXR1C1D2E3F4G5H6I7J8K9L0

╭─ Execution ─────────────────────────────╮ │ Plan: 01HXR1C1D2E3F4G5H6I7J8K9L0 │ │ Phase: execute │ │ Sandbox: git_worktree │ │ Worker: anthropic/claude-3.5-sonnet │ │ Started: 09:18:30 │ │ Attempt: 1 │ ╰─────────────────────────────────────────╯

╭─ Sandbox ───────────────────────────────────────────────────────╮ │ Strategy: git_worktree │ │ Path: /home/dev/projects/api-service/.worktrees/plan-01HXR1C1 │ │ Branch: cleveragents/plan-01HXR1C1D2 │ │ Status: active │ ╰─────────────────────────────────────────────────────────────────╯

╭─ Progress ────────────────────────────────╮ │ Collect context (0.4s) │ │ Modify src/routes/health.py (1.2s) │ │ Add tests/test_health.py test (2.1s) │ │ Validate: pytest tests/ (3.8s) │ ╰───────────────────────────────────────────╯

✓ OK Execution complete — awaiting apply

The execution actor worked in the sandbox (git worktree), modifying src/routes/health.py and adding a test in tests/test_health.py. Validations ran automatically within the sandbox and passed.

Step 4: Review and Apply


# Review the changes
$ agents plan diff 01HXR1C1D2E3F4G5H6I7J8K9L0

╭─ Diff Summary ──────────────────────────────────╮ │ Plan: 01HXR1C1D2E3F4G5H6I7J8K9L0 │ │ Project: local/api-service │ │ Files Changed: 2 │ │ Insertions: 19 │ │ Deletions: 3 │ │ Net Change: +16 lines │ ╰─────────────────────────────────────────────────╯

╭─ Files ────────────────────────────────────╮ │ Path Change Status │ │ ─────────────────────── ────── ──────── │ │ src/routes/health.py +8 -3 modified │ │ tests/test_health.py +11 -0 modified │ ╰────────────────────────────────────────────╯

╭─ Patch Preview ──────────────────────────────────────────────────╮ │ --- a/src/routes/health.py │ │ +++ b/src/routes/health.py │ │ @@ -8,6 +8,11 @@ │ │ @app.get("/health") │ │ def health_check(): │ │ - db_status = check_database_connection() │ │ - return {"status": "ok", "database": db_status} │ │ + try: │ │ + db_status = check_database_connection() │ │ + return {"status": "ok", "database": "available"} │ │ + except ConnectionError: │ │ + return {"status": "degraded", "database": "unavailable"}│ │ --- a/tests/test_health.py │ │ +++ b/tests/test_health.py │ │ @@ -15,0 +16,11 @@ │ │ +def test_health_db_unavailable(client, monkeypatch): │ │ + def mock_check(): │ │ + raise ConnectionError("DB unreachable") │ │ + monkeypatch.setattr("src.routes.health.check_database…") │ │ + response = client.get("/health") │ │ + assert response.status_code == 200 │ │ + data = response.json() │ │ + assert data["status"] == "degraded" │ │ + assert data["database"] == "unavailable" │ ╰──────────────────────────────────────────────────────────────────╯

╭─ Risk Assessment ────────────────╮ │ API Compatibility: preserved │ │ Test Coverage: increased │ │ Breaking Changes: none detected │ ╰──────────────────────────────────╯

✓ OK Diff generated


# Check plan status (should be execute/complete, awaiting apply)
$ agents plan status 01HXR1C1D2E3F4G5H6I7J8K9L0

╭─ Plan Status ───────────────────────────╮ │ Plan: 01HXR1C1D2E3F4G5H6I7J8K9L0 │ │ Phase: execute │ │ State: complete │ │ Action: local/fix-bug │ │ Project: local/api-service │ │ Automation: manual │ │ Attempt: 1 │ ╰─────────────────────────────────────────╯

╭─ Progress ───────────╮ │ Strategize │ │ Execute │ │ Apply (awaiting) │ ╰──────────────────────╯

╭─ Timing ───────────────────────────╮ │ Started: 2026-02-11 09:17:02 │ │ Elapsed: 00:01:42 │ │ ETA: awaiting manual apply │ ╰────────────────────────────────────╯

╭─ Execution Detail ──────────╮ │ Sandbox: git_worktree │ │ Tool Calls: 5 │ │ Files Modified: 2 │ │ Child Plans: 0 │ │ Checkpoints: 1 created │ ╰─────────────────────────────╯

╭─ Cost ───────────────╮ │ Tokens Used: 6,840 │ │ Cost So Far: $0.023 │ │ Estimated: $0.025 │ ╰──────────────────────╯

✓ OK Status refreshed


# Apply the changes to the real repository
$ agents plan apply --yes 01HXR1C1D2E3F4G5H6I7J8K9L0

╭─ Apply Summary ──────────────────────────╮ │ Plan: 01HXR1C1D2E3F4G5H6I7J8K9L0 │ │ Artifacts: 2 files updated │ │ Changes: 19 insertions, 3 deletions │ │ Project: local/api-service │ │ Applied At: 2026-02-11 09:19 │ ╰──────────────────────────────────────────╯

╭─ Validation ───────────────────╮ │ Tests: passed (14/14) │ │ Duration: 3.8s │ ╰────────────────────────────────╯

╭─ Sandbox Cleanup ──────────────────────╮ │ Worktree: removed │ │ Branch: merged to main │ │ Checkpoint: archived │ ╰────────────────────────────────────────╯

╭─ Plan Lifecycle ──────────────────────╮ │ Phase: apply │ │ State: applied │ │ Total Duration: 00:01:58 │ │ Total Cost: $0.025 │ │ Decisions Made: 4 │ │ Child Plans: 0 │ ╰───────────────────────────────────────╯

╭─ Next Steps ──────╮ │ - Review git diff │ │ - Commit changes │ ╰───────────────────╯

✓ OK Changes applied

The sandbox changes are merged into the main working tree. The bug is fixed.


Example 2: Automated Test Generation for a Module

Scenario: A team wants to increase test coverage for their auth module from 45% to 80%. They trust CleverAgents to generate tests autonomously, but want to review before applying.

Complexity: Intermediate. Single project, trusted automation profile, validation-driven.

Step 1: Setup (Assuming Project Exists)


# Register a coverage validation
# (project local/api-service and resource local/api-service-repo already exist)
$ agents validation add \
    --config validations/auth-coverage.yaml \
    --required \
    local/auth-coverage

╭─ Validation Registered ──────────────────────────────────────╮ │ Name: local/auth-coverage │ │ Command: pytest --cov=src/auth --cov-fail-under=80 │ │ tests/test_auth/ │ │ Description: Coverage must be at least 80% │ │ Mode: required │ │ Timeout: 600s │ ╰──────────────────────────────────────────────────────────────╯

✓ OK Validation registered

$ agents validation attach --project local/api-service local/auth-coverage

✓ OK Validation attached to project local/api-service

# Automation profile will be set per-plan via --automation-profile on plan use

Step 2: Create the Test Generation Action

Create actions/generate-tests.yaml:


name: local/generate-tests
description: "Generate comprehensive tests for a module"
long_description: |
  Analyze the target module's code paths, identify untested branches,
  edge cases, and error conditions, then generate thorough test cases.
  Uses the project's existing test framework and conventions.
definition_of_done: |
  - Coverage of the target module reaches the specified threshold
  - All new tests pass
  - All existing tests continue to pass
  - Tests follow existing project conventions (fixtures, naming, structure)
  - No production code is modified
strategy_actor: anthropic/claude-3.5-sonnet
execution_actor: anthropic/claude-3.5-sonnet
reusable: true
state: available
read_only: false
args:
  - name: target_module
    type: string
    required: true
    description: "Module path to generate tests for (e.g., src/auth)"
  - name: coverage_target
    type: integer
    required: false
    description: "Target coverage percentage"
    default: 80
invariants:
  - "Do not modify any production code — only add or modify test files"
  - "Follow the existing test file naming convention (test_<module>.py)"
  - "Use the project's existing test fixtures and conftest.py patterns"

$ agents action create --config ./actions/generate-tests.yaml

╭─ Action Created ───────────────────────────────────╮ │ Name: local/generate-tests │ │ ID: 01HXR2B1C2D3E4F5G6H7I8J9K0 │ │ State: available │ │ Strategy Actor: anthropic/claude-3.5-sonnet │ │ Execution Actor: anthropic/claude-3.5-sonnet │ │ Reusable: yes │ │ Read Only: no │ │ Config: ./actions/generate-tests.yaml │ │ Created: 2026-02-11 09:15 │ ╰────────────────────────────────────────────────────╯

╭─ Definition of Done ──────────────────────────────────────────────╮ │ - Coverage of the target module reaches the specified threshold │ │ - All new tests pass │ │ - All existing tests continue to pass │ │ - Tests follow existing project conventions │ │ - No production code is modified │ ╰───────────────────────────────────────────────────────────────────╯

╭─ Arguments ───────────────────────────────────────────────────────────────────╮ │ Name Type Required Description │ │ ──────────────── ─────── ──────── ─────────────────────────────────────── │ │ target_module string yes Module path to generate tests for │ │ coverage_target integer no Target coverage percentage (default 80) │ ╰───────────────────────────────────────────────────────────────────────────────╯

╭─ Invariants ──────────────────────────────────────────────────────────────────╮ │ 1. Do not modify any production code — only add or modify test files │ │ 2. Follow the existing test file naming convention (test_<module>.py) │ │ 3. Use the project's existing test fixtures and conftest.py patterns │ ╰───────────────────────────────────────────────────────────────────────────────╯

╭─ Automation ──────────────────────────╮ │ Profile: supervised │ │ Source: default │ ╰───────────────────────────────────────╯

✓ OK Action created

Step 3: Execute


# With trusted profile: Strategize and Execute run automatically,
# only Apply requires human approval
$ agents plan use \
    --automation-profile trusted \
    --arg target_module="src/auth" \
    --arg coverage_target=80 \
    local/generate-tests local/api-service

╭─ Plan Created ──────────────────────────────────────╮ │ Plan: 01HXR2B3C4D5E6F7G8H9J0K1L2 │ │ Phase: strategize │ │ State: processing │ │ Action: local/generate-tests │ │ Project: local/api-service │ │ Automation: trusted │ │ Attempt: 1 │ ╰─────────────────────────────────────────────────────╯

╭─ Inputs ────────────────────────────╮ │ - target_module=src/auth │ │ - coverage_target=80 │ │ - automation_profile=trusted │ ╰─────────────────────────────────────╯

╭─ Actors ────────────────────────────────────────╮ │ Strategy: anthropic/claude-3.5-sonnet │ │ Execution: anthropic/claude-3.5-sonnet │ │ Estimation: (none) │ ╰─────────────────────────────────────────────────╯

╭─ Automation ───────────────────────────╮ │ Profile: trusted │ │ Source: plan override │ │ Read-Only: no │ ╰────────────────────────────────────────╯

╭─ Context ────────────────────────╮ │ Resources: 1 (repo) │ │ Indexed Files: 214 │ │ View: strategize │ │ Hot Token Budget: 8,000 │ ╰──────────────────────────────────╯

╭─ Next Steps ─────────────────────────────────────────────────────────╮ │ Trusted profile — strategize and execute will proceed automatically. │ │ - agents plan status 01HXR2B3C4D5E6F7G8H9J0K1L2 │ │ - agents plan tree 01HXR2B3C4D5E6F7G8H9J0K1L2 │ ╰──────────────────────────────────────────────────────────────────────╯

✓ OK Plan created

The system runs through Strategize and Execute automatically. The strategy actor analyzes coverage gaps, identifies 12 untested code paths, and plans test cases. The execution actor writes tests in the sandbox. Validations run. If coverage reaches 80%, the plan reaches execute/complete and waits for apply.


# Check progress while it runs
$ agents plan status 01HXR2B3C4D5E6F7G8H9J0K1L2

╭─ Plan Status ───────────────────────────────╮ │ Plan: 01HXR2B3C4D5E6F7G8H9J0K1L2 │ │ Phase: execute │ │ State: processing │ │ Action: local/generate-tests │ │ Project: local/api-service │ │ Automation: trusted │ │ Attempt: 1 │ ╰─────────────────────────────────────────────╯

╭─ Progress ───────╮ │ Strategize │ │ Execute │ │ Apply (queued) │ ╰──────────────────╯

╭─ Timing ──────────╮ │ Started: 09:15:42 │ │ Elapsed: 00:02:18 │ │ ETA: 00:04:30 │ ╰───────────────────╯

╭─ Execution Detail ──────────╮ │ Sandbox: git_worktree │ │ Tool Calls: 14 │ │ Files Modified: 5 │ │ Child Plans: 0 │ │ Checkpoints: 3 created │ ╰─────────────────────────────╯

╭─ Cost ───────────────╮ │ Tokens Used: 18,740 │ │ Cost So Far: $0.062 │ │ Estimated: $0.110 │ ╰──────────────────────╯

✓ OK Status refreshed


# When complete, review the generated tests
$ agents plan diff 01HXR2B3C4D5E6F7G8H9J0K1L2

╭─ Diff Summary ─────────────────────────────────────╮ │ Plan: 01HXR2B3C4D5E6F7G8H9J0K1L2 │ │ Project: local/api-service │ │ Files Changed: 5 │ │ Insertions: 287 │ │ Deletions: 3 │ │ Net Change: +284 lines │ ╰────────────────────────────────────────────────────╯

╭─ Files ────────────────────────────────────────────────╮ │ Path Change Status │ │ ────────────────────────────── ──────── ────────── │ │ tests/test_auth/test_login.py +72 -0 new file │ │ tests/test_auth/test_tokens.py +68 -0 new file │ │ tests/test_auth/test_session.py +54 -0 new file │ │ tests/test_auth/test_rbac.py +41 -0 new file │ │ tests/test_auth/conftest.py +52 -3 modified │ ╰────────────────────────────────────────────────────────╯

╭─ Patch Preview ───────────────────────────────────────────╮ │ --- /dev/null │ │ +++ b/tests/test_auth/test_login.py │ │ @@ -0,0 +1,72 @@ │ │ + import pytest │ │ + from src.auth.login import authenticate, verify_mfa │ │ + ... │ │ --- a/tests/test_auth/conftest.py │ │ +++ b/tests/test_auth/conftest.py │ │ @@ -1,5 +1,54 @@ │ │ - # minimal fixtures │ │ + # shared auth test fixtures │ │ + @pytest.fixture │ │ + def mock_user_db(): ... │ ╰───────────────────────────────────────────────────────────╯

╭─ Risk Assessment ────────────────╮ │ API Compatibility: preserved │ │ Test Coverage: 45% → 83% │ │ Breaking Changes: none detected │ ╰──────────────────────────────────╯

✓ OK Diff generated


$ agents plan artifacts 01HXR2B3C4D5E6F7G8H9J0K1L2

╭─ Artifacts ──────────────────────────────────────────────────────────╮ │ Path Type Size Change Child Plan │ │ ──────────────────────────────── ───── ────── ────── ─────── │ │ tests/test_auth/test_login.py write 3.8 KB +72 -0 root │ │ tests/test_auth/test_tokens.py write 3.4 KB +68 -0 root │ │ tests/test_auth/test_session.py write 2.7 KB +54 -0 root │ │ tests/test_auth/test_rbac.py write 2.1 KB +41 -0 root │ │ tests/test_auth/conftest.py edit 2.6 KB +52 -3 root │ ╰──────────────────────────────────────────────────────────────────────╯

╭─ Summary ────────────╮ │ Total: 5 │ │ Writes: 4 (new) │ │ Edits: 1 (modified) │ │ Deletes: 0 │ │ Total Size: 14.6 KB │ ╰──────────────────────╯

✓ OK 5 artifacts listed


# Apply the generated tests to the real repository
$ agents plan apply --yes 01HXR2B3C4D5E6F7G8H9J0K1L2

╭─ Apply Summary ──────────────────────────╮ │ Plan: 01HXR2B3C4D5E6F7G8H9J0K1L2 │ │ Artifacts: 5 files updated │ │ Changes: 287 insertions, 3 deletions │ │ Project: local/api-service │ │ Applied At: 2026-02-11 09:22 │ ╰──────────────────────────────────────────╯

╭─ Validation ──────────────────────────╮ │ Coverage: passed (83% ≥ 80%) │ │ All tests: passed (47/47) │ │ Duration: 18.2s │ ╰───────────────────────────────────────╯

╭─ Sandbox Cleanup ─────────╮ │ Worktree: removed │ │ Branch: merged to main │ │ Checkpoint: archived │ ╰───────────────────────────╯

╭─ Plan Lifecycle ─────────────────────────╮ │ Phase: apply │ │ State: applied │ │ Total Duration: 00:06:38 │ │ Total Cost: $0.107 │ │ Decisions Made: 12 │ │ Child Plans: 0 │ ╰──────────────────────────────────────────╯

╭─ Next Steps ──────╮ │ - Review git diff │ │ - Commit changes │ ╰───────────────────╯

✓ OK Changes applied


Example 3: Multi-File Refactoring with Invariants

Scenario: A senior engineer wants to refactor the authentication module to replace raw SQL queries with an ORM. The refactoring must maintain API backward compatibility and all existing tests must pass. Multiple invariants constrain the work.

Complexity: Intermediate-Advanced. Single project, multiple invariants, decision tree review.

Step 1: Define Invariants


$ agents invariant add --global "All public APIs must maintain backward compatibility"

╭─ Invariant Added ──────────────────────────────────────────────────────╮ │ Invariant: All public APIs must maintain backward compatibility │ │ Scope: global │ │ ID: inv_01HXR3A1B │ ╰────────────────────────────────────────────────────────────────────────╯

✓ OK Invariant added

$ agents invariant add --project local/api-service
"Database queries must use the SQLAlchemy ORM, not raw SQL"

╭─ Invariant Added ───────────────────────────────────────────────────────╮ │ Project: local/api-service │ │ Invariant: Database queries must use the SQLAlchemy ORM, not raw SQL │ │ Scope: project │ │ ID: inv_01HXR3A2C │ ╰─────────────────────────────────────────────────────────────────────────╯

✓ OK Invariant added

$ agents invariant list --project local/api-service --effective

╭─ Effective Invariants (Project local/api-service) ─────────────────────────────────────╮ │ ID Source Text │ │ ────────────── ──────── ────────────────────────────────────────────────────── │ │ inv_01HXR3A1B global All public APIs must maintain backward compatibility │ │ inv_01HXR3A2C project Database queries must use the SQLAlchemy ORM, not raw SQL │ ╰────────────────────────────────────────────────────────────────────────────────────────╯

✓ OK 2 effective invariants (1 global, 1 project)

Step 2: Create a Refactoring Action with Custom Actors

Create actors/refactoring-strategist.yaml:

name: local/refactoring-strategist
cleveragents:
  version: "3.0"
actors:
  strategist:
    type: llm
    config:
      provider: anthropic
      model: claude-3.5-sonnet
      temperature: 0.2
      system_prompt: |
        You are an expert software architect specializing in database layer refactoring.
        You analyze codebases to plan safe, incremental refactoring strategies.
        You always preserve backward compatibility and plan for comprehensive testing.
        Project: {{ context.project_name }}
        Resources: {{ context.resources | join(', ') }}
      memory_enabled: true
      max_history: 100

$ agents actor add --config ./actors/refactoring-strategist.yaml

╭─ Actor Added ────────────────────────────────────╮ │ Name: local/refactoring-strategist │ │ Provider: anthropic │ │ Model: claude-3.5-sonnet │ │ Default: yes │ │ Unsafe: no │ │ Type: graph │ ╰──────────────────────────────────────────────────╯

╭─ Config ────────────────────────────────────────────────────╮ │ Path: ./actors/refactoring-strategist.yaml │ │ Hash: 7a1e4c9 │ │ Options: 3 │ │ Nodes: 1 │ │ Edges: 0 │ ╰─────────────────────────────────────────────────────────────╯

╭─ Capabilities ────────────────╮ │ - database layer refactoring │ │ - incremental strategy │ │ - backward compatibility │ ╰───────────────────────────────╯

✓ OK Actor added

Create actions/refactor-to-orm.yaml:

name: local/refactor-to-orm
description: "Refactor raw SQL queries to use SQLAlchemy ORM"
long_description: |
  Systematically replace all raw SQL queries in the target module with
  SQLAlchemy ORM equivalents. Create ORM model definitions, replace
  query construction, update transaction handling, and ensure all
  existing behavior is preserved.
definition_of_done: |
  - Zero raw SQL strings remain in the target module
  - All ORM models are defined with proper relationships
  - All existing tests pass without modification
  - New integration tests verify ORM query correctness
  - API response formats are unchanged
  - Database migration script is generated
strategy_actor: local/refactoring-strategist
execution_actor: anthropic/claude-3.5-sonnet
estimation_actor: anthropic/claude-3.5-sonnet
reusable: true
state: available
automation_profile: cautious
args:
  - name: target_module
    type: string
    required: true
    description: "Module to refactor"
invariants:
  - "Each file must be refactored in a separate commit-sized change"
  - "ORM models must be defined before queries are converted"
  - "All raw SQL must be replaced — no partial conversion"

$ agents action create --config ./actions/refactor-to-orm.yaml

╭─ Action Created ─────────────────────────────────────────────╮ │ Name: local/refactor-to-orm │ │ ID: 01HXR3B1D2E3F4G5H6J7K8L9M0 │ │ State: available │ │ Strategy Actor: local/refactoring-strategist │ │ Execution Actor: anthropic/claude-3.5-sonnet │ │ Reusable: yes │ │ Read Only: no │ │ Created: 2026-02-11 09:14 │ ╰──────────────────────────────────────────────────────────────╯

╭─ Definition of Done ──────────────────────────────────────────╮ │ - Zero raw SQL strings remain in the target module │ │ - All ORM models are defined with proper relationships │ │ - All existing tests pass without modification │ │ - New integration tests verify ORM query correctness │ │ - API response formats are unchanged │ │ - Database migration script is generated │ ╰───────────────────────────────────────────────────────────────╯

╭─ Arguments ──────────────────────────────────────────────╮ │ Name Type Required Description │ │ ────────────── ────── ──────── ───────────────────── │ │ target_module string yes Module to refactor │ ╰──────────────────────────────────────────────────────────╯

╭─ Automation ──────────────────────────╮ │ Profile: cautious │ │ Source: action config │ ╰───────────────────────────────────────╯

╭─ Action Invariants ──────────────────────────────────────────────────────╮ │ 1. Each file must be refactored in a separate commit-sized change │ │ 2. ORM models must be defined before queries are converted │ │ 3. All raw SQL must be replaced — no partial conversion │ ╰──────────────────────────────────────────────────────────────────────────╯

╭─ Usage ───────────────────────────────────────────────────────────────────╮ │ agents plan use local/refactor-to-orm local/api-service │ │ --arg target_module="src/auth" │ ╰───────────────────────────────────────────────────────────────────────────╯

✓ OK Action created

Step 3: Execute with Cautious Profile


$ agents plan use \
    --arg target_module="src/auth" \
    local/refactor-to-orm local/api-service

╭─ Plan Created ───────────────────────────────────────╮ │ Plan: 01HXR3C4D5E6F7G8H9J0K1L2M3 │ │ Action: local/refactor-to-orm │ │ Project: local/api-service │ │ Automation: cautious │ │ Attempt: 1 │ ╰──────────────────────────────────────────────────────╯

╭─ Inputs ────────────────────────╮ │ - target_module="src/auth" │ │ - automation_profile=cautious │ ╰─────────────────────────────────╯

╭─ Actors ───────────────────────────────────────╮ │ Strategy: local/refactoring-strategist │ │ Execution: anthropic/claude-3.5-sonnet │ │ Estimation: anthropic/claude-3.5-sonnet │ ╰────────────────────────────────────────────────╯

╭─ Automation ──────────────────────────────╮ │ Profile: cautious │ │ Source: action config │ │ Read-Only: no │ ╰───────────────────────────────────────────╯

╭─ Context ───────────────────────╮ │ Resources: 2 (repo, db) │ │ Indexed Files: 214 │ │ View: strategize │ │ Hot Token Budget: 16,000 │ ╰─────────────────────────────────╯

╭─ Next Steps ──────────────────────────────────────────╮ │ - agents plan execute 01HXR3C4D5E6F7G8H9J0K1L2M3 │ │ - agents plan status 01HXR3C4D5E6F7G8H9J0K1L2M3 │ │ - agents plan tree 01HXR3C4D5E6F7G8H9J0K1L2M3 │ ╰───────────────────────────────────────────────────────╯

✓ OK Plan created

Because the cautious profile uses intermediate confidence thresholds, the system:

  1. Enters Strategize automatically (threshold 0.7, confidence is high for well-understood tasks)
  2. Makes strategy decisions automatically when confidence >= 0.6, but pauses for complex architectural choices
  3. The Invariant Reconciliation Actor reconciles global ("backward compatibility"), project ("use ORM"), and action ("separate commits") invariants

# The system pauses at a decision where confidence is below threshold.
# Review the decision tree:

$ agents plan tree 01HXR3C4D5E6F7G8H9J0K1L2M3

╭─ Decision Tree ────────────────────────────────────────────────────────────────────────────╮ │ Plan: 01HXR3C4D5E6F7G8H9J0K1L2M3 │ │ ├─ [prompt_definition] "Refactor raw SQL in src/auth to SQLAlchemy ORM" │ │ ├─ [invariant_enforced] "All public APIs must maintain backward compatibility" │ │ ├─ [invariant_enforced] "Database queries must use the SQLAlchemy ORM" │ │ ├─ [invariant_enforced] "Each file refactored in a separate commit-sized change" │ │ ├─ [invariant_enforced] "ORM models must be defined before queries are converted" │ │ ├─ [strategy_choice] "Define ORM models first, then convert queries" (conf: 0.85) │ │ ├─ [strategy_choice] "Place models inline in route files" (conf: 0.48) ⏸ PAUSED │ │ └─ (awaiting input — confidence below threshold) │ ╰────────────────────────────────────────────────────────────────────────────────────────────╯

╭─ Tree Summary ─────────────╮ │ Nodes: 8 │ │ Depth: 2 │ │ Child Plans: 0 │ │ Invariants: 4 │ │ Superseded: 0 (hidden) │ ╰────────────────────────────╯

╭─ Decision IDs (for correction) ──────────────────────╮ │ Root: 01HXR3D1A2B3C4D5E6F7G8H9 │ │ Invariant 1: 01HXR3D1B3C4D5E6F7G8H9J0 │ │ Invariant 2: 01HXR3D1C4D5E6F7G8H9J0K1 │ │ Invariant 3: 01HXR3D1D5E6F7G8H9J0K1L2 │ │ Invariant 4: 01HXR3D1E6F7G8H9J0K1L2M3 │ │ Strategy 1: 01HXR3D2E3F4G5H6I7J8K9L0 │ │ Strategy 2: 01HXR3D5F6G7H8I9J0K1L2M3 (paused) │ ╰──────────────────────────────────────────────────────╯

✓ OK Decision tree rendered

# The paused decision is about model placement. Inspect it:

$ agents plan explain 01HXR3D5F6G7H8I9J0K1L2M3

╭─ Decision ──────────────────────────────────────────────╮ │ ID: 01HXR3D5F6G7H8I9J0K1L2M3 │ │ Type: strategy_choice │ │ Question: Where should ORM model classes be placed? │ │ Chosen: Place models inline in route files │ │ Confidence: 0.48 │ │ Plan: 01HXR3C4D5E6F7G8H9J0K1L2M3 │ │ Sequence: 6 of 7 │ │ Created: 2026-02-11 09:15 │ ╰─────────────────────────────────────────────────────────╯

╭─ Alternatives Considered ─────────────────────────────────────╮ │ 1. Place models inline in route files (chosen) │ │ 2. Separate models/ directory with one model per file │ │ 3. Single models.py file alongside routes │ ╰───────────────────────────────────────────────────────────────╯

╭─ Impact ──────────────────────╮ │ Downstream Decisions: 4 │ │ Downstream Child Plans: 0 │ │ Artifacts Produced: 0 │ │ Correction Impact: high │ ╰───────────────────────────────╯

╭─ Rationale ────────────────────────────────────────────────────────╮ │ Placing models inline minimizes import changes and keeps related │ │ code co-located. However, this conflicts with project conventions │ │ and may make models harder to reuse across modules. The low │ │ confidence reflects genuine uncertainty about the trade-off. │ ╰────────────────────────────────────────────────────────────────────╯

╭─ Correction ───────────────────────────────────────────────────────╮ │ agents plan correct 01HXR3D5F6G7H8I9J0K1L2M3 │ │ --mode revert --guidance "Use separate models/ directory..." │ ╰────────────────────────────────────────────────────────────────────╯

✓ OK Decision explained

# Provide guidance via the plan prompt command

$ agents plan prompt 01HXR3C4D5E6F7G8H9J0K1L2M3
"Convert the User model first, then Session, then Token.
Use Alembic for the migration script."

╭─ Guidance Added ─────────────────────────────────────────────────────────╮ │ Plan: 01HXR3C4D5E6F7G8H9J0K1L2M3 │ │ Guidance: Convert the User model first, then Session, then Token. │ │ Use Alembic for the migration script. │ │ Scope: next execution step │ │ Phase: strategize │ │ State: awaiting_input → processing │ ╰──────────────────────────────────────────────────────────────────────────╯

╭─ Decision Created ───────────────────────────╮ │ Type: user_intervention │ │ ID: 01HXR3E1A2B3C4D5E6F7G8H9 │ │ Parent: 01HXR3D5F6G7H8I9J0K1L2M3 │ ╰──────────────────────────────────────────────╯

╭─ Queue ────╮ │ Pending: 1 │ │ Applied: 0 │ ╰────────────╯

✓ OK Guidance queued

# Execution continues. Monitor progress:

$ agents plan status 01HXR3C4D5E6F7G8H9J0K1L2M3

╭─ Plan Status ───────────────────────────────────╮ │ Plan: 01HXR3C4D5E6F7G8H9J0K1L2M3 │ │ Phase: execute │ │ State: processing │ │ Action: local/refactor-to-orm │ │ Project: local/api-service │ │ Automation: cautious │ │ Attempt: 1 │ ╰─────────────────────────────────────────────────╯

╭─ Progress ───────╮ │ Strategize │ │ Execute │ │ Apply (queued) │ ╰──────────────────╯

╭─ Timing ──────────╮ │ Started: 09:14:22 │ │ Elapsed: 00:02:38 │ │ ETA: 00:05:10 │ ╰───────────────────╯

╭─ Execution Detail ──────────╮ │ Sandbox: git_worktree │ │ Tool Calls: 14 │ │ Files Modified: 4 │ │ Child Plans: 0/0 complete │ │ Checkpoints: 3 created │ ╰─────────────────────────────╯

╭─ Cost ───────────────╮ │ Tokens Used: 18,740 │ │ Cost So Far: $0.062 │ │ Estimated: $0.128 │ ╰──────────────────────╯

✓ OK Status refreshed

Step 4: Correct a Decision

During execution, you notice the strategy chose to inline the ORM models in the same file as the routes. You want them in a separate models/ directory:


# Find the decision that chose inline models

$ agents plan tree --show-superseded 01HXR3C4D5E6F7G8H9J0K1L2M3

╭─ Decision Tree ────────────────────────────────────────────────────────────────────────────╮ │ Plan: 01HXR3C4D5E6F7G8H9J0K1L2M3 │ │ ├─ [prompt_definition] "Refactor raw SQL in src/auth to SQLAlchemy ORM" │ │ ├─ [invariant_enforced] "All public APIs must maintain backward compatibility" │ │ ├─ [invariant_enforced] "Database queries must use the SQLAlchemy ORM" │ │ ├─ [invariant_enforced] "Each file refactored in a separate commit-sized change" │ │ ├─ [invariant_enforced] "ORM models must be defined before queries are converted" │ │ ├─ [strategy_choice] "Define ORM models first, then convert queries" (conf: 0.85) │ │ ├─ [strategy_choice] "Place models inline in route files" (superseded) │ │ ├─ [user_intervention] "Convert User first, then Session, then Token" │ │ ├─ [strategy_choice] "Create User ORM model in routes/auth.py" (conf: 0.74) │ │ ├─ [tool_invocation] write_file src/auth/routes/auth.py │ │ └─ [tool_invocation] write_file src/auth/routes/session.py │ ╰────────────────────────────────────────────────────────────────────────────────────────────╯

╭─ Tree Summary ─────────────╮ │ Nodes: 11 │ │ Depth: 3 │ │ Child Plans: 0 │ │ Invariants: 4 │ │ Superseded: 1 (shown) │ ╰────────────────────────────╯

╭─ Decision IDs (for correction) ──────────────────────╮ │ Root: 01HXR3D1A2B3C4D5E6F7G8H9 │ │ Strategy 1: 01HXR3D2E3F4G5H6I7J8K9L0 │ │ Strategy 2: 01HXR3D5F6G7H8I9J0K1L2M3 (superseded) │ │ Intervention: 01HXR3E1A2B3C4D5E6F7G8H9 │ │ Strategy 3: 01HXR3F1A2B3C4D5E6F7G8H9 │ │ Tool 1: 01HXR3F2B3C4D5E6F7G8H9J0 │ │ Tool 2: 01HXR3F3C4D5E6F7G8H9J0K1 │ ╰──────────────────────────────────────────────────────╯

✓ OK Decision tree rendered

# Correct it — revert mode replays from that decision

$ agents plan correct 01HXR3F1A2B3C4D5E6F7G8H9
--mode revert
--guidance "Place ORM models in src/auth/models/ with one model per file.
Import them in src/auth/models/__init__.py."
--yes

╭─ Correction ────────────────────────────────────────────────╮ │ Mode: revert │ │ Impact: 2 decisions, 0 child plans, 2 artifacts │ │ New Decision: 01HXR3G1A2B3C4D5E6F7G8H9 │ │ Corrects: 01HXR3F1A2B3C4D5E6F7G8H9 │ │ Attempt: 2 │ │ Correction ID: 01HXR3CORR1A2B3C4D5E6 │ ╰─────────────────────────────────────────────────────────────╯

╭─ Affected Subtree ──────────────╮ │ Decisions Invalidated: 2 │ │ Child Plans Rolled Back: 0 │ │ Artifacts Archived: 2 │ │ Unaffected Decisions: 6 │ ╰─────────────────────────────────╯

╭─ Sandbox Rollback ─────────────╮ │ Checkpoint: cp_01HXR3C4 │ │ Files Reverted: 2 │ │ Status: restored │ ╰────────────────────────────────╯

╭─ Recompute ───────────────╮ │ Queued: re-execute │ │ ETA: 3m │ ╰───────────────────────────╯

╭─ History ───────────────────────────────────────────────╮ │ - Original decision superseded │ │ - Prior artifacts archived for comparison │ │ - agents plan diff --correction 01HXR3CORR1A2B3C4D5E6 │ ╰─────────────────────────────────────────────────────────╯

✓ OK Correction applied

# The affected subtree is recomputed. Review the new approach:

$ agents plan diff --correction 01HXR3CORR1A2B3C4D5E6

╭─ Correction Diff ────────────────────────────────────────────╮ │ Correction: 01HXR3CORR1A2B3C4D5E6 │ │ Original Decision: 01HXR3F1A2B3C4D5E6F7G8H9 │ │ Mode: revert │ │ Files Changed: 5 │ │ New Insertions: 84 │ │ New Deletions: 36 │ ╰──────────────────────────────────────────────────────────────╯

╭─ Comparison ──────────────────────────────────────────────────────────────╮ │ File Before (original) After (corrected) │ │ ────────────────────────────── ──────────────── ──────────────────── │ │ src/auth/routes/auth.py +42 -8 (inline) +6 -8 (imports only) │ │ src/auth/routes/session.py +28 -6 (inline) +4 -6 (imports only) │ │ src/auth/models/__init__.py (did not exist) (new file) │ │ src/auth/models/user.py (did not exist) (new file, +38) │ │ src/auth/models/session.py (did not exist) (new file, +26) │ ╰───────────────────────────────────────────────────────────────────────────╯

╭─ Patch Preview (corrected vs original) ───────────────────────────╮ │ --- a/src/auth/routes/auth.py (original) │ │ +++ b/src/auth/routes/auth.py (corrected) │ │ @@ -1,42 +1,6 @@ │ │ - class User(Base): # was inline │ │ - tablename = "users" │ │ - id = Column(Integer, primary_key=True) │ │ + from src.auth.models import User │ │ ... │ │ +++ b/src/auth/models/user.py (new file) │ │ @@ -0,0 +1,38 @@ │ │ + from sqlalchemy import Column, Integer, String │ │ + from sqlalchemy.orm import relationship │ │ + class User(Base): │ │ + tablename = "users" │ │ ... │ ╰───────────────────────────────────────────────────────────────────╯

✓ OK Correction diff generated

Step 5: Apply


$ agents plan diff 01HXR3C4D5E6F7G8H9J0K1L2M3

╭─ Diff Summary ───────────────────────────────────────╮ │ Plan: 01HXR3C4D5E6F7G8H9J0K1L2M3 │ │ Project: local/api-service │ │ Files Changed: 9 │ │ Insertions: 186 │ │ Deletions: 94 │ │ Net Change: +92 lines │ ╰──────────────────────────────────────────────────────╯

╭─ Files ───────────────────────────────────────────────╮ │ Path Change Status │ │ ────────────────────────────── ──────── ────────── │ │ src/auth/models/__init__.py +8 new file │ │ src/auth/models/user.py +38 new file │ │ src/auth/models/session.py +26 new file │ │ src/auth/models/token.py +22 new file │ │ src/auth/routes/auth.py +12 -28 modified │ │ src/auth/routes/session.py +8 -22 modified │ │ src/auth/routes/token.py +6 -18 modified │ │ src/auth/db.py +14 -26 modified │ │ alembic/versions/001_orm.py +52 new file │ ╰───────────────────────────────────────────────────────╯

╭─ Risk Assessment ────────────────╮ │ API Compatibility: preserved │ │ Test Coverage: maintained │ │ Breaking Changes: none detected │ ╰──────────────────────────────────╯

✓ OK Diff generated

$ agents plan apply --yes 01HXR3C4D5E6F7G8H9J0K1L2M3

╭─ Apply Summary ───────────────────────────────────╮ │ Plan: 01HXR3C4D5E6F7G8H9J0K1L2M3 │ │ Artifacts: 9 files updated │ │ Changes: 186 insertions, 94 deletions │ │ Project: local/api-service │ │ Applied At: 2026-02-11 09:23 │ ╰───────────────────────────────────────────────────╯

╭─ Validation ───────────────────────╮ │ Tests: passed (47/47) │ │ Lint: passed (0 warnings) │ │ Type Check: passed (0 errors) │ │ Duration: 18.6s │ ╰────────────────────────────────────╯

╭─ Sandbox Cleanup ─────────╮ │ Worktree: removed │ │ Branch: merged to main │ │ Checkpoint: archived │ ╰───────────────────────────╯

╭─ Plan Lifecycle ───────────────────────────────╮ │ Phase: apply │ │ State: applied │ │ Total Duration: 00:08:42 │ │ Total Cost: $0.128 │ │ Decisions Made: 11 │ │ Corrections: 1 │ │ Child Plans: 0 │ ╰────────────────────────────────────────────────╯

╭─ Next Steps ──────╮ │ - Review git diff │ │ - Commit changes │ ╰───────────────────╯

✓ OK Changes applied


Example 4: Multi-Project Dependency Update

Scenario: An organization has three microservices that share a common library. The library has a breaking change that requires coordinated updates across all three services. The update must be validated in each service independently before any changes are applied.

Complexity: Advanced. Multiple projects, child plans, coordinated apply.

Step 1: Register All Projects and Resources

# Register the common-lib resource (full output shown)

$ agents resource add git-checkout local/common-lib-repo --path /repos/common-lib --branch main

╭─ Resource ──────────────────────────────────────╮ │ Name: local/common-lib-repo │ │ ID: 01HXR4A1B2C3D4E5F6G7H8J9K0 │ │ Type: git-checkout │ │ Physical/Virtual: physical │ │ Path: /repos/common-lib │ │ Branch: main │ │ Created: 2026-02-11 10:00 │ ╰─────────────────────────────────────────────────╯

╭─ Auto-discovered Children ─────────────────────────────────────────╮ │ ID Type Status │ │ ─────────────── ────────────── ───────────────── │ │ 01HXR4A1B2C3… git created │ │ 01HXR4A1B2C4… git-remote created │ │ 01HXR4A1B2C5… git-branch created │ │ + 31 git-commit resources │ │ + 186 git-tree-entry resources │ │ + 2 fs-directory + 14 fs-file │ ╰────────────────────────────────────────────────────────────────────╯

╭─ Capabilities ─────────────────╮ │ Readable: yes │ │ Writable: yes │ │ Sandboxable: yes │ │ Checkpointable: yes │ │ Sandbox Strategy: git_worktree │ ╰────────────────────────────────╯

✓ OK Resource registered (236 child resources discovered)

# Register the remaining 3 service resources (condensed output)

$ agents resource add git-checkout local/user-service-repo --path /repos/user-service --branch main ✓ OK Resource registered — Name: local/user-service-repo (312 children)

$ agents resource add git-checkout local/order-service-repo --path /repos/order-service --branch main ✓ OK Resource registered — Name: local/order-service-repo (287 children)

$ agents resource add git-checkout local/billing-service-repo --path /repos/billing-service --branch main ✓ OK Resource registered — Name: local/billing-service-repo (264 children)

# Create the common-lib project (full output shown)

$ agents project create -d "Shared common library"
--resource local/common-lib-repo local/common-lib

╭─ Project Created ─────────────────────────╮ │ Name: local/common-lib │ │ Description: Shared common library │ │ Type: local │ │ Created: 2026-02-11 10:01 │ ╰───────────────────────────────────────────╯

╭─ Linked Resources ───────────────────────────────────────╮ │ Resource Type Read-Only │ │ ───────────────────── ────────────── ───────── │ │ local/common-lib-repo git-checkout no │ ╰──────────────────────────────────────────────────────────╯

╭─ Defaults ──────────────────────────────╮ │ Sandbox: git_worktree │ │ Validations: 0 │ │ Context Filters: none │ │ Automation Profile: (inherits global) │ ╰─────────────────────────────────────────╯

✓ OK Project created

# Create the remaining 3 service projects (condensed output)

$ agents project create -d "User microservice"
--resource local/user-service-repo local/user-service ✓ OK Project created — Name: local/user-service

$ agents project create -d "Order microservice"
--resource local/order-service-repo local/order-service ✓ OK Project created — Name: local/order-service

$ agents project create -d "Billing microservice"
--resource local/billing-service-repo local/billing-service ✓ OK Project created — Name: local/billing-service

# Register a shared validation — first one with full output

$ agents validation add --config validations/pytest-mypy.yaml --required local/pytest-mypy

╭─ Validation Registered ─────────────────────────────╮ │ Name: local/pytest-mypy │ │ Command: pytest tests/ && mypy src/ │ │ Mode: required │ │ Timeout: 300s │ ╰─────────────────────────────────────────────────────╯

✓ OK Validation registered

# Attach the validation to all 4 projects

$ agents validation attach --project local/common-lib local/pytest-mypy ✓ OK Validation attached to project local/common-lib

$ agents validation attach --project local/user-service local/pytest-mypy ✓ OK Validation attached to project local/user-service

$ agents validation attach --project local/order-service local/pytest-mypy ✓ OK Validation attached to project local/order-service

$ agents validation attach --project local/billing-service local/pytest-mypy ✓ OK Validation attached to project local/billing-service

Step 2: Create a Coordinated Update Action

Create actions/coordinated-dependency-update.yaml:

name: local/coordinated-dep-update
description: "Update a shared dependency across multiple projects"
long_description: |
  When a shared library introduces a breaking change, this action coordinates
  the update across all dependent projects. It analyzes the breaking changes,
  creates per-project update plans as child plans, validates each independently,
  and produces a coordinated changeset.
definition_of_done: |
  - The shared library is updated to the target version
  - All dependent projects compile and pass tests with the new version
  - API contracts between services remain compatible
  - Migration guides are generated for each project if needed
  - All changes can be applied atomically or rolled back as a unit
strategy_actor: anthropic/claude-3.5-sonnet
execution_actor: anthropic/claude-3.5-sonnet
automation_profile: supervised
reusable: true
state: available
args:
  - name: library_name
    type: string
    required: true
    description: "Name of the shared library to update"
  - name: target_version
    type: string
    required: true
    description: "Target version to update to"
  - name: changelog_url
    type: string
    required: false
    description: "URL to the library changelog or migration guide"
invariants:
  - "Each dependent project must be updated in its own child plan"
  - "All child plans must pass validation before any can be applied"
  - "The library update in common-lib must be applied first"
$ agents action create --config ./actions/coordinated-dependency-update.yaml

╭─ Action Created ─────────────────────────────────────────────╮ │ Name: local/coordinated-dep-update │ │ ID: 01HXR4B1D2E3F4G5H6J7K8L9M0 │ │ State: available │ │ Strategy Actor: anthropic/claude-3.5-sonnet │ │ Execution Actor: anthropic/claude-3.5-sonnet │ │ Reusable: yes │ │ Read Only: no │ │ Created: 2026-02-11 10:02 │ ╰──────────────────────────────────────────────────────────────╯

╭─ Definition of Done ──────────────────────────────────────────────────╮ │ - The shared library is updated to the target version │ │ - All dependent projects compile and pass tests with the new version │ │ - API contracts between services remain compatible │ │ - Migration guides are generated for each project if needed │ │ - All changes can be applied atomically or rolled back as a unit │ ╰───────────────────────────────────────────────────────────────────────╯

╭─ Arguments ──────────────────────────────────────────────────────────────────────╮ │ Name Type Required Description │ │ ────────────── ────── ──────── ────────────────────────────────────────── │ │ library_name string yes Name of the shared library to update │ │ target_version string yes Target version to update to │ │ changelog_url string no URL to the library changelog or migration │ ╰──────────────────────────────────────────────────────────────────────────────────╯

╭─ Automation ──────────────────────────╮ │ Profile: supervised │ │ Source: action config │ ╰───────────────────────────────────────╯

╭─ Action Invariants ──────────────────────────────────────────────────────────────╮ │ 1. Each dependent project must be updated in its own child plan │ │ 2. All child plans must pass validation before any can be applied │ │ 3. The library update in common-lib must be applied first │ ╰──────────────────────────────────────────────────────────────────────────────────╯

╭─ Usage ───────────────────────────────────────────────────────────────────────────╮ │ agents plan use local/coordinated-dep-update local/common-lib │ │ --arg library_name="authlib" --arg target_version="2.0.0" │ ╰───────────────────────────────────────────────────────────────────────────────────╯

✓ OK Action created

Step 3: Execute Across Multiple Projects

# Create a plan targeting all four projects

$ agents plan use
--automation-profile supervised
--arg library_name="authlib"
--arg target_version="2.0.0"
--arg changelog_url="https://authlib.org/changelog/2.0"
local/coordinated-dep-update
local/common-lib local/user-service local/order-service local/billing-service

╭─ Plan Created ────────────────────────────────────────────╮ │ Plan: 01HXR4D5E6F7G8H9J0K1L2M3N4 │ │ Action: local/coordinated-dep-update │ │ Projects: local/common-lib, local/user-service, │ │ local/order-service, local/billing-service │ │ Automation: supervised │ │ Attempt: 1 │ ╰───────────────────────────────────────────────────────────╯

╭─ Inputs ───────────────────────────────────────────────────────────╮ │ - library_name="authlib" │ │ - target_version="2.0.0" │ │ - changelog_url="https://authlib.org/changelog/2.0" │ │ - automation_profile=supervised │ ╰────────────────────────────────────────────────────────────────────╯

╭─ Actors ───────────────────────────────────────╮ │ Strategy: anthropic/claude-3.5-sonnet │ │ Execution: anthropic/claude-3.5-sonnet │ ╰────────────────────────────────────────────────╯

╭─ Automation ──────────────────────────────╮ │ Profile: supervised │ │ Source: CLI override │ │ Read-Only: no │ ╰───────────────────────────────────────────╯

╭─ Context ──────────────────────────╮ │ Resources: 4 (git-checkout) │ │ Indexed Files: 1,099 │ │ View: strategize │ │ Hot Token Budget: 24,000 │ ╰────────────────────────────────────╯

╭─ Next Steps ──────────────────────────────────────────────╮ │ - agents plan tree 01HXR4D5E6F7G8H9J0K1L2M3N4 │ │ - agents plan status 01HXR4D5E6F7G8H9J0K1L2M3N4 │ │ - agents plan execute 01HXR4D5E6F7G8H9J0K1L2M3N4 │ ╰───────────────────────────────────────────────────────────╯

✓ OK Plan created

The system creates a parent plan. Strategize runs automatically (supervised profile). It produces a strategy that:

  1. Analyzes the breaking changes in authlib 2.0.0
  2. Plans child plans for each project
  3. Orders the updates: common-lib first, then services in parallel
# Review the strategy before execution begins

$ agents plan tree 01HXR4D5E6F7G8H9J0K1L2M3N4

╭─ Decision Tree ─────────────────────────────────────────────────────────────────────────────────────╮ │ Plan: 01HXR4D5E6F7G8H9J0K1L2M3N4 │ │ ├─ [prompt_definition] "Update authlib to 2.0.0 across 4 projects" │ │ ├─ [invariant_enforced] "Each dependent project must be updated in its own child plan" │ │ ├─ [invariant_enforced] "All child plans must pass validation before any can be applied" │ │ ├─ [invariant_enforced] "The library update in common-lib must be applied first" │ │ ├─ [strategy_choice] "Analyze changelog for breaking API changes" (conf: 0.92) │ │ ├─ [strategy_choice] "Update common-lib first, then services in parallel" (conf: 0.88) │ │ ├─ [strategy_choice] "Create 4 child plans: common-lib, user-svc, order-svc, billing-svc" │ │ │ (conf: 0.90) │ │ └─ (awaiting execution approval — supervised profile) │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────╯

╭─ Tree Summary ──────────────╮ │ Nodes: 8 │ │ Depth: 2 │ │ Child Plans: 0 (planned: 4) │ │ Invariants: 3 │ │ Superseded: 0 (hidden) │ ╰─────────────────────────────╯

╭─ Decision IDs (for correction) ──────────────────────────╮ │ Root: 01HXR4D6A1B2C3D4E5F6G7H8 │ │ Invariant 1: 01HXR4D6B2C3D4E5F6G7H8J9 │ │ Invariant 2: 01HXR4D6C3D4E5F6G7H8J9K0 │ │ Invariant 3: 01HXR4D6D4E5F6G7H8J9K0L1 │ │ Strategy 1: 01HXR4D7E5F6G7H8I9J0K1L2 │ │ Strategy 2: 01HXR4D7F6G7H8I9J0K1L2M3 │ │ Strategy 3: 01HXR4D7G7H8I9J0K1L2M3N4 │ ╰──────────────────────────────────────────────────────────╯

✓ OK Decision tree rendered

$ agents plan status 01HXR4D5E6F7G8H9J0K1L2M3N4

╭─ Plan Status ──────────────────────────────────────────╮ │ Plan: 01HXR4D5E6F7G8H9J0K1L2M3N4 │ │ Phase: strategize → execute (pending approval) │ │ State: awaiting_input │ │ Action: local/coordinated-dep-update │ │ Projects: 4 │ │ Automation: supervised │ │ Attempt: 1 │ ╰────────────────────────────────────────────────────────╯

╭─ Progress ────────────────────────────╮ │ Strategize │ │ Execute (awaiting approval) │ │ Apply (queued) │ ╰───────────────────────────────────────╯

╭─ Timing ──────────╮ │ Started: 10:02:14 │ │ Elapsed: 00:01:42 │ │ ETA: 00:12:00 │ ╰───────────────────╯

╭─ Cost ───────────────╮ │ Tokens Used: 8,420 │ │ Cost So Far: $0.028 │ │ Estimated: $0.340 │ ╰──────────────────────╯

✓ OK Status refreshed

# Manually approve execution (supervised requires explicit approval)

$ agents plan execute 01HXR4D5E6F7G8H9J0K1L2M3N4

╭─ Execution Started ──────────────────────────────────────╮ │ Plan: 01HXR4D5E6F7G8H9J0K1L2M3N4 │ │ Phase: execute │ │ State: processing │ │ Automation: supervised │ ╰──────────────────────────────────────────────────────────╯

╭─ Child Plans Spawned ───────────────────────────────────────────────────╮ │ Plan ID Project Status │ │ ────────────────────────── ─────────────────── ──────────────── │ │ 01HXR4C1D2E3F4G5H6I7J8K9 local/common-lib executing │ │ 01HXR4C2E3F4G5H6I7J8K9L0 local/user-service waiting (common) │ │ 01HXR4C3F4G5H6I7J8K9L0M1 local/order-service waiting (common) │ │ 01HXR4C4G5H6I7J8K9L0M1N2 local/billing-service waiting (common) │ ╰─────────────────────────────────────────────────────────────────────────╯

╭─ Execution Order ──────────────────────────────────────────╮ │ Phase 1: common-lib (blocking) │ │ Phase 2: user-service, order-service, billing-service │ │ (parallel, after common-lib completes) │ ╰────────────────────────────────────────────────────────────╯

✓ OK Execution started (4 child plans spawned)

Step 4: Review and Apply in Order

Once all child plans complete execution and pass validation, review and apply them in dependency order.

# Review common-lib changes first (must be applied before services)

$ agents plan diff 01HXR4C1D2E3F4G5H6I7J8K9

╭─ Diff Summary ───────────────────────────────────────╮ │ Plan: 01HXR4C1D2E3F4G5H6I7J8K9 │ │ Project: local/common-lib │ │ Files Changed: 4 │ │ Insertions: 62 │ │ Deletions: 38 │ │ Net Change: +24 lines │ ╰──────────────────────────────────────────────────────╯

╭─ Files ───────────────────────────────────────────────────────╮ │ Path Change Status │ │ ──────────────────────────────── ──────── ────────── │ │ requirements.txt +1 -1 modified │ │ src/common/auth.py +28 -19 modified │ │ src/common/token_validator.py +18 -12 modified │ │ tests/test_auth.py +15 -6 modified │ ╰───────────────────────────────────────────────────────────────╯

╭─ Patch Preview ───────────────────────────────────────────────────╮ │ --- a/requirements.txt │ │ +++ b/requirements.txt │ │ @@ -3,1 +3,1 @@ │ │ - authlib==1.3.2 │ │ + authlib==2.0.0 │ │ --- a/src/common/auth.py │ │ +++ b/src/common/auth.py │ │ @@ -1,6 +1,8 @@ │ │ - from authlib.integrations.requests_client import OAuth2Session │ │ + from authlib.integrations.httpx_client import AsyncOAuth2Client │ │ ... │ ╰───────────────────────────────────────────────────────────────────╯

╭─ Validation ────────────────────────╮ │ pytest tests/ && mypy src/: passed │ │ Duration: 12.4s │ ╰─────────────────────────────────────╯

✓ OK Diff generated

$ agents plan apply --yes 01HXR4C1D2E3F4G5H6I7J8K9

╭─ Apply Summary ──────────────────────────────────────╮ │ Plan: 01HXR4C1D2E3F4G5H6I7J8K9 │ │ Artifacts: 4 files updated │ │ Changes: 62 insertions, 38 deletions │ │ Project: local/common-lib │ │ Applied At: 2026-02-11 10:14 │ ╰──────────────────────────────────────────────────────╯

╭─ Validation ───────────────────────╮ │ Tests: passed (24/24) │ │ Type Check: passed (0 errors) │ │ Duration: 12.4s │ ╰────────────────────────────────────╯

╭─ Sandbox Cleanup ─────────╮ │ Worktree: removed │ │ Branch: merged to main │ │ Checkpoint: archived │ ╰───────────────────────────╯

╭─ Plan Lifecycle ───────────────────────────────╮ │ Phase: apply │ │ State: applied │ │ Total Duration: 00:03:18 │ │ Total Cost: $0.064 │ ╰────────────────────────────────────────────────╯

✓ OK Changes applied

# Once common-lib is applied, review the 3 service child plans.
# Full diff shown for user-service; order-service and billing-service condensed.

$ agents plan diff 01HXR4C2E3F4G5H6I7J8K9L0

╭─ Diff Summary ───────────────────────────────────────╮ │ Plan: 01HXR4C2E3F4G5H6I7J8K9L0 │ │ Project: local/user-service │ │ Files Changed: 6 │ │ Insertions: 84 │ │ Deletions: 52 │ │ Net Change: +32 lines │ ╰──────────────────────────────────────────────────────╯

╭─ Files ───────────────────────────────────────────────────────╮ │ Path Change Status │ │ ──────────────────────────────── ──────── ────────── │ │ requirements.txt +1 -1 modified │ │ src/auth/middleware.py +22 -14 modified │ │ src/auth/oauth.py +18 -12 modified │ │ src/auth/token.py +16 -11 modified │ │ tests/test_middleware.py +14 -8 modified │ │ tests/test_oauth.py +13 -6 modified │ ╰───────────────────────────────────────────────────────────────╯

╭─ Validation ────────────────────────╮ │ pytest tests/ && mypy src/: passed │ │ Duration: 18.2s │ ╰─────────────────────────────────────╯

✓ OK Diff generated

$ agents plan diff 01HXR4C3F4G5H6I7J8K9L0M1

╭─ Diff Summary ───────────────────────────────────────╮ │ Plan: 01HXR4C3F4G5H6I7J8K9L0M1 │ │ Project: local/order-service │ │ Files Changed: 5 │ │ Insertions: 71 │ │ Deletions: 46 │ │ Net Change: +25 lines │ ╰──────────────────────────────────────────────────────╯

╭─ Validation ────────────────────────╮ │ pytest tests/ && mypy src/: passed │ │ Duration: 14.7s │ ╰─────────────────────────────────────╯

✓ OK Diff generated

$ agents plan diff 01HXR4C4G5H6I7J8K9L0M1N2

╭─ Diff Summary ───────────────────────────────────────╮ │ Plan: 01HXR4C4G5H6I7J8K9L0M1N2 │ │ Project: local/billing-service │ │ Files Changed: 4 │ │ Insertions: 58 │ │ Deletions: 34 │ │ Net Change: +24 lines │ ╰──────────────────────────────────────────────────────╯

╭─ Validation ────────────────────────╮ │ pytest tests/ && mypy src/: passed │ │ Duration: 11.3s │ ╰─────────────────────────────────────╯

✓ OK Diff generated

# Apply all 3 service child plans

$ agents plan apply --yes 01HXR4C2E3F4G5H6I7J8K9L0

╭─ Apply Summary ──────────────────────────────────────╮ │ Plan: 01HXR4C2E3F4G5H6I7J8K9L0 │ │ Artifacts: 6 files updated │ │ Changes: 84 insertions, 52 deletions │ │ Project: local/user-service │ │ Applied At: 2026-02-11 10:16 │ ╰──────────────────────────────────────────────────────╯

╭─ Validation ───────────────────────╮ │ Tests: passed (89/89) │ │ Type Check: passed (0 errors) │ │ Duration: 18.2s │ ╰────────────────────────────────────╯

╭─ Sandbox Cleanup ─────────╮ │ Worktree: removed │ │ Branch: merged to main │ │ Checkpoint: archived │ ╰───────────────────────────╯

╭─ Plan Lifecycle ───────────────────────────────╮ │ Phase: apply │ │ State: applied │ │ Total Duration: 00:04:12 │ │ Total Cost: $0.082 │ ╰────────────────────────────────────────────────╯

✓ OK Changes applied

$ agents plan apply --yes 01HXR4C3F4G5H6I7J8K9L0M1

╭─ Apply Summary ──────────────────────────────────────╮ │ Plan: 01HXR4C3F4G5H6I7J8K9L0M1 │ │ Artifacts: 5 files updated │ │ Changes: 71 insertions, 46 deletions │ │ Project: local/order-service │ │ Applied At: 2026-02-11 10:17 │ ╰──────────────────────────────────────────────────────╯

╭─ Validation ───────────────────────╮ │ Tests: passed (76/76) │ │ Type Check: passed (0 errors) │ │ Duration: 14.7s │ ╰────────────────────────────────────╯

╭─ Plan Lifecycle ───────────────────────────────╮ │ Phase: apply │ │ State: applied │ │ Total Duration: 00:03:48 │ │ Total Cost: $0.074 │ ╰────────────────────────────────────────────────╯

✓ OK Changes applied

$ agents plan apply --yes 01HXR4C4G5H6I7J8K9L0M1N2

╭─ Apply Summary ──────────────────────────────────────╮ │ Plan: 01HXR4C4G5H6I7J8K9L0M1N2 │ │ Artifacts: 4 files updated │ │ Changes: 58 insertions, 34 deletions │ │ Project: local/billing-service │ │ Applied At: 2026-02-11 10:18 │ ╰──────────────────────────────────────────────────────╯

╭─ Validation ───────────────────────╮ │ Tests: passed (63/63) │ │ Type Check: passed (0 errors) │ │ Duration: 11.3s │ ╰────────────────────────────────────╯

╭─ Plan Lifecycle ───────────────────────────────╮ │ Phase: apply │ │ State: applied │ │ Total Duration: 00:03:22 │ │ Total Cost: $0.068 │ ╰────────────────────────────────────────────────╯

╭─ Parent Plan Complete ──────────────────────────────────╮ │ Plan: 01HXR4D5E6F7G8H9J0K1L2M3N4 │ │ Phase: apply │ │ State: applied │ │ Child Plans: 4/4 complete │ │ Total Duration: 00:16:04 │ │ Total Cost: $0.340 │ │ All Validations: passed │ ╰─────────────────────────────────────────────────────────╯

✓ OK Changes applied


Example 5: Database Schema Migration with Safety Nets

Scenario: A team needs to add a last_login_at column to a users table with 10 million rows, backfill it from an audit log, and update the application code — all without downtime.

Complexity: Advanced. Mixed resource types (git + database), custom tools, checkpointing critical.

Step 1: Register the Database as a Resource

Register the custom resource type, then create a resource instance and link it to the project:


# Register the custom postgres-db resource type
$ agents resource type add --config ./resource-types/postgres-db.yaml

╭─ Resource Type ─────────────────────────╮ │ Name: local/postgres-db │ │ Physical/Virtual: physical │ │ User Addable: yes │ │ Registered: 2026-02-11 09:01 │ ╰─────────────────────────────────────────╯

╭─ CLI Arguments ──────────────────────────────────────────╮ │ Argument Required Description │ │ ──────────── ──────── ────────────────────────────── │ │ --host yes Database hostname │ │ --port no Port (default: 5432) │ │ --database yes Database name │ │ --schema no Schema (default: public) │ ╰──────────────────────────────────────────────────────────╯

╭─ Sandbox ──────────────────────────────╮ │ Strategy: transaction_rollback │ │ Handler: PostgresHandler (custom) │ ╰────────────────────────────────────────╯

✓ OK Resource type registered New subcommand available: agents resource add local/postgres-db


# Register the production database instance
$ agents resource add local/postgres-db local/prod-users-db \
    --host db.internal.example.com \
    --port 5432 \
    --database users_db \
    --schema public

╭─ Resource ───────────────────────────────────────╮ │ Name: local/prod-users-db │ │ ID: 01HXR5D3E4F5G6H7J8K9L0M1N2 │ │ Type: local/postgres-db │ │ Physical/Virtual: physical │ │ Host: db.internal.example.com │ │ Port: 5432 │ │ Database: users_db │ │ Schema: public │ │ Created: 2026-02-11 09:02 │ ╰──────────────────────────────────────────────────╯

╭─ Capabilities ──────────────────────────╮ │ Readable: yes │ │ Writable: yes │ │ Sandboxable: yes │ │ Checkpointable: yes │ │ Sandbox Strategy: transaction_rollback │ ╰─────────────────────────────────────────╯

✓ OK Resource registered


# Link the database resource to the project
$ agents project link-resource local/api-service local/prod-users-db

╭─ Resource Linked ────────────────────────────╮ │ Project: local/api-service │ │ Resource: local/prod-users-db │ │ Type: local/postgres-db │ │ Read-Only: no │ ╰──────────────────────────────────────────────╯

╭─ Access ─────────────────╮ │ Read: allowed │ │ Write: allowed │ │ Apply: requires approval │ ╰──────────────────────────╯

╭─ Project Resources ─────────────────────────────────────────────╮ │ ID Type Status │ │ ────────────────────────────── ──────────────── ────────── │ │ 01HXR1A1B2C3D4E5F6G7H8J9K0 git-checkout linked │ │ 01HXR5D3E4F5G6H7J8K9L0M1N2 local/postgres-db linked (new) │ ╰─────────────────────────────────────────────────────────────────╯

✓ OK Resource linked to project

Step 2: Create a Custom Skill with Database Tools

Create skills/database-ops.yaml:


name: local/database-ops
description: "Safe database operations with transaction support"
tools:
  - name: query_db
    description: "Execute a read-only SQL query and return results"
    input_schema:
      type: object
      properties:
        sql:
          type: string
          description: "SQL SELECT query"
        params:
          type: array
          description: "Query parameters"
      required: [sql]
    writes: false
    checkpointable: false
    resource_slots:
      - name: database
        type: local/postgres-db
        binding: contextual
  • name: execute_migration description: "Execute a DDL migration within a transaction" input_schema: type: object properties: up_sql: type: string description: "Forward migration SQL" down_sql: type: string description: "Rollback migration SQL" description: type: string description: "Human-readable description" required: [up_sql, down_sql, description] writes: true checkpointable: true resource_slots:

    • name: database type: local/postgres-db binding: contextual
  • name: backfill_column description: "Batch-update a column using a source query" input_schema: type: object properties: table: type: string column: type: string source_query: type: string description: "Query that returns (id, value) pairs" batch_size: type: integer default: 10000 required: [table, column, source_query] writes: true checkpointable: true resource_slots:

    • name: database type: local/postgres-db binding: contextual

$ agents skill add --config ./skills/database-ops.yaml

╭─ Skill Registered ─────────────────────────────────────────────╮ │ Name: local/database-ops │ │ Description: Safe database operations with transaction support │ │ Config: ./skills/database-ops.yaml │ │ Created: 2026-02-11 09:04 │ ╰────────────────────────────────────────────────────────────────╯

╭─ Tool Sources ──────────────────────────────────────╮ │ Source Count Details │ │ ──────── ───── ──────────────────────────── │ │ custom 3 query_db, execute_migration, │ │ backfill_column │ │ ──────── ───── ──────────────────────────── │ │ Total: 3 │ ╰─────────────────────────────────────────────────────╯

╭─ Resource Slots ───────────────────────────────────╮ │ Tool Slot Type │ │ ───────────────── ──────── ────────────────── │ │ query_db database local/postgres-db │ │ execute_migration database local/postgres-db │ │ backfill_column database local/postgres-db │ ╰────────────────────────────────────────────────────╯

✓ OK Skill registered with 3 tools

Step 3: Create the Migration Action and Execute the Plan

Create actions/add-column-with-backfill.yaml:


name: local/add-column-with-backfill
description: "Add a column, backfill data, and update application code"
definition_of_done: |
  - Database migration adds the column with a sensible default
  - Backfill completes for all rows using the specified source
  - Application code reads/writes the new column
  - All tests pass with the new schema
  - A rollback migration is available and tested
strategy_actor: anthropic/claude-3.5-sonnet
execution_actor: anthropic/claude-3.5-sonnet
automation_profile: review
reusable: true
state: available
args:
  - name: table_name
    type: string
    required: true
  - name: column_name
    type: string
    required: true
  - name: column_type
    type: string
    required: true
  - name: backfill_source
    type: string
    required: true
    description: "Description of where to source backfill data"
invariants:
  - "Migration must be backward-compatible (add column, don't rename or drop)"
  - "Backfill must be batched to avoid locking the table"
  - "Rollback migration must be provided and tested"
  - "Application code must handle both old (null) and new values gracefully"

$ agents action create --config ./actions/add-column-with-backfill.yaml

╭─ Action Created ────────────────────────────────────╮ │ Name: local/add-column-with-backfill │ │ State: available │ │ Strategy Actor: anthropic/claude-3.5-sonnet │ │ Execution Actor: anthropic/claude-3.5-sonnet │ │ Reusable: yes │ │ Read Only: no │ │ Config: ./actions/add-column-with-backfill.yaml │ │ Created: 2026-02-11 09:05 │ ╰─────────────────────────────────────────────────────╯

╭─ Definition of Done ─────────────────────────────────────────────╮ │ - Database migration adds the column with a sensible default │ │ - Backfill completes for all rows using the specified source │ │ - Application code reads/writes the new column │ │ - All tests pass with the new schema │ │ - A rollback migration is available and tested │ ╰──────────────────────────────────────────────────────────────────╯

╭─ Arguments ────────────────────────────────────────────────────────────╮ │ Name Type Required Description │ │ ──────────────── ────── ──────── ───────────────────────────────── │ │ table_name string yes (none) │ │ column_name string yes (none) │ │ column_type string yes (none) │ │ backfill_source string yes Where to source backfill data │ ╰────────────────────────────────────────────────────────────────────────╯

╭─ Invariants ──────────────────────────────────────────────────────────────────────╮ │ 1. Migration must be backward-compatible (add column, don't rename or drop) │ │ 2. Backfill must be batched to avoid locking the table │ │ 3. Rollback migration must be provided and tested │ │ 4. Application code must handle both old (null) and new values gracefully │ ╰───────────────────────────────────────────────────────────────────────────────────╯

╭─ Automation ──────────────────────╮ │ Profile: review │ │ Source: action config │ ╰───────────────────────────────────╯

╭─ Usage ─────────────────────────────────────────────────────────────────────────╮ │ agents plan use local/add-column-with-backfill local/api-service │ │ --arg table_name=TABLE --arg column_name=COL │ │ --arg column_type=TYPE --arg backfill_source=SOURCE │ ╰─────────────────────────────────────────────────────────────────────────────────╯

✓ OK Action created


$ agents plan use local/add-column-with-backfill local/api-service \
    --arg table_name="users" \
    --arg column_name="last_login_at" \
    --arg column_type="TIMESTAMP WITH TIME ZONE" \
    --arg backfill_source="audit_log table, event_type='login', max(created_at) per user_id"

╭─ Plan Created ──────────────────────────────────────╮ │ Plan ID: 01HXR5E6F7G8H9J0K1L2M3N4O5 │ │ Phase: strategize │ │ Action: local/add-column-with-backfill │ │ Project: local/api-service │ │ Automation: review │ │ Attempt: 1 │ ╰─────────────────────────────────────────────────────╯

╭─ Inputs ────────────────────────────────────────────────────────────────────────────╮ │ - table_name=users │ │ - column_name=last_login_at │ │ - column_type=TIMESTAMP WITH TIME ZONE │ │ - backfill_source=audit_log table, event_type='login', max(created_at) per user_id │ ╰─────────────────────────────────────────────────────────────────────────────────────╯

╭─ Actors ───────────────────────────────────╮ │ Strategy: anthropic/claude-3.5-sonnet │ │ Execution: anthropic/claude-3.5-sonnet │ │ Estimation: (none) │ ╰────────────────────────────────────────────╯

╭─ Plan Invariants ──────────────────────────────────────────────────────────────╮ │ Scope Source Invariant │ │ ──────── ─────── ────────────────────────────────────────────────────── │ │ plan action Migration must be backward-compatible │ │ plan action Backfill must be batched to avoid locking the table │ │ plan action Rollback migration must be provided and tested │ │ plan action Application code must handle both old (null) and new values │ ╰────────────────────────────────────────────────────────────────────────────────╯

╭─ Context ──────────────────────────────╮ │ Resources: 2 (git-checkout, postgres) │ │ Indexed Files: 347 │ │ View: strategize │ │ Hot Token Budget: 12,000 │ ╰────────────────────────────────────────╯

╭─ Next Steps ───────────────────────────────────────╮ │ - agents plan execute 01HXR5E6F7G8H9J0K1L2M3N4O5 │ │ - agents plan status 01HXR5E6F7G8H9J0K1L2M3N4O5 │ │ - agents plan tree 01HXR5E6F7G8H9J0K1L2M3N4O5 │ ╰────────────────────────────────────────────────────╯

✓ OK Plan created

The strategy produces a phased plan with child plans:

  1. Generate Alembic migration (add column with NULL default)
  2. Generate rollback migration
  3. Backfill from audit_log in batches of 10,000
  4. Update ORM model and application code
  5. Run tests with new schema

Each phase creates checkpoints. If the backfill fails at batch 850 of 1,000, the system can restore to the pre-backfill checkpoint rather than starting over.

Step 4: Monitor Execution and Rollback if Needed


# Monitor the migration execution
$ agents plan status 01HXR5E6F7G8H9J0K1L2M3N4O5

╭─ Plan Status ───────────────────────────────────────╮ │ Plan: 01HXR5E6F7G8H9J0K1L2M3N4O5 │ │ Phase: execute │ │ State: processing │ │ Action: local/add-column-with-backfill │ │ Project: local/api-service │ │ Automation: review │ │ Attempt: 1 │ ╰─────────────────────────────────────────────────────╯

╭─ Progress ─────────────────╮ │ Strategize │ │ Execute (phase 3 of 5) │ │ Apply (queued) │ ╰────────────────────────────╯

╭─ Child Plans ────────────────────────────────────────────────────────────╮ │ Phase Description Status │ │ ───── ─────────────────────────────────── ────────────────────── │ │ 1 Generate Alembic migration ✓ complete │ │ 2 Generate rollback migration ✓ complete │ │ 3 Backfill from audit_log ⏳ batch 620/1,000 │ │ 4 Update ORM model and application code ○ waiting │ │ 5 Run tests with new schema ○ waiting │ ╰──────────────────────────────────────────────────────────────────────────╯

╭─ Execution Detail ──────────────────────╮ │ Sandbox: transaction_rollback │ │ Tool Calls: 14 │ │ Files Modified: 2 │ │ Rows Backfilled: 6,200,000 / 10,000,000 │ │ Checkpoints: 4 created │ ╰─────────────────────────────────────────╯

╭─ Timing ──────────────╮ │ Started: 09:06:01 │ │ Elapsed: 00:04:32 │ │ ETA: 00:02:50 │ ╰───────────────────────╯

╭─ Cost ───────────────╮ │ Tokens Used: 18,940 │ │ Cost So Far: $0.063 │ │ Estimated: $0.110 │ ╰──────────────────────╯

✓ OK Status refreshed


# If something goes wrong during backfill, rollback to the pre-backfill checkpoint
$ agents plan rollback --yes 01HXR5E6F7G8H9J0K1L2M3N4O5 cp_01HXR5CP3

╭─ Rollback Summary ───────────────────────────────────╮ │ Plan: 01HXR5E6F7G8H9J0K1L2M3N4O5 │ │ Checkpoint: cp_01HXR5CP3 │ │ Label: before backfill (after migration applied) │ │ Files: 2 reverted │ ╰──────────────────────────────────────────────────────╯

╭─ Changes Reverted ──────────────────────────────────────────╮ │ File Action │ │ ─────────────────────────────────────────── ────────── │ │ alembic/versions/20260211_add_last_login.py restored │ │ alembic/versions/20260211_rollback.py restored │ ╰─────────────────────────────────────────────────────────────╯

╭─ Database Rollback ─────────────────────────────────────────╮ │ Transaction: rolled back │ │ Rows Affected: 6,200,000 updates reverted │ │ Schema State: restored to pre-backfill │ ╰─────────────────────────────────────────────────────────────╯

╭─ Impact ──────────────────────────────────────╮ │ Child Plans Invalidated: 1 (backfill phase) │ │ Sandbox: restored to cp_01HXR5CP3 │ │ Decisions After CP: 3 discarded │ │ Tool Calls After CP: 8 undone │ ╰───────────────────────────────────────────────╯

╭─ Post-Rollback State ──────────╮ │ Phase: execute │ │ State: queued (awaiting input) │ │ Checkpoints Remaining: 3 │ ╰────────────────────────────────╯

✓ OK Rollback complete


Example 6: Writing Technical Documentation from Codebase Analysis

Scenario: A project has grown to 50,000 lines of code with minimal documentation. The team wants CleverAgents to generate comprehensive API documentation, architecture guides, and developer onboarding docs by analyzing the actual codebase.

Complexity: Intermediate. Read-only action, large context usage, no sandbox modifications to source code.

Step 1: Create a Read-Only Documentation Action

Create actions/generate-docs.yaml:


name: local/generate-docs
description: "Generate comprehensive documentation from codebase analysis"
long_description: |
  Analyze the project codebase to produce developer documentation:
  API reference, architecture overview, module guides, and onboarding
  material. Uses code intelligence (search, vector similarity, graph
  queries) to understand structure and relationships.
definition_of_done: |
  - API reference covers all public endpoints/functions
  - Architecture document shows module dependencies and data flow
  - Each major module has a developer guide
  - An onboarding document provides setup and contribution instructions
  - All generated docs are accurate to the current codebase
  - Documentation is written in Markdown format
strategy_actor: anthropic/claude-3.5-sonnet
execution_actor: anthropic/claude-3.5-sonnet
read_only: false
reusable: true
state: available
args:
  - name: doc_types
    type: string
    required: true
    description: "Comma-separated list: api-reference, architecture, module-guides, onboarding"
  - name: output_dir
    type: string
    required: false
    default: "docs/"
    description: "Output directory for generated docs"
invariants:
  - "Do not modify any source code files — only create or update files in the output directory"
  - "All code examples in documentation must be actual code from the project, not fabricated"
  - "Architecture diagrams must reflect actual module dependencies, not aspirational ones"

$ agents action create --config ./actions/generate-docs.yaml

╭─ Action Created ──────────────────────────────────────────────────────────────────╮ │ Name: local/generate-docs │ │ State: available │ │ Strategy Actor: anthropic/claude-3.5-sonnet │ │ Execution Actor: anthropic/claude-3.5-sonnet │ │ Reusable: yes │ │ Read Only: no │ │ Created: 2026-02-11 10:30 │ ╰───────────────────────────────────────────────────────────────────────────────────╯

╭─ Definition of Done ──────────────────────────────────────────────────────────────╮ │ - API reference covers all public endpoints/functions │ │ - Architecture document shows module dependencies and data flow │ │ - Each major module has a developer guide │ │ - An onboarding document provides setup and contribution instructions │ │ - All generated docs are accurate to the current codebase │ │ - Documentation is written in Markdown format │ ╰───────────────────────────────────────────────────────────────────────────────────╯

╭─ Arguments ───────────────────────────────────────────────────────────────────────────────────────╮ │ Name Type Required Description │ │ ────────── ────── ──────── ────────────────────────────────────────────────────────────── │ │ doc_types string yes Comma-separated list: api-reference, architecture, module-guides… │ │ output_dir string no Output directory for generated docs (default: docs/) │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────╯

╭─ Invariants ───────────────────────────────────────────────────────────────────────────────────────╮ │ 1. Do not modify any source code files — only create or update files in the output directory │ │ 2. All code examples in documentation must be actual code from the project, not fabricated │ │ 3. Architecture diagrams must reflect actual module dependencies, not aspirational ones │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────╯

╭─ Automation ──────────────────────────╮ │ Profile: (inherits global) │ │ Source: default │ ╰───────────────────────────────────────╯

╭─ Usage ────────────────────────────────────────────────────────────────────────────╮ │ agents plan use local/generate-docs local/api-service │ │ --arg doc_types="api-reference,architecture,module-guides,onboarding" │ ╰────────────────────────────────────────────────────────────────────────────────────╯

✓ OK Action created

Step 2: Configure Context for Large Codebase Analysis


# Set a generous context policy for the strategize view (needs to see architecture)
$ agents project context set --view strategize \
    --include-resource 01HXR6A1B2C3D4E5F6G7H8J9K0 \
    --exclude-path "**/node_modules/**" \
    --exclude-path "**/.git/**" \
    --exclude-path "**/dist/**" \
    --hot-max-tokens 32000 \
    --warm-max-decisions 200 \
    --query-limit 50 \
    --max-file-size 2097152 \
    --summarize \
    --summary-max-tokens 1500 \
    local/api-service

╭─ Context Policy ────────────────────────────────────────╮ │ Project: local/api-service │ │ View: strategize │ │ Include: 01HXR6A1B2C3D4E5F6G7H8J9K0 │ │ Exclude: /node_modules/, /.git/, /dist/ │ ╰─────────────────────────────────────────────────────────╯

╭─ Limits ──────────────────────────╮ │ Hot Tokens: 32000 (soft cap) │ │ Warm Decisions: 200 │ │ Query Limit: 50 │ │ Max File Size: 2 MB │ │ Max Total Size: 50 MB │ ╰───────────────────────────────────╯

╭─ Summarization ──────╮ │ Enabled: yes │ │ Max Tokens: 1500 │ ╰──────────────────────╯

╭─ Other Views ──────────╮ │ execute: (default) │ │ apply: (default) │ │ default: (unset) │ ╰────────────────────────╯

✓ OK Context policy updated


# Execution view can be narrower (writing docs, not analyzing all code)
$ agents project context set --view execute \
    --include-resource 01HXR6A1B2C3D4E5F6G7H8J9K0 \
    --hot-max-tokens 16000 \
    --query-limit 30 \
    --summarize \
    local/api-service

╭─ Context Policy ────────────────────────────────────────╮ │ Project: local/api-service │ │ View: execute │ │ Include: 01HXR6A1B2C3D4E5F6G7H8J9K0 │ ╰─────────────────────────────────────────────────────────╯

╭─ Limits ──────────────────────────╮ │ Hot Tokens: 16000 (soft cap) │ │ Query Limit: 30 │ │ Max File Size: 1 MB │ │ Max Total Size: 50 MB │ ╰───────────────────────────────────╯

╭─ Summarization ─╮ │ Enabled: yes │ │ Max Tokens: 800 │ ╰─────────────────╯

╭─ Other Views ──────────────╮ │ strategize: configured │ │ apply: (default) │ │ default: (unset) │ ╰────────────────────────────╯

✓ OK Context policy updated

Step 3: Execute


$ agents plan use \
    --automation-profile trusted \
    --arg doc_types="api-reference,architecture,module-guides,onboarding" \
    --arg output_dir="docs/generated/" \
    local/generate-docs local/api-service

╭─ Plan Created ──────────────────────────────────────╮ │ Plan: 01HXR6F7G8H9J0K1L2M3N4O5P6 │ │ Phase: strategize │ │ State: processing │ │ Action: local/generate-docs │ │ Project: local/api-service │ │ Automation: trusted │ │ Attempt: 1 │ ╰─────────────────────────────────────────────────────╯

╭─ Inputs ─────────────────────────────────────────────────────────────────────╮ │ - doc_types="api-reference,architecture,module-guides,onboarding" │ │ - output_dir="docs/generated/" │ │ - automation_profile=trusted │ ╰──────────────────────────────────────────────────────────────────────────────╯

╭─ Actors ────────────────────────────────────────╮ │ Strategy: anthropic/claude-3.5-sonnet │ │ Execution: anthropic/claude-3.5-sonnet │ │ Estimation: (none) │ ╰─────────────────────────────────────────────────╯

╭─ Automation ───────────────────────────╮ │ Profile: trusted │ │ Source: plan override │ │ Read-Only: no │ ╰────────────────────────────────────────╯

╭─ Context ──────────────────────────╮ │ Resources: 1 (git-checkout) │ │ Indexed Files: 487 │ │ View: strategize │ │ Hot Token Budget: 32,000 │ ╰────────────────────────────────────╯

╭─ Next Steps ─────────────────────────────────────────────────────────╮ │ Trusted profile — strategize and execute will proceed automatically. │ │ - agents plan status 01HXR6F7G8H9J0K1L2M3N4O5P6 │ │ - agents plan tree 01HXR6F7G8H9J0K1L2M3N4O5P6 │ ╰──────────────────────────────────────────────────────────────────────╯

✓ OK Plan created

The system analyzes the codebase using the code intelligence indexes (full-text, vector, and graph) to understand module relationships, public APIs, and data flows. It generates documentation files in the sandbox.


# Review generated docs
$ agents plan artifacts 01HXR6F7G8H9J0K1L2M3N4O5P6

╭─ Artifacts ───────────────────────────────────────────────────────────────────────────╮ │ Path Type Size Change Child Plan │ │ ──────────────────────────────────────── ───── ─────── ──────── ─────── │ │ docs/generated/api-reference.md write 18.2 KB +412 -0 root │ │ docs/generated/architecture.md write 12.6 KB +284 -0 root │ │ docs/generated/modules/auth.md write 6.4 KB +148 -0 root │ │ docs/generated/modules/billing.md write 5.8 KB +131 -0 root │ │ docs/generated/modules/notifications.md write 4.2 KB +97 -0 root │ │ docs/generated/modules/data-pipeline.md write 5.1 KB +118 -0 root │ │ docs/generated/onboarding.md write 8.9 KB +201 -0 root │ ╰───────────────────────────────────────────────────────────────────────────────────────╯

╭─ Summary ─────────────╮ │ Total: 7 │ │ Writes: 7 (new) │ │ Edits: 0 │ │ Deletes: 0 │ │ Total Size: 61.2 KB │ ╰───────────────────────╯

✓ OK 7 artifacts listed


$ agents plan diff 01HXR6F7G8H9J0K1L2M3N4O5P6

╭─ Diff Summary ─────────────────────────────────────╮ │ Plan: 01HXR6F7G8H9J0K1L2M3N4O5P6 │ │ Project: local/api-service │ │ Files Changed: 7 │ │ Insertions: 1,391 │ │ Deletions: 0 │ │ Net Change: +1,391 lines │ ╰────────────────────────────────────────────────────╯

╭─ Files ──────────────────────────────────────────────────────────╮ │ Path Change Status │ │ ──────────────────────────────────────── ───────── ───────── │ │ docs/generated/api-reference.md +412 -0 new file │ │ docs/generated/architecture.md +284 -0 new file │ │ docs/generated/modules/auth.md +148 -0 new file │ │ docs/generated/modules/billing.md +131 -0 new file │ │ docs/generated/modules/notifications.md +97 -0 new file │ │ docs/generated/modules/data-pipeline.md +118 -0 new file │ │ docs/generated/onboarding.md +201 -0 new file │ ╰──────────────────────────────────────────────────────────────────╯

╭─ Patch Preview ───────────────────────────────────────────────────────╮ │ --- /dev/null │ │ +++ b/docs/generated/api-reference.md │ │ @@ -0,0 +1,412 @@ │ │ + # API Reference │ │ + ## Authentication │ │ + ### POST /auth/login │ │ + ... │ │ --- /dev/null │ │ +++ b/docs/generated/architecture.md │ │ @@ -0,0 +1,284 @@ │ │ + # Architecture Overview │ │ + ## Module Dependency Graph │ │ + ... │ ╰───────────────────────────────────────────────────────────────────────╯

╭─ Risk Assessment ─────────────────╮ │ Source Code: unmodified │ │ Breaking Changes: none detected │ │ New Files Only: yes │ ╰───────────────────────────────────╯

✓ OK Diff generated


# Apply the documentation to the project
$ agents plan apply --yes 01HXR6F7G8H9J0K1L2M3N4O5P6

╭─ Apply Summary ──────────────────────────────╮ │ Plan: 01HXR6F7G8H9J0K1L2M3N4O5P6 │ │ Artifacts: 7 files created │ │ Changes: 1,391 insertions, 0 deletions │ │ Project: local/api-service │ │ Applied At: 2026-02-11 10:38 │ ╰──────────────────────────────────────────────╯

╭─ Validation ────────────────────────────────╮ │ Invariant: no source files modified │ │ Invariant: code examples verified │ │ Invariant: architecture matches codebase │ │ Duration: 4.1s │ ╰─────────────────────────────────────────────╯

╭─ Sandbox Cleanup ─────────╮ │ Worktree: removed │ │ Branch: merged to main │ │ Checkpoint: archived │ ╰───────────────────────────╯

╭─ Plan Lifecycle ─────────────────────────╮ │ Phase: apply │ │ State: applied │ │ Total Duration: 00:08:12 │ │ Total Cost: $0.184 │ │ Decisions Made: 18 │ │ Child Plans: 0 │ ╰──────────────────────────────────────────╯

╭─ Next Steps ──────╮ │ - Review git diff │ │ - Commit changes │ ╰───────────────────╯

✓ OK Changes applied


Example 7: CI/CD Integration — Automated PR Review and Fix

Scenario: A CI pipeline triggers CleverAgents to automatically review incoming pull requests, run analysis, and if issues are found, create fix commits. This runs headlessly without human intervention.

Complexity: Advanced. Non-interactive (CI profile), headless execution, full automation.

Step 1: Configure for CI


# Set CI-appropriate configuration

$ agents config set core.automation-profile ci

╭─ Config Updated ───────────────────────────╮ │ Key: core.automation-profile │ │ Value: ci │ │ Previous: (not set) │ │ Source: config │ │ Scope: global │ ╰────────────────────────────────────────────╯

╭─ Effective ────────────────────────╮ │ Sessions: new sessions │ │ Plans: future plans (unless set) │ │ Existing: unchanged │ ╰────────────────────────────────────╯

╭─ Saved To ──────────────────────────╮ │ File: ~/.cleveragents/config.toml │ │ Line: 3 │ ╰─────────────────────────────────────╯

✓ OK Config updated

$ agents config set core.format json

╭─ Config Updated ─────────────────╮ │ Key: core.format │ │ Value: json │ │ Previous: (not set) │ │ Source: config │ │ Scope: global │ ╰──────────────────────────────────╯

╭─ Saved To ──────────────────────────╮ │ File: ~/.cleveragents/config.toml │ │ Line: 4 │ ╰─────────────────────────────────────╯

✓ OK Config updated

$ agents config set core.log.level WARN

╭─ Config Updated ──────────────────╮ │ Key: core.log.level │ │ Value: WARN │ │ Previous: FATAL │ │ Source: config │ │ Scope: global │ ╰───────────────────────────────────╯

╭─ Saved To ──────────────────────────╮ │ File: ~/.cleveragents/config.toml │ │ Line: 5 │ ╰─────────────────────────────────────╯

✓ OK Config updated

Step 2: Create the PR Review Action

Create actions/review-pr.yaml:


name: local/review-pr
description: "Automatically review a PR and fix issues"
definition_of_done: |
  - All lint issues are resolved
  - Type checking passes
  - Test coverage does not decrease
  - Security scan passes
  - All fixes are committed to the PR branch
strategy_actor: anthropic/claude-3.5-sonnet
execution_actor: anthropic/claude-3.5-sonnet
automation_profile: ci
reusable: true
state: available
args:
  - name: pr_branch
    type: string
    required: true
    description: "Branch name of the PR"
  - name: base_branch
    type: string
    required: false
    default: "main"
    description: "Base branch to compare against"
invariants:
  - "Only modify files that are already changed in the PR"
  - "Do not change the intent of any code — only fix style, types, and test issues"
  - "All fixes must include a comment explaining what was changed and why"

$ agents action create --config ./actions/review-pr.yaml

╭─ Action Created ──────────────────────────────╮ │ Name: local/review-pr │ │ State: available │ │ Strategy Actor: anthropic/claude-3.5-sonnet │ │ Execution Actor: anthropic/claude-3.5-sonnet │ │ Reusable: yes │ │ Read Only: no │ │ Created: 2026-02-11 14:00 │ ╰───────────────────────────────────────────────╯

╭─ Definition of Done ─────────────────────────────────────╮ │ - All lint issues are resolved │ │ - Type checking passes │ │ - Test coverage does not decrease │ │ - Security scan passes │ │ - All fixes are committed to the PR branch │ ╰──────────────────────────────────────────────────────────╯

╭─ Arguments ──────────────────────────────────────────────────────╮ │ Name Type Required Description │ │ ────────── ────── ──────── ────────────────────────────────── │ │ pr_branch string yes Branch name of the PR │ │ base_branch string no Base branch to compare against │ ╰──────────────────────────────────────────────────────────────────╯

╭─ Invariants ─────────────────────────────────────────────────────────────────────╮ │ 1. Only modify files that are already changed in the PR │ │ 2. Do not change the intent of any code — only fix style, types, and test │ │ issues │ │ 3. All fixes must include a comment explaining what was changed and why │ ╰──────────────────────────────────────────────────────────────────────────────────╯

╭─ Automation ──────────────────╮ │ Profile: ci │ │ Source: action config │ ╰───────────────────────────────╯

╭─ Usage ──────────────────────────────────────────────────────────────────╮ │ agents plan use local/review-pr <PROJECT> │ │ --arg pr_branch="..." │ ╰──────────────────────────────────────────────────────────────────────────╯

✓ OK Action created

Step 3: CI Pipeline Script


#!/bin/bash
# ci-review.sh — called by CI when a PR is opened or updated

set -euo pipefail

PR_BRANCH="${1:?Usage: ci-review.sh <branch>}" BASE_BRANCH="${2:-main}"

# Register the resource pointing to the CI workspace. RESOURCE_NAME="local/ci-${PR_BRANCH}" agents resource add git-checkout "$RESOURCE_NAME"
--path "$GITHUB_WORKSPACE"
--branch "$PR_BRANCH"

echo "Registered resource: $RESOURCE_NAME"

# Create a project for this CI run, linking the resource by name. # If the project already exists from a previous run, ignore the error. agents --format json project create -d "CI workspace for PR $PR_BRANCH"
--resource "$RESOURCE_NAME"
local/ci-workspace 2>/dev/null || true

# Register and attach validations (idempotent — duplicates are ignored) agents --format json validation add
--config validations/lint.yaml --required local/ci-lint 2>/dev/null || true agents --format json validation add
--config validations/typecheck.yaml --required local/ci-typecheck 2>/dev/null || true agents --format json validation add
--config validations/tests.yaml --required local/ci-tests 2>/dev/null || true agents --format json validation attach
--project local/ci-workspace local/ci-lint 2>/dev/null || true agents --format json validation attach
--project local/ci-workspace local/ci-typecheck 2>/dev/null || true agents --format json validation attach
--project local/ci-workspace local/ci-tests 2>/dev/null || true

# Run the plan — ci profile means full automation including apply PLAN_OUTPUT=$(agents --format json plan use
--automation-profile ci
--arg pr_branch="$PR_BRANCH"
--arg base_branch="$BASE_BRANCH"
local/review-pr local/ci-workspace)

PLAN_ID=$(echo "$PLAN_OUTPUT" | jq -r '.plan_id') echo "Plan started: $PLAN_ID"

# Wait for completion (ci profile runs everything automatically) while true; do STATUS=$(agents --format json plan status "$PLAN_ID" | jq -r '.state') case "$STATUS" in applied) echo "PR review complete. Changes applied." break ;; failed|cancelled) echo "PR review failed." agents --format json plan status "$PLAN_ID" exit 1 ;; *) sleep 10 ;; esac done

# Output the diff for the CI log agents --format json plan diff "$PLAN_ID"

Sample --format json output from resource add (captured into $RESOURCE_JSON):


{
  "id": "01HXR7G1A2B3C4D5E6F7H8J9K0",
  "type": "git-checkout",
  "physical": true,
  "path": "/home/runner/work/acme-api/acme-api",
  "branch": "fix/handle-null-users",
  "created": "2026-02-11T14:05:32Z",
  "children_discovered": 247,
  "capabilities": {
    "readable": true,
    "writable": true,
    "sandboxable": true,
    "checkpointable": true,
    "sandbox_strategy": "git_worktree"
  }
}

Sample --format json output from plan use (captured into $PLAN_OUTPUT):


{
  "plan_id": "01HXR7H2B3C4D5E6F7G8J9K0L1",
  "phase": "strategize",
  "action": "local/review-pr",
  "project": "local/ci-workspace",
  "automation_profile": "ci",
  "attempt": 1,
  "args": {
    "pr_branch": "fix/handle-null-users",
    "base_branch": "main"
  },
  "resources": ["01HXR7G1A2B3C4D5E6F7H8J9K0"]
}

Example 8: Cloud Infrastructure Management

Scenario: A DevOps team manages Terraform infrastructure. They want CleverAgents to analyze their current infrastructure, propose optimizations (right-sizing, unused resources), and generate the Terraform changes.

Complexity: Advanced. Custom resource types, custom skills, infrastructure domain.

Step 1: Register Custom Resource Types and Skills

Create resource-types/terraform-state.yaml:


name: local/terraform-state
description: "Terraform state file resource type"
physical: true
user_addable: true
sandbox_strategy: filesystem_copy
cli_args:
  - name: state-path
    type: string
    required: true
    description: "Path to terraform.tfstate or remote state configuration"
  - name: workspace
    type: string
    required: false
    default: "default"
    description: "Terraform workspace name"
handler: local/terraform-state-handler
child_types:
  - type: fs-file
    relationship: contains

Create skills/terraform-ops.yaml:


name: local/terraform-ops
description: "Terraform infrastructure operations"
tools:
  - name: terraform_plan
    description: "Run terraform plan and return the execution plan"
    input_schema:
      type: object
      properties:
        target:
          type: string
          description: "Specific resource to target (optional)"
      required: []
    writes: false
    checkpointable: false
  • name: terraform_show description: "Show current state of a Terraform resource" input_schema: type: object properties: resource_address: type: string description: "Terraform resource address (e.g., aws_instance.web)" required: [resource_address] writes: false checkpointable: false

  • name: cloud_metrics description: "Fetch CloudWatch/cloud monitoring metrics for a resource" input_schema: type: object properties: resource_id: type: string metric: type: string description: "e.g., CPUUtilization, NetworkIn, RequestCount" period_days: type: integer default: 30 required: [resource_id, metric] writes: false checkpointable: false

include_skills:

  • local/file-ops

$ agents resource type add --config ./resource-types/terraform-state.yaml

╭─ Resource Type ──────────────────────────────╮ │ Name: local/terraform-state │ │ Physical/Virtual: physical │ │ User Addable: yes │ │ Registered: 2026-02-11 14:30 │ ╰──────────────────────────────────────────────╯

╭─ CLI Arguments ──────────────────────────────────────────────────────────────╮ │ Argument Required Description │ │ ────────────── ──────── ────────────────────────────────────────────── │ │ --state-path yes Path to terraform.tfstate or remote state config │ │ --workspace no Terraform workspace name (default: "default") │ ╰──────────────────────────────────────────────────────────────────────────────╯

╭─ Child Types ──────────────╮ │ Auto-discover: fs-file │ ╰────────────────────────────╯

╭─ Sandbox ───────────────────────────────────────────╮ │ Strategy: filesystem_copy │ │ Handler: local/terraform-state-handler (custom) │ ╰─────────────────────────────────────────────────────╯

✓ OK Resource type registered New subcommand available: agents resource add local/terraform-state


$ agents skill add --config ./skills/terraform-ops.yaml

╭─ Skill ──────────────────────────────────────────╮ │ Name: local/terraform-ops │ │ Description: Terraform infrastructure operations │ │ Config: ./skills/terraform-ops.yaml │ │ Created: 2026-02-11 14:31 │ ╰──────────────────────────────────────────────────╯

╭─ Includes ────────────────────╮ │ local/file-ops (registered) │ ╰───────────────────────────────╯

╭─ Tool Sources ───────────────────────────────────────────────────────╮ │ Source Count Details │ │ ──────── ───── ────────────────────────────────────────────── │ │ custom 3 terraform_plan, terraform_show, cloud_metrics │ │ included 5 local/file-ops (5) │ │ ──────── ───── ────────────────────────────────────────────── │ │ Total: 8 │ ╰──────────────────────────────────────────────────────────────────────╯

✓ OK Skill registered with 8 tools

Step 2: Set Up the Project


# Register the infrastructure git checkout
$ agents resource add git-checkout local/infra-repo \
    --path /repos/infrastructure --branch main

╭─ Resource ──────────────────────────────────────╮ │ Name: local/infra-repo │ │ ID: 01HXR8A1B2C3D4E5F6G7H8J9K0 │ │ Type: git-checkout │ │ Physical/Virtual: physical │ │ Path: /repos/infrastructure │ │ Branch: main │ │ Created: 2026-02-11 14:32 │ ╰─────────────────────────────────────────────────╯

╭─ Auto-discovered Children ─────────────────────────────────────────╮ │ ID Type Status │ │ ─────────────── ────────────── ───────────────── │ │ 01HXR8A1B2C3… git created │ │ 01HXR8A1B2C4… git-remote created │ │ 01HXR8A1B2C5… git-branch created │ │ 01HXR8A1B2C6… fs-directory created │ │ + 18 git-commit resources │ │ + 84 git-tree-entry resources │ │ + 6 fs-directory + 42 fs-file │ ╰────────────────────────────────────────────────────────────────────╯

╭─ Capabilities ─────────────────╮ │ Readable: yes │ │ Writable: yes │ │ Sandboxable: yes │ │ Checkpointable: yes │ │ Sandbox Strategy: git_worktree │ ╰────────────────────────────────╯

✓ OK Resource registered (155 child resources discovered)


# Register the Terraform state for the production workspace
$ agents resource add local/terraform-state local/prod-tfstate \
    --state-path /repos/infrastructure/environments/prod/terraform.tfstate \
    --workspace production

╭─ Resource ──────────────────────────────────────────────────────────────────╮ │ Name: local/prod-tfstate │ │ ID: 01HXR8B2C3D4E5F6G7H8J9K0L1 │ │ Type: local/terraform-state │ │ Physical/Virtual: physical │ │ State Path: /repos/infrastructure/environments/prod/terraform.tfstate │ │ Workspace: production │ │ Created: 2026-02-11 14:32 │ ╰─────────────────────────────────────────────────────────────────────────────╯

╭─ Auto-discovered Children ─────────────╮ │ + 1 fs-file (terraform.tfstate) │ ╰────────────────────────────────────────╯

╭─ Capabilities ──────────────────────╮ │ Readable: yes │ │ Writable: yes │ │ Sandboxable: yes │ │ Checkpointable: yes │ │ Sandbox Strategy: filesystem_copy │ ╰─────────────────────────────────────╯

✓ OK Resource registered (1 child resource discovered)


# Create the project, linking both resources by name
$ agents project create -d "Production infrastructure" \
    --resource local/infra-repo \
    --resource local/prod-tfstate \
    --invariant "Never delete or modify resources tagged with 'critical: true'" \
    --invariant "All changes must be backward-compatible with running services" \
    --invariant "Cost changes must be documented in the PR description" \
    local/infra-prod

╭─ Project Created ─────────────────────────────╮ │ Name: local/infra-prod │ │ Description: Production infrastructure │ │ Remote: no │ ╰───────────────────────────────────────────────╯

╭─ Linked Resources ──────────────────────────────────────────────────╮ │ Resource Type Read-Only │ │ ──────────────────────────── ────────────────────── ───────── │ │ local/infra-repo git-checkout no │ │ local/prod-tfstate local/terraform-state no │ ╰─────────────────────────────────────────────────────────────────────╯

╭─ Invariants ──────────────────────────────────────────────────────────────╮ │ 1. Never delete or modify resources tagged with 'critical: true' │ │ 2. All changes must be backward-compatible with running services │ │ 3. Cost changes must be documented in the PR description │ ╰───────────────────────────────────────────────────────────────────────────╯

✓ OK Project created


$ agents validation add --config validations/tf-validate.yaml --required local/tf-validate

╭─ Validation Registered ────────────────────────────────────────────╮ │ Name: local/tf-validate │ │ Command: cd environments/prod && terraform validate │ │ Mode: required │ │ Timeout: 300s │ ╰────────────────────────────────────────────────────────────────────╯

✓ OK Validation registered

$ agents validation attach --project local/infra-prod local/tf-validate ✓ OK Validation attached to project local/infra-prod


$ agents validation add --config validations/tf-plan.yaml --informational local/tf-plan

╭─ Validation Registered ─────────────────────────────────────────────────────────╮ │ Name: local/tf-plan │ │ Command: cd environments/prod && terraform plan -detailed-exitcode │ │ Mode: informational │ │ Timeout: 300s │ ╰─────────────────────────────────────────────────────────────────────────────────╯

✓ OK Validation registered

$ agents validation attach --project local/infra-prod local/tf-plan ✓ OK Validation attached to project local/infra-prod

Step 3: Create and Run the Optimization Action

Create actions/infra-optimize.yaml:


name: local/infra-optimize
description: "Analyze and optimize cloud infrastructure costs"
definition_of_done: |
  - Unused resources are identified and removal plans generated
  - Over-provisioned resources are identified with right-sizing recommendations
  - Terraform changes implement the approved optimizations
  - terraform validate passes on all changes
  - terraform plan shows no unexpected resource deletions
  - Estimated monthly cost savings are documented
strategy_actor: local/refactoring-strategist
execution_actor: anthropic/claude-3.5-sonnet
automation_profile: supervised
reusable: true
state: available
args:
  - name: optimization_targets
    type: string
    required: false
    default: "all"
    description: "Comma-separated: compute, storage, networking, database, all"
  - name: min_savings_threshold
    type: float
    required: false
    default: 10.0
    description: "Minimum monthly savings (USD) to include an optimization"
invariants:
  - "Changes to production databases require manual approval regardless of profile"
  - "Instance type downgrades must verify current peak CPU < 70% of new capacity"

$ agents action create --config ./actions/infra-optimize.yaml

╭─ Action Created ──────────────────────────────────────╮ │ Name: local/infra-optimize │ │ ID: 01HXR8C3D4E5F6G7H8J9K0L1M2 │ │ State: available │ │ Strategy Actor: local/refactoring-strategist │ │ Execution Actor: anthropic/claude-3.5-sonnet │ │ Reusable: yes │ │ Read Only: no │ │ Config: ./actions/infra-optimize.yaml │ │ Created: 2026-02-11 14:33 │ ╰───────────────────────────────────────────────────────╯

╭─ Definition of Done ──────────────────────────────────────────────────╮ │ - Unused resources are identified and removal plans generated │ │ - Over-provisioned resources identified with right-sizing recs │ │ - Terraform changes implement the approved optimizations │ │ - terraform validate passes on all changes │ │ - terraform plan shows no unexpected resource deletions │ │ - Estimated monthly cost savings are documented │ ╰───────────────────────────────────────────────────────────────────────╯

╭─ Arguments ───────────────────────────────────────────────────────────────────────╮ │ Name Type Required Description │ │ ────────────────────── ────── ──────── ────────────────────────────────── │ │ optimization_targets string no Comma-separated: compute, storage, … │ │ min_savings_threshold float no Min monthly savings (USD) to include │ ╰───────────────────────────────────────────────────────────────────────────────────╯

╭─ Invariants ──────────────────────────────────────────────────────────────────────────╮ │ 1. Changes to production databases require manual approval regardless of profile │ │ 2. Instance type downgrades must verify current peak CPU < 70% of new capacity │ ╰───────────────────────────────────────────────────────────────────────────────────────╯

╭─ Automation ──────────────────────────╮ │ Profile: supervised │ │ Source: action config │ ╰───────────────────────────────────────╯

╭─ Usage ─────────────────────────────────────────────────────────────╮ │ agents plan use local/infra-optimize local/infra-prod │ │ --arg optimization_targets=TARGET --arg min_savings_threshold=N │ ╰─────────────────────────────────────────────────────────────────────╯

✓ OK Action created


$ agents plan use \
    --arg optimization_targets="compute,storage" \
    --arg min_savings_threshold=25.0 \
    local/infra-optimize local/infra-prod

╭─ Plan Created ─────────────────────────────────────────╮ │ Plan ID: 01HXR8D4E5F6G7H8J9K0L1M2N3 │ │ Phase: strategize │ │ Action: local/infra-optimize │ │ Project: local/infra-prod │ │ Automation: supervised │ │ Attempt: 1 │ ╰────────────────────────────────────────────────────────╯

╭─ Inputs ──────────────────────────────────────╮ │ - optimization_targets=compute,storage │ │ - min_savings_threshold=25.0 │ ╰───────────────────────────────────────────────╯

╭─ Actors ─────────────────────────────────────╮ │ Strategy: local/refactoring-strategist │ │ Execution: anthropic/claude-3.5-sonnet │ │ Estimation: (none) │ ╰──────────────────────────────────────────────╯

╭─ Plan Invariants ──────────────────────────────────────────────────────────────────╮ │ Scope Source Invariant │ │ ──────── ──────── ───────────────────────────────────────────────────────── │ │ plan action Prod DB changes require manual approval regardless │ │ plan action Instance downgrades must verify peak CPU < 70% of new cap │ │ project config Never delete or modify resources tagged 'critical: true' │ │ project config All changes must be backward-compatible with running svcs │ │ project config Cost changes must be documented in PR description │ ╰────────────────────────────────────────────────────────────────────────────────────╯

╭─ Context ──────────────────────────────────────────╮ │ Resources: 2 (git-checkout, terraform-state) │ │ Indexed Files: 156 │ │ View: strategize │ │ Hot Token Budget: 12,000 │ ╰────────────────────────────────────────────────────╯

╭─ Next Steps ──────────────────────────────────────────╮ │ - agents plan execute 01HXR8D4E5F6G7H8J9K0L1M2N3 │ │ - agents plan status 01HXR8D4E5F6G7H8J9K0L1M2N3 │ │ - agents plan tree 01HXR8D4E5F6G7H8J9K0L1M2N3 │ ╰───────────────────────────────────────────────────────╯

✓ OK Plan created

The strategy actor queries cloud metrics via the cloud_metrics tool, analyzes the Terraform state using terraform_show, and proposes optimizations with cost estimates. Because the automation profile is supervised, the engineer reviews the full strategy — including any proposed instance downgrades or resource removals — before execution modifies any Terraform files. The execution actor then generates the .tf changes, and both validations (terraform validate and terraform plan -detailed-exitcode) run automatically before the plan can be applied.


Example 9: Session-Driven Interactive Exploration

Scenario: A developer uses a CleverAgents session to interactively explore a new codebase, ask questions, and gradually build up an action to automate a recurring task.

Complexity: Beginner-Intermediate. Session-based, conversational, action creation through natural language.

Step 1: Start a Session


$ agents session create --actor anthropic/claude-3.5-sonnet

╭─ Session ────────────────────────────────╮ │ ID: 01HXR9A1B2C3D4E5F6G7H8I9J0 │ │ Actor: anthropic/claude-3.5-sonnet │ │ Created: 2026-02-11 09:15 │ │ Namespace: local │ ╰──────────────────────────────────────────╯

╭─ Settings ─────────────╮ │ Automation: review │ │ Streaming: off │ │ Context: default │ │ Memory: enabled │ │ Max History: 50 turns │ ╰────────────────────────╯

╭─ Actor Details ────────────────────────╮ │ Provider: anthropic │ │ Model: claude-3.5-sonnet │ │ Temperature: 0.7 │ │ Context Window: 200K tokens │ ╰────────────────────────────────────────╯

✓ OK Session created

Step 2: Explore the Codebase Interactively


$ agents session tell --session 01HXR9A1B2C3D4E5F6G7H8I9J0 \
    "I just joined the team and need to understand the api-service project. What are the main modules and how do they interact?"

╭─ Plan Request ──────────────────────────────────────────────────────────────────────────╮ │ Actor: anthropic/claude-3.5-sonnet │ │ Session: 01HXR9A1B2C3D4E5F6G7H8I9J0 │ │ Automation: review │ │ Prompt: I just joined the team and need to understand the api-service project. Wha... │ ╰─────────────────────────────────────────────────────────────────────────────────────────╯

The api-service project is organized into 5 core modules:

  1. src/api/routes/ — HTTP route definitions (REST + GraphQL). Each domain entity has its own router file (e.g. users.py, orders.py, products.py).

  2. src/api/services/ — Business logic layer. Services are injected into routes via dependency injection. Key files: user_service.py, order_service.py, notification_service.py.

  3. src/api/models/ — SQLAlchemy ORM models and Pydantic schemas. The models map 1:1 to database tables; schemas handle request/response validation.

  4. src/api/auth/ — Authentication and authorization. Implements JWT-based auth with role-based access control. Middleware in auth_middleware.py intercepts every request before it reaches a route handler.

  5. src/api/workers/ — Background task processors using Celery. Handles async jobs like email dispatch, report generation, and webhook delivery.

Data flow: Routes → Services → Models → Database. Auth middleware gates every route. Workers are dispatched by services for async operations.

╭─ Commands Executed ──────────────────────────────────────────────────────────────╮ │ - agents resource inspect local/api-service-repo --tree │ │ - agents resource inspect local/api-service-repo --file src/api/__init__.py │ ╰──────────────────────────────────────────────────────────────────────────────────╯

╭─ Usage ─────────────────────╮ │ Input Tokens: 2,480 │ │ Output Tokens: 846 │ │ Cost: $0.0128 │ │ Duration: 4.1s │ │ Tool Calls: 2 │ ╰─────────────────────────────╯

✓ OK Orchestrator completed 2 commands


$ agents session tell --session 01HXR9A1B2C3D4E5F6G7H8I9J0 \
    "Show me how authentication works. What's the flow from request to response?"

╭─ Plan Request ───────────────────────────────────────────────────────────────────╮ │ Actor: anthropic/claude-3.5-sonnet │ │ Session: 01HXR9A1B2C3D4E5F6G7H8I9J0 │ │ Automation: review │ │ Prompt: Show me how authentication works. What's the flow from request to res... │ ╰──────────────────────────────────────────────────────────────────────────────────╯

Here is the authentication flow in api-service:

1. Request arrivesauth_middleware.py:34 extracts the Authorization header and parses the Bearer token.

2. Token validationtoken_service.py:71 decodes the JWT using the public key from config.AUTH_PUBLIC_KEY. It verifies: - Signature (RS256) - Expiry (exp claim, default 3600s) - Issuer (iss must match api-service)

3. User resolutionuser_service.py:45 loads the user record from the database using the sub claim. Returns 401 if the user is inactive or deleted.

4. Permission checkrbac.py:22 evaluates the user's roles against the endpoint's required permissions (defined as decorators on each route, e.g. @require_role("admin")). Returns 403 on insufficient permissions.

5. Context injection → The authenticated user object is attached to request.state.user so downstream route handlers can access it without repeating lookups.

Key files: src/api/auth/auth_middleware.py, src/api/auth/token_service.py, src/api/auth/rbac.py, src/api/services/user_service.py.

╭─ Commands Executed ──────────────────────────────────────────────────────────────────────╮ │ - agents resource inspect local/api-service-repo --file src/api/auth/auth_middleware.py │ │ - agents resource inspect local/api-service-repo --file src/api/auth/token_service.py │ │ - agents resource inspect local/api-service-repo --file src/api/auth/rbac.py │ ╰──────────────────────────────────────────────────────────────────────────────────────────╯

╭─ Usage ─────────────────────╮ │ Input Tokens: 3,960 │ │ Output Tokens: 1,024 │ │ Cost: $0.0187 │ │ Duration: 5.3s │ │ Tool Calls: 3 │ ╰─────────────────────────────╯

✓ OK Orchestrator completed 3 commands


$ agents session tell --session 01HXR9A1B2C3D4E5F6G7H8I9J0 \
    "I notice the team manually runs a dependency lock refresh every week. Can you create an action that automates this?"

╭─ Plan Request ──────────────────────────────────────────────────────────────────────────╮ │ Actor: anthropic/claude-3.5-sonnet │ │ Session: 01HXR9A1B2C3D4E5F6G7H8I9J0 │ │ Automation: review │ │ Prompt: I notice the team manually runs a dependency lock refresh every week. Can... │ ╰─────────────────────────────────────────────────────────────────────────────────────────╯

I've created a local/refresh-locks action that automates the weekly dependency lock refresh. Here's what it does:

  1. Checks out the target project's repository
  2. Runs pip compile / poetry lock (auto-detected from project config)
  3. Runs the test suite to verify nothing broke
  4. Commits the updated lock files on a new branch
  5. Opens a pull request for review

The action is now available. You can review the generated config with agents action show local/refresh-locks.

╭─ Commands Executed ──────────────────────────────────────────────────────╮ │ - agents action create --config ./actions/refresh-locks.yaml │ ╰──────────────────────────────────────────────────────────────────────────╯

╭─ Result ──────────────────────────────╮ │ Action: local/refresh-locks │ │ Steps: 5 │ │ Trigger: manual │ ╰───────────────────────────────────────╯

╭─ Usage ─────────────────────╮ │ Input Tokens: 5,210 │ │ Output Tokens: 1,380 │ │ Cost: $0.0249 │ │ Duration: 6.7s │ │ Tool Calls: 1 │ ╰─────────────────────────────╯

✓ OK Orchestrator completed 1 command

Step 3: Review Session History


$ agents session show 01HXR9A1B2C3D4E5F6G7H8I9J0

╭─ Session Summary ─────────────────────────────╮ │ ID: 01HXR9A1B2C3D4E5F6G7H8I9J0 │ │ Actor: anthropic/claude-3.5-sonnet │ │ Messages: 6 │ │ Created: 2026-02-11 09:15 │ │ Updated: 2026-02-11 09:28 │ │ Automation: review │ ╰───────────────────────────────────────────────╯

╭─ Recent Messages ──────────────────────────────────────────────────────────────────────╮ │ user I just joined the team and need to understand the api-service project... │ │ assistant The api-service project is organized into 5 core modules... │ │ user Show me how authentication works... │ │ assistant Here is the authentication flow in api-service... │ │ user I notice the team manually runs a dependency lock refresh... │ │ assistant I've created a local/refresh-locks action that automates... │ ╰────────────────────────────────────────────────────────────────────────────────────────╯

╭─ Linked Plans ──────────────────────────────────╮ │ Plan ID Phase State │ │ ─────────────────────────── ─────── ──────── │ │ 01HXR9K4M7N2P3Q5R6S8T9U0V1 execute complete │ ╰─────────────────────────────────────────────────╯

╭─ Token Usage ──────────────╮ │ Input Tokens: 11,650 │ │ Output Tokens: 3,250 │ │ Estimated Cost: $0.0564 │ ╰────────────────────────────╯

✓ OK Session details loaded


$ agents session export --output /tmp/onboarding-session.json 01HXR9A1B2C3D4E5F6G7H8I9J0

╭─ Session Export ────────────────────────────╮ │ Session: 01HXR9A1B2C3D4E5F6G7H8I9J0 │ │ Output: /tmp/onboarding-session.json │ │ Messages: 6 │ │ Size: 38 KB │ │ Format: JSON │ ╰─────────────────────────────────────────────╯

╭─ Contents ─────────────────╮ │ Messages: 6 │ │ Plan References: 1 │ │ Metadata Keys: 3 │ │ Actor Config: included │ │ Schema Version: v3 │ ╰────────────────────────────╯

╭─ Integrity ──────────────────╮ │ Checksum: sha256:3e8f...b7d2 │ │ Encrypted: no │ ╰──────────────────────────────╯

✓ OK Export completed


Example 10: Full-Auto Batch Operations — Formatting and Linting

Scenario: A team has 15 Python packages in a monorepo that need to be reformatted with ruff format and have their imports sorted. This is low-risk and can be fully automated.

Complexity: Simple. Full-auto profile, batch execution, minimal risk.

Step 1: Create a Formatting Action

Create actions/format-codebase.yaml:


name: local/format-codebase
description: "Apply code formatting and import sorting"
definition_of_done: |
  - ruff format passes with zero changes remaining
  - ruff check --select I passes (imports sorted)
  - All existing tests pass
  - No semantic changes to any code
strategy_actor: anthropic/claude-3.5-sonnet
execution_actor: anthropic/claude-3.5-sonnet
automation_profile: full-auto
reusable: true
state: available
args:
  - name: formatter_config
    type: string
    required: false
    default: "pyproject.toml"
    description: "Path to the formatter configuration file"
invariants:
  - "Only whitespace and import ordering changes — no semantic modifications"
  - "Every package must pass its own test suite after formatting"

$ agents action create --config ./actions/format-codebase.yaml

╭─ Action Created ─────────────────────────────────╮ │ Name: local/format-codebase │ │ State: available │ │ Strategy Actor: anthropic/claude-3.5-sonnet │ │ Execution Actor: anthropic/claude-3.5-sonnet │ │ Reusable: yes │ │ Read Only: no │ │ Created: 2026-02-11 14:00 │ ╰──────────────────────────────────────────────────╯

╭─ Definition of Done ──────────────────────────────────────╮ │ - ruff format passes with zero changes remaining │ │ - ruff check --select I passes (imports sorted) │ │ - All existing tests pass │ │ - No semantic changes to any code │ ╰───────────────────────────────────────────────────────────╯

╭─ Arguments ──────────────────────────────────────────────────────────────────╮ │ Name Type Required Description │ │ ──────────────── ────── ──────── ────────────────────────────────────── │ │ formatter_config string no Path to the formatter configuration file │ ╰──────────────────────────────────────────────────────────────────────────────╯

╭─ Invariants ──────────────────────────────────────────────────────────────────────╮ │ 1. Only whitespace and import ordering changes — no semantic modifications │ │ 2. Every package must pass its own test suite after formatting │ ╰───────────────────────────────────────────────────────────────────────────────────╯

╭─ Automation ──────────────────────────╮ │ Profile: full-auto │ │ Source: action │ ╰───────────────────────────────────────╯

╭─ Usage ─────────────────────────────────────────────────────────╮ │ agents plan use local/format-codebase local/<project> │ │ --arg formatter_config="pyproject.toml" (optional) │ ╰─────────────────────────────────────────────────────────────────╯

✓ OK Action created

Step 2: Register All Packages and Run in Batch


# Register all 15 packages as resources and projects.
# resource add accepts a name — use it to link to project create.

for pkg in auth billing common gateway inventory notifications
orders payments reporting search shipping users webhooks
workers analytics; do agents resource add git-checkout "local/pkg-${pkg}-repo"
--path "/repos/monorepo/packages/${pkg}" --branch main agents project create -d "Package: ${pkg}"
--resource "local/pkg-${pkg}-repo"
"local/pkg-${pkg}" agents validation add --config "validations/pkg-${pkg}.yaml" --required "local/pkg-${pkg}-test" agents validation attach --project "local/pkg-${pkg}" "local/pkg-${pkg}-test" done


# Loop output — first 2 iterations shown, remaining 13 condensed

# --- auth ---

$ agents resource add git-checkout local/pkg-auth-repo
--path /repos/monorepo/packages/auth --branch main

╭─ Resource ──────────────────────────────────────────╮ │ Name: local/pkg-auth-repo │ │ ID: 01HXR7B1A2B3C4D5E6F7G8H9J0 │ │ Type: git-checkout │ │ Physical/Virtual: physical │ │ Path: /repos/monorepo/packages/auth │ │ Branch: main │ │ Created: 2026-02-11 14:01 │ ╰─────────────────────────────────────────────────────╯

╭─ Auto-discovered Children ─────────────────────────────────────────╮ │ ID Type Status │ │ ─────────────── ────────────── ───────────────── │ │ 01HXR7B1A2B3… git created │ │ 01HXR7B1A2B4… git-remote created │ │ 01HXR7B1A2B5… git-branch created │ │ + 22 git-commit resources │ │ + 134 git-tree-entry resources │ │ + 2 fs-directory + 11 fs-file │ ╰────────────────────────────────────────────────────────────────────╯

╭─ Capabilities ─────────────────╮ │ Readable: yes │ │ Writable: yes │ │ Sandboxable: yes │ │ Checkpointable: yes │ │ Sandbox Strategy: git_worktree │ ╰────────────────────────────────╯

✓ OK Resource registered (172 child resources discovered)

$ agents project create -d "Package: auth"
--resource local/pkg-auth-repo local/pkg-auth

╭─ Project Created ──────────────────────────────╮ │ Name: local/pkg-auth │ │ Description: Package: auth │ │ Type: local │ │ Created: 2026-02-11 14:01 │ ╰────────────────────────────────────────────────╯

╭─ Linked Resources ───────────────────────────────────────╮ │ Resource Type Read-Only │ │ ───────────────── ────────────── ───────── │ │ local/pkg-auth-repo git-checkout no │ ╰──────────────────────────────────────────────────────────╯

╭─ Defaults ──────────────────────────────╮ │ Sandbox: git_worktree │ │ Validations: 0 │ │ Context Filters: none │ │ Automation Profile: (inherits global) │ ╰─────────────────────────────────────────╯

✓ OK Project created

$ agents validation add --config validations/pkg-auth.yaml --required local/pkg-auth-test

╭─ Validation Registered ─────────────────────────────────────────────────╮ │ Name: local/pkg-auth-test │ │ Command: cd /repos/monorepo/packages/auth && pytest tests/ │ │ Mode: required │ │ Timeout: 300s │ ╰─────────────────────────────────────────────────────────────────────────╯

✓ OK Validation registered

$ agents validation attach --project local/pkg-auth local/pkg-auth-test ✓ OK Validation attached to project local/pkg-auth

# --- billing ---

$ agents resource add git-checkout local/pkg-billing-repo
--path /repos/monorepo/packages/billing --branch main ✓ OK Resource registered — Name: local/pkg-billing-repo (189 children)

$ agents project create -d "Package: billing"
--resource local/pkg-billing-repo local/pkg-billing ✓ OK Project created — Name: local/pkg-billing

$ agents validation add --config validations/pkg-billing.yaml --required local/pkg-billing-test ✓ OK Validation registered — Name: local/pkg-billing-test

$ agents validation attach --project local/pkg-billing local/pkg-billing-test ✓ OK Validation attached to project local/pkg-billing

# ... (common, gateway, inventory, notifications, orders, payments, # reporting, search, shipping, users, webhooks, workers, analytics) # Each iteration: resource add → project create → validation add → validation attach # All 15 packages registered.


# Run formatting across all packages.
# full-auto: Strategize, Execute, AND Apply all run without human intervention.
# Capture plan IDs so we can check results afterward.

PLAN_IDS=() for pkg in auth billing common gateway inventory notifications
orders payments reporting search shipping users webhooks
workers analytics; do PID=$(agents --format json plan use
--automation-profile full-auto
local/format-codebase "local/pkg-${pkg}"
| jq -r '.data.plan_id') PLAN_IDS+=("$PID") done


# Plan creation output — first 2 shown, remaining condensed

# --- auth ---

$ agents plan use
--automation-profile full-auto
local/format-codebase local/pkg-auth

╭─ Plan Created ───────────────────────────────────╮ │ Plan ID: 01HXR7D1A2B3C4D5E6F7G8H9J0 │ │ Phase: strategize (running) │ │ Action: local/format-codebase │ │ Project: local/pkg-auth │ │ Automation: full-auto │ │ Attempt: 1 │ ╰──────────────────────────────────────────────────╯

╭─ Invariants ─────────────────────────────────────────────────────────────╮ │ Scope Source Invariant │ │ ────── ────── ───────────────────────────────────────────────────── │ │ plan action Only whitespace and import ordering changes — no se… │ │ plan action Every package must pass its own test suite after fo… │ ╰──────────────────────────────────────────────────────────────────────────╯

✓ OK Plan created — full-auto: strategize → execute → apply will proceed automatically

# --- billing ---

$ agents plan use
--automation-profile full-auto
local/format-codebase local/pkg-billing

╭─ Plan Created ───────────────────────────────────╮ │ Plan ID: 01HXR7D2B3C4D5E6F7G8H9J0K1 │ │ Phase: strategize (running) │ │ Action: local/format-codebase │ │ Project: local/pkg-billing │ │ Automation: full-auto │ │ Attempt: 1 │ ╰──────────────────────────────────────────────────╯

✓ OK Plan created — full-auto: strategize → execute → apply will proceed automatically

# ... (common, gateway, inventory, notifications, orders, payments, # reporting, search, shipping, users, webhooks, workers, analytics) # 15 plans launched — all running full-auto in background.


# Wait for all plans to finish, then check results

$ agents plan list --state applied

╭─ Plans ──────────────────────────────────────────────────────────────────────────────────────────────╮ │ ID Phase State Action Project Elapsed │ │ ──────── ─────── ──────── ───────────────────── ───────────────────── ───────── │ │ 01HXR7D1 apply applied local/format-codebase local/pkg-auth 00:02:14 │ │ 01HXR7D2 apply applied local/format-codebase local/pkg-billing 00:02:31 │ │ 01HXR7D3 apply applied local/format-codebase local/pkg-common 00:01:47 │ │ 01HXR7D4 apply applied local/format-codebase local/pkg-gateway 00:02:08 │ │ 01HXR7D5 apply applied local/format-codebase local/pkg-inventory 00:01:55 │ │ 01HXR7D6 apply applied local/format-codebase local/pkg-notifications 00:02:42 │ │ 01HXR7D7 apply applied local/format-codebase local/pkg-orders 00:02:19 │ │ 01HXR7D8 apply applied local/format-codebase local/pkg-payments 00:01:58 │ │ 01HXR7D9 apply applied local/format-codebase local/pkg-reporting 00:02:36 │ │ 01HXR7DA apply applied local/format-codebase local/pkg-search 00:01:41 │ │ 01HXR7DB apply applied local/format-codebase local/pkg-shipping 00:02:22 │ │ 01HXR7DC apply applied local/format-codebase local/pkg-users 00:01:49 │ │ 01HXR7DD apply applied local/format-codebase local/pkg-webhooks 00:02:05 │ │ 01HXR7DE apply applied local/format-codebase local/pkg-analytics 00:02:11 │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────╯

╭─ Filters ───────────────────╮ │ Phase: (any) │ │ State: applied │ │ Project: (any) │ │ Action: (any) │ ╰─────────────────────────────╯

╭─ Summary ─────────────╮ │ Total: 14 │ │ Processing: 0 │ │ Completed: 14 │ │ Errored: 0 │ ╰───────────────────────╯

✓ OK 14 plans listed

$ agents plan list --state failed

╭─ Plans ──────────────────────────────────────────────────────────────────────────────────────────────╮ │ ID Phase State Action Project Elapsed │ │ ──────── ─────── ─────── ───────────────────── ─────────────────── ───────── │ │ 01HXR7DF execute errored local/format-codebase local/pkg-workers 00:03:07 │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────╯

╭─ Filters ───────────────────╮ │ Phase: (any) │ │ State: failed │ │ Project: (any) │ │ Action: (any) │ ╰─────────────────────────────╯

╭─ Summary ─────────────╮ │ Total: 1 │ │ Processing: 0 │ │ Completed: 0 │ │ Errored: 1 │ ╰───────────────────────╯

✓ OK 1 plan listed


Example 11: Complex Graph Actor for Multi-Stage Code Review

Scenario: A team builds a custom multi-stage code review actor that performs security analysis, performance analysis, and style checking in a graph topology — with each stage running a different specialized sub-actor.

Complexity: Expert. Custom actor graph, multiple sub-actors, advanced actor configuration.

Step 1: Define Specialized Sub-Actors

Create actors/review-pipeline.yaml:


name: local/review-pipeline
cleveragents:
  version: "3.0"
  template_engine: JINJA2
  default_actor: orchestrator

global_context: review_standards: "Google Python Style Guide" severity_levels: "critical, high, medium, low, info"

actors: orchestrator: type: llm config: actor: anthropic/claude-3.5-sonnet temperature: 0.1 system_prompt: | You are a code review orchestrator. You coordinate specialized reviewers and synthesize their findings into a unified review report. Standards: {{ context.review_standards }} Severity levels: {{ context.severity_levels }} memory_enabled: true

security_reviewer: type: llm config: actor: anthropic/claude-3.5-sonnet temperature: 0.0 system_prompt: | You are a security-focused code reviewer. Analyze code for: - Injection vulnerabilities (SQL, command, XSS) - Authentication and authorization flaws - Sensitive data exposure - Insecure cryptographic practices - Dependency vulnerabilities Report findings with severity levels: {{ context.severity_levels }}

performance_reviewer: type: llm config: actor: anthropic/claude-3.5-sonnet temperature: 0.0 system_prompt: | You are a performance-focused code reviewer. Analyze code for: - N+1 query patterns - Unbounded loops or recursion - Memory leaks and excessive allocation - Missing caching opportunities - Blocking operations in async contexts Report findings with severity levels: {{ context.severity_levels }}

style_reviewer: type: llm config: actor: anthropic/claude-3.5-sonnet temperature: 0.0 system_prompt: | You are a code style and maintainability reviewer. Standards: {{ context.review_standards }} Analyze code for: - Naming conventions - Function/class design - Documentation completeness - Test coverage patterns - Code duplication

routes: review_graph: type: graph entry_point: dispatch checkpointing: true nodes: dispatch: type: agent agent: orchestrator security: type: agent agent: security_reviewer performance: type: agent agent: performance_reviewer style: type: agent agent: style_reviewer synthesize: type: agent agent: orchestrator edges: - from: dispatch to: security - from: dispatch to: performance - from: dispatch to: style - from: security to: synthesize - from: performance to: synthesize - from: style to: synthesize parallel_execution: true


$ agents actor add --config ./actors/review-pipeline.yaml \
    --skill local/code-analysis \
    --skill local/file-ops

╭─ Actor Added ──────────────────────────────────╮ │ Name: local/review-pipeline │ │ Provider: anthropic │ │ Model: claude-3.5-sonnet │ │ Default: yes │ │ Unsafe: no │ │ Type: graph │ ╰────────────────────────────────────────────────╯

╭─ Config ─────────────────────────────────────────────╮ │ Path: ./actors/review-pipeline.yaml │ │ Hash: 4f7a1e3 │ │ Options: 6 │ │ Nodes: 5 │ │ Edges: 6 │ ╰──────────────────────────────────────────────────────╯

╭─ Graph ────────────────────────────────────────────────────╮ │ Route: review_graph │ │ Entry Point: dispatch │ │ Checkpointing: yes │ │ Parallel Execution: yes │ │ │ │ Node Type Agent │ │ ─────────── ───── ────────────────────────────── │ │ dispatch agent orchestrator │ │ security agent security_reviewer │ │ performance agent performance_reviewer │ │ style agent style_reviewer │ │ synthesize agent orchestrator │ │ │ │ From To │ │ ─────────── ─────────── │ │ dispatch security │ │ dispatch performance │ │ dispatch style │ │ security synthesize │ │ performance synthesize │ │ style synthesize │ ╰────────────────────────────────────────────────────────────╯

╭─ Capabilities ──────────────────────╮ │ - multi-stage code review │ │ - security analysis │ │ - performance analysis │ │ - style and maintainability checks │ │ - unified report synthesis │ ╰─────────────────────────────────────╯

╭─ Skills ──────────────────────────────────────────╮ │ Skill Status │ │ ─────────────────── ──────────────────────── │ │ local/code-analysis attached │ │ local/file-ops attached │ ╰───────────────────────────────────────────────────╯

✓ OK Actor added

Step 2: Create the Review Action Using the Graph Actor

Create actions/deep-review.yaml:


name: local/deep-review
description: "Multi-stage deep code review with specialized reviewers"
definition_of_done: |
  - Security analysis complete with all findings classified by severity
  - Performance analysis complete with all findings classified by severity
  - Style analysis complete with all findings classified by severity
  - Unified review report generated with prioritized action items
  - Critical and high-severity issues have suggested fixes
strategy_actor: local/review-pipeline
execution_actor: local/review-pipeline
read_only: true
reusable: true
state: available
args:
  - name: target_paths
    type: string
    required: true
    description: "Comma-separated file or directory paths to review"
  - name: review_depth
    type: string
    required: false
    default: "standard"
    description: "Review depth: quick, standard, thorough"

$ agents action create --config ./actions/deep-review.yaml

╭─ Action Created ─────────────────────────────────────────────╮ │ Name: local/deep-review │ │ State: available │ │ Strategy Actor: local/review-pipeline │ │ Execution Actor: local/review-pipeline │ │ Reusable: yes │ │ Read Only: yes │ │ Created: 2026-02-11 16:30 │ ╰──────────────────────────────────────────────────────────────╯

╭─ Definition of Done ──────────────────────────────────────────────────────╮ │ - Security analysis complete with all findings classified by severity │ │ - Performance analysis complete with all findings classified by severity │ │ - Style analysis complete with all findings classified by severity │ │ - Unified review report generated with prioritized action items │ │ - Critical and high-severity issues have suggested fixes │ ╰───────────────────────────────────────────────────────────────────────────╯

╭─ Arguments ───────────────────────────────────────────────────────────────────────╮ │ Name Type Required Description │ │ ──────────── ────── ──────── ────────────────────────────────────────────── │ │ target_paths string yes Comma-separated file or directory paths to review │ │ review_depth string no Review depth: quick, standard, thorough │ ╰───────────────────────────────────────────────────────────────────────────────────╯

╭─ Automation ──────────────────────────╮ │ Profile: supervised │ │ Source: default │ ╰───────────────────────────────────────╯

╭─ Usage ────────────────────────────────────────────────────────────────────────╮ │ agents plan use local/deep-review local/<project> │ │ --arg target_paths="src/auth/,src/routes/" │ │ --arg review_depth="thorough" (optional, default: "standard") │ ╰────────────────────────────────────────────────────────────────────────────────╯

✓ OK Action created


$ agents plan use \
    --automation-profile trusted \
    --arg target_paths="src/auth/,src/routes/" \
    --arg review_depth="thorough" \
    local/deep-review local/api-service

╭─ Plan Created ───────────────────────────────────────╮ │ Plan: 01HXR8D2E3F4G5H6J7K8L9M0N1 │ │ Phase: strategize │ │ State: processing │ │ Action: local/deep-review │ │ Project: local/api-service │ │ Automation: trusted │ │ Attempt: 1 │ ╰──────────────────────────────────────────────────────╯

╭─ Inputs ───────────────────────────────────────╮ │ - target_paths="src/auth/,src/routes/" │ │ - review_depth="thorough" │ │ - automation_profile=trusted │ ╰────────────────────────────────────────────────╯

╭─ Actors ────────────────────────────────────────╮ │ Strategy: local/review-pipeline │ │ Execution: local/review-pipeline │ │ Estimation: (none) │ │ Actor Type: graph (5 nodes, 6 edges) │ ╰─────────────────────────────────────────────────╯

╭─ Automation ───────────────────────────╮ │ Profile: trusted │ │ Source: plan override │ │ Read-Only: yes │ ╰────────────────────────────────────────╯

╭─ Context ──────────────────────────╮ │ Resources: 1 (git-checkout) │ │ Indexed Files: 312 │ │ View: strategize │ │ Hot Token Budget: 24,000 │ ╰────────────────────────────────────╯

╭─ Next Steps ─────────────────────────────────────────────────────────╮ │ Trusted profile — strategize and execute will proceed automatically. │ │ - agents plan status 01HXR8D2E3F4G5H6J7K8L9M0N1 │ │ - agents plan tree 01HXR8D2E3F4G5H6J7K8L9M0N1 │ ╰──────────────────────────────────────────────────────────────────────╯

✓ OK Plan created

The graph actor executes review_graph with parallel fan-out. The dispatch node (orchestrator) analyzes the target paths and prepares review context, then three edges fire concurrently: security, performance, and style nodes each run their specialized sub-actor in parallel. Once all three complete, their outputs converge at the synthesize node, where the orchestrator merges findings into a single prioritized report. Because read_only: true, no file modifications are produced — only the review report.


Example 12: Large-Scale Feature Implementation with Hierarchical Decomposition

Scenario: A startup is building a new notification system that spans the backend API, a message queue, a worker service, and a frontend dashboard. The feature requires changes across 4 projects and should decompose into manageable child plans.

Complexity: Expert. Multi-project, hierarchical child plans, deep decision tree, invariant reconciliation, full plan lifecycle.

Step 1: Set Up All Projects

# Register the API repo resource (full output shown)

$ agents resource add git-checkout local/api-repo --path /repos/api --branch main

╭─ Resource ──────────────────────────────────────╮ │ Name: local/api-repo │ │ ID: 01HXRA1A2B3C4D5E6F7G8H9J0K │ │ Type: git-checkout │ │ Physical/Virtual: physical │ │ Path: /repos/api │ │ Branch: main │ │ Created: 2026-02-11 14:00 │ ╰─────────────────────────────────────────────────╯

╭─ Auto-discovered Children ─────────────────────────────────────────╮ │ ID Type Status │ │ ─────────────── ────────────── ───────────────── │ │ 01HXRA1A2B3C… git created │ │ 01HXRA1A2B3D… git-remote created │ │ 01HXRA1A2B3E… git-branch created │ │ + 48 git-commit resources │ │ + 312 git-tree-entry resources │ │ + 6 fs-directory + 42 fs-file │ ╰────────────────────────────────────────────────────────────────────╯

╭─ Capabilities ─────────────────╮ │ Readable: yes │ │ Writable: yes │ │ Sandboxable: yes │ │ Checkpointable: yes │ │ Sandbox Strategy: git_worktree │ ╰────────────────────────────────╯

✓ OK Resource registered (412 child resources discovered)

# Register the remaining 3 resources (condensed output)

$ agents resource add git-checkout local/worker-repo --path /repos/worker --branch main ✓ OK Resource registered — Name: local/worker-repo (278 children)

$ agents resource add git-checkout local/frontend-repo --path /repos/frontend --branch main ✓ OK Resource registered — Name: local/frontend-repo (524 children)

$ agents resource add git-checkout local/protos-repo --path /repos/protos --branch main ✓ OK Resource registered — Name: local/protos-repo (96 children)

# Create the Backend API project (full output shown)

$ agents project create -d "Backend API"
--resource local/api-repo
--resource local/protos-repo
--invariant "All new endpoints must have OpenAPI docs"
--invariant "All database changes require migration scripts"
local/api

╭─ Project Created ─────────────────────────╮ │ Name: local/api │ │ Description: Backend API │ │ Type: local │ │ Created: 2026-02-11 14:01 │ ╰───────────────────────────────────────────╯

╭─ Linked Resources ───────────────────────────────────────╮ │ Resource Type Read-Only │ │ ───────────────── ────────────── ───────── │ │ local/api-repo git-checkout no │ │ local/protos-repo git-checkout no │ ╰──────────────────────────────────────────────────────────╯

╭─ Invariants ─────────────────────────────────────────────╮ │ 1. All new endpoints must have OpenAPI docs │ │ 2. All database changes require migration scripts │ ╰──────────────────────────────────────────────────────────╯

╭─ Defaults ──────────────────────────────╮ │ Sandbox: git_worktree │ │ Validations: 0 │ │ Context Filters: none │ │ Automation Profile: (inherits global) │ ╰─────────────────────────────────────────╯

✓ OK Project created

# Create the remaining 3 projects (condensed output)

$ agents project create -d "Background worker service"
--resource local/worker-repo
--resource local/protos-repo
--invariant "Workers must be idempotent"
--invariant "All queue consumers must implement dead-letter handling"
local/worker ✓ OK Project created — Name: local/worker

$ agents project create -d "Frontend dashboard"
--resource local/frontend-repo
--invariant "All components must have Storybook stories"
--invariant "Accessibility: WCAG 2.1 AA compliance required"
local/frontend ✓ OK Project created — Name: local/frontend

$ agents project create -d "Shared protobuf definitions"
--resource local/protos-repo
--invariant "Proto changes must be backward-compatible (no field renumbering)"
local/protos ✓ OK Project created — Name: local/protos

# Register validations — first one with full output

$ agents validation add --config validations/api-tests.yaml --required local/api-tests

╭─ Validation Registered ─────────────────────────────╮ │ Name: local/api-tests │ │ Command: pytest tests/ && mypy src/ │ │ Mode: required │ │ Timeout: 300s │ ╰─────────────────────────────────────────────────────╯

✓ OK Validation registered

$ agents validation add --config validations/worker-tests.yaml --required local/worker-tests ✓ OK Validation registered — Name: local/worker-tests

$ agents validation add --config validations/frontend-tests.yaml --required local/frontend-tests ✓ OK Validation registered — Name: local/frontend-tests

$ agents validation add --config validations/proto-lint.yaml --required local/proto-lint ✓ OK Validation registered — Name: local/proto-lint

# Attach validations to their respective projects

$ agents validation attach --project local/api local/api-tests ✓ OK Validation attached to project local/api

$ agents validation attach --project local/worker local/worker-tests ✓ OK Validation attached to project local/worker

$ agents validation attach --project local/frontend local/frontend-tests ✓ OK Validation attached to project local/frontend

$ agents validation attach --project local/protos local/proto-lint ✓ OK Validation attached to project local/protos

# Global invariant for this effort

$ agents invariant add --global
"All inter-service communication must use the shared proto definitions"

╭─ Invariant Added ──────────────────────────────────────────────────────────────╮ │ Invariant: All inter-service communication must use the shared proto defs. │ │ Scope: global │ │ ID: inv_01HXRI1A │ ╰────────────────────────────────────────────────────────────────────────────────╯

✓ OK Invariant added

Step 2: Create the Feature Action

Create actions/build-notification-system.yaml:


name: local/build-notification-system
description: "Build a complete notification system spanning multiple services"
long_description: |
  Implement a full notification system with:
  - Backend API: notification preferences, send endpoints, delivery tracking
  - Message Queue: notification event schemas, routing rules
  - Worker: email/SMS/push delivery workers with retry logic
  - Frontend: notification center UI, preference settings, real-time updates

The system must be designed for reliability (at-least-once delivery), scalability (async processing via queue), and user control (per-channel preferences with quiet hours). definition_of_done: |

  • Proto definitions for notification events are complete and compatible
  • API endpoints: CRUD for preferences, send notification, list history
  • Worker processes: email, SMS, push delivery with retry and dead-letter
  • Frontend: notification center, preference settings, real-time badge
  • Integration tests verify end-to-end notification flow
  • All per-project validations pass
  • API docs updated, Storybook stories added strategy_actor: local/refactoring-strategist execution_actor: anthropic/claude-3.5-sonnet estimation_actor: anthropic/claude-3.5-sonnet invariant_actor: local/invariant-resolver automation_profile: cautious reusable: false state: available args:
  • name: notification_channels type: string required: true description: "Comma-separated: email, sms, push, in-app"
  • name: priority_levels type: string required: false default: "critical,high,normal,low"
  • name: max_retry_attempts type: integer required: false default: 5 invariants:
  • "Proto definitions must be implemented and reviewed before any service code"
  • "Each service must be deployable independently after its changes"
  • "Frontend must gracefully degrade if the notification API is unavailable"
  • "All notification delivery must be async — API endpoints return immediately"
$ agents action create --config ./actions/build-notification-system.yaml

╭─ Action Created ──────────────────────────────────────────────────╮ │ Name: local/build-notification-system │ │ ID: 01HXRB1A2B3C4D5E6F7G8H9J0 │ │ State: available │ │ Strategy Actor: local/refactoring-strategist │ │ Execution Actor: anthropic/claude-3.5-sonnet │ │ Estimation Actor: anthropic/claude-3.5-sonnet │ │ Invariant Actor: local/invariant-resolver │ │ Reusable: no │ │ Read Only: no │ │ Created: 2026-02-11 14:02 │ ╰───────────────────────────────────────────────────────────────────╯

╭─ Definition of Done ───────────────────────────────────────────────────╮ │ - Proto definitions for notification events are complete/compatible │ │ - API endpoints: CRUD for preferences, send notification, list hist. │ │ - Worker: email, SMS, push delivery with retry and dead-letter │ │ - Frontend: notification center, preference settings, real-time badge │ │ - Integration tests verify end-to-end notification flow │ │ - All per-project validations pass │ │ - API docs updated, Storybook stories added │ ╰────────────────────────────────────────────────────────────────────────╯

╭─ Arguments ──────────────────────────────────────────────────────────────────────────╮ │ Name Type Required Description │ │ ────────────────────── ──────── ──────── ────────────────────────────────── │ │ notification_channels string yes Comma-separated: email, sms, push │ │ priority_levels string no (default: critical,high,normal,low) │ │ max_retry_attempts integer no (default: 5) │ ╰──────────────────────────────────────────────────────────────────────────────────────╯

╭─ Automation ──────────────────────────╮ │ Profile: cautious │ │ Source: action config │ ╰───────────────────────────────────────╯

╭─ Action Invariants ───────────────────────────────────────────────────────────────────╮ │ 1. Proto definitions must be implemented and reviewed before any service code │ │ 2. Each service must be deployable independently after its changes │ │ 3. Frontend must gracefully degrade if the notification API is unavailable │ │ 4. All notification delivery must be async — API endpoints return immediately │ ╰───────────────────────────────────────────────────────────────────────────────────────╯

╭─ Usage ────────────────────────────────────────────────────────────────────────────╮ │ agents plan use local/build-notification-system local/protos │ │ --arg notification_channels="email,push,in-app" │ ╰────────────────────────────────────────────────────────────────────────────────────╯

✓ OK Action created

Step 3: Initiate the Plan

$ agents plan use \
    --arg notification_channels="email,push,in-app" \
    --arg priority_levels="critical,high,normal,low" \
    --arg max_retry_attempts=5 \
    local/build-notification-system \
    local/protos local/api local/worker local/frontend

╭─ Plan Created ────────────────────────────────────────────────╮ │ Plan: 01HXRF1A2B3C4D5E6F7G8H9J0 │ │ Action: local/build-notification-system │ │ Projects: local/protos, local/api, │ │ local/worker, local/frontend │ │ Automation: cautious │ │ Attempt: 1 │ ╰───────────────────────────────────────────────────────────────╯

╭─ Inputs ───────────────────────────────────────────────────────────╮ │ - notification_channels="email,push,in-app" │ │ - priority_levels="critical,high,normal,low" │ │ - max_retry_attempts=5 │ │ - automation_profile=cautious │ ╰────────────────────────────────────────────────────────────────────╯

╭─ Actors ────────────────────────────────────────────╮ │ Strategy: local/refactoring-strategist │ │ Execution: anthropic/claude-3.5-sonnet │ │ Estimation: anthropic/claude-3.5-sonnet │ │ Invariant: local/invariant-resolver │ ╰─────────────────────────────────────────────────────╯

╭─ Automation ──────────────────────────────╮ │ Profile: cautious │ │ Source: action config │ │ Read-Only: no │ ╰───────────────────────────────────────────╯

╭─ Context ──────────────────────────╮ │ Resources: 4 (git-checkout) │ │ Indexed Files: 1,310 │ │ View: strategize │ │ Hot Token Budget: 32,000 │ ╰────────────────────────────────────╯

╭─ Next Steps ──────────────────────────────────────────────╮ │ - agents plan tree 01HXRF1A2B3C4D5E6F7G8H9J0 │ │ - agents plan status 01HXRF1A2B3C4D5E6F7G8H9J0 │ │ - agents plan execute 01HXRF1A2B3C4D5E6F7G8H9J0 │ ╰───────────────────────────────────────────────────────────╯

✓ OK Plan created

The plan (01HXRF1A2B3C4D5E6F7G8H9J0) enters Strategize. The strategy actor:

  1. Reconciles invariants from all four projects plus global plus action-level
  2. Decomposes the work into ordered child plans:
    • Phase 1: Proto definitions (local/protos)
    • Phase 2 (parallel): API endpoints (local/api) + Worker skeleton (local/worker)
    • Phase 3 (parallel): Worker delivery logic (local/worker) + Frontend notification center (local/frontend)
    • Phase 4: Integration wiring and end-to-end tests

Because the cautious profile uses confidence thresholds, the system pauses on complex architectural decisions (e.g., "Should notifications use a fan-out or point-to-point queue topology?") but proceeds automatically on routine decisions.

Step 4: Monitor and Guide the Hierarchical Execution

# View the full plan tree with child plans

$ agents plan tree 01HXRF1A2B3C4D5E6F7G8H9J0

╭─ Decision Tree ─────────────────────────────────────────────────────────────────────────────────────────╮ │ Plan: 01HXRF1A2B3C4D5E6F7G8H9J0 │ │ ├─ [prompt_definition] "Build notification system across 4 projects" │ │ ├─ [invariant_enforced] "All inter-service communication must use shared proto defs" │ │ ├─ [invariant_enforced] "Proto definitions must be implemented before any service code" │ │ ├─ [invariant_enforced] "Each service must be deployable independently after its changes" │ │ ├─ [invariant_enforced] "Frontend must gracefully degrade if notification API unavailable" │ │ ├─ [invariant_enforced] "All notification delivery must be async" │ │ ├─ [strategy_choice] "Phase 1: proto definitions first (blocking)" (conf: 0.95) │ │ ├─ [strategy_choice] "Phase 2: API + worker skeleton in parallel" (conf: 0.88) │ │ ├─ [strategy_choice] "Phase 3: worker delivery + frontend UI in parallel" (conf: 0.84) │ │ ├─ [strategy_choice] "Phase 4: integration wiring and e2e tests" (conf: 0.90) │ │ ├─ [strategy_choice] "Queue topology: fan-out vs point-to-point?" (conf: 0.55 — PAUSED) │ │ ├─ [child_plan] 01HXRC1A2B3C4D5E6F7G8H9J0 (Phase 1: proto defs) [execute/complete] │ │ ├─ [child_plan] 01HXRC2B3C4D5E6F7G8H9J0K1 (Phase 2a: API endpoints) [execute/processing] │ │ ├─ [child_plan] 01HXRC3C4D5E6F7G8H9J0K1L2 (Phase 2b: worker skeleton) [execute/processing] │ │ ├─ [child_plan] 01HXRC4D5E6F7G8H9J0K1L2M3 (Phase 3a: worker delivery) [strategize/pending] │ │ ├─ [child_plan] 01HXRC5E6F7G8H9J0K1L2M3N4 (Phase 3b: frontend UI) [strategize/pending] │ │ └─ [child_plan] 01HXRC6F7G8H9J0K1L2M3N4P5 (Phase 4: integration) [strategize/pending] │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────╯

╭─ Tree Summary ──────────────────╮ │ Nodes: 17 │ │ Depth: 2 │ │ Child Plans: 6 (1 done, 2 exec) │ │ Invariants: 5 │ │ Superseded: 0 (hidden) │ ╰─────────────────────────────────╯

╭─ Decision IDs (for correction) ──────────────────────────╮ │ Root: 01HXRD1A2B3C4D5E6F7G8H9J │ │ Invariant 1: 01HXRD1B3C4D5E6F7G8H9J0 │ │ Invariant 2: 01HXRD1C4D5E6F7G8H9J0K1 │ │ Invariant 3: 01HXRD1D5E6F7G8H9J0K1L2 │ │ Invariant 4: 01HXRD1E6F7G8H9J0K1L2M3 │ │ Invariant 5: 01HXRD1F7G8H9J0K1L2M3N4 │ │ Strategy 1: 01HXRD2A3B4C5D6E7F8G9H0 │ │ Strategy 2: 01HXRD2B4C5D6E7F8G9H0J1 │ │ Strategy 3: 01HXRD2C5D6E7F8G9H0J1K2 │ │ Strategy 4: 01HXRD2D6E7F8G9H0J1K2L3 │ │ Paused: 01HXRD2E7F8G9H0J1K2L3M4 │ ╰──────────────────────────────────────────────────────────╯

✓ OK Decision tree rendered

# The system paused at a decision about queue topology (confidence 0.55 < threshold 0.7)

$ agents plan explain 01HXRD2E7F8G9H0J1K2L3M4

╭─ Decision ──────────────────────────────────────────────────╮ │ ID: 01HXRD2E7F8G9H0J1K2L3M4 │ │ Type: strategy_choice │ │ Question: Queue topology for notification delivery │ │ Chosen: (none — paused for user input) │ │ Confidence: 0.55 │ │ Plan: 01HXRF1A2B3C4D5E6F7G8H9J0 │ │ Sequence: 5 of 5 strategy choices │ │ Created: 2026-02-11 14:04 │ ╰─────────────────────────────────────────────────────────────╯

╭─ Alternatives Considered ──────────────────────────────────────────────────────╮ │ 1. Fan-out exchange: one exchange per event type, separate queues per channel │ │ 2. Point-to-point: single queue, worker routes by notification type │ │ 3. Topic-based: single exchange with routing keys for channel + priority │ ╰────────────────────────────────────────────────────────────────────────────────╯

╭─ Impact ─────────────────────────╮ │ Downstream Decisions: 4 │ │ Downstream Child Plans: 3 │ │ Artifacts Produced: 0 (pending) │ │ Correction Impact: high │ ╰──────────────────────────────────╯

╭─ Rationale ──────────────────────────────────────────────────────────╮ │ The confidence is low because both fan-out and topic-based patterns │ │ are viable. Fan-out gives independent scaling per channel but adds │ │ more exchanges. Topic-based is simpler but couples routing logic. │ │ The existing codebase uses RabbitMQ but has no precedent for this │ │ pattern — no strong signal either way. │ ╰──────────────────────────────────────────────────────────────────────╯

╭─ Correction ──────────────────────────────────────────────────╮ │ agents plan correct 01HXRD2E7F8G9H0J1K2L3M4 │ │ --mode revert --guidance "Use fan-out exchange pattern..." │ ╰───────────────────────────────────────────────────────────────╯

✓ OK Decision explained

# Provide guidance to resolve the paused decision

$ agents plan prompt 01HXRF1A2B3C4D5E6F7G8H9J0
"Use a fan-out exchange pattern: one exchange per notification event type,
with separate queues per delivery channel. This matches our existing
RabbitMQ setup in the order service."

╭─ Guidance Added ────────────────────────────────────────────────────────╮ │ Plan: 01HXRF1A2B3C4D5E6F7G8H9J0 │ │ Guidance: Use a fan-out exchange pattern: one exchange per │ │ notification event type, with separate queues per delivery │ │ channel. This matches our existing RabbitMQ setup. │ │ Scope: next execution step │ │ Phase: strategize │ │ State: awaiting_input → processing │ ╰─────────────────────────────────────────────────────────────────────────╯

╭─ Decision Created ────────────────────────╮ │ Type: user_intervention │ │ ID: 01HXRD3A2B3C4D5E6F7G8H9J │ │ Parent: 01HXRD2E7F8G9H0J1K2L3M4 │ ╰───────────────────────────────────────────╯

╭─ Queue ────╮ │ Pending: 1 │ │ Applied: 0 │ ╰────────────╯

✓ OK Guidance queued

Step 5: Handle a Failure in One Child Plan

The worker delivery child plan fails validation because the SMS provider SDK is not installed. Rather than failing the entire tree:

# Check which child plan failed

$ agents plan status 01HXRC4D5E6F7G8H9J0K1L2M3

╭─ Plan Status ──────────────────────────────────────────────╮ │ Plan: 01HXRC4D5E6F7G8H9J0K1L2M3 │ │ Phase: execute │ │ State: errored │ │ Action: local/build-notification-system (child plan) │ │ Project: local/worker │ │ Automation: cautious │ │ Attempt: 1 │ │ Parent: 01HXRF1A2B3C4D5E6F7G8H9J0 │ ╰────────────────────────────────────────────────────────────╯

╭─ Progress ───────────╮ │ Strategize │ │ Execute │ │ Apply (skipped) │ ╰──────────────────────╯

╭─ Error Detail ──────────────────────────────────────────────────────────╮ │ Error: Validation failed: ModuleNotFoundError: No module named │ │ 'twilio' — SMS provider SDK not installed │ │ Tool: execute_command (pip install check) │ │ Step: 3 of 8 │ │ Checkpoint: cp_01HXRC4D (sandbox state preserved) │ │ Recoverable: yes — use "agents plan prompt" to provide guidance │ ╰─────────────────────────────────────────────────────────────────────────╯

╭─ Cost ───────────────╮ │ Tokens Used: 14,280 │ │ Cost So Far: $0.048 │ ╰──────────────────────╯

✗ ERROR Plan errored — use agents plan prompt to resume or agents plan cancel to abort

# Correct the strategy — tell it to skip SMS for now

$ agents plan correct 01HXRD4A2B3C4D5E6F7G8H9J
--mode append
--guidance "Skip SMS delivery for the initial implementation.
Add a placeholder worker that logs a warning.
SMS support will be added in a follow-up action."

╭─ Correction ─────────────────────────────────────────────────╮ │ Mode: append │ │ Impact: adds to existing subtree, no rollback │ │ New Decision: 01HXRD5A2B3C4D5E6F7G8H9J │ │ Appended After: 01HXRD4A2B3C4D5E6F7G8H9J │ │ Attempt: 2 │ ╰──────────────────────────────────────────────────────────────╯

╭─ Append Detail ────────────────────────────────────────────────────╮ │ Original decision preserved: yes │ │ Existing artifacts kept: yes │ │ Additional work: re-execute with SMS placeholder │ │ The worker delivery plan will be re-executed. Email and push │ │ workers are kept; SMS worker replaced with a logging stub. │ │ Other child plans (API, frontend, integration) are unaffected. │ ╰────────────────────────────────────────────────────────────────────╯

╭─ Queued ──────────────╮ │ Re-execute: 1 child │ │ ETA: 3m │ ╰───────────────────────╯

✓ OK Append correction queued

Step 6: Apply in Phases

# Apply proto definitions first (other plans depend on this)

$ agents plan apply --yes 01HXRC1A2B3C4D5E6F7G8H9J0

╭─ Apply Summary ──────────────────────────────────────────╮ │ Plan: 01HXRC1A2B3C4D5E6F7G8H9J0 │ │ Artifacts: 3 files updated │ │ Changes: 84 insertions, 0 deletions │ │ Project: local/protos │ │ Applied At: 2026-02-11 14:18 │ ╰──────────────────────────────────────────────────────────╯

╭─ Validation ──────────────────────────────────────────╮ │ buf lint: passed │ │ buf breaking: passed (no breaking changes) │ │ Duration: 2.1s │ ╰───────────────────────────────────────────────────────╯

╭─ Sandbox Cleanup ─────────╮ │ Worktree: removed │ │ Branch: merged to main │ │ Checkpoint: archived │ ╰───────────────────────────╯

╭─ Plan Lifecycle ─────────────────────────╮ │ Phase: apply │ │ State: applied │ │ Total Duration: 00:02:41 │ │ Total Cost: $0.024 │ │ Decisions Made: 4 │ │ Child Plans: 0 │ ╰──────────────────────────────────────────╯

✓ OK Changes applied

# Apply API and worker in parallel

$ agents plan apply --yes 01HXRC2B3C4D5E6F7G8H9J0K1 ✓ OK Changes applied — Plan: 01HXRC2B...J0K1 Project: local/api (8 files, +246 -12)

$ agents plan apply --yes 01HXRC3C4D5E6F7G8H9J0K1L2 ✓ OK Changes applied — Plan: 01HXRC3C...K1L2 Project: local/worker (5 files, +178 -4)

# Apply worker delivery and frontend

$ agents plan apply --yes 01HXRC4D5E6F7G8H9J0K1L2M3 ✓ OK Changes applied — Plan: 01HXRC4D...L2M3 Project: local/worker (6 files, +312 -18)

$ agents plan apply --yes 01HXRC5E6F7G8H9J0K1L2M3N4 ✓ OK Changes applied — Plan: 01HXRC5E...M3N4 Project: local/frontend (12 files, +487 -22)

# Apply integration tests

$ agents plan apply --yes 01HXRC6F7G8H9J0K1L2M3N4P5 ✓ OK Changes applied — Plan: 01HXRC6F...N4P5 Projects: local/api, local/worker (4 files, +156 -0)

# Apply the parent plan (finalizes everything)

$ agents plan apply --yes 01HXRF1A2B3C4D5E6F7G8H9J0

╭─ Apply Summary ───────────────────────────────────────────╮ │ Plan: 01HXRF1A2B3C4D5E6F7G8H9J0 │ │ Artifacts: 38 files across 4 projects │ │ Changes: 1,463 insertions, 56 deletions │ │ Projects: local/protos, local/api, │ │ local/worker, local/frontend │ │ Applied At: 2026-02-11 14:32 │ ╰───────────────────────────────────────────────────────────╯

╭─ Validation ─────────────────────────────────────────╮ │ local/protos: passed (buf lint + breaking) │ │ local/api: passed (pytest 48/48, mypy 0 err) │ │ local/worker: passed (pytest 31/31, mypy 0) │ │ local/frontend: passed (tests + lint) │ │ Duration: 34.2s │ ╰──────────────────────────────────────────────────────╯

╭─ Sandbox Cleanup ─────────╮ │ Worktrees: 4 removed │ │ Branches: merged to main │ │ Checkpoints: archived │ ╰───────────────────────────╯

╭─ Plan Lifecycle ────────────────────────╮ │ Phase: apply │ │ State: applied │ │ Total Duration: 00:30:14 │ │ Total Cost: $0.847 │ │ Decisions Made: 42 │ │ Child Plans: 6 (all completed) │ │ Corrections: 1 │ ╰─────────────────────────────────────────╯

╭─ Next Steps ──────╮ │ - Review git diff │ │ - Commit changes │ ╰───────────────────╯

✓ OK Changes applied


Example 13: Custom Automation Profile with Semantic Escalation

Scenario: A team wants fine-grained automation control. They trust the system with routine planning but want human oversight on any decision involving database schema changes or security-sensitive code, regardless of confidence.

Complexity: Intermediate. Custom profile, invariant-driven escalation.

Step 1: Create a Custom Profile

Create profiles/db-cautious.yaml:


name: local/db-cautious
description: "Auto for most tasks, manual for database and security decisions"

decompose_task: 0.0 create_tool: 0.3 select_tool: 1.0 edit_code: 0.4 execute_command: 0.6 create_file: 0.5 delete_content: 0.8 access_network: 0.9 install_dependency: 0.3 modify_config: 0.0 approve_plan: 0.0

require_sandbox: true require_checkpoints: true allow_unsafe_tools: false


$ agents automation-profile add --config ./profiles/db-cautious.yaml

╭─ Profile Registered ──────────────────────────────────────────────────────╮ │ Name: local/db-cautious │ │ Description: Auto for most tasks, manual for database and security │ │ decisions │ │ Created: 2026-02-11 09:00 │ ╰───────────────────────────────────────────────────────────────────────────╯

╭─ Confidence Thresholds ────────────────╮ │ decompose_task: 0.0 │ │ create_tool: 0.3 │ │ select_tool: 1.0 │ │ edit_code: 0.4 │ │ execute_command: 0.6 │ │ create_file: 0.5 │ │ delete_content: 0.8 │ │ install_dependency: 0.3 │ │ modify_config: 0.0 │ │ approve_plan: 0.0 │ │ require_sandbox: true │ │ require_checkpoints: true │ │ allow_unsafe_tools: false │ ╰────────────────────────────────────────╯

✓ OK Profile registered

Step 2: Apply to a Project with Database Resources


$ agents config set core.automation-profile local/db-cautious --project local/api-service

╭─ Config Updated ─────────────────────────────────╮ │ Key: core.automation-profile │ │ Value: local/db-cautious │ │ Previous: review │ │ Source: config │ │ Scope: project (local/api-service) │ ╰──────────────────────────────────────────────────╯

╭─ Effective ────────────────────────────────────╮ │ Sessions: new sessions on local/api-service │ │ Plans: future plans (unless overridden) │ │ Existing: unchanged │ ╰────────────────────────────────────────────────╯

╭─ Saved To ──────────────────────────╮ │ File: ~/.cleveragents/config.toml │ │ Line: 24 │ ╰─────────────────────────────────────╯

✓ OK Config updated


# Add invariants that force escalation on sensitive operations
$ agents invariant add --project local/api-service \
    "Any change to database migration files requires explicit human approval"

╭─ Invariant Added ───────────────────────────────────────────────────────────────╮ │ Project: local/api-service │ │ Invariant: Any change to database migration files requires explicit human │ │ approval │ │ Scope: project │ │ ID: inv_01HXRBA1A │ ╰─────────────────────────────────────────────────────────────────────────────────╯

✓ OK Invariant added

$ agents invariant add --project local/api-service
"Changes to authentication or authorization logic require security review"

╭─ Invariant Added ───────────────────────────────────────────────────────────────╮ │ Project: local/api-service │ │ Invariant: Changes to authentication or authorization logic require │ │ security review │ │ Scope: project │ │ ID: inv_01HXRBA2B │ ╰─────────────────────────────────────────────────────────────────────────────────╯

✓ OK Invariant added

Step 3: Run a Plan and Observe Escalation


$ agents plan use \
    --arg target_module="src/auth" \
    local/refactor-to-orm local/api-service

╭─ Plan Created ─────────────────────────────────────╮ │ Plan ID: 01HXRBG1H2I3J4K5L6M7N8O9P0 │ │ Phase: strategize (running) │ │ Action: local/refactor-to-orm │ │ Project: local/api-service │ │ Automation: local/db-cautious │ │ Attempt: 1 │ ╰────────────────────────────────────────────────────╯

╭─ Inputs ─────────────────────╮ │ - target_module=src/auth │ ╰──────────────────────────────╯

╭─ Actors ───────────────────────────────╮ │ Strategy: anthropic/claude-3.5-sonnet │ │ Execution: anthropic/claude-3.5-sonnet │ │ Invariant: local/invariant-resolver │ ╰────────────────────────────────────────╯

╭─ Plan Invariants ──────────────────────────────────────────────────────────────╮ │ Scope Source Invariant │ │ ──────── ─────── ────────────────────────────────────────────────────── │ │ project config Any change to database migration files requires explicit │ │ human approval │ │ project config Changes to authentication or authorization logic require │ │ security review │ ╰────────────────────────────────────────────────────────────────────────────────╯

╭─ Next Steps ─────────────────────────────────────────╮ │ - agents plan status 01HXRBG1H2I3J4K5L6M7N8O9P0 │ │ - agents plan tree 01HXRBG1H2I3J4K5L6M7N8O9P0 │ ╰──────────────────────────────────────────────────────╯

✓ OK Plan created — strategize in progress

During execution, the system encounters a decision to create a new Alembic migration. The confidence score is 0.82 (the system is fairly confident), but the invariant "database migration files require explicit human approval" causes the Invariant Reconciliation Actor to enforce escalation regardless. The system pauses:


# The plan pauses — check why
$ agents plan status 01HXRBG1H2I3J4K5L6M7N8O9P0

╭─ Plan Status ───────────────────────────────────────╮ │ Plan: 01HXRBG1H2I3J4K5L6M7N8O9P0 │ │ Phase: execute │ │ State: awaiting_input │ │ Action: local/refactor-to-orm │ │ Project: local/api-service │ │ Automation: local/db-cautious │ │ Attempt: 1 │ ╰─────────────────────────────────────────────────────╯

╭─ Progress ────────────────╮ │ Strategize │ │ Execute (paused) │ │ Apply (waiting) │ ╰───────────────────────────╯

╭─ Escalation ───────────────────────────────────────────────────────────────╮ │ ⚠ Decision paused — invariant forced escalation │ │ Decision: 01HXRBD1E2F3G4H5I6J7K8L9 │ │ Type: implementation_choice │ │ Question: Create Alembic migration for users table schema change? │ │ Confidence: 0.82 (would auto-proceed, but invariant overrides) │ │ Invariant: "Any change to database migration files requires explicit │ │ human approval" │ ╰────────────────────────────────────────────────────────────────────────────╯

╭─ Timing ───────────╮ │ Started: 09:05:12 │ │ Elapsed: 00:02:48 │ │ ETA: (paused) │ ╰────────────────────╯

╭─ Cost ───────────────╮ │ Tokens Used: 18,740 │ │ Cost So Far: $0.062 │ │ Estimated: $0.095 │ ╰──────────────────────╯

⚠ WARN Plan paused — awaiting human input for escalated decision


# Review the decision requiring approval
$ agents plan explain 01HXRBD1E2F3G4H5I6J7K8L9

╭─ Decision ───────────────────────────────────────────────────────────╮ │ ID: 01HXRBD1E2F3G4H5I6J7K8L9 │ │ Type: implementation_choice │ │ Question: Create Alembic migration for users table schema change? │ │ Chosen: Add nullable orm_version column with migration │ │ Confidence: 0.82 │ │ Plan: 01HXRBG1H2I3J4K5L6M7N8O9P0 │ │ Sequence: 4 of 7 │ │ Created: 2026-02-11 09:07 │ ╰──────────────────────────────────────────────────────────────────────╯

╭─ Alternatives Considered ────────────────────────────────────────────╮ │ 1. Add nullable orm_version column with migration (chosen) │ │ 2. Use a shadow table approach (no schema change) │ │ 3. In-place column rename via ALTER (risky in production) │ ╰──────────────────────────────────────────────────────────────────────╯

╭─ Escalation Reason ──────────────────────────────────────────────────╮ │ Invariant Override: inv_01HXRBA1A │ │ Text: "Any change to database migration files requires explicit │ │ human approval" │ │ Effect: Confidence 0.82 would normally auto-proceed (threshold │ │ 0.6), but invariant forces manual escalation regardless │ ╰──────────────────────────────────────────────────────────────────────╯

╭─ Impact ─────────────────────────╮ │ Downstream Decisions: 3 │ │ Downstream Child Plans: 0 │ │ Artifacts Produced: 2 │ │ Correction Impact: medium │ ╰──────────────────────────────────╯

╭─ Rationale ──────────────────────────────────────────────────────────╮ │ The auth module currently uses raw SQL queries. Refactoring to ORM │ │ requires an orm_version tracking column on the users table. │ │ Adding as nullable first avoids downtime; the backfill + NOT NULL │ │ constraint can follow in a second migration after data is populated. │ ╰──────────────────────────────────────────────────────────────────────╯

╭─ Correction ────────────────────────────────────────────────╮ │ agents plan prompt 01HXRBG1H2I3J4K5L6M7N8O9P0 │ │ "Approved. <guidance>" │ │ agents plan correct 01HXRBD1E2F3G4H5I6J7K8L9 │ │ --mode revert --guidance "Use approach 2 instead..." │ ╰─────────────────────────────────────────────────────────────╯

✓ OK Decision explained


# Approve the migration approach with specific guidance
$ agents plan prompt 01HXRBG1H2I3J4K5L6M7N8O9P0 \
    "Approved. Use a two-phase migration: first add the column as nullable, \
     then in a separate migration add the NOT NULL constraint after backfill."

╭─ Guidance Added ──────────────────────────────────────────────────────────╮ │ Plan: 01HXRBG1H2I3J4K5L6M7N8O9P0 │ │ Guidance: Approved. Use a two-phase migration: first add the column as │ │ nullable, then in a separate migration add the NOT NULL │ │ constraint after backfill. │ │ Scope: next execution step │ │ Phase: execute │ │ State: awaiting_input → processing │ ╰───────────────────────────────────────────────────────────────────────────╯

╭─ Decision Created ──────────────────────────────╮ │ Type: user_intervention │ │ ID: 01HXRBE2F3G4H5I6J7K8L9M0 │ │ Parent: 01HXRBD1E2F3G4H5I6J7K8L9 │ ╰─────────────────────────────────────────────────╯

╭─ Queue ────╮ │ Pending: 1 │ │ Applied: 0 │ ╰────────────╯

✓ OK Guidance queued — execution resuming


Example 14: Server Mode — Team Collaboration

Scenario: A distributed team uses CleverAgents in server mode. Multiple engineers share actions, actors, and projects while working on the same codebase from different machines.

Complexity: Intermediate. Server mode, multi-user, namespace management.

Step 1: Connect to the Server


# Configure server connection
$ agents config set server.url https://agents.example.com

╭─ Config Updated ──────────────────────────────╮ │ Key: server.url │ │ Value: https://agents.example.com │ │ Previous: (not set) │ │ Source: config │ │ Scope: global │ ╰───────────────────────────────────────────────╯

╭─ Saved To ──────────────────────────╮ │ File: ~/.cleveragents/config.toml │ │ Line: 3 │ ╰─────────────────────────────────────╯

✓ OK Config updated

$ agents config set server.token "tok_01HXR..."

╭─ Config Updated ──────────────────────────╮ │ Key: server.token │ │ Value: tok_01HXR... (redacted) │ │ Previous: (not set) │ │ Source: config │ │ Scope: global │ ╰───────────────────────────────────────────╯

╭─ Saved To ──────────────────────────╮ │ File: ~/.cleveragents/config.toml │ │ Line: 4 │ ╰─────────────────────────────────────╯

✓ OK Config updated

$ agents config set core.namespace myteam

╭─ Config Updated ──────────────────────╮ │ Key: core.namespace │ │ Value: myteam │ │ Previous: local │ │ Source: config │ │ Scope: global │ ╰───────────────────────────────────────╯

╭─ Saved To ──────────────────────────╮ │ File: ~/.cleveragents/config.toml │ │ Line: 5 │ ╰─────────────────────────────────────╯

✓ OK Config updated


# Verify connection
$ agents diagnostics

╭─ Checks ────────────────────────────────────────────────╮ │ Check Status Details │ │ ─────────────── ────── ─────────────────────── │ │ Config file OK readable │ │ Database OK writable │ │ Anthropic key OK configured │ │ OPENAI_API_KEY WARN missing │ │ Disk space OK 4.8 GB free │ │ Text index OK tantivy 0.22 │ │ Vector index OK faiss (CPU) │ │ Graph store OK neo4j 5.15 │ │ File permissions OK data dir r/w │ │ Git OK git 2.43.0 │ │ Server OK connected (agents.example.com) │ │ Namespace OK myteam (4 members) │ ╰─────────────────────────────────────────────────────────╯

╭─ Summary ─────────╮ │ Checks: 12 total │ │ Warnings: 1 │ │ Errors: 0 │ │ Duration: 0.8s │ ╰───────────────────╯

╭─ Server ────────────────────────────╮ │ URL: https://agents.example.com │ │ Auth: valid (tok_01HXR...) │ │ Namespace: myteam │ │ Members: 4 │ │ Shared Actions: 7 │ │ Shared Actors: 3 │ │ Latency: 42ms │ ╰─────────────────────────────────────╯

╭─ Recommendations ──────────────────────────────╮ │ - Set OPENAI_API_KEY to enable OpenAI models │ ╰────────────────────────────────────────────────╯

⚠ WARN 1 warning requires attention

Step 2: Publish Shared Resources


# Publish an action to the team namespace (visible to all team members)
$ agents action create --config ./actions/generate-tests.yaml

╭─ Action Created ──────────────────────────────────────╮ │ Name: myteam/generate-tests │ │ ID: 01HXRC1A2B3C4D5E6F7G8H9J0K │ │ State: available │ │ Strategy Actor: anthropic/claude-3.5-sonnet │ │ Execution Actor: anthropic/claude-3.5-sonnet │ │ Reusable: yes │ │ Read Only: no │ │ Created: 2026-02-11 09:15 │ ╰───────────────────────────────────────────────────────╯

╭─ Definition of Done ───────────────────────────────────╮ │ All target modules have test coverage above 80% │ │ Tests pass in CI without flaky failures │ ╰────────────────────────────────────────────────────────╯

╭─ Arguments ─────────────────────────────────────────────────────╮ │ Name Type Required Description │ │ ────────────── ────── ──────── ─────────────────────── │ │ target_module string yes Module path to generate tests │ ╰─────────────────────────────────────────────────────────────────╯

╭─ Automation ────────────────────────╮ │ Profile: supervised │ │ Source: action default │ ╰─────────────────────────────────────╯

╭─ Usage ───────────────────────────────────────────────────────────╮ │ agents plan use myteam/generate-tests local/my-project │ │ --arg target_module="src/payments" │ ╰───────────────────────────────────────────────────────────────────╯

✓ OK Action created


# Publish a shared actor
$ agents actor add --config ./actors/review-pipeline.yaml

╭─ Actor Added ──────────────────────────────────╮ │ Name: myteam/review-pipeline │ │ Provider: anthropic │ │ Model: claude-3.5-sonnet │ │ Default: no │ │ Unsafe: no │ │ Type: graph │ ╰────────────────────────────────────────────────╯

╭─ Config ───────────────────────────────╮ │ Path: ./actors/review-pipeline.yaml │ │ Hash: 5a7c2e1 │ │ Options: 3 │ │ Nodes: 5 │ │ Edges: 6 │ ╰────────────────────────────────────────╯

╭─ Capabilities ──────────────────╮ │ - multi-stage code review │ │ - security analysis │ │ - performance analysis │ │ - style checking │ ╰─────────────────────────────────╯

╭─ Tools ────────────────────────╮ │ Tool Read-Only Safe │ │ ──────────── ───────── ──── │ │ read_file yes yes │ │ search_files yes yes │ │ git_diff yes yes │ │ run_tests yes yes │ ╰────────────────────────────────╯

✓ OK Actor added


# List available actions (team namespace)
$ agents action list --namespace myteam

╭─ Actions ────────────────────────────────────────────────────────────────────────────────────────────────╮ │ Name State Strategy Actor Execution Actor Reusable │ │ ──────────────────────── ───────── ─────────────────────────── ─────────────────────────── ──────── │ │ myteam/generate-tests available anthropic/claude-3.5-sonnet anthropic/claude-3.5-sonnet ✓ │ │ myteam/deep-review available myteam/review-pipeline myteam/review-pipeline ✓ │ │ myteam/format-codebase available anthropic/claude-3.5-sonnet anthropic/claude-3.5-sonnet ✓ │ │ myteam/security-audit available anthropic/claude-3.5-sonnet anthropic/claude-3.5-sonnet ✓ │ │ myteam/refactor-to-orm available anthropic/claude-3.5-sonnet anthropic/claude-3.5-sonnet ✗ │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────╯

╭─ Filters ──────────────────╮ │ State: (any) │ │ Namespace: myteam │ ╰────────────────────────────╯

╭─ Summary ──────────────────╮ │ Total: 5 │ │ Available: 5 │ │ Archived: 0 │ ╰────────────────────────────╯

✓ OK 5 actions listed

Step 3: Use Shared Resources from Another Machine

On a different developer's machine:


# Configure server connection
$ agents config set server.url https://agents.example.com

╭─ Config Updated ──────────────────────────────╮ │ Key: server.url │ │ Value: https://agents.example.com │ │ Previous: (not set) │ │ Source: config │ │ Scope: global │ ╰───────────────────────────────────────────────╯

✓ OK Config updated

$ agents config set server.token "tok_01HXS..."

╭─ Config Updated ──────────────────────────╮ │ Key: server.token │ │ Value: tok_01HXS... (redacted) │ │ Previous: (not set) │ │ Source: config │ │ Scope: global │ ╰───────────────────────────────────────────╯

✓ OK Config updated

$ agents config set core.namespace myteam

╭─ Config Updated ──────────────────────╮ │ Key: core.namespace │ │ Value: myteam │ │ Previous: local │ │ Source: config │ │ Scope: global │ ╰───────────────────────────────────────╯

✓ OK Config updated


# Use the team's shared action
$ agents plan use \
    --arg target_module="src/payments" \
    myteam/generate-tests local/payment-service

╭─ Plan Created ─────────────────────────────────────────╮ │ Plan ID: 01HXRC2B3C4D5E6F7G8H9J0K1L2 │ │ Phase: strategize (running) │ │ Action: myteam/generate-tests │ │ Project: local/payment-service │ │ Automation: supervised │ │ Attempt: 1 │ ╰────────────────────────────────────────────────────────╯

╭─ Inputs ──────────────────────╮ │ - target_module=src/payments │ ╰───────────────────────────────╯

╭─ Actors ──────────────────────────────────╮ │ Strategy: anthropic/claude-3.5-sonnet │ │ Execution: anthropic/claude-3.5-sonnet │ │ Source: server (myteam) │ ╰───────────────────────────────────────────╯

╭─ Context ───────────────────────╮ │ Resources: 1 (repo) │ │ Indexed Files: 189 │ │ View: strategize │ │ Hot Token Budget: 12,000 │ ╰─────────────────────────────────╯

╭─ Next Steps ──────────────────────────────────────────────╮ │ - agents plan status 01HXRC2B3C4D5E6F7G8H9J0K1L2 │ │ - agents plan tree 01HXRC2B3C4D5E6F7G8H9J0K1L2 │ ╰───────────────────────────────────────────────────────────╯

✓ OK Plan created — strategize in progress

Step 4: Monitor Across the Team


# List all plans across the team
$ agents plan list --namespace myteam

╭─ Plans ────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │ ID Phase State Action Project User Elapsed │ │ ──────── ──────── ────────── ────────────────────── ───────────────────── ─────── ───────── │ │ 01HXRC2B execute processing myteam/generate-tests local/payment-service jdoe 00:04:12 │ │ 01HXRC3C apply applied myteam/deep-review local/api-service asmith 00:08:47 │ │ 01HXRC4D execute processing myteam/generate-tests local/api-service mwong 00:02:35 │ │ 01HXRC5E apply applied myteam/format-codebase local/frontend blee 00:01:58 │ │ 01HXRC6F execute awaiting myteam/security-audit local/worker-service asmith 00:06:20 │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯

╭─ Filters ──────────────────╮ │ Phase: (any) │ │ State: (any) │ │ Namespace: myteam │ │ Project: (any) │ │ Action: (any) │ ╰────────────────────────────╯

╭─ Summary ─────────────╮ │ Total: 5 │ │ Processing: 2 │ │ Completed: 2 │ │ Awaiting: 1 │ │ Errored: 0 │ ╰───────────────────────╯

✓ OK 5 plans listed


# See who is running what — filter to executing state
$ agents plan list --namespace myteam --state processing

╭─ Plans ────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │ ID Phase State Action Project User Elapsed │ │ ──────── ─────── ────────── ───────────────────── ───────────────────── ─────── ───────── │ │ 01HXRC2B execute processing myteam/generate-tests local/payment-service jdoe 00:04:12 │ │ 01HXRC4D execute processing myteam/generate-tests local/api-service mwong 00:02:35 │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯

╭─ Filters ──────────────────────╮ │ Phase: (any) │ │ State: processing │ │ Namespace: myteam │ │ Project: (any) │ │ Action: (any) │ ╰────────────────────────────────╯

╭─ Summary ─────────────╮ │ Total: 2 │ │ Processing: 2 │ │ Completed: 0 │ │ Errored: 0 │ ╰───────────────────────╯

✓ OK 2 plans listed


Example 15: Disaster Recovery — Rollback a Failed Apply

Scenario: A plan was applied, but the changes broke production. The team needs to understand what happened and roll back.

Complexity: Intermediate. Rollback, checkpoint restore, post-mortem analysis.

Step 1: Investigate the Failed Apply


# Check the plan status
$ agents plan status 01HXRCF1G2H3I4J5K6L7M8N9O0

╭─ Plan Status ───────────────────────────────────────╮ │ Plan: 01HXRCF1G2H3I4J5K6L7M8N9O0 │ │ Phase: apply │ │ State: errored │ │ Action: local/optimize-db-connections │ │ Project: local/api-service │ │ Automation: trusted │ │ Attempt: 1 │ ╰─────────────────────────────────────────────────────╯

╭─ Progress ─────────────────────╮ │ Strategize │ │ Execute │ │ Apply (post-apply failure) │ ╰────────────────────────────────╯

╭─ Timing ──────────────────────────╮ │ Started: 2026-02-11 08:30:00 │ │ Applied At: 2026-02-11 08:42:18 │ │ Failed At: 2026-02-11 08:44:05 │ │ Total Duration: 00:14:05 │ ╰───────────────────────────────────╯

╭─ Result ───────────────────────────────────────────────╮ │ Decisions Made: 6 │ │ Child Plans: 0 │ │ Artifacts: 4 files updated │ │ Validations: 2/3 passed (1 FAILED post-apply) │ │ Total Cost: $0.072 │ ╰────────────────────────────────────────────────────────╯

╭─ Error ──────────────────────────────────────────────────────────────────────╮ │ Post-apply health check failed: connection pool exhaustion detected │ │ Impact: API response times increased 10x, 503 errors in production │ │ Root Cause Decision: 01HXRCD3E4F5G6H7I8J9K0L1 │ ╰──────────────────────────────────────────────────────────────────────────────╯

╭─ Recovery Options ──────────────────────────────────────────────────╮ │ - agents plan rollback — restore to a previous checkpoint │ │ - agents plan correct — revert and re-execute with guidance │ │ - agents plan explain — investigate the root cause decision │ ╰─────────────────────────────────────────────────────────────────────╯

✗ ERROR Plan applied but post-apply validation failed


# Review the decision tree to understand what was done
$ agents plan tree 01HXRCF1G2H3I4J5K6L7M8N9O0

╭─ Decision Tree ────────────────────────────────────────────────────────────────────────────────╮ │ Plan: 01HXRCF1G2H3I4J5K6L7M8N9O0 │ │ ├─ [prompt_definition] "Optimize database connection handling" │ │ ├─ [invariant_enforced] "All DB config changes must preserve existing pool limits" │ │ ├─ [strategy_choice] "Refactor connection pool configuration" (confidence: 0.91) │ │ ├─ [tool_invocation] write_file: src/db/pool_config.py │ │ ├─ [implementation_choice] "Increase pool size to 100" (confidence: 0.78) ← ROOT CAUSE │ │ └─ [tool_invocation] write_file: src/db/health_check.py │ ╰────────────────────────────────────────────────────────────────────────────────────────────────╯

╭─ Tree Summary ──────────────────╮ │ Nodes: 6 │ │ Depth: 2 │ │ Child Plans: 0 │ │ Invariants: 1 │ │ Superseded: 0 (hidden) │ ╰─────────────────────────────────╯

╭─ Decision IDs (for correction) ──────────────────────╮ │ Root: 01HXRCC1D2E3F4G5H6I7J8K9 │ │ Invariant 1: 01HXRCC2E3F4G5H6I7J8K9L0 │ │ Strategy: 01HXRCC3F4G5H6I7J8K9L0M1 │ │ Tool 1: 01HXRCC4G5H6I7J8K9L0M1N2 │ │ Pool Size (root cause): 01HXRCD3E4F5G6H7I8J9K0L1 │ │ Tool 2: 01HXRCE4F5G6H7I8J9K0L1M2 │ ╰──────────────────────────────────────────────────────╯

✓ OK Decision tree rendered


# Look at the specific changes that were applied
$ agents plan diff 01HXRCF1G2H3I4J5K6L7M8N9O0

╭─ Diff Summary ─────────────────────────────────────╮ │ Plan: 01HXRCF1G2H3I4J5K6L7M8N9O0 │ │ Project: local/api-service │ │ Files Changed: 4 │ │ Insertions: 38 │ │ Deletions: 12 │ │ Net Change: +26 lines │ ╰────────────────────────────────────────────────────╯

╭─ Files ──────────────────────────────────────╮ │ Path Change Status │ │ ──────────────────────── ────── ──────── │ │ src/db/pool_config.py +14 -6 modified │ │ src/db/health_check.py +18 -0 new file │ │ src/db/__init__.py +4 -4 modified │ │ config/database.toml +2 -2 modified │ ╰──────────────────────────────────────────────╯

╭─ Patch Preview ──────────────────────────────────╮ │ --- a/src/db/pool_config.py │ │ +++ b/src/db/pool_config.py │ │ @@ -8,6 +8,14 @@ │ │ - POOL_SIZE = 20 │ │ - POOL_MAX_OVERFLOW = 10 │ │ + POOL_SIZE = 100 │ │ + POOL_MAX_OVERFLOW = 50 │ │ + POOL_RECYCLE = 3600 │ │ + POOL_PRE_PING = True │ │ --- a/config/database.toml │ │ +++ b/config/database.toml │ │ @@ -3,2 +3,2 @@ │ │ - pool_size = 20 │ │ + pool_size = 100 │ ╰──────────────────────────────────────────────────╯

╭─ Risk Assessment ──────────────────────────╮ │ API Compatibility: preserved │ │ Test Coverage: maintained │ │ Breaking Changes: pool size 5x increase │ │ Resource Impact: high (connection limit) │ ╰────────────────────────────────────────────╯

✓ OK Diff generated


# Examine the decision that led to the problematic change
$ agents plan explain 01HXRCD3E4F5G6H7I8J9K0L1 --show-context --show-reasoning

╭─ Decision ───────────────────────────────────────────────────────────╮ │ ID: 01HXRCD3E4F5G6H7I8J9K0L1 │ │ Type: implementation_choice │ │ Question: What pool size should be configured? │ │ Chosen: Increase pool size from 20 to 100 │ │ Confidence: 0.78 │ │ Plan: 01HXRCF1G2H3I4J5K6L7M8N9O0 │ │ Sequence: 5 of 6 │ │ Created: 2026-02-11 08:38 │ ╰──────────────────────────────────────────────────────────────────────╯

╭─ Alternatives Considered ─────────────────────────────────────╮ │ 1. Increase pool size from 20 to 100 (chosen) │ │ 2. Keep pool at 20, add connection health checks only │ │ 3. Increase to 50 with overflow to 75 │ ╰───────────────────────────────────────────────────────────────╯

╭─ Impact ─────────────────────────╮ │ Downstream Decisions: 1 │ │ Downstream Child Plans: 0 │ │ Artifacts Produced: 2 │ │ Correction Impact: low │ ╰──────────────────────────────────╯

╭─ Context Snapshot ────────────────────────────────────────────────╮ │ - Current pool_size=20 in config/database.toml │ │ - Database server: PostgreSQL 15, max_connections=150 │ │ - 3 application replicas running (total: 60 connections used) │ │ - Slow query logs show connection wait times of 200-500ms │ │ Hot Context Hash: sha256:9c4f...a1b2 │ ╰───────────────────────────────────────────────────────────────────╯

╭─ Rationale ─────────────────────────────────────────────────────╮ │ Connection wait times of 200-500ms suggest pool exhaustion. │ │ Increasing to 100 connections per replica provides headroom │ │ for burst traffic and eliminates the queueing bottleneck. │ ╰─────────────────────────────────────────────────────────────────╯

╭─ Model Reasoning (raw) ────────────────────────────────────────────────╮ │ The slow query logs show connection acquisition delays of 200-500ms │ │ which indicates the pool is frequently exhausted. Current config: │ │ pool_size=20, max_overflow=10 (total 30 per replica). │ │ │ │ With 3 replicas that's 90 connections max. The PostgreSQL server │ │ allows 150. I'll increase to 100 per replica (300 total with │ │ overflow). This exceeds the server's max_connections=150 but │ │ overflow connections are temporary and unlikely to all be active │ │ simultaneously. │ │ │ │ [NOTE: The model failed to account for 3 replicas × 100 = 300 │ │ exceeding PostgreSQL max_connections=150] │ ╰────────────────────────────────────────────────────────────────────────╯

╭─ Correction ─────────────────────────────────────────────────╮ │ agents plan correct 01HXRCD3E4F5G6H7I8J9K0L1 │ │ --mode revert --guidance "Keep pool at 20, add health..." │ ╰──────────────────────────────────────────────────────────────╯

✓ OK Decision explained

Step 2: Roll Back to a Checkpoint


# List available checkpoints for this plan
$ agents plan artifacts 01HXRCF1G2H3I4J5K6L7M8N9O0

╭─ Artifacts ────────────────────────────────────────────────────────╮ │ Path Type Size Change Child Plan │ │ ─────────────────────── ───── ────── ─────── ────────── │ │ src/db/pool_config.py edit 1.4 KB +14 -6 root │ │ src/db/health_check.py write 2.1 KB +18 -0 root │ │ src/db/__init__.py edit 0.8 KB +4 -4 root │ │ config/database.toml edit 0.3 KB +2 -2 root │ ╰────────────────────────────────────────────────────────────────────╯

╭─ Summary ───────────╮ │ Total: 4 │ │ Writes: 1 (new) │ │ Edits: 3 (modified) │ │ Deletes: 0 │ │ Total Size: 4.6 KB │ ╰─────────────────────╯

╭─ Checkpoints ──────────────────────────────────────────────────────╮ │ ID Label Created │ │ ──────────────────── ─────────────────────────── ────────────── │ │ checkpoint_01HXRCP1 before pool config changes 08:36:12 │ │ checkpoint_01HXRCP2 before health check addition 08:39:45 │ │ checkpoint_01HXRCP3 pre-apply snapshot 08:42:00 │ ╰────────────────────────────────────────────────────────────────────╯

╭─ By Plan ──────────────╮ │ Root Plan: 4 artifacts │ ╰────────────────────────╯

✓ OK 4 artifacts listed


# Roll back to the checkpoint before the problematic pool config change
$ agents plan rollback --yes 01HXRCF1G2H3I4J5K6L7M8N9O0 checkpoint_01HXRCP1

╭─ Rollback Summary ─────────────────────────────────╮ │ Plan: 01HXRCF1G2H3I4J5K6L7M8N9O0 │ │ Checkpoint: checkpoint_01HXRCP1 │ │ Label: before pool config changes │ │ Files: 4 reverted │ ╰────────────────────────────────────────────────────╯

╭─ Changes Reverted ──────────────────────╮ │ File Action │ │ ──────────────────────── ────────── │ │ src/db/pool_config.py restored │ │ src/db/health_check.py removed │ │ src/db/__init__.py restored │ │ config/database.toml restored │ ╰─────────────────────────────────────────╯

╭─ Impact ──────────────────────────────────╮ │ Child Plans Invalidated: 0 │ │ Sandbox: restored to checkpoint_01HXRCP1 │ │ Decisions After CP: 2 discarded │ │ Tool Calls After CP: 3 undone │ ╰───────────────────────────────────────────╯

╭─ Post-Rollback State ──────────╮ │ Phase: execute │ │ State: queued (awaiting input) │ │ Checkpoints Remaining: 1 │ ╰────────────────────────────────╯

✓ OK Rollback complete

Step 3: Correct and Re-Execute


# Correct the decision that caused the issue
$ agents plan correct 01HXRCD3E4F5G6H7I8J9K0L1 \
    --mode revert \
    --guidance "The connection pool size change caused the outage. \
                Keep the pool at 20 connections maximum and add \
                connection health checks instead." --yes

╭─ Correction ──────────────────────────────────────────────╮ │ Mode: revert │ │ Impact: 1 decision, 0 child plans, 2 artifacts │ │ New Decision: 01HXRCCORR1A2B3C4D5 │ │ Corrects: 01HXRCD3E4F5G6H7I8J9K0L1 │ │ Attempt: 2 │ ╰───────────────────────────────────────────────────────────╯

╭─ Affected Subtree ──────────────╮ │ Decisions Invalidated: 1 │ │ Child Plans Rolled Back: 0 │ │ Artifacts Archived: 2 │ │ Unaffected Decisions: 4 │ ╰─────────────────────────────────╯

╭─ Sandbox Rollback ──────────────────╮ │ Checkpoint: checkpoint_01HXRCP1 │ │ Files Reverted: 2 │ │ Status: restored │ ╰─────────────────────────────────────╯

╭─ Recompute ───────────╮ │ Queued: 1 re-execute │ │ ETA: 2m │ ╰───────────────────────╯

╭─ History ───────────────────────────────────────────────╮ │ - Original decision superseded │ │ - Prior artifacts archived for comparison │ │ - agents plan diff --correction 01HXRCCORR1A2B3C4D5 │ ╰─────────────────────────────────────────────────────────╯

✓ OK Correction applied — re-executing affected subtree


# The affected subtree is re-executed. Review the new changes.
$ agents plan diff --correction 01HXRCCORR1A2B3C4D5

╭─ Correction Diff ────────────────────────────────────╮ │ Correction: 01HXRCCORR1A2B3C4D5 │ │ Original Decision: 01HXRCD3E4F5G6H7I8J9K0L1 │ │ Mode: revert │ │ Files Changed: 3 │ │ New Insertions: 32 │ │ New Deletions: 4 │ ╰──────────────────────────────────────────────────────╯

╭─ Comparison ─────────────────────────────────────────────────────────────╮ │ File Before (original) After (corrected) │ │ ──────────────────────── ────────────────── ────────────────────── │ │ src/db/pool_config.py +14 -6 (pool=100) +8 -2 (pool=20, checks) │ │ src/db/health_check.py +18 -0 (basic) +24 -0 (robust checks) │ │ config/database.toml +2 -2 (pool=100) (unchanged — pool=20) │ ╰──────────────────────────────────────────────────────────────────────────╯

╭─ Patch Preview (corrected vs original) ───────────────────────╮ │ --- a/src/db/pool_config.py (original) │ │ +++ b/src/db/pool_config.py (corrected) │ │ @@ -8,6 +8,10 @@ │ │ - POOL_SIZE = 100 │ │ - POOL_MAX_OVERFLOW = 50 │ │ + POOL_SIZE = 20 │ │ + POOL_MAX_OVERFLOW = 10 │ │ + POOL_PRE_PING = True │ │ + POOL_HEALTH_CHECK_INTERVAL = 30 │ │ --- a/src/db/health_check.py (original) │ │ +++ b/src/db/health_check.py (corrected) │ │ @@ -1,18 +1,24 @@ │ │ + # Added: connection validation, stale connection eviction, │ │ + # periodic health probes, and circuit breaker pattern │ │ ... │ ╰───────────────────────────────────────────────────────────────╯

✓ OK Correction diff generated


# Re-apply with the fix
$ agents plan apply --yes 01HXRCF1G2H3I4J5K6L7M8N9O0

╭─ Apply Summary ──────────────────────────╮ │ Plan: 01HXRCF1G2H3I4J5K6L7M8N9O0 │ │ Artifacts: 3 files updated │ │ Changes: 32 insertions, 4 deletions │ │ Project: local/api-service │ │ Applied At: 2026-02-11 09:02 │ ╰──────────────────────────────────────────╯

╭─ Validation ───────────────────╮ │ Tests: passed (31/31) │ │ Lint: passed (0 warnings) │ │ Type Check: passed (0 errors) │ │ Health Check: passed │ │ Duration: 18.6s │ ╰────────────────────────────────╯

╭─ Sandbox Cleanup ─────────╮ │ Worktree: removed │ │ Branch: merged to main │ │ Checkpoint: archived │ ╰───────────────────────────╯

╭─ Plan Lifecycle ──────────────────────────╮ │ Phase: apply │ │ State: applied │ │ Total Duration: 00:32:02 (incl. rollback) │ │ Total Cost: $0.118 │ │ Decisions Made: 7 (1 corrected) │ │ Child Plans: 0 │ ╰───────────────────────────────────────────╯

╭─ Next Steps ──────╮ │ - Review git diff │ │ - Commit changes │ ╰───────────────────╯

✓ OK Changes applied

Example 16: Devcontainer-Driven Development

!!! adr "Related ADRs" ADR-043: Devcontainer Integration · ADR-042: Resource Type Inheritance · ADR-039: Container Resource Types

Scenario: A developer registers a git checkout that contains a .devcontainer/ directory. The system auto-detects the devcontainer, creates it in detected state, then lazily builds and starts it when a plan executes. All tool invocations route to the container via execution environment routing.

What this example demonstrates:

  • Devcontainer auto-discovery during agents resource add
  • Lazy activation (container not built until first plan execution)
  • Execution environment routing selecting the nearest-ancestor devcontainer (precedence level 3)
  • Tool invocations running inside the container
  • Apply phase writing changes back to host via bind mount

Step 1 — Register the git checkout (devcontainer auto-detected):

=== "Rich"

<div class="highlight"><pre><code>
<span style="color: #66cc66; font-weight: 600;">$</span> <span style="color: cyan; font-weight: 600;">agents</span> resource add git-checkout local/webapp --path /home/user/projects/webapp

╭─ Resource ──────────────────────────────────────────╮
│ <span style="color: cyan; font-weight: 600;">Name:</span> local/webapp                                  │
│ <span style="color: cyan; font-weight: 600;">ID:</span> 01J0A1B2C3D4E5F6G7H8J9K0L1                      │
│ <span style="color: #5599ff; font-weight: 600;">Type:</span> git-checkout                                  │
│ <span style="color: #66cc66; font-weight: 600;">Path:</span> /home/user/projects/webapp                    │
│ <span style="color: #5599ff; font-weight: 600;">Branch:</span> main                                        │
╰─────────────────────────────────────────────────────╯

╭─ Auto-discovered Children ─────────────────────────────────────────╮
│ <span style="color: cyan; font-weight: 600;">ID</span>               <span style="color: cyan; font-weight: 600;">Type</span>                    <span style="color: cyan; font-weight: 600;">Status</span>                    │
│ <span style="opacity: 0.7;">───────────────  ──────────────────────  ─────────────────</span>         │
│ 01J0A1B2C3D5…   git                     <span style="color: #66cc66; font-weight: 600;">created</span>                    │
│ 01J0A1B2C3D6…   <span style="color: yellow; font-weight: 600;">devcontainer-instance</span>   <span style="color: yellow; font-weight: 600;">detected (not built)</span>       │
│ 01J0A1B2C3D7…   fs-directory            <span style="color: #66cc66; font-weight: 600;">created</span>                    │
│   + 38 git-commit, 205 git-tree-entry, 12 fs-file                  │
╰────────────────────────────────────────────────────────────────────╯

<span style="color: yellow; font-weight: 600;">⚠ Devcontainer detected</span> at .devcontainer/devcontainer.json
  Container will be built lazily on first access.

<span style="color: #66cc66; font-weight: 600;">✓ OK</span> Resource registered (259 child resources discovered)
</code></pre></div>

=== "Plain"

```
$ agents resource add git-checkout local/webapp --path /home/user/projects/webapp

Resource
  Name: local/webapp
  ID: 01J0A1B2C3D4E5F6G7H8J9K0L1
  Type: git-checkout
  Path: /home/user/projects/webapp
  Branch: main

Auto-discovered Children
ID               Type                    Status
---------------  ----------------------  -----------------
01J0A1B2C3D5..   git                     created
01J0A1B2C3D6..   devcontainer-instance   detected (not built)
01J0A1B2C3D7..   fs-directory            created
  + 38 git-commit, 205 git-tree-entry, 12 fs-file

[WARN] Devcontainer detected at .devcontainer/devcontainer.json
  Container will be built lazily on first access.

[OK] Resource registered (259 child resources discovered)
```

Step 2 — Create project and link the resource:

=== "Rich"

<div class="highlight"><pre><code>
<span style="color: #66cc66; font-weight: 600;">$</span> <span style="color: cyan; font-weight: 600;">agents</span> project create local/webapp-project
<span style="color: #66cc66; font-weight: 600;">✓ OK</span> Project created: local/webapp-project

<span style="color: #66cc66; font-weight: 600;">$</span> <span style="color: cyan; font-weight: 600;">agents</span> project link-resource local/webapp-project local/webapp
<span style="color: #66cc66; font-weight: 600;">✓ OK</span> Resource local/webapp linked to project local/webapp-project
</code></pre></div>

=== "Plain"

```
$ agents project create local/webapp-project
[OK] Project created: local/webapp-project

$ agents project link-resource local/webapp-project local/webapp
[OK] Resource local/webapp linked to project local/webapp-project
```

Step 3 — Start a plan (devcontainer lazily built):

=== "Rich"

<div class="highlight"><pre><code>
<span style="color: #66cc66; font-weight: 600;">$</span> <span style="color: cyan; font-weight: 600;">agents</span> plan use local/add-dark-mode local/webapp-project \
  <span style="color: cyan;">--arg</span> feature=dark-mode <span style="color: cyan;">--automation-profile</span> supervised

╭─ Plan Created ──────────────────────────────────────╮
│ <span style="color: cyan; font-weight: 600;">Plan ID:</span> 01J0B2C3D4E5F6G7H8J9K0L1M2                 │
│ <span style="color: yellow; font-weight: 600;">Phase:</span> strategize                                   │
│ <span style="color: magenta; font-weight: 600;">Action:</span> local/add-dark-mode                         │
│ <span style="color: #5599ff; font-weight: 600;">Project:</span> local/webapp-project                       │
│ <span style="color: #5599ff; font-weight: 600;">Automation:</span> supervised                              │
╰─────────────────────────────────────────────────────╯

<span style="color: #66cc66; font-weight: 600;">✓ OK</span> Plan created — strategize phase starting
</code></pre></div>

=== "Plain"

```
$ agents plan use local/add-dark-mode local/webapp-project \
  --arg feature=dark-mode --automation-profile supervised

Plan Created
  Plan ID: 01J0B2C3D4E5F6G7H8J9K0L1M2
  Phase: strategize
  Action: local/add-dark-mode
  Project: local/webapp-project
  Automation: supervised

[OK] Plan created — strategize phase starting
```

Step 4 — Execute the plan (devcontainer activation + tool routing):

=== "Rich"

<div class="highlight"><pre><code>
<span style="color: #66cc66; font-weight: 600;">$</span> <span style="color: cyan; font-weight: 600;">agents</span> plan execute 01J0B2C3D4E5F6G7H8J9K0L1M2

<span style="color: yellow; font-weight: 600;">⏳ Building devcontainer</span> from .devcontainer/devcontainer.json …
  Image: mcr.microsoft.com/devcontainers/typescript-node:1-20
  Features: ghcr.io/devcontainers/features/git:1
  Running postCreateCommand: npm install
<span style="color: #66cc66; font-weight: 600;">✓ Devcontainer ready</span> (01J0A1B2C3D6…) in 42s

╭─ Execution Environment ───────────────────────────╮
│ <span style="color: cyan; font-weight: 600;">Resolved via:</span> nearest-ancestor devcontainer       │
│ <span style="color: #5599ff; font-weight: 600;">Resource:</span> 01J0A1B2C3D6… (devcontainer-instance)   │
│ <span style="color: #66cc66; font-weight: 600;">Workspace:</span> /workspaces/webapp                     │
│ <span style="color: #5599ff; font-weight: 600;">Precedence:</span> level 3 of 6                          │
╰───────────────────────────────────────────────────╯

<span style="color: #5599ff; font-weight: 600;">▸ Decision 1/3:</span> Create src/theme/dark-mode.ts
  <span style="color: #66cc66; font-weight: 600;">→ write_file()</span> in <span style="color: yellow;">container</span> /workspaces/webapp/src/theme/dark-mode.ts
  <span style="color: #66cc66; font-weight: 600;">✓</span> Checkpoint cp-001

<span style="color: #5599ff; font-weight: 600;">▸ Decision 2/3:</span> Update src/App.tsx to import ThemeProvider
  <span style="color: #66cc66; font-weight: 600;">→ edit_file()</span> in <span style="color: yellow;">container</span> /workspaces/webapp/src/App.tsx
  <span style="color: #66cc66; font-weight: 600;">✓</span> Checkpoint cp-002

<span style="color: #5599ff; font-weight: 600;">▸ Decision 3/3:</span> Run tests
  <span style="color: #66cc66; font-weight: 600;">→ run_command()</span> in <span style="color: yellow;">container</span> npm test
  <span style="color: #66cc66; font-weight: 600;">✓</span> All 47 tests passed
  <span style="color: #66cc66; font-weight: 600;">✓</span> Checkpoint cp-003

╭─ Execution Complete ──────────────────╮
│ <span style="color: #66cc66; font-weight: 600;">Decisions:</span> 3/3 succeeded              │
│ <span style="color: #66cc66; font-weight: 600;">Checkpoints:</span> 3                        │
│ <span style="color: #5599ff; font-weight: 600;">Environment:</span> devcontainer             │
│ <span style="color: #5599ff; font-weight: 600;">Duration:</span> 1m 12s (incl. 42s build)    │
╰───────────────────────────────────────╯

<span style="color: #66cc66; font-weight: 600;">✓ OK</span> Execution complete — ready for apply
</code></pre></div>

=== "Plain"

```
$ agents plan execute 01J0B2C3D4E5F6G7H8J9K0L1M2

[WAIT] Building devcontainer from .devcontainer/devcontainer.json …
  Image: mcr.microsoft.com/devcontainers/typescript-node:1-20
  Features: ghcr.io/devcontainers/features/git:1
  Running postCreateCommand: npm install
[OK] Devcontainer ready (01J0A1B2C3D6…) in 42s

Execution Environment
  Resolved via: nearest-ancestor devcontainer
  Resource: 01J0A1B2C3D6… (devcontainer-instance)
  Workspace: /workspaces/webapp
  Precedence: level 3 of 6

Decision 1/3: Create src/theme/dark-mode.ts
  -> write_file() in container /workspaces/webapp/src/theme/dark-mode.ts
  [OK] Checkpoint cp-001

Decision 2/3: Update src/App.tsx to import ThemeProvider
  -> edit_file() in container /workspaces/webapp/src/App.tsx
  [OK] Checkpoint cp-002

Decision 3/3: Run tests
  -> run_command() in container npm test
  [OK] All 47 tests passed
  [OK] Checkpoint cp-003

Execution Complete
  Decisions: 3/3 succeeded
  Checkpoints: 3
  Environment: devcontainer
  Duration: 1m 12s (incl. 42s build)

[OK] Execution complete — ready for apply
```

Step 5 — Apply changes to host:

=== "Rich"

<div class="highlight"><pre><code>
<span style="color: #66cc66; font-weight: 600;">$</span> <span style="color: cyan; font-weight: 600;">agents</span> plan apply --yes 01J0B2C3D4E5F6G7H8J9K0L1M2

╭─ Apply ──────────────────────────────────────────╮
│ <span style="color: #66cc66; font-weight: 600;">Files modified:</span> 2                                │
│   <span style="color: #66cc66;">+ src/theme/dark-mode.ts</span> (new)                 │
│   <span style="color: yellow;">~ src/App.tsx</span> (modified)                       │
│ <span style="color: #5599ff; font-weight: 600;">Target:</span> /home/user/projects/webapp (host)        │
│ <span style="color: #5599ff; font-weight: 600;">Via:</span> bind mount sync from container workspace    │
╰──────────────────────────────────────────────────╯

<span style="color: #66cc66; font-weight: 600;">✓ OK</span> Changes applied to host filesystem
</code></pre></div>

=== "Plain"

```
$ agents plan apply --yes 01J0B2C3D4E5F6G7H8J9K0L1M2

Apply
  Files modified: 2
    + src/theme/dark-mode.ts (new)
    ~ src/App.tsx (modified)
  Target: /home/user/projects/webapp (host)
  Via: bind mount sync from container workspace

[OK] Changes applied to host filesystem
```

Example 17: Explicit Container with Directory Mount

!!! adr "Related ADRs" ADR-043: Devcontainer Integration · ADR-039: Container Resource Types

Scenario: A team uses a custom container image (not a devcontainer) for development. The operator explicitly creates a container-instance resource with --mount to bind a local directory into the container, then sets it as the project's execution environment with override priority.

What this example demonstrates:

  • Explicit container-instance creation via agents resource add with --mount flags
  • Both resource-reference and raw host-path mount styles
  • Project-level execution_environment with priority: override (precedence level 2)
  • Container started on first plan execution, no devcontainer.json needed

Step 1 — Register the code repository and container:

=== "Rich"

<div class="highlight"><pre><code>
<span style="color: #66cc66; font-weight: 600;">$</span> <span style="color: cyan; font-weight: 600;">agents</span> resource add git-checkout local/api-repo --path /home/user/projects/api
<span style="color: #66cc66; font-weight: 600;">✓ OK</span> Resource registered: local/api-repo (127 children)

<span style="color: #66cc66; font-weight: 600;">$</span> <span style="color: cyan; font-weight: 600;">agents</span> resource add container-instance local/api-container \
  <span style="color: cyan;">--mount</span> local/api-repo:/workspace \
  <span style="color: cyan;">--mount</span> /home/user/.ssh:/home/dev/.ssh:ro

╭─ Resource ──────────────────────────────────────╮
│ <span style="color: cyan; font-weight: 600;">Name:</span> local/api-container                       │
│ <span style="color: cyan; font-weight: 600;">ID:</span> 01J0C3D4E5F6G7H8J9K0L1M2N3                  │
│ <span style="color: #5599ff; font-weight: 600;">Type:</span> container-instance                        │
│ <span style="color: #66cc66; font-weight: 600;">State:</span> created (not started)                    │
╰─────────────────────────────────────────────────╯

╭─ Mounts ──────────────────────────────────────────────────────────╮
│ <span style="color: cyan; font-weight: 600;">Source</span>                    <span style="color: cyan; font-weight: 600;">Container Path</span>       <span style="color: cyan; font-weight: 600;">Kind</span>    <span style="color: cyan; font-weight: 600;">Mode</span>       │
│ <span style="opacity: 0.7;">────────────────────────  ──────────────────   ──────  ────</span>       │
│ local/api-repo            /workspace           res-ref rw         │
│ /home/user/.ssh           /home/dev/.ssh       host    ro         │
╰───────────────────────────────────────────────────────────────────╯

<span style="color: #66cc66; font-weight: 600;">✓ OK</span> Container resource registered
</code></pre></div>

=== "Plain"

```
$ agents resource add git-checkout local/api-repo --path /home/user/projects/api
[OK] Resource registered: local/api-repo (127 children)

$ agents resource add container-instance local/api-container \
  --mount local/api-repo:/workspace \
  --mount /home/user/.ssh:/home/dev/.ssh:ro

Resource
  Name: local/api-container
  ID: 01J0C3D4E5F6G7H8J9K0L1M2N3
  Type: container-instance
  State: created (not started)

Mounts
Source                    Container Path       Kind     Mode
------------------------  ------------------   ------   ----
local/api-repo            /workspace           res-ref  rw
/home/user/.ssh           /home/dev/.ssh       host     ro

[OK] Container resource registered
```

Step 2 — Create project, link resources, set execution environment:

=== "Rich"

<div class="highlight"><pre><code>
<span style="color: #66cc66; font-weight: 600;">$</span> <span style="color: cyan; font-weight: 600;">agents</span> project create local/api-project
<span style="color: #66cc66; font-weight: 600;">✓ OK</span> Project created

<span style="color: #66cc66; font-weight: 600;">$</span> <span style="color: cyan; font-weight: 600;">agents</span> project link-resource local/api-project local/api-repo
<span style="color: #66cc66; font-weight: 600;">✓ OK</span> Resource linked

<span style="color: #66cc66; font-weight: 600;">$</span> <span style="color: cyan; font-weight: 600;">agents</span> project link-resource local/api-project local/api-container
<span style="color: #66cc66; font-weight: 600;">✓ OK</span> Resource linked

<span style="color: #66cc66; font-weight: 600;">$</span> <span style="color: cyan; font-weight: 600;">agents</span> project context set \
  <span style="color: cyan;">--execution-environment</span> local/api-container \
  <span style="color: cyan;">--execution-env-priority</span> override \
  local/api-project

╭─ Context Policy Updated ──────────────────────────╮
│ <span style="color: cyan; font-weight: 600;">Execution Environment:</span> local/api-container        │
│ <span style="color: #5599ff; font-weight: 600;">Priority:</span> override (precedence level 2)           │
│ <span style="color: yellow; font-weight: 600;">Note:</span> All tool invocations will route to this     │
│       container, bypassing devcontainer detection │
╰───────────────────────────────────────────────────╯

<span style="color: #66cc66; font-weight: 600;">✓ OK</span> Context policy updated
</code></pre></div>

=== "Plain"

```
$ agents project create local/api-project
[OK] Project created

$ agents project link-resource local/api-project local/api-repo
[OK] Resource linked

$ agents project link-resource local/api-project local/api-container
[OK] Resource linked

$ agents project context set \
  --execution-environment local/api-container \
  --execution-env-priority override \
  local/api-project

Context Policy Updated
  Execution Environment: local/api-container
  Priority: override (precedence level 2)
  Note: All tool invocations will route to this
        container, bypassing devcontainer detection

[OK] Context policy updated
```

Step 3 — Execute a plan (container started, tools routed):

=== "Rich"

<div class="highlight"><pre><code>
<span style="color: #66cc66; font-weight: 600;">$</span> <span style="color: cyan; font-weight: 600;">agents</span> plan use local/fix-auth-bug local/api-project --automation-profile trusted

<span style="color: #66cc66; font-weight: 600;">✓ OK</span> Plan 01J0D4E5F6G7H8J9K0L1M2N3O4 created — strategize phase starting

<span style="color: #66cc66; font-weight: 600;">$</span> <span style="color: cyan; font-weight: 600;">agents</span> plan execute 01J0D4E5F6G7H8J9K0L1M2N3O4

<span style="color: yellow; font-weight: 600;">⏳ Starting container</span> local/api-container …
<span style="color: #66cc66; font-weight: 600;">✓ Container ready</span> in 3s

╭─ Execution Environment ───────────────────────────╮
│ <span style="color: cyan; font-weight: 600;">Resolved via:</span> project override                    │
│ <span style="color: #5599ff; font-weight: 600;">Resource:</span> local/api-container                     │
│ <span style="color: #66cc66; font-weight: 600;">Workspace:</span> /workspace                             │
│ <span style="color: #5599ff; font-weight: 600;">Precedence:</span> level 2 of 6                          │
╰───────────────────────────────────────────────────╯

<span style="color: #5599ff; font-weight: 600;">▸ Decision 1/2:</span> Fix token validation in src/auth/jwt.ts
  <span style="color: #66cc66; font-weight: 600;">→ edit_file()</span> in <span style="color: yellow;">container</span> /workspace/src/auth/jwt.ts
  <span style="color: #66cc66; font-weight: 600;">✓</span> Checkpoint cp-001

<span style="color: #5599ff; font-weight: 600;">▸ Decision 2/2:</span> Run auth test suite
  <span style="color: #66cc66; font-weight: 600;">→ run_command()</span> in <span style="color: yellow;">container</span> npm test -- --grep auth
  <span style="color: #66cc66; font-weight: 600;">✓</span> 12/12 tests passed
  <span style="color: #66cc66; font-weight: 600;">✓</span> Checkpoint cp-002

<span style="color: #66cc66; font-weight: 600;">✓ OK</span> Execution complete — ready for apply
</code></pre></div>

=== "Plain"

```
$ agents plan use local/fix-auth-bug local/api-project --automation-profile trusted
[OK] Plan 01J0D4E5F6G7H8J9K0L1M2N3O4 created — strategize phase starting

$ agents plan execute 01J0D4E5F6G7H8J9K0L1M2N3O4

[WAIT] Starting container local/api-container …
[OK] Container ready in 3s

Execution Environment
  Resolved via: project override
  Resource: local/api-container
  Workspace: /workspace
  Precedence: level 2 of 6

Decision 1/2: Fix token validation in src/auth/jwt.ts
  -> edit_file() in container /workspace/src/auth/jwt.ts
  [OK] Checkpoint cp-001

Decision 2/2: Run auth test suite
  -> run_command() in container npm test -- --grep auth
  [OK] 12/12 tests passed
  [OK] Checkpoint cp-002

[OK] Execution complete — ready for apply
```

Example 18: Container with Remote Repo Clone

!!! adr "Related ADRs" ADR-043: Devcontainer Integration · ADR-039: Container Resource Types

Scenario: A CI/CD pipeline creates a container-instance that clones a remote repository on first start. There is no local checkout — the code lives entirely inside the container. This pattern is useful for cloud-based or ephemeral build environments where the operator has no local filesystem access to the repository.

What this example demonstrates:

  • --clone-into flag on agents resource add container-instance
  • Remote repository cloned lazily on container first start
  • Plan-level execution environment with fallback priority (precedence level 4)
  • No local git-checkout or devcontainer involved

Step 1 — Create container resource with clone-into:

=== "Rich"

<div class="highlight"><pre><code>
<span style="color: #66cc66; font-weight: 600;">$</span> <span style="color: cyan; font-weight: 600;">agents</span> resource add container-instance cloud/build-env \
  <span style="color: cyan;">--clone-into</span> https://github.com/acme/billing-api.git:/workspace

╭─ Resource ──────────────────────────────────────────╮
│ <span style="color: cyan; font-weight: 600;">Name:</span> cloud/build-env                               │
│ <span style="color: cyan; font-weight: 600;">ID:</span> 01J0E5F6G7H8J9K0L1M2N3O4P5                      │
│ <span style="color: #5599ff; font-weight: 600;">Type:</span> container-instance                            │
│ <span style="color: #66cc66; font-weight: 600;">State:</span> created (not started)                        │
│ <span style="color: #66cc66; font-weight: 600;">Clone:</span> https://github.com/acme/billing-api.git      │
│ <span style="color: #66cc66; font-weight: 600;">Clone Target:</span> /workspace                            │
╰─────────────────────────────────────────────────────╯

<span style="color: #66cc66; font-weight: 600;">✓ OK</span> Container resource registered (repo will be cloned on first start)
</code></pre></div>

=== "Plain"

```
$ agents resource add container-instance cloud/build-env \
  --clone-into https://github.com/acme/billing-api.git:/workspace

Resource
  Name: cloud/build-env
  ID: 01J0E5F6G7H8J9K0L1M2N3O4P5
  Type: container-instance
  State: created (not started)
  Clone: https://github.com/acme/billing-api.git
  Clone Target: /workspace

[OK] Container resource registered (repo will be cloned on first start)
```

Step 2 — Create project and link the container:

=== "Rich"

<div class="highlight"><pre><code>
<span style="color: #66cc66; font-weight: 600;">$</span> <span style="color: cyan; font-weight: 600;">agents</span> project create cloud/billing-api
<span style="color: #66cc66; font-weight: 600;">✓ OK</span> Project created

<span style="color: #66cc66; font-weight: 600;">$</span> <span style="color: cyan; font-weight: 600;">agents</span> project link-resource cloud/billing-api cloud/build-env
<span style="color: #66cc66; font-weight: 600;">✓ OK</span> Resource linked
</code></pre></div>

=== "Plain"

```
$ agents project create cloud/billing-api
[OK] Project created

$ agents project link-resource cloud/billing-api cloud/build-env
[OK] Resource linked
```

Step 3 — Execute plan with plan-level execution environment:

=== "Rich"

<div class="highlight"><pre><code>
<span style="color: #66cc66; font-weight: 600;">$</span> <span style="color: cyan; font-weight: 600;">agents</span> plan use local/add-pagination cloud/billing-api \
  <span style="color: cyan;">--execution-environment</span> cloud/build-env \
  <span style="color: cyan;">--execution-env-priority</span> fallback \
  <span style="color: cyan;">--automation-profile</span> trusted

<span style="color: #66cc66; font-weight: 600;">✓ OK</span> Plan 01J0F6G7H8J9K0L1M2N3O4P5Q6 created

<span style="color: #66cc66; font-weight: 600;">$</span> <span style="color: cyan; font-weight: 600;">agents</span> plan execute 01J0F6G7H8J9K0L1M2N3O4P5Q6

<span style="color: yellow; font-weight: 600;">⏳ Starting container</span> cloud/build-env …
<span style="color: yellow; font-weight: 600;">⏳ Cloning</span> https://github.com/acme/billing-api.git into /workspace …
<span style="color: #66cc66; font-weight: 600;">✓ Clone complete</span> (1.2 GB, 47s)
<span style="color: #66cc66; font-weight: 600;">✓ Container ready</span> in 52s

╭─ Execution Environment ─────────────────────────────╮
│ <span style="color: cyan; font-weight: 600;">Resolved via:</span> plan fallback                         │
│ <span style="color: #5599ff; font-weight: 600;">Resource:</span> cloud/build-env                           │
│ <span style="color: #66cc66; font-weight: 600;">Workspace:</span> /workspace                               │
│ <span style="color: #5599ff; font-weight: 600;">Precedence:</span> level 4 of 6                            │
│ <span style="color: yellow; font-weight: 600;">Note:</span> No devcontainer detected; using plan fallback │
╰─────────────────────────────────────────────────────╯

<span style="color: #5599ff; font-weight: 600;">▸ Decision 1/4:</span> Add cursor-based pagination to /workspace/src/api/invoices.ts
  <span style="color: #66cc66; font-weight: 600;">→ edit_file()</span> in <span style="color: yellow;">container</span>
  <span style="color: #66cc66; font-weight: 600;">✓</span> Checkpoint cp-001

<span style="color: #5599ff; font-weight: 600;">▸ Decision 2/4:</span> Add pagination types to /workspace/src/types/pagination.ts
  <span style="color: #66cc66; font-weight: 600;">→ write_file()</span> in <span style="color: yellow;">container</span>
  <span style="color: #66cc66; font-weight: 600;">✓</span> Checkpoint cp-002

<span style="color: #5599ff; font-weight: 600;">▸ Decision 3/4:</span> Update API tests
  <span style="color: #66cc66; font-weight: 600;">→ edit_file()</span> in <span style="color: yellow;">container</span>
  <span style="color: #66cc66; font-weight: 600;">✓</span> Checkpoint cp-003

<span style="color: #5599ff; font-weight: 600;">▸ Decision 4/4:</span> Run test suite
  <span style="color: #66cc66; font-weight: 600;">→ run_command()</span> in <span style="color: yellow;">container</span> npm test
  <span style="color: #66cc66; font-weight: 600;">✓</span> 89/89 tests passed
  <span style="color: #66cc66; font-weight: 600;">✓</span> Checkpoint cp-004

<span style="color: #66cc66; font-weight: 600;">✓ OK</span> Execution complete — ready for apply
</code></pre></div>

=== "Plain"

```
$ agents plan use local/add-pagination cloud/billing-api \
  --execution-environment cloud/build-env \
  --execution-env-priority fallback \
  --automation-profile trusted

[OK] Plan 01J0F6G7H8J9K0L1M2N3O4P5Q6 created

$ agents plan execute 01J0F6G7H8J9K0L1M2N3O4P5Q6

[WAIT] Starting container cloud/build-env …
[WAIT] Cloning https://github.com/acme/billing-api.git into /workspace …
[OK] Clone complete (1.2 GB, 47s)
[OK] Container ready in 52s

Execution Environment
  Resolved via: plan fallback
  Resource: cloud/build-env
  Workspace: /workspace
  Precedence: level 4 of 6
  Note: No devcontainer detected; using plan fallback

Decision 1/4: Add cursor-based pagination to /workspace/src/api/invoices.ts
  -> edit_file() in container
  [OK] Checkpoint cp-001

Decision 2/4: Add pagination types to /workspace/src/types/pagination.ts
  -> write_file() in container
  [OK] Checkpoint cp-002

Decision 3/4: Update API tests
  -> edit_file() in container
  [OK] Checkpoint cp-003

Decision 4/4: Run test suite
  -> run_command() in container npm test
  [OK] 89/89 tests passed
  [OK] Checkpoint cp-004

[OK] Execution complete — ready for apply
```

Step 4 — Apply (changes committed inside container, pushed to remote):

=== "Rich"

<div class="highlight"><pre><code>
<span style="color: #66cc66; font-weight: 600;">$</span> <span style="color: cyan; font-weight: 600;">agents</span> plan apply --yes 01J0F6G7H8J9K0L1M2N3O4P5Q6

╭─ Apply ──────────────────────────────────────────╮
│ <span style="color: #66cc66; font-weight: 600;">Files modified:</span> 3                                │
│   <span style="color: yellow;">~ src/api/invoices.ts</span> (modified)               │
│   <span style="color: #66cc66;">+ src/types/pagination.ts</span> (new)                │
│   <span style="color: yellow;">~ tests/api/invoices.test.ts</span> (modified)        │
│ <span style="color: #5599ff; font-weight: 600;">Target:</span> container /workspace                     │
│ <span style="color: #5599ff; font-weight: 600;">Apply mode:</span> commit + push to origin/main         │
╰──────────────────────────────────────────────────╯

<span style="color: #66cc66; font-weight: 600;">✓ OK</span> Changes applied (committed as abc1234, pushed to origin/main)
</code></pre></div>

=== "Plain"

```
$ agents plan apply --yes 01J0F6G7H8J9K0L1M2N3O4P5Q6

Apply
  Files modified: 3
    ~ src/api/invoices.ts (modified)
    + src/types/pagination.ts (new)
    ~ tests/api/invoices.test.ts (modified)
  Target: container /workspace
  Apply mode: commit + push to origin/main

[OK] Changes applied (committed as abc1234, pushed to origin/main)
```

Architecture

This section provides the complete architectural blueprint for CleverAgents. It defines the system's structural composition, technology choices, layer boundaries, data flow, and operational characteristics. An implementor should be able to build the entire system from this section combined with the behavioral specification above.

Architecture Overview

!!! adr "Architecture Decision" The layered architecture, DDD boundaries, and deployment modes are defined in ADR-001: Layered Architecture.

CleverAgents is architected as a layered, event-driven application following Domain-Driven Design (DDD) principles with clean architecture boundaries. The system is organized into four primary layers — Presentation, Application, Domain, and Infrastructure — with strict dependency rules: outer layers depend on inner layers, never the reverse. Cross-cutting concerns (logging, configuration, security) are handled through dependency injection and aspect-oriented patterns.

The architecture supports two deployment modes:

  1. Local Mode: A single-process CLI application where all components run in the same Python process. The CLI is the primary entry point, the database is SQLite, and all resources are local. A2A flows over stdio (agent as subprocess).
  2. Server Mode: A multi-user service where the CLI acts as a thin client communicating with a remote CleverAgents server via A2A over HTTP (JSON-RPC 2.0). The server hosts shared storage (PostgreSQL), namespace resolution, and remote plan execution (via LangGraph Platform RemoteGraph).

Both modes share the same domain model and application layer. The infrastructure layer provides swappable implementations for each deployment target. All communication uses the Agent-to-Agent Protocol (A2A) standard — there is no REST API.

High-Level Component Diagram

@startuml
skinparam componentStyle rectangle
skinparam defaultFontSize 12
skinparam packageStyle frame
skinparam packageFontSize 14
skinparam packageFontStyle bold
skinparam componentFontSize 11

package "Presentation Layer" <<Frame>> #E0F7FA {
  component [CLI\n(Typer)] as CLI
  component [TUI\n(Textual)] as TUI
  component [Web\n(Textual Web)] as Web
  component [IDE Plugin\n(Embedded TUI)] as IDE
  component [A2A Server\n(JSON-RPC 2.0 Endpoint)] as A2ASRV
}

interface "A2A\n(Agent-to-Agent Protocol)" as A2A #FFD54F
note right of A2A : Local: A2A over stdio\nServer: A2A over HTTP\nJSON-RPC 2.0 wire format

package "Application Layer" <<Frame>> #E3F2FD {
  package "Service Facade" {
    component [PlanService] as PS
    component [ProjectService] as ProjS
    component [ActorService] as ActS
    component [ContextService] as CtxS
    component [ToolService] as ToolS
    component [SkillService] as SkillS
    component [ResourceSvc] as ResS
  }
  package "Workflow Engine" {
    component [PlanLifecycle] as PL
    component [SessionWorkflow] as SW
    component [CorrectionFlow] as CF
    component [MergeWorkflow] as MW
  }
  package "Event Bus" {
    component [StructuredLog] as SL
    component [EventEmitter] as EE
    component [MetricsCollector] as MC
  }
  component [DI Container\n(dependency-injector DeclarativeContainer)] as DI #D1C4E9
}

package "Domain Layer" <<Frame>> #F3E5F5 {
  package "Domain Models" {
    component [Plan / Decision / Project\nResource / Actor / Action\nTool / Skill / Session\nInvariant / AutomationProf\nCheckpoint / CorrectionAtmpt] as DM
  }
  package "Domain Services" {
    component [InvariantEnforcer\nAutonomyCtrl\nContextBuilder\nMergeResolver] as DS
    note bottom of DS : Repository Interfaces\n(Protocol classes)
  }
  package "Domain Events" {
    component [PlanCreated / PhaseChanged\nDecisionMade / ToolInvoked\nApplyCompleted / CorrectionReq] as DE
  }
}

package "Infrastructure Layer" <<Frame>> #FFEBEE {
  package "Database" {
    component [SQLite / SQLAlchemy\nAlembic / UoW] as DB
  }
  package "Indexing" {
    component [Tantivy / FAISS\nNeo4j / Qdrant / RDFLib] as IDX
  }
  package "Sandbox" {
    component [GitWorktree / FsCopy\nTxnRollback / NoOp] as SBX
  }
  package "LLM / AI Runtime" {
    component [LangChain / LangGraph\nProviderRegistry\nRxPY Bridge / MCP SDK] as LLM
  }
  package "External Integrations" {
    component [MCP Servers\nAgent Skills Std\nREST/gRPC Clients] as EXT
  }
  package "LSP Runtime" {
    component [LSP Server Manager\nLSPToolAdapter\nLanguage Servers] as LSPR
  }
  package "File System / OS" {
    component [Watchdog / File I/O\nGit CLI / Subprocess\nOS Process / Signals] as FS
  }
}

CLI -[hidden]right-> TUI
TUI -[hidden]right-> Web
Web -[hidden]right-> IDE
IDE -[hidden]right-> A2ASRV

CLI -down-> A2A
TUI -down-> A2A
Web -down-> A2A
IDE -down-> A2A
A2ASRV -down-> A2A

A2A -down-> PS
A2A -down-> SW
A2A -down-> EE

PS -down-> DI
DI -down-> DM
DS -down-> DB
DS -down-> IDX
@enduml

Standards and Protocols in the Architecture

!!! adr "Architecture Decision" The four protocol standards and their architectural roles are defined across ADR-026: Agent-to-Agent Protocol (A2A), ADR-027: Language Server Protocol (LSP) Integration, ADR-028: Agent Skills Standard (AgentSkills.io), and ADR-029: Model Context Protocol (MCP) Adoption.

Each adopted standard maps to an explicit architectural boundary:

  • A2A (Agent-to-Agent Protocol) — the A2A standard (JSON-RPC 2.0) connects Presentation-layer clients (CLI, TUI, IDE) to the Application layer. Standard A2A operations handle agent messaging (message/send, message/stream), task lifecycle, and streaming updates. CleverAgents _cleveragents/-prefixed extension methods handle platform operations (plans, registries, context, sync). In local mode A2A flows over stdio; in server mode over HTTP. This keeps clients interchangeable and supports third-party client and agent development.
  • LSP (Language Server Protocol) — LSP servers are registered in the LSP Registry (Infrastructure layer) and bound to actor graph nodes to provide language intelligence. The LSPToolAdapter exposes LSP capabilities as tools (diagnostics, hover, go-to-definition, completions, references, rename) that actors call during reasoning, and the LSP Runtime automatically enriches actor context with diagnostic and type information. This gives actors the same semantic code understanding that human developers get from IDEs. See LSP Integration.
  • MCP (Model Context Protocol) — external MCP servers reside in the Infrastructure layer. The MCPToolAdapter bridges them into first-class Tools in the Domain model with extended capability metadata. MCP tools are composed into skills and used as tool nodes in actor graphs, giving the system standardized tool discovery and invocation.
  • Agent Skills (https://AgentSkills.io) — Agent Skills are ingested into the Skill Registry and loaded by the actor runtime via progressive disclosure. Inside actor graphs they appear as tool nodes whose execution is instruction-driven — the agent follows multi-step procedures that may call MCP tools, built-ins, or other Agent Skills. This adds portable, reusable workflows that complement schema-driven MCP tools.

A2A Integration Architecture

!!! adr "Architecture Decision" The Agent-to-Agent Protocol is defined in ADR-026: Agent-to-Agent Protocol (A2A). The adoption of the A2A standard is defined in ADR-047: A2A Standard Adoption. See also Core Concepts > Server > A2A for the behavioral specification.

Of the four standards CleverAgents adopts, A2A has the deepest architectural integration. While MCP, Agent Skills, and LSP plug into the Infrastructure and Domain layers to provide capabilities to actors, A2A defines the fundamental boundary between the Presentation and Application layers — it is the protocol surface through which every client operation flows, in every deployment mode. This section provides the full architectural detail.

Why A2A Exists

The architecture faces a structural tension: the system has multiple Presentation-layer surfaces (CLI, TUI, Web, IDE plugin) and multiple deployment modes (local, server). Without a shared contract:

  1. Each client would implement its own calling convention into the Application layer.
  2. Local and server modes would develop behavioral drift.
  3. Third-party client development would be impractical.
  4. Testing would require N x M test matrices (N clients x M modes).

CleverAgents adopts the Agent-to-Agent Protocol standard (a2a-protocol.org), built on JSON-RPC 2.0, as the sole protocol for all client-server communication. The contract is the same whether the transport is stdio (local) or HTTP (server). This is the architectural guarantee that clients are interchangeable.

A2A in the Layer Diagram

A2A sits at the boundary between the Presentation and Application layers. Every arrow from a Presentation-layer component to the Service Facade in the High-Level Component Diagram represents an A2A method call:

@startuml
skinparam componentStyle rectangle
skinparam defaultFontSize 11

package "Presentation Layer" #E0F7FA {
  component [CLI] as CLI
  component [TUI] as TUI
  component [Web] as Web
  component [IDE Plugin\n(Embedded TUI)] as IDE
  component [A2A Server Endpoint\n(JSON-RPC 2.0)] as A2ASRV
}

interface "A2A" as A2A_IF #FFD54F

package "Application Layer" #E3F2FD {
  component [Service Facade\n(PlanService, ProjectService,\nActorService, ContextService,\nToolService, SkillService,\nResourceService)] as SF
  component [SessionWorkflow] as SW
  component [EventEmitter] as EE
}

CLI -down-> A2A_IF
TUI -down-> A2A_IF
Web -down-> A2A_IF
IDE -down-> A2A_IF
A2ASRV -down-> A2A_IF

A2A_IF -down-> SF
A2A_IF -down-> SW
A2A_IF -down-> EE

note right of A2A_IF
  **Local mode**: A2A over stdio
  **Server mode**: A2A over HTTP
  JSON-RPC 2.0 wire format
end note
@enduml
Transport Modes

The transport mechanism is the core architectural enabler that makes A2A work across deployment modes. The A2A Python SDK provides both transports:

Local Mode — A2A over stdio

CLI  ──stdio──→  Agent Subprocess  ──→  A2aLocalFacade  ──→  ServiceFacade.method()  ──→  Domain / Infrastructure

The client spawns the agent as a subprocess. A2A JSON-RPC messages flow over stdin/stdout. Standard A2A operations (message/send, message/stream) drive the conversation. Extension methods (_cleveragents/*) are intercepted by A2aLocalFacade and routed to in-process Application-layer services. No network, no authentication — the agent runs with local user permissions.

Server Mode — A2A over HTTP

CLI  ──HTTP──→  CleverAgents A2A Server  ──→  ServiceFacade.method()  ──→  Domain / Infrastructure
                     │                                                        │
                     └── RemoteGraph.invoke() ──→  LangGraph Platform ────────┘

The client connects to the CleverAgents server via the A2A SDK's HTTP transport. All methods — both standard A2A and _cleveragents/ extensions — flow through the single A2A JSON-RPC 2.0 endpoint. The server delegates actor execution to LangGraph Platform via RemoteGraph. Authentication is required via HTTP auth schemes declared in the Agent Card.

External Agent Mode — Split Routing

CLI  ──HTTP──→  External A2A Server  (standard A2A operations: message/send, message/stream, etc.)
     ──HTTP──→  CleverAgents Server  (_cleveragents/* extension methods only)

When an agent is hosted on an external A2A-compatible server, the client routes standard A2A operations to that server and _cleveragents/ extension methods to the CleverAgents server. This enables interoperability with any A2A-compliant agent.

This transport architecture is enforced by:

  • The same A2A test suite runs in both modes. Contract tests validate that a given method with given parameters produces the same response shape over stdio and HTTP.
  • No Presentation-layer module imports from Infrastructure. Architecture tests (import-linter) verify this on every commit, preventing clients from bypassing A2A.
  • The Service Facade is the sole entry point. No Application-layer service is exposed directly to the Presentation layer.
Complete Method Routing

Every A2A method routes to a specific Application-layer service method. The following tables are the authoritative mapping.

Standard A2A Operations → Service Mapping

A2A Operation Service Method
initialize Capability negotiation — returns supported extensions, automation profiles, tool registries
authenticate Token validation against user/token store
session/new SessionWorkflow.create()
session/load SessionWorkflow.resume()
session/list SessionWorkflow.list()
session/prompt SessionWorkflow.tell()
session/cancel PlanService.cancel() (cancels in-progress plan execution)
session/set_mode AutomationProfileService.switch()
session/set_model ProviderRegistry.switch_model()
session/fork SessionWorkflow.fork()
session/resume SessionWorkflow.resume()

Plan Extension Methods → Service Mapping

A2A Extension Method Service Method
_cleveragents/plan/use PlanService.create_plan()
_cleveragents/plan/execute PlanLifecycle.execute()
_cleveragents/plan/apply PlanLifecycle.apply()
_cleveragents/plan/cancel PlanService.cancel()
_cleveragents/plan/status PlanService.get_status()
_cleveragents/plan/tree PlanService.get_tree()
_cleveragents/plan/explain PlanService.explain_decision()
_cleveragents/plan/correct CorrectionFlow.correct()
_cleveragents/plan/diff PlanService.get_diff()
_cleveragents/plan/artifacts PlanService.get_artifacts()
_cleveragents/plan/prompt PlanService.inject_guidance()
_cleveragents/plan/rollback PlanLifecycle.rollback()
_cleveragents/plan/list PlanService.list()

Registry Extension Methods → Service Mapping (pattern applies to all entity types)

A2A Extension Method Pattern Service Method Pattern
_cleveragents/registry/{entity}/list {Entity}Service.list()
_cleveragents/registry/{entity}/show {Entity}Service.show()
_cleveragents/registry/{entity}/add {Entity}Service.add()
_cleveragents/registry/{entity}/update {Entity}Service.update()
_cleveragents/registry/{entity}/remove {Entity}Service.remove()

Where {entity} is one of: actor, skill, tool, validation, resource, resource_type, project, action, automation_profile, invariant, lsp.

Context Extension Methods → Service Mapping

A2A Extension Method Service Method
_cleveragents/context/show ContextService.show()
_cleveragents/context/inspect ContextService.inspect()
_cleveragents/context/simulate ContextService.simulate()
_cleveragents/context/set ContextService.set()

Sync, Namespace, and Health Extension Methods

A2A Extension Method Service Method
_cleveragents/sync/pull SyncService.pull()
_cleveragents/sync/push SyncService.push()
_cleveragents/sync/status SyncService.status()
_cleveragents/namespace/list NamespaceService.list()
_cleveragents/namespace/show NamespaceService.show()
_cleveragents/namespace/members NamespaceService.members()
_cleveragents/health/check Health check handler
_cleveragents/diagnostics/run Diagnostic runner
Streaming Architecture

A2A streaming uses Server-Sent Events (SSE) via message/stream. The server pushes TaskStatusUpdateEvent and TaskArtifactUpdateEvent to the client in real-time during long-running operations:

Event types and their CleverAgents mappings:

Event Type Emitted When Payload
TaskStatusUpdateEvent Agent produces response tokens, task state changes, automation profile switched status (state + message with agent response Parts)
TaskArtifactUpdateEvent Tool execution results, plan artifacts generated artifact (named artifact with Parts)

Example streaming event:

{
  "jsonrpc": "2.0",
  "method": "task/statusUpdate",
  "params": {
    "taskId": "task_01HXM8C2...",
    "status": { "state": "working" },
    "message": { "role": "agent", "parts": [{ "kind": "text", "text": "Running git-diff on platform-repo..." }] }
  }
}

Causal Ordering Guarantee: Events within a single session/plan lifecycle are delivered in causal order. A phase transition event always arrives after the preceding phase completion.

In local mode (stdio), events flow as JSON-RPC messages over stdout. In server mode (HTTP), the A2A SDK manages the SSE streaming connection. Both modes deliver the same event types with the same payload shapes.

Authentication and Authorization

Authentication follows the A2A standard Agent Card-based discovery:

  1. Agent Card discovery: Client fetches the server's Agent Card (via /.well-known/agent.json or configured URL). The Agent Card declares supported capabilities, _cleveragents extensions, and authentication schemes.
  2. HTTP authentication: Client authenticates using the scheme declared in the Agent Card (OAuth2, API key, Bearer token). Typically Authorization: Bearer <server.token> header on every request.
  3. All subsequent operations: Authenticated. The A2A-Version header is sent on every request.

Local mode (stdio): Authentication is bypassed entirely. The agent subprocess runs with the user's local permissions.

Server mode: Every connection must discover the Agent Card and authenticate before any operation. Unauthenticated requests receive a JSON-RPC error:

{
  "jsonrpc": "2.0",
  "id": 1,
  "error": { "code": -32002, "message": "Authentication required" }
}

Authorization is namespace-scoped: users can only access entities within namespaces they have been granted access to. The local/ namespace is never accessible via server.

Error Taxonomy

A2A uses JSON-RPC 2.0 integer error codes. Standard protocol errors use the reserved range; application-specific errors use -32001 to -32099:

Error Code Meaning Domain Exception(s)
-32700 Parse error (malformed JSON)
-32600 Invalid request
-32601 Method not found A2aOperationNotFoundError
-32602 Invalid params ValidationError
-32603 Internal error Any unhandled Exception
-32001 Entity not found ResourceNotFoundError
-32002 Authentication required AuthenticationError
-32003 Authorization forbidden AuthorizationError
-32004 Invalid state BusinessRuleViolation
-32005 Already exists DuplicateEntityError
-32006 Budget exceeded BudgetExceededError
-32007 Version mismatch A2aVersionMismatchError
-32008 Plan error PlanError

All errors follow the JSON-RPC 2.0 error structure: { "code": <int>, "message": <string>, "data": <optional object> }. Clients should dispatch on code for precise error handling.

A2A Versioning
  • The JSON-RPC protocol version is always "2.0" (in the jsonrpc field)
  • A2A protocol version is declared in the Agent Card and sent via the A2A-Version HTTP header
  • CleverAgents extension version is declared in the Agent Card's extensions section under _cleveragents.version
  • Backward-compatible additions (new optional params, new extension methods) are permitted within a major version; breaking changes require a major version bump
  • Servers support the current extension version plus one prior minor version
A2A and the IDE Plugin

The IDE plugin is an embedded TUI — essentially the same Textual-based interface used by the standalone TUI, but hosted inside an IDE environment. Like all Presentation-layer clients, it communicates with the backend exclusively through A2A. It does not access the Domain or Infrastructure layers directly — all plan operations, validation queries, and context lookups go through A2A. This means:

  • The IDE plugin works identically in local mode (A2A over stdio to a subprocess) and server mode (A2A over HTTP).
  • IDE features stay exactly aligned with CLI/TUI behavior (same A2A operations, same JSON-RPC 2.0 format).
  • The IDE plugin receives TaskStatusUpdateEvent / TaskArtifactUpdateEvent streaming events for real-time plan and session updates.
  • Extension methods (_cleveragents/fs/*, _cleveragents/terminal/*) allow the agent to interact with the IDE's workspace and integrated terminal.

Note that the Language Server Protocol (LSP) in CleverAgents is unrelated to the IDE plugin. LSP serves an entirely different purpose: providing language intelligence to actors and agents in the Infrastructure layer. See LSP Integration and ADR-027: Language Server Protocol (LSP) Integration for the full LSP architecture.

Server and Client Architecture

!!! adr "Architecture Decision" The server application architecture is defined in ADR-048: Server Application Architecture. The A2A standard adoption is defined in ADR-047: A2A Standard Adoption.

This section defines the complete architecture of the CleverAgents server application and the client-side components that interact with it. The server is a separate application (distinct deployment unit) that shares the same Domain and Application layers as the client.

Server Application Structure

The server follows the same four-layer architecture as the client (ADR-001), sharing two layers and providing its own implementations for the other two:

Layer Client Server Shared?
Domain Business rules, domain models, domain events Same Yes — identical package
Application Service facades, workflows, event bus Same Yes — identical package
Infrastructure SQLite, local sandbox, local file access, A2aLocalFacade PostgreSQL, LangGraph Platform RemoteGraph, server-side sandbox, A2A SDK server No
Presentation CLI (Typer), TUI (Textual), IDE plugin A2A JSON-RPC 2.0 endpoint No

The shared Domain and Application layers are consumed as a Python package dependency. This ensures zero behavioral drift between local and server modes.

@startuml
skinparam componentStyle rectangle
skinparam defaultFontSize 11

package "Client Application" #E0F7FA {
  component [CLI / TUI / IDE Plugin] as ClientPres
  component [Application Layer\n(shared)] as ClientApp
  component [Domain Layer\n(shared)] as ClientDom
  component [Infrastructure\n(SQLite, local sandbox,\nA2aLocalFacade)] as ClientInfra
}

package "Server Application" #FFEBEE {
  component [A2A JSON-RPC 2.0\nEndpoint] as ServerPres
  component [Application Layer\n(shared)] as ServerApp
  component [Domain Layer\n(shared)] as ServerDom
  component [Infrastructure\n(PostgreSQL, RemoteGraph,\nA2A SDK server)] as ServerInfra
}

component [LangGraph\nPlatform] as LGP #FFF9C4

ClientPres -down-> ClientApp
ClientApp -down-> ClientDom
ClientDom -down-> ClientInfra

ServerPres -down-> ServerApp
ServerApp -down-> ServerDom
ServerDom -down-> ServerInfra

ServerInfra -right-> LGP : RemoteGraph

ClientPres -right[hidden]-> ServerPres

note bottom of ClientApp : Same Python package
note bottom of ServerApp : Same Python package
@enduml
Server Presentation Layer

The server's sole client-facing interface is an A2A JSON-RPC 2.0 endpoint implemented using the A2A Python SDK's server-side connection handler. There is no REST API, no GraphQL, no separate admin endpoint.

The endpoint handles three categories of messages:

  1. Standard A2A operations (message/send, message/stream): Routed to SessionWorkflow and actor execution (via LangGraph Platform RemoteGraph); Agent Card served at discovery endpoint
  2. Extension methods (_cleveragents/*): Routed to Application-layer services (PlanService, RegistryServices, SyncService, NamespaceService, etc.)
  3. Multi-turn interactions to clients (Task input-required state, _cleveragents/fs/*, _cleveragents/terminal/*): Forwarded from executing actors back to the connected client — enabling server-hosted agents to access client-local resources
Server Infrastructure: LangGraph Platform

Actor graphs (StateGraphs defined in YAML) are deployed to LangGraph Platform as separate deployments. The server invokes them via RemoteGraph:

sequenceDiagram
    participant C as Client
    participant S as A2A Server
    participant SW as SessionWorkflow
    participant RG as RemoteGraph
    participant LG as LangGraph Platform

    C->>S: message/send {"message": {"role": "user", "parts": [{"text": "Add pagination"}]}}
    S->>SW: tell("Add pagination")
    SW->>RG: invoke(strategy_actor, state)
    RG->>LG: Execute strategy actor graph
    LG-->>RG: Strategy result (plan entries)
    RG-->>SW: Plan with decisions
    SW-->>S: TaskStatusUpdateEvent (plan notification)
    S-->>C: TaskStatusUpdateEvent (streaming)
    SW->>RG: invoke(execution_actor, state)
    RG->>LG: Execute execution actor graph
    LG-->>RG: Execution result
    RG-->>SW: Completed execution
    SW-->>S: TaskArtifactUpdateEvent (tool_call results)
    S-->>C: TaskArtifactUpdateEvent (streaming)

Different actors for different plan phases (strategy, execution, estimation) each deploy as separate RemoteGraphs, enabling independent scaling without limiting plan capabilities. The SessionWorkflow orchestrates them the same way it orchestrates local actor graphs, but through the RemoteGraph interface.

Server Infrastructure: Persistence
Component Technology Notes
Database PostgreSQL via SQLAlchemy Same ORM as client (SQLite), different dialect
Migrations Alembic Schema migrations for server-specific tables (users, tokens, namespace ACLs)
Session store PostgreSQL Multi-user session persistence with namespace isolation
Plan store PostgreSQL Plan lifecycle records, decision trees, artifacts

Server-specific tables (authentication, namespace ACLs, user management) are additive — they extend the shared schema without modifying it.

Server Security Architecture

Authentication: Uses HTTP auth schemes declared in the server's Agent Card (OAuth2, API key, Bearer token). Tokens are validated against the server's user/token store. HTTP transport carries Authorization: Bearer <token> header on every request.

Authorization: Namespace-scoped. Users can only access entities in namespaces they have been granted access to. The local/ namespace is never accessible via server.

Transport security: HTTPS required for all production deployments (TLS termination at Kubernetes ingress).

Client-Side Architecture

The client uses an A2aClient that wraps the A2A Python SDK and routes method calls through a TransportSelector:

# Conceptual client architecture (not literal implementation)
class TransportSelector:
    """Selects transport based on configuration."""

    def get_transport(self) -> A2aTransport:
        if config.server_url:
            return A2aRemoteTransport(url=config.server_url, token=config.server_token)
        else:
            return A2aStdioTransport(agent_command=["cleveragents", "agent", "serve"])

class A2aClient:
    """Unified client for all A2A communication."""

    def __init__(self, transport: A2aTransport):
        self.connection = A2AClient(transport)

    async def send_message(self, task_id: str, message: str) -> AsyncIterator[Event]:
        """Send a message and stream task events."""
        return await self.connection.send_message(Message(
            role="user",
            parts=[TextPart(text=message)],
        ), task_id=task_id)

    async def plan_status(self, plan_id: str) -> dict:
        """Get plan status via extension method."""
        return await self.connection.request("_cleveragents/plan/status", {
            "plan_id": plan_id,
        })

Evolution of existing source code:

Current Component Becomes Notes
A2aLocalFacade Retained Handles _cleveragents/ dispatch to in-process services; adapts A2A message/send to local workflows
A2aClient Wraps A2A SDK A2AClient SDK provides production transport for both stdio and HTTP
A2aEvent Mapped to TaskStatusUpdateEvent / TaskArtifactUpdateEvent Event types map to A2A streaming event types
A2aEventQueue Retained for local mode Delivers events as in-process streaming events
Local/Server Interchangeability

The transport selector pattern ensures that application code is transport-agnostic. A CLI command works identically regardless of whether the backend is local or remote:

# CLI command — identical in local and server mode
async def plan_status_command(plan_id: str):
    client = get_a2a_client()  # TransportSelector picks stdio or HTTP
    result = await client.plan_status(plan_id)
    render_plan_status(result)

The only difference is where computation happens:

Aspect Local Mode Server Mode
Transport A2A over stdio A2A over HTTP
Actor execution In-process LangGraph LangGraph Platform RemoteGraph
Database SQLite PostgreSQL
Authentication Bypassed HTTP auth (Agent Card scheme) + Bearer token
File/terminal access Direct Via A2A multi-turn interactions (server → client)
Namespace resolution local/ only local/ + server namespaces
Server Deployment
Component Technology Notes
Container Docker Server packaged as container image
Orchestration Kubernetes Production deployment target
Configuration Helm chart (k8s/) Configurable replicas, resource limits, ingress
Ingress Kubernetes Ingress TLS termination, routing to A2A endpoint
Database PostgreSQL (managed or self-hosted) External to the application container
LangGraph Platform Managed or self-hosted External service for graph execution
Caching Redis (optional) Multi-instance session affinity, rate limiting

Architectural Principles

  1. Hexagonal Architecture (Ports and Adapters): The domain layer defines abstract repository interfaces (ports). The infrastructure layer provides concrete implementations (adapters). This allows swapping SQLite for PostgreSQL, FAISS for Qdrant, or local execution for remote server execution without touching domain logic.

  2. Command Query Responsibility Segregation (CQRS): Write operations (plan creation, decision recording, resource modification) flow through command handlers that enforce invariants and emit domain events. Read operations (plan status, decision tree visualization, context queries) use optimized read paths that may bypass the ORM for performance.

  3. Event-Driven Architecture: All significant state changes emit domain events through a structured event bus. Events flow through RxPY reactive streams for real-time processing and through the structured logging pipeline for persistence. This enables decoupled observability, audit logging, and future webhook/notification systems.

  4. Dependency Injection: All service dependencies are wired through a DeclarativeContainer (from the dependency-injector library). This eliminates hidden coupling, enables testing with mock implementations, and supports runtime reconfiguration (e.g., switching providers).

  5. Convention over Configuration: Default behavior requires zero configuration. For entity registration commands, YAML configuration files are the complete definition (no CLI overrides). For runtime commands (e.g., plan use), CLI flags may override entity defaults. Environment variables can be interpolated into configuration files via ${ENV_VAR} syntax. The resolution chain depends on context: entity registration uses config-file-only semantics, while runtime commands support per-invocation CLI overrides of entity defaults.

Data Flow: Plan Lifecycle

The following diagram traces data flow through the system during a complete plan lifecycle:

sequenceDiagram
    participant U as User
    participant C as CLI
    participant A as Application
    participant D as Domain
    participant I as Infrastructure

    U->>C: plan use
    C->>A: PlanService.create_plan()
    A->>D: Plan(phase=strategize)
    D->>I: PlanRepository.save(plan)

    A->>D: InvariantEnforcer.reconcile()
    A->>D: AutonomyCtrl.should_proceed

    rect rgb(200, 230, 255)
        Note over A,D: Strategize Phase (if auto)
        A->>D: StrategyActor.invoke()
        D->>I: LangGraph StateGraph.invoke
        D->>I: ContextBuilder.build_hot_ctx()
        I-->>D: [FAISS/Tantivy results]
        D->>I: LLM Provider.generate()
        I-->>D: [LLM response]
        D-->>A: Decision[]
        D->>I: DecisionRepo.save_batch()
    end

    rect rgb(255, 230, 200)
        Note over A,D: Execute Phase
        A->>D: SandboxManager.create()
        D->>I: GitWorktree.create_worktree()
        A->>D: ExecutionActor.invoke(sandbox)
        D->>I: Tool calls via SkillRegistry
        D-->>A: ChangeSet
    end

    rect rgb(200, 255, 200)
        Note over A,D: Apply Phase
        A->>D: Validation.run_all()
        D->>I: Subprocess.run(commands)
        A->>D: SandboxManager.commit()
        D->>I: git merge
    end

    C-->>U: OK Applied

Technical Stack

!!! adr "Architecture Decision" The technology selections, version constraints, and rationale are defined in ADR-005: Technical Stack.

This section enumerates every technology choice in the CleverAgents stack, organized by functional area. Version constraints are minimum versions; newer compatible versions are acceptable.

Core Runtime

Technology Version Role Rationale
Python >= 3.13 Primary language Type hint maturity, async/await, pattern matching, performance improvements in 3.13. The entire codebase is Python-only for consistency and LLM ecosystem alignment.
Hatchling >= 1.21.0 PEP 517 build backend Lightweight, standards-compliant build system. Produces wheels and sdists without complex configuration.
uv >= 0.8.0 Package installer and resolver 10-100x faster than pip. Used in CI, Docker builds, and Nox sessions for dependency installation. Drop-in replacement for pip with full PEP 723 support.

CLI and Presentation

Technology Version Role Rationale
Typer >= 0.9.0 CLI framework Built on Click with automatic help generation, type inference from Python type hints, and Rich integration for styled output. Supports nested command groups matching the agents <noun> <verb> pattern.
Rich (transitive via Typer) Terminal rendering Provides Panel, Table, Tree, Syntax, Progress, Spinner, and Markdown rendering used by all output formats except plain and structured (json/yaml).
Textual (future) TUI framework Built by the Rich maintainers. Provides a reactive terminal UI framework that can be served as a web app via Textual Web, enabling the "single UI codebase" strategy for TUI, Web, and IDE plugin.

LLM and AI Runtime

Technology Version Role Rationale
LangChain >= 0.2.14 LLM abstraction layer Provider-agnostic interface for chat models, embeddings, output parsing, prompt templates, and tool calling. The BaseLanguageModel protocol enables swapping providers without changing application code.
LangGraph (transitive) Stateful workflow orchestration StateGraph with conditional edges, checkpointing (MemorySaver), and streaming execution. Used for the plan generation graph (load_context -> analyze -> generate -> validate) and auto-debug graph.
LangChain Provider Packages varies LLM provider integrations langchain-openai (>= 0.2.0), langchain-google-genai (>= 0.2.0), langchain-anthropic, langchain-groq, langchain-together, langchain-cohere. Each provides a ChatModel implementation.
LangChain Community >= 0.2.14 Community integrations FAISS vector store, FakeListLLM/FakeEmbeddings for testing.
RxPY >= 3.2.0 Reactive stream processing Subject, BehaviorSubject, ReplaySubject, and operators (map, filter, flat_map, debounce, throttle, scan) for real-time event routing between actors, stream-to-graph bridging, and backpressure management.
MCP SDK >= 1.4.0 Model Context Protocol Client SDK for communicating with MCP servers. Enables CleverAgents to discover and invoke tools exposed by any MCP-compliant server.

Data and Persistence

Technology Version Role Rationale
SQLite (system) Primary database Zero-configuration, file-based, ACID-compliant. Sufficient for local mode; supports WAL mode for concurrent reads during plan execution. For server mode, the infrastructure layer swaps in PostgreSQL via the same SQLAlchemy interface.
SQLAlchemy (transitive via Alembic) ORM and database abstraction Declarative model mapping, session management, Unit of Work pattern, and dialect abstraction enabling database portability.
Alembic >= 1.13.1 Database migrations Version-controlled schema migrations with upgrade/downgrade support. Migrations are auto-applied on first run and during agents init.
python-ulid >= 2.7.0 ID generation ULID generation for plans, decisions, resources, and correction attempts. ULIDs are lexicographically sortable by creation time, enabling efficient time-range queries without separate timestamp indexes.

Indexing and Code Intelligence

Technology Version Role Rationale
Tantivy (via tantivy-py) configurable Full-text search Rust-based search engine providing sub-millisecond full-text search across project resources. Used for keyword-based code search during context building. Alternative: SQLite FTS5 for zero-dependency installations.
FAISS (faiss-cpu) >= 1.7.4 Vector similarity search Facebook AI Similarity Search for semantic code search. Indexes embedding vectors generated from project files. Supports approximate nearest neighbor search for fast retrieval during context building.
Qdrant configurable Vector search (alternative) External vector database for production deployments requiring horizontal scaling, persistence, and filtering. Connected via qdrant-client.
Neo4j configurable Knowledge graph Graph database for structural code relationships (class hierarchies, call graphs, dependency trees). Enables queries like "find all callers of function X" during Strategize. Alternative: rdflib for in-process graph queries.
rdflib >= 7.1.4 In-process graph store Python RDF library for lightweight structural code analysis without external dependencies. Stores RDF triples representing code relationships.
OpenAI Embeddings configurable Vector generation Default embedding provider (text-embedding-3-small). Alternative providers: Anthropic, local sentence-transformers models via index.embedding.provider config.

Configuration and Validation

Technology Version Role Rationale
Pydantic >= 2.7.0 Data validation and modeling All domain models, configuration objects, and API schemas use Pydantic v2 for runtime validation, JSON Schema generation, and serialization. Provides type-safe data boundaries between layers.
Pydantic Settings >= 2.11.0 Environment configuration BaseSettings subclasses for environment variable loading with the CLEVERAGENTS_ prefix, .env file support, and nested model resolution. Implements the four-tier configuration resolution chain.
Jinja2 >= 3.1.0 Template rendering Used for actor system prompts, YAML configuration interpolation, and prompt template rendering. Supports {{ context.* }} variable expansion with sandboxed execution.
TOML (stdlib tomllib) Configuration file format Global configuration uses TOML for its native support of nested tables, which maps naturally to the dot-separated hierarchical key structure. Python 3.13 includes tomllib in stdlib.
YAML (via PyYAML) Entity configuration format All entity definitions (actors, skills, tools, actions, resource types, automation profiles) use YAML as the canonical human-authored format. JSON is accepted as valid YAML.

Testing

Technology Version Role Rationale
Behave >= 1.2.6 BDD feature tests Gherkin .feature files describe behavioral scenarios in natural language. Step implementations exercise the full application stack. Custom behave-parallel runner enables parallel execution via ProcessPoolExecutor.
Robot Framework >= 7.3.2 Integration tests Keyword-driven integration tests for end-to-end CLI workflows. robotframework-pabot enables parallel suite execution.
pytest >= 8.0.0 Unit tests Standard Python test framework for unit-level testing with fixtures, parametrization, and plugin support.
pytest-asyncio >= 0.23.0 Async test support Enables async def test_* functions for testing async LangGraph workflows and RxPY stream operations.
pytest-cov / coverage >= 7.11.0 Coverage reporting Target: 85% line coverage. HTML and XML reports generated by Nox sessions. Coverage gates enforce minimum thresholds in CI.
Hypothesis >= 6.136.6 Property-based testing Generates randomized test inputs for invariant verification, particularly useful for testing decision tree operations, merge strategies, and context tier boundaries.
ASV (Airspeed Velocity) >= 0.6.5 Performance benchmarks Tracks plan generation latency, CLI startup time, and indexing throughput across commits. Prevents performance regressions.

Code Quality

Technology Version Role Rationale
Ruff >= 0.1.0 Linting and formatting Single tool replacing Black, Flake8, isort, and pyupgrade. Rules: E, F, W, B, UP, I, SIM, RUF. Line length: 88. Double quotes. 4-space indent.
Pyright >= 1.1.350 Static type checking Strict mode type checking for the entire codebase. Catches type errors at development time rather than runtime. Configured via pyrightconfig.json.

Infrastructure and Deployment

Technology Version Role Rationale
Docker multi-stage Containerization Production image uses python:3.13-slim with multi-stage build. Non-root user (appuser, uid 1000). Entrypoint: python -m cleveragents.
Helm (chart) Kubernetes deployment Helm chart in k8s/ directory for server mode deployment. Linted and template-tested in CI.
Nox >= 2025.4.22 Task automation Session-based task runner for lint, test, build, docs, benchmarks, and coverage. Uses uv as the virtual environment backend for fast session creation.
Forgejo CI (workflow) Continuous integration Self-hosted Forgejo instance with GitHub Actions-compatible workflow syntax. Pipeline: lint -> typecheck -> behave (matrix 3.11/3.12/3.13) -> build -> docker -> helm.
devcontainer CLI (optional) Devcontainer management Used for building and managing devcontainer-instance resources from .devcontainer/devcontainer.json configurations. Falls back to direct Docker/Podman CLI when not installed, with reduced feature support (e.g., devcontainer features may not be available). See ADR-043.

Monitoring and Observability

Technology Version Role Rationale
structlog >= 24.4.0 Structured logging JSON-structured log output with context binding (plan_id, decision_id, actor_name, tool_name). Enables log aggregation, filtering, and correlation across complex plan hierarchies.
LangSmith (optional) LLM observability Optional integration for tracing LLM calls, token usage, latency, and cost. Configured via CLEVERAGENTS_LANGSMITH_* environment variables. Syncs to LANGCHAIN_TRACING_V2.

Additional Libraries

Technology Version Role Rationale
dependency-injector >= 4.41.0 DI container DeclarativeContainer with Singleton, Factory, and Configuration providers. Wires all services, repositories, and infrastructure components.
watchdog >= 4.0.0 File system monitoring Watches project resource directories for changes. Triggers automatic re-indexing when index.auto-reindex is enabled.
numpy >= 2.1.0 Numerical computing Vector operations for embedding similarity computation, confidence score aggregation, and distance calculations in the vector store layer.
uvicorn >= 0.30.1 ASGI server HTTP server for server mode A2A endpoint. Hosts the A2A JSON-RPC 2.0 server for remote plan execution, namespace resolution, and multi-user collaboration.

Dependency Injection

!!! adr "Architecture Decision" The DI container design, wiring pattern, and testing strategy are defined in ADR-003: Dependency Injection.

All service dependencies in CleverAgents are wired through a DeclarativeContainer from the dependency-injector library (>= 4.41.0). This container is the single composition root for the entire application. No service instantiates its own dependencies — all are received through constructor injection managed by the container.

Container Structure

The DI container is a DeclarativeContainer subclass that declares all providers:

  • Singleton providers for long-lived services: database engine, repository implementations, event bus, index backends, provider registry.
  • Factory providers for per-request or per-operation objects: sandbox instances, plan execution contexts, LangGraph state graphs.
  • Configuration provider for loading and exposing the global TOML configuration and environment variables.

Wiring Pattern

The wiring pattern enforces the layered architecture (see Architecture Overview) by ensuring that dependencies always flow inward:

  1. The Domain Layer defines repository interfaces as Protocol classes (e.g., PlanRepository, DecisionRepository, ResourceRepository).
  2. The Infrastructure Layer provides concrete implementations (e.g., SQLAlchemyPlanRepository, SQLAlchemyDecisionRepository).
  3. The DI container maps protocols to implementations using Singleton or Factory providers.
  4. Application Layer services declare their dependencies as constructor parameters typed to the domain protocols.
  5. The container auto-wires these dependencies at application startup.

This pattern ensures that domain and application code never imports infrastructure modules directly. Swapping backends (e.g., SQLite → PostgreSQL, FAISS → Qdrant, local execution → remote server) requires only changing the container provider mapping — no application or domain code changes.

Provider Types

Provider Type Use Case Example
Singleton Shared stateful services Database session factory, event bus, index backends
Factory Per-use transient objects Sandbox instances, execution contexts
Configuration External configuration values TOML config keys, environment variables

Testing Support

In test configurations, the container is reconfigured to supply mock or fake implementations:

  • FakeListLLM / FakeEmbeddings from LangChain Community replace real LLM providers.
  • In-memory repositories replace SQLAlchemy-backed repositories.
  • No-op sandbox strategies replace git worktree or filesystem copy strategies.

The container's override() context manager allows swapping providers for individual test cases without affecting the global configuration.

Container Rules

  1. All cross-layer dependencies must flow through the DI container. Direct instantiation of infrastructure components in application or domain code is prohibited.
  2. The container is initialized exactly once at application startup (in the CLI entry point or server bootstrap). It must not be re-initialized during request handling.
  3. Domain Layer code must never import or reference the dependency-injector library. Domain protocols are plain Python Protocol classes with no DI framework annotations.
  4. Every new service or repository must be registered in the container before use. Unregistered dependencies cause explicit startup failures, not silent runtime errors.
  5. A startup self-test verifies that all declared providers can be resolved without errors. This runs as part of the application bootstrap and in CI.

Data Validation

!!! adr "Architecture Decision" The data validation strategy, Pydantic usage, and schema generation approach are defined in ADR-004: Data Validation.

CleverAgents processes data from multiple untrusted or semi-trusted sources: user CLI input, YAML configuration files, LLM-generated tool call arguments, MCP server responses, environment variables, and database records. Every boundary crossing — between layers, between the system and external services, between the user and the application — is an opportunity for invalid data to enter the system and cause subtle downstream failures.

All domain models, configuration objects, and API schemas use Pydantic V2 (>= 2.7.0) for runtime data validation, JSON Schema generation, and serialization. Pydantic Settings (>= 2.11.0) extends this to environment variable loading. Pydantic serves as the type-safe data boundary between all layers.

Domain Models

Every domain entity — Plan, Decision, Action, Resource, Actor, Tool, Skill, Session, Invariant, AutomationProfile, Checkpoint, CorrectionAttempt — is a Pydantic model subclass. Domain entities that share the canonical configuration (str_strip_whitespace, validate_assignment, arbitrary_types_allowed=False, populate_by_name, use_enum_values) inherit from the shared DomainBaseModel base class (defined in domain/models/base.py) rather than directly from pydantic.BaseModel. Classes with genuinely different configuration requirements may still subclass BaseModel directly. Fields use Python type annotations with Pydantic field validators for business rule enforcement.

Configuration Objects

YAML entity configuration files (actors, skills, tools, actions, resource types, automation profiles, context views) are parsed into Pydantic models. Validation errors are reported with field paths and human-readable messages before any processing occurs. A partially valid configuration must never be applied — YAML files must validate completely before any side effects occur.

Environment and Global Configuration

BaseSettings subclasses from Pydantic Settings handle environment variable loading with the CLEVERAGENTS_ prefix, .env file support, and nested model resolution. This implements the four-tier configuration resolution chain: CLI flag > environment variable > project-scoped config > global config file > built-in default.

JSON Schema Generation

Pydantic models automatically generate JSON Schema definitions, which are used for:

  • Tool input/output schema definitions consumed by LLM tool-calling protocols.
  • Validation of LLM-generated tool call arguments before execution.
  • API request/response schema documentation.

JSON Schema generated from Pydantic models is the canonical schema for tool definitions. Manual schema definitions must not diverge from the model.

Serialization

Pydantic handles serialization to and from JSON, YAML (via dict intermediary), and database column types. Custom serializers are defined for domain-specific types (ULIDs, datetime formats, enum values).

Data Validation Rules

  1. All data crossing a layer boundary must pass through a Pydantic model. Raw dictionaries or untyped data structures must not propagate between layers.
  2. Strict mode is used where appropriate to prevent implicit type coercion (e.g., strings silently becoming integers).
  3. Validation error messages must include the field path and a human-readable explanation suitable for CLI display.
  4. Pyright in strict mode verifies that Pydantic model field types are consistent across the codebase.
  5. Schema drift tests verify that JSON Schema generated from Pydantic tool models matches the schemas expected by MCP and LangChain tool-calling protocols.

ACMS (Advanced Context Management System)

!!! adr "Architecture Decision" The ACMS architecture, UKO layer, CRP protocol, and context strategy framework are defined in ADR-014: Context Management (ACMS).

This section provides the complete architectural design of the Advanced Context Management System (ACMS). For the conceptual overview, core data types, CRP skill definition, strategy protocol, and plan lifecycle integration, see Core Concepts > Advanced Context Management System (ACMS).

Architectural Overview

@startuml
skinparam componentStyle rectangle
skinparam defaultFontSize 12
skinparam packageFontSize 13
skinparam packageFontStyle bold
skinparam componentFontSize 11

package "Actor / Skill" as actor #E0F7FA {
  component [Issues ContextRequests\nvia Context Request Protocol] as CRP
}

package "Context Assembly Pipeline\n(10-component pluggable)" as pipeline #C8E6C9 {
  package "Phase 1: Strategy Orchestration" {
    component [StrategySelector] as SS
    component [BudgetAllocator] as BA
    component [StrategyExecutor] as SE
  }
  package "Phase 2: Fragment Fusion" {
    component [Deduplicator → DepthResolver\n→ Scorer → Packer → Orderer] as Fusion
  }
  package "Phase 3: Finalization" {
    component [PreambleGenerator\nSkeletonCompressor] as Final
  }
}

package "Strategies" as strats #E8EAF6 {
  component [ARCE\n(graph-aware)] as S1
  component [Simple\n(keyword/embed)] as S2
  component [Breadth/Depth\nNavigator] as S3
  component [Custom\n(user-defined)] as S4
}

package "Backend Abstraction Layer (BAL)" as bal #FFF9C4 {
  component [ScopedView\n(Text DB)] as TV
  component [ScopedView\n(Vector DB)] as VV
  component [ScopedView\n(Graph DB)] as GV
}

package "Physical Data Stores" as stores #E3F2FD {
  component [Tantivy /\nSQLite FTS] as TDB
  component [FAISS /\nQdrant / Weaviate] as VDB
  component [Blazegraph / Jena /\nNeo4j / Stardog\n(UKO triples)] as GDB
}

package "Resource Registry" as rr #F3E5F5 {
  component [Physical/virtual resources\nDAG links] as RR
}

CRP -down-> SS
SS -down-> BA
BA -down-> SE
SE -down-> S1
SE -down-> S2
SE -down-> S3
SE -down-> S4
S1 -down-> TV
S1 -down-> VV
S1 -down-> GV
S2 -down-> TV
S2 -down-> VV
S3 -down-> GV
S4 -down-> TV

SE -right[hidden]-> Fusion
Fusion -right[hidden]-> Final

TV -down-> TDB
VV -down-> VDB
GV -down-> GDB
TDB -down-> RR
VDB -down-> RR
GDB -down-> RR
@enduml

Key Design Principles:

  1. Strategy-agnostic framework: The framework itself has no opinion about how context is assembled. That is the job of strategies. The framework provides the request protocol, the data plumbing, and the Context Assembly Pipeline.
  2. Dynamic budget: The token budget for context assembly may change on every invocation. Strategies receive the current budget and must respect it. The pipeline's BudgetPacker enforces the budget as a hard ceiling.
  3. Hierarchical by default: Every context assembly occurs within the context of a plan hierarchy. The system tracks parent context and provides progressive focusing primitives.
  4. Provenance everywhere: Every piece of context delivered to an actor can be traced back to a specific resource, a specific location within that resource, a specific revision, and a specific UKO node.
  5. Pluggable Architecture: Every component can be extended or replaced.
  6. Progressive Enhancement: System works with basic text search, enhances with advanced features.
  7. Eager Indexing: Indices are built immediately when resources are added and kept continuously up-to-date.
  8. Agent Awareness: Agents understand available indices through skills.
  9. Real-time Synchronization: Indices update immediately as code changes.

Universal Knowledge Ontology (UKO)

Design Philosophy

The UKO is designed around a single principle: everything that an actor might want to know about a resource should be representable as a node in the knowledge graph, at any level of detail, with provenance back to the originating resource.

The ontology uses an inheritance hierarchy so that:

  • Layer 0 defines universal concepts that apply to any information (code, docs, data, infra). DetailDepth is defined here as a non-negative integer with domain-agnostic semantics: depth 0 is the most minimal representation (just a name/identifier), and each increment reveals progressively more structure, content, and relationships. There is no fixed upper bound — the maximum meaningful depth depends on the domain.
  • Layer 1 specializes for domain-specific concepts: general software (uko-code: — modules, callables, types), documents (uko-doc: — sections, paragraphs, citations), data schemas (uko-data: — tables, columns, constraints), and infrastructure (uko-infra: — services, endpoints, config). Each domain registers a DetailLevelMap that assigns named labels to specific integer depths (e.g., uko-code: maps MODULE_LISTING → 0, SIGNATURES → 4, FULL_SOURCE → 9).
  • Layer 2 specializes for paradigm/format-specific concepts within a domain: object-oriented (uko-oo:), functional (uko-func:), procedural (uko-proc:) for code. Future extensions could add academic vs. technical for documents, or relational vs. graph for databases. Each paradigm extends its parent domain's DetailLevelMap, potentially inserting new named levels between existing ones (which shifts subsequent integer assignments).
  • Layer 3 specializes for technology-specific concepts: Python (uko-py:), TypeScript (uko-ts:), Rust (uko-rs:), Java (uko-java:), and potentially Markdown, PostgreSQL, or others. Language/technology-specific DetailLevelMap extensions are added only when they provide meaningful additional semantics beyond the paradigm level.

This layering means a strategy that operates at Layer 0 can work with any resource — code, documents, databases, infrastructure — while a strategy that operates at Layer 3 can provide language-specific or technology-specific intelligence. The breadth-depth-navigator strategy typically operates at Layers 0-1 (universal graph traversal), while arce leverages all layers. DetailDepth resolution follows the DetailLevelMap inheritance chain: when a named level is requested, the system looks up the name in the most specific map available for the target UKO node type (e.g., uko-py: first), then walks up to the parent map (uko-oo:uko-code:uko:) until a match is found. When a raw integer is specified, it is used directly.

Ontology Hierarchy

The UKO defines five namespace prefixes:

Prefix IRI Purpose
uko: https://cleveragents.ai/ontology/uko# Layer 0: Universal foundation
uko-code: https://cleveragents.ai/ontology/uko/code# Layer 1: General software
uko-oo: https://cleveragents.ai/ontology/uko/oo# Layer 2: Object-oriented paradigm
uko-func: https://cleveragents.ai/ontology/uko/func# Layer 2: Functional paradigm
uko-proc: https://cleveragents.ai/ontology/uko/proc# Layer 2: Procedural paradigm
uko-py: https://cleveragents.ai/ontology/uko/py# Layer 3: Python-specific
uko-ts: https://cleveragents.ai/ontology/uko/ts# Layer 3: TypeScript-specific
uko-rs: https://cleveragents.ai/ontology/uko/rs# Layer 3: Rust-specific
uko-java: https://cleveragents.ai/ontology/uko/java# Layer 3: Java-specific
uko-doc: https://cleveragents.ai/ontology/uko/doc# Layer 1: Documents
uko-data: https://cleveragents.ai/ontology/uko/data# Layer 1: Data schemas
uko-infra: https://cleveragents.ai/ontology/uko/infra# Layer 1: Infrastructure
Layer 0: Universal Foundation (uko:)

Every UKO node, regardless of domain, is one of five base classes:

@prefix uko: <https://cleveragents.ai/ontology/uko#> .
@prefix owl: <http://www.w3.org/2002/07/owl#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .

# ── Base Classes ────────────────────────────────────────────────── uko:InformationUnit a owl:Class ; rdfs:comment "The root of every UKO node. Anything that can appear in an actor's context." .

uko:Container a owl:Class ; rdfs:subClassOf uko:InformationUnit ; rdfs:comment "An information unit that contains other information units (file, module, class, section)." .

uko:Atom a owl:Class ; rdfs:subClassOf uko:InformationUnit ; rdfs:comment "A leaf-level information unit (function body, paragraph, config value)." .

uko:Annotation a owl:Class ; rdfs:subClassOf uko:InformationUnit ; rdfs:comment "Metadata attached to another information unit (comment, docstring, attribute)." .

uko:Boundary a owl:Class ; rdfs:subClassOf uko:InformationUnit ; rdfs:comment "An interface point between containers (export, API endpoint, public method signature)." .

# ── Core Relationships ──────────────────────────────────────────── uko:contains a owl:ObjectProperty ; rdfs:domain uko:Container ; rdfs:range uko:InformationUnit ; rdfs:comment "Parent contains child (e.g., module contains class)." .

uko:references a owl:ObjectProperty ; rdfs:domain uko:InformationUnit ; rdfs:range uko:InformationUnit ; rdfs:comment "Weak reference (e.g., a function mentions a type in a docstring)." .

uko:dependsOn a owl:ObjectProperty ; rdfs:domain uko:InformationUnit ; rdfs:range uko:InformationUnit ; rdfs:comment "Strong dependency (e.g., import, inheritance, call)." .

# ── Content Properties ──────────────────────────────────────────── uko:hasRendering a owl:DatatypeProperty ; rdfs:domain uko:InformationUnit ; rdfs:range xsd:string ; rdfs:comment "A rendered text representation of this node at a specific detail depth. Multiple renderings at different depths may exist for the same node. The depth is indicated by the associated uko:renderingDepth value." .

uko:renderingDepth a owl:DatatypeProperty ; rdfs:domain uko:InformationUnit ; rdfs:range xsd:nonNegativeInteger ; rdfs:comment "The integer detail depth at which the associated uko:hasRendering was produced. Paired with uko:hasRendering via a blank node or reification." .

uko:hasFullContent a owl:DatatypeProperty ; rdfs:domain uko:InformationUnit ; rdfs:range xsd:string ; rdfs:comment "Shorthand for the maximum-depth rendering of this node (complete, unabridged content). Equivalent to uko:hasRendering at the domain's maximum depth." .

# ── Provenance ──────────────────────────────────────────────────── uko:sourceResource a owl:ObjectProperty ; rdfs:domain uko:InformationUnit ; rdfs:comment "The CleverAgents Resource (by ULID) this node was extracted from." .

uko:sourcePath a owl:DatatypeProperty ; rdfs:domain uko:InformationUnit ; rdfs:range xsd:string ; rdfs:comment "Path within the resource (e.g., file path relative to the resource root)." .

uko:sourceRange a owl:DatatypeProperty ; rdfs:domain uko:InformationUnit ; rdfs:range xsd:string ; rdfs:comment "Byte or line range within the source file (e.g., '42:1-87:0')." .

# ── Temporal ────────────────────────────────────────────────────── uko:validFrom a owl:DatatypeProperty ; rdfs:domain uko:InformationUnit ; rdfs:range xsd:dateTime .

uko:validUntil a owl:DatatypeProperty ; rdfs:domain uko:InformationUnit ; rdfs:range xsd:dateTime .

uko:isCurrent a owl:DatatypeProperty ; rdfs:domain uko:InformationUnit ; rdfs:range xsd:boolean .

uko:isRevisionOf a owl:ObjectProperty ; rdfs:domain uko:InformationUnit ; rdfs:range uko:InformationUnit ; rdfs:comment "Links a revised node to its predecessor." .

Layer 1: Domain Specializations

Layer 1 specializes the universal Layer 0 foundation for four domains: general software (uko-code:), documents (uko-doc:), data schemas (uko-data:), and infrastructure (uko-infra:). Each domain defines its own classes, relationships, properties, and a DetailLevelMap mapping named levels to integer DetailDepth values.

General Software (uko-code:):

@prefix uko-code: <https://cleveragents.ai/ontology/uko/code#> .

uko-code:Module a owl:Class ; rdfs:subClassOf uko:Container ; rdfs:comment "A source code module (file, package, namespace)." .

uko-code:Callable a owl:Class ; rdfs:subClassOf uko:Atom ; rdfs:comment "Any callable unit (function, method, procedure, lambda)." .

uko-code:TypeDefinition a owl:Class ; rdfs:subClassOf uko:Atom ; rdfs:comment "A type or schema definition (class, struct, interface, enum, typedef)." .

uko-code:TestCase a owl:Class ; rdfs:subClassOf uko:Atom ; rdfs:comment "A test case or test function." .

uko-code:Import a owl:Class ; rdfs:subClassOf uko:Annotation ; rdfs:comment "An import/include/require statement." .

uko-code:hasReturnType a owl:DatatypeProperty ; rdfs:domain uko-code:Callable ; rdfs:range xsd:string .

uko-code:hasParameters a owl:DatatypeProperty ; rdfs:domain uko-code:Callable ; rdfs:range xsd:string ; rdfs:comment "JSON-encoded parameter list." .

uko-code:testsCallable a owl:ObjectProperty ; rdfs:domain uko-code:TestCase ; rdfs:range uko-code:Callable ; rdfs:comment "Links a test case to the callable it tests." .

Documents (uko-doc:) — Layer 1:

The document ontology represents structured and semi-structured documents (articles, technical papers, documentation, reports) as a hierarchy of containers and atoms. A key design principle is semantic awareness: when the system processes a document, it does not merely parse the structural hierarchy (sections, subsections, paragraphs) — it also analyzes the content of each text unit to infer implicit relationships. If a paragraph discusses a concept that is the subject of another section (even without an explicit hyperlink or mention by name), the RDF graph captures that relationship via uko:references or uko-doc:discussesTopic edges. This enables strategies to surface related content that a purely structural traversal would miss.

@prefix uko-doc: <https://cleveragents.ai/ontology/uko/doc#> .

# ── Container Classes ────────────────────────────────────────────── uko-doc:Document a owl:Class ; rdfs:subClassOf uko:Container ; rdfs:comment "A complete document (article, report, manual, specification)." .

uko-doc:Part a owl:Class ; rdfs:subClassOf uko:Container ; rdfs:comment "A top-level division of a document (e.g., Part I, Part II)." .

uko-doc:Chapter a owl:Class ; rdfs:subClassOf uko:Container ; rdfs:comment "A chapter within a document or part." .

uko-doc:Section a owl:Class ; rdfs:subClassOf uko:Container ; rdfs:comment "A numbered or titled section within a chapter." .

uko-doc:Subsection a owl:Class ; rdfs:subClassOf uko-doc:Section ; rdfs:comment "A subsection nested within a section (arbitrary depth)." .

# ── Atom Classes ─────────────────────────────────────────────────── uko-doc:Paragraph a owl:Class ; rdfs:subClassOf uko:Atom ; rdfs:comment "A paragraph of prose text." .

uko-doc:CodeBlock a owl:Class ; rdfs:subClassOf uko:Atom ; rdfs:comment "An inline code listing or example within a document." .

uko-doc:Figure a owl:Class ; rdfs:subClassOf uko:Atom ; rdfs:comment "A figure, diagram, or image with an optional caption." .

uko-doc:Table a owl:Class ; rdfs:subClassOf uko:Atom ; rdfs:comment "A tabular data element within a document." .

uko-doc:BlockQuote a owl:Class ; rdfs:subClassOf uko:Atom ; rdfs:comment "A quoted passage from another source." .

uko-doc:ListBlock a owl:Class ; rdfs:subClassOf uko:Atom ; rdfs:comment "An ordered or unordered list." .

# ── Annotation Classes ───────────────────────────────────────────── uko-doc:Footnote a owl:Class ; rdfs:subClassOf uko:Annotation ; rdfs:comment "A footnote or endnote." .

uko-doc:Citation a owl:Class ; rdfs:subClassOf uko:Annotation ; rdfs:comment "A bibliographic citation or reference." .

uko-doc:Comment a owl:Class ; rdfs:subClassOf uko:Annotation ; rdfs:comment "An editorial comment or annotation (e.g., HTML comment, review note)." .

# ── Boundary Classes ─────────────────────────────────────────────── uko-doc:TableOfContents a owl:Class ; rdfs:subClassOf uko:Boundary ; rdfs:comment "The document's table of contents — an interface into the document's structure." .

uko-doc:Index a owl:Class ; rdfs:subClassOf uko:Boundary ; rdfs:comment "A back-of-book index or keyword index." .

uko-doc:Glossary a owl:Class ; rdfs:subClassOf uko:Boundary ; rdfs:comment "A glossary of terms defined in the document." .

# ── Document-Specific Relationships ──────────────────────────────── uko-doc:discussesTopic a owl:ObjectProperty ; rdfs:subPropertyOf uko:references ; rdfs:domain uko:InformationUnit ; rdfs:range uko:InformationUnit ; rdfs:comment "Semantic relationship: this unit discusses a topic that is the subject of another unit. Inferred by the document analyzer even when no explicit link exists." .

uko-doc:cites a owl:ObjectProperty ; rdfs:subPropertyOf uko:references ; rdfs:domain uko:InformationUnit ; rdfs:range uko-doc:Citation ; rdfs:comment "This unit cites a bibliographic reference." .

uko-doc:crossReferences a owl:ObjectProperty ; rdfs:subPropertyOf uko:references ; rdfs:domain uko:InformationUnit ; rdfs:range uko:InformationUnit ; rdfs:comment "Explicit cross-reference (e.g., 'see Section 3.2')." .

uko-doc:precedes a owl:ObjectProperty ; rdfs:domain uko:InformationUnit ; rdfs:range uko:InformationUnit ; rdfs:comment "Reading order: this unit comes before the target unit." .

# ── Document-Specific Properties ─────────────────────────────────── uko-doc:headingLevel a owl:DatatypeProperty ; rdfs:domain uko-doc:Section ; rdfs:range xsd:integer ; rdfs:comment "The nesting depth of this section (1 = top-level, 2 = subsection, etc.)." .

uko-doc:headingText a owl:DatatypeProperty ; rdfs:domain uko-doc:Section ; rdfs:range xsd:string ; rdfs:comment "The title/heading text of this section." .

uko-doc:wordCount a owl:DatatypeProperty ; rdfs:domain uko:InformationUnit ; rdfs:range xsd:integer ; rdfs:comment "Word count of the text content in this unit." .

uko-doc:language a owl:DatatypeProperty ; rdfs:domain uko-doc:Document ; rdfs:range xsd:string ; rdfs:comment "Natural language of the document (e.g., 'en', 'fr')." .

uko-doc:topicKeywords a owl:DatatypeProperty ; rdfs:domain uko:InformationUnit ; rdfs:range xsd:string ; rdfs:comment "JSON-encoded list of topic keywords extracted from this unit." .

Semantic Inference for Documents: When the document analyzer processes a document, it performs semantic analysis beyond pure structural parsing:

  1. Topic extraction: Each paragraph, section, and subsection is analyzed for key topics. These become uko-doc:topicKeywords properties.
  2. Implicit cross-references: If Paragraph A in Section 2 discusses "authentication flow" and Section 5 is titled "Authentication Architecture", the analyzer creates a uko-doc:discussesTopic edge from Paragraph A to Section 5 — even though no explicit link exists in the source document.
  3. Citation resolution: Bibliographic citations are linked to the paragraphs that cite them.
  4. Concept clustering: Paragraphs discussing related concepts across different sections are linked via uko:references, enabling strategies to pull in contextually relevant content from distant parts of a document.

This semantic awareness means that a breadth-depth-navigator strategy operating on a document resource can follow not just the structural containment hierarchy but also the semantic topic graph, surfacing related sections that an author would need to consider when modifying any part of the document.

Document DetailLevelMap Refinements (per-type rendering at selected depths — see Core Data Types for the authoritative uko-doc: depth map):

Depth Named Level uko-doc:Document uko-doc:Section uko-doc:Paragraph
0 TITLE_ONLY Document title only Heading text only — (not individually listed)
1 TABLE_OF_CONTENTS_L1 Title + top-level section headings Heading + child section headings First sentence only
4 TOC_WITH_SUMMARIES All headings + one-sentence abstract per section Heading + paragraph count + topic keywords + abstract First two sentences + topic keywords
8 STRUCTURAL_DETAIL Full TOC + figure/table captions + list item headers Heading + first sentence of each paragraph + all captions Full text with inline code and links preserved
10 FULL_CONTENT Complete document content Complete section content including all children Complete paragraph text with all formatting

Data Schemas (uko-data:) — Layer 1:

The data schema ontology represents databases, data warehouses, and structured data stores. It models the schema structure (schemas, tables, columns, constraints) and captures query-time relationships (views referencing tables, foreign keys, stored procedures accessing tables). Like the document ontology, the system performs semantic analysis: when a stored procedure references columns from multiple tables, those dependency edges are created automatically.

@prefix uko-data: <https://cleveragents.ai/ontology/uko/data#> .

# ── Container Classes ────────────────────────────────────────────── uko-data:Database a owl:Class ; rdfs:subClassOf uko:Container ; rdfs:comment "A database instance containing schemas." .

uko-data:Schema a owl:Class ; rdfs:subClassOf uko:Container ; rdfs:comment "A database schema (namespace for tables, views, etc.)." .

uko-data:Table a owl:Class ; rdfs:subClassOf uko:Container ; rdfs:comment "A database table containing columns." .

uko-data:View a owl:Class ; rdfs:subClassOf uko:Container ; rdfs:comment "A database view (virtual table defined by a query)." .

# ── Atom Classes ─────────────────────────────────────────────────── uko-data:Column a owl:Class ; rdfs:subClassOf uko:Atom ; rdfs:comment "A column within a table or view." .

uko-data:StoredProcedure a owl:Class ; rdfs:subClassOf uko:Atom ; rdfs:comment "A stored procedure or function in the database." .

uko-data:Trigger a owl:Class ; rdfs:subClassOf uko:Atom ; rdfs:comment "A database trigger." .

uko-data:Migration a owl:Class ; rdfs:subClassOf uko:Atom ; rdfs:comment "A schema migration (DDL change script)." .

# ── Annotation Classes ───────────────────────────────────────────── uko-data:Constraint a owl:Class ; rdfs:subClassOf uko:Annotation ; rdfs:comment "A column or table constraint (NOT NULL, UNIQUE, CHECK, etc.)." .

uko-data:Index a owl:Class ; rdfs:subClassOf uko:Annotation ; rdfs:comment "A database index on one or more columns." .

uko-data:ColumnComment a owl:Class ; rdfs:subClassOf uko:Annotation ; rdfs:comment "A COMMENT ON COLUMN annotation in the database." .

# ── Boundary Classes ─────────────────────────────────────────────── uko-data:ForeignKey a owl:Class ; rdfs:subClassOf uko:Boundary ; rdfs:comment "A foreign key constraint — an interface point between tables." .

uko-data:APIEndpoint a owl:Class ; rdfs:subClassOf uko:Boundary ; rdfs:comment "A database-level API endpoint (e.g., PostgREST, GraphQL)." .

# ── Data-Specific Relationships ──────────────────────────────────── uko-data:foreignKeyTo a owl:ObjectProperty ; rdfs:subPropertyOf uko:dependsOn ; rdfs:domain uko-data:Column ; rdfs:range uko-data:Column ; rdfs:comment "Foreign key relationship from this column to a column in another table." .

uko-data:viewReferences a owl:ObjectProperty ; rdfs:subPropertyOf uko:dependsOn ; rdfs:domain uko-data:View ; rdfs:range uko-data:Table ; rdfs:comment "This view's query references columns from the target table." .

uko-data:procedureAccesses a owl:ObjectProperty ; rdfs:subPropertyOf uko:dependsOn ; rdfs:domain uko-data:StoredProcedure ; rdfs:range uko-data:Table ; rdfs:comment "This stored procedure reads from or writes to the target table." .

uko-data:triggerFires a owl:ObjectProperty ; rdfs:subPropertyOf uko:dependsOn ; rdfs:domain uko-data:Trigger ; rdfs:range uko-data:Table ; rdfs:comment "This trigger fires on events on the target table." .

uko-data:migrationAlters a owl:ObjectProperty ; rdfs:subPropertyOf uko:dependsOn ; rdfs:domain uko-data:Migration ; rdfs:range uko-data:Table ; rdfs:comment "This migration modifies the schema of the target table." .

# ── Data-Specific Properties ────────────────────────────────────── uko-data:dataType a owl:DatatypeProperty ; rdfs:domain uko-data:Column ; rdfs:range xsd:string ; rdfs:comment "SQL data type (e.g., 'VARCHAR(255)', 'INTEGER', 'JSONB')." .

uko-data:isNullable a owl:DatatypeProperty ; rdfs:domain uko-data:Column ; rdfs:range xsd:boolean .

uko-data:isPrimaryKey a owl:DatatypeProperty ; rdfs:domain uko-data:Column ; rdfs:range xsd:boolean .

uko-data:rowEstimate a owl:DatatypeProperty ; rdfs:domain uko-data:Table ; rdfs:range xsd:long ; rdfs:comment "Estimated row count from database statistics." .

uko-data:viewDefinition a owl:DatatypeProperty ; rdfs:domain uko-data:View ; rdfs:range xsd:string ; rdfs:comment "The SQL query that defines this view." .

uko-data:procedureBody a owl:DatatypeProperty ; rdfs:domain uko-data:StoredProcedure ; rdfs:range xsd:string ; rdfs:comment "The SQL/PL body of the stored procedure." .

Database DetailLevelMap Refinements (per-type rendering at selected depths — see Core Data Types for the authoritative uko-data: depth map):

Depth Named Level uko-data:Database uko-data:Table uko-data:Column
0 SCHEMA_LISTING Schema names only — (not individually listed)
1 TABLE_LISTING Schema names + table/view names Table name only
3 TYPED_COLUMNS + column types + nullability Column names + types + nullability Name + type + nullable
5 RELATIONSHIPS + foreign key graph + row estimates + primary key + foreign key targets + row estimate + FK references + constraints
7 DDL Full CREATE TABLE DDL with constraints Full CREATE TABLE DDL + index definitions Full column DDL + all constraints
11 FULL_CATALOG Complete DDL + views + procedures + sample data Full DDL + triggers + sample rows + statistics Full DDL + value distribution + sample values

Infrastructure (uko-infra:) — Layer 1:

@prefix uko-infra: <https://cleveragents.ai/ontology/uko/infra#> .

# ── Container Classes ────────────────────────────────────────────── uko-infra:Service a owl:Class ; rdfs:subClassOf uko:Container ; rdfs:comment "A deployable service or application." .

uko-infra:ConfigBlock a owl:Class ; rdfs:subClassOf uko:Container ; rdfs:comment "A configuration block or section (e.g., a YAML map, TOML table)." .

uko-infra:DeploymentUnit a owl:Class ; rdfs:subClassOf uko:Container ; rdfs:comment "A deployment unit (e.g., Kubernetes Deployment, Docker Compose service)." .

# ── Atom Classes ─────────────────────────────────────────────────── uko-infra:ConfigKey a owl:Class ; rdfs:subClassOf uko:Atom ; rdfs:comment "A configuration key-value pair." .

uko-infra:EnvironmentVariable a owl:Class ; rdfs:subClassOf uko:Atom ; rdfs:comment "An environment variable definition." .

# ── Boundary Classes ─────────────────────────────────────────────── uko-infra:Endpoint a owl:Class ; rdfs:subClassOf uko:Boundary ; rdfs:comment "A network endpoint (HTTP route, gRPC service, message queue)." .

uko-infra:Port a owl:Class ; rdfs:subClassOf uko:Boundary ; rdfs:comment "A network port binding." .

# ── Infrastructure-Specific Relationships ────────────────────────── uko-infra:connectsTo a owl:ObjectProperty ; rdfs:subPropertyOf uko:dependsOn ; rdfs:domain uko-infra:Service ; rdfs:range uko-infra:Service ; rdfs:comment "This service connects to or depends on the target service." .

uko-infra:exposes a owl:ObjectProperty ; rdfs:domain uko-infra:Service ; rdfs:range uko-infra:Endpoint ; rdfs:comment "This service exposes the target endpoint." .

Layer 2: Paradigm / Format Specializations

Layer 2 refines Layer 1 domains with paradigm- or format-specific specializations. For code: OO, functional, and procedural paradigms. For documents: academic vs. technical. For databases: relational vs. graph. Each may extend the parent domain's DetailLevelMap with additional named levels.

Object-Oriented (uko-oo:):

@prefix uko-oo: <https://cleveragents.ai/ontology/uko/oo#> .

uko-oo:Class a owl:Class ; rdfs:subClassOf uko-code:TypeDefinition, uko:Container ; rdfs:comment "An OO class that contains methods and attributes." .

uko-oo:Interface a owl:Class ; rdfs:subClassOf uko-code:TypeDefinition, uko:Boundary ; rdfs:comment "An interface or abstract base class." .

uko-oo:Method a owl:Class ; rdfs:subClassOf uko-code:Callable .

uko-oo:Attribute a owl:Class ; rdfs:subClassOf uko:Atom .

uko-oo:inheritsFrom a owl:ObjectProperty ; rdfs:subPropertyOf uko:dependsOn ; rdfs:domain uko-oo:Class ; rdfs:range uko-oo:Class .

uko-oo:implements a owl:ObjectProperty ; rdfs:subPropertyOf uko:dependsOn ; rdfs:domain uko-oo:Class ; rdfs:range uko-oo:Interface .

Functional (uko-func:):

@prefix uko-func: <https://cleveragents.ai/ontology/uko/func#> .

uko-func:PureFunction a owl:Class ; rdfs:subClassOf uko-code:Callable .

uko-func:TypeClass a owl:Class ; rdfs:subClassOf uko-code:TypeDefinition, uko:Boundary .

uko-func:Monad a owl:Class ; rdfs:subClassOf uko-code:TypeDefinition .

Code DetailLevelMap Refinements by Paradigm and Language:

The authoritative DetailLevelMap definitions — with full integer depth assignments and named level descriptions for each domain — are specified in Core Concepts > Advanced Context Management System (ACMS) > Core Data Types > DetailDepth and DetailLevelMap. The tables below show how paradigm-specific (Layer 2) and language-specific (Layer 3) UKO node types map their content at selected depth levels from the parent domain's map. These are illustrative excerpts showing what content each node type contributes at key depths, not exhaustive depth-by-depth definitions.

General Software (uko-code:) — Layer 1 (per-type rendering at selected depths):

Depth Named Level uko-code:Module uko-code:Callable uko-code:TypeDefinition
0 MODULE_LISTING Module name only — (not individually listed) — (not individually listed)
2 MEMBER_LISTING + names of top-level members Function name (no signature) Type name + kind (class/struct/enum)
4 SIGNATURES + all top-level signatures Name + parameter types + return type Name + field types + method signatures
6 STRUCTURAL_OUTLINE + control flow outline of callables Signature + control flow outline (branches, loops) Name + all field + method signatures + inheritance
9 FULL_SOURCE Complete module source Complete function body Complete type definition

Object-Oriented (uko-oo:) — Layer 2 (per-type rendering at selected depths):

Depth Named Level uko-oo:Class uko-oo:Method uko-oo:Interface
2 MEMBER_LISTING Class name + parent classes Method name + visibility Interface name
3 CLASS_HIERARCHY (OO-inserted) + inheritance chain + interface implementations + method signatures
4 SIGNATURES + attribute types + method signatures + params + return type + visibility + all method signatures with param types
5 SIGNATURES_WITH_DOCS + class docstring + attribute descriptions + docstring + side effect hints + description + implementor count
9+ FULL_SOURCE Complete class definition Complete method body Complete interface definition

Functional (uko-func:) — Layer 2 (per-type rendering at selected depths):

Depth Named Level uko-func:PureFunction uko-func:TypeClass uko-func:Monad
2 MEMBER_LISTING Function name Type class name Monad name
4 SIGNATURES Name + type signature + purity annotation Name + associated types Name + bind/return types
6 STRUCTURAL_OUTLINE + pattern match structure + guard conditions + all method signatures + laws + bind/return implementations
9 FULL_SOURCE Complete function definition Complete type class definition Complete monad definition

Procedural (uko-proc:) — Layer 2 (rendering at selected depths):

Depth Named Level Procedural Specifics
2 MEMBER_LISTING Function names + global variable names (no types)
4 SIGNATURES Function declarations + global variable types + header file exports
5 SIGNATURES_WITH_DOCS + function descriptions + global variable purposes + header summaries
6 STRUCTURAL_OUTLINE + function signatures with parameter names + struct definitions + macro signatures
9 FULL_SOURCE Complete source including all function bodies, macros, and inline assembly
Layer 3: Technology-Specific Specializations

Layer 3 extends Layer 2 (or Layer 1 directly) with technology-specific refinements — e.g., Python, TypeScript, Rust, Java for code; PostgreSQL, MongoDB for databases; Markdown, LaTeX for documents. Each may insert additional named DetailLevelMap levels into its parent's map.

Language-Specific (Layer 3) DetailLevelMap insertions — examples:

Each language extension may insert additional named levels into its parent paradigm's map, shifting subsequent depths upward to maintain consecutive integer numbering (see the fully expanded effective maps in Core Data Types). The table below summarizes the key additions:

Language Inserted Levels Additional Content at Those Depths
Python (uko-py:) DECORATED_SIGNATURES (between SIGNATURES_WITH_DOCS and STRUCTURAL_OUTLINE), TYPE_STUBS (between KEY_LOGIC and NEAR_COMPLETE), WITH_TESTS (beyond FULL_SOURCE) Decorator chains (@property, @staticmethod, custom), .pyi stub type annotations, associated test cases
TypeScript (uko-ts:) (extends at SIGNATURES level) export/default export markers, declare ambient types, generic type parameters, mapped/conditional types
Rust (uko-rs:) (extends at SIGNATURES level) Visibility modifiers (pub, pub(crate)), lifetime parameters, trait bounds, unsafe markers, #[derive] macros, #[must_use] annotations
Java (uko-java:) (extends at SIGNATURES level) Package visibility, final/abstract/sealed modifiers, annotation lists (@Override, @Deprecated), checked exceptions

Not every language requires a Layer 3 extension. If a language's semantics are fully captured by the Layer 1 (uko-code:) and Layer 2 (paradigm) maps, no Layer 3 extension is needed. The system falls back to the nearest parent layer's DetailLevelMap.

The Universal View Guarantee

Because every domain-specific class ultimately inherits from Layer 0 (uko:InformationUnit, uko:Container, etc.), any strategy or query written against Layer 0 automatically works across all domains — not just different programming languages, but across code, documents, databases, and infrastructure alike. For example:

# "Find all containers that depend on this atom" — works for Python classes,
# TypeScript modules, Rust crates, SQL tables, document sections, or config blocks.
SELECT ?container WHERE {
    ?container a uko:Container .
    ?container uko:dependsOn <target_atom> .
}

Similarly, a DetailDepth request at Layer 0 is resolved by the most specific DetailLevelMap available in the inheritance chain. A request for depth 3 of a uko:Container will produce a member summary with one-line docstrings for a Python module (MEMBER_SUMMARY), a full table of contents for a document chapter (FULL_TOC), or typed column definitions for a database table (TYPED_COLUMNS) — all through the same universal integer-depth interface, with each domain mapping the integer to its domain-appropriate rendering.

Provenance Contract

Every UKO node carries provenance back to the originating resource:

uko-py:class/AuthManager
    uko:sourceResource  <resource-ulid-01HXM7...>
    uko:sourcePath      "src/auth/manager.py"
    uko:sourceRange     "15:1-87:0"
    uko:validFrom       "2026-01-15T10:30:00Z"
    uko:isCurrent       true

This allows the PreambleGenerator pipeline component to generate provenance summaries in the context preamble, and allows actors to trace any piece of context back to its exact source location.

Temporal Data Model and Storage Tiers

Revision-Aware RDF

Each UKO node carries temporal metadata. When code changes, existing nodes are not deleted — instead, their validUntil is set and isCurrent becomes false, and a new node is created with isRevisionOf pointing to the predecessor:

# Before code change:
uko-py:class/AuthManager_v1
    uko:isCurrent true ;
    uko:validFrom "2026-01-10T00:00:00Z"^^xsd:dateTime .

# After code change: uko-py:class/AuthManager_v1 uko:isCurrent false ; uko:validFrom "2026-01-10T00:00:00Z"^^xsd:dateTime ; uko:validUntil "2026-01-15T10:30:00Z"^^xsd:dateTime .

uko-py:class/AuthManager_v2 uko:isCurrent true ; uko:validFrom "2026-01-15T10:30:00Z"^^xsd:dateTime ; uko:isRevisionOf uko-py:class/AuthManager_v1 .

This temporal chain is what enables the temporal-archaeology strategy to find patterns like "this class was refactored 3 times in the last month" or "this function's signature changed after we updated the auth library."

Three Storage Tiers with Temporal Alignment
Tier Content Retention Access Pattern Temporal
Hot Current UKO graph + recent embeddings + active text index Until resource removed Direct query via BAL isCurrent = true only
Warm Recent decision contexts, plan context snapshots context.tiers.warm.retention-hours (default: 24h) Scoped query via plan hierarchy Current + recently-expired nodes
Cold Archived decision contexts, historical UKO snapshots context.tiers.cold.retention-days (default: 90d) Full historical query All temporal versions

Scoped Views and Plan Subgraph Projection

Resource Scope Resolution

When a plan is created (via agents plan use), the system resolves its resource scope:

class ResourceScopeResolver:
    """Determines which resources a plan can see."""
<span style="color: magenta; font-weight: 600;">def</span> <span style="color: cyan; font-weight: 600;">resolve</span>(<span style="color: cyan;">self</span>, plan: <span style="color: cyan;">Plan</span>) -> <span style="color: cyan;">ResourceScope</span>:
    <span style="color: #888;"># 1. Get all projects this plan targets</span>
    projects = plan.target_projects
    <span style="color: #888;"># 2. Collect all resources linked to those projects</span>
    resources = <span style="color: cyan;">set</span>()
    <span style="color: magenta; font-weight: 600;">for</span> project <span style="color: magenta; font-weight: 600;">in</span> projects:
        resources.update(project.linked_resources)
    <span style="color: #888;"># 3. Expand to include child resources (DAG traversal)</span>
    expanded = <span style="color: cyan;">set</span>()
    <span style="color: magenta; font-weight: 600;">for</span> resource <span style="color: magenta; font-weight: 600;">in</span> resources:
        expanded.add(resource)
        expanded.update(<span style="color: cyan;">self</span>.registry.get_descendants(resource))
    <span style="color: #888;"># 4. Apply context view filters (include/exclude)</span>
    view = <span style="color: cyan;">self</span>._resolve_view(plan)
    filtered = <span style="color: cyan;">self</span>._apply_filters(expanded, view)
    <span style="color: magenta; font-weight: 600;">return</span> ResourceScope(resources=filtered, projects=projects)

UKO Subgraph Projection

Given a resource scope, the system projects only the relevant UKO subgraph:

class UKOSubgraphProjector:
    """Projects the UKO graph to only include nodes from scoped resources."""
<span style="color: magenta; font-weight: 600;">def</span> <span style="color: cyan; font-weight: 600;">project</span>(<span style="color: cyan;">self</span>, scope: <span style="color: cyan;">ResourceScope</span>,
            graph_backend: <span style="color: cyan;">GraphBackend</span>) -> <span style="color: cyan;">ProjectedGraph</span>:
    <span style="color: #888;"># Only include UKO nodes whose sourceResource is in scope</span>
    <span style="color: magenta; font-weight: 600;">return</span> graph_backend.subgraph_by_resources(
        resource_ulids=[r.ulid <span style="color: magenta; font-weight: 600;">for</span> r <span style="color: magenta; font-weight: 600;">in</span> scope.resources],
        include_temporal=scope.temporal_scope,
    )

Scoped Backend Views

Each backend (text, vector, graph) is wrapped in a ScopedView that automatically filters queries to only return results from in-scope resources:

class ScopedBackendView:
    """Wraps a backend to restrict queries to a resource scope."""
<span style="color: magenta; font-weight: 600;">def</span> <span style="color: cyan; font-weight: 600;">__init__</span>(<span style="color: cyan;">self</span>, backend, scope: <span style="color: cyan;">ResourceScope</span>):
    <span style="color: cyan;">self</span>.backend = backend
    <span style="color: cyan;">self</span>.scope = scope
    <span style="color: cyan;">self</span>._resource_filter = {r.ulid <span style="color: magenta; font-weight: 600;">for</span> r <span style="color: magenta; font-weight: 600;">in</span> scope.resources}

<span style="color: magenta; font-weight: 600;">def</span> <span style="color: cyan; font-weight: 600;">search</span>(<span style="color: cyan;">self</span>, query, **kwargs):
    <span style="color: #888;"># Inject the resource filter into the backend query</span>
    results = <span style="color: cyan;">self</span>.backend.search(
        query,
        resource_filter=<span style="color: cyan;">self</span>._resource_filter,
        **kwargs,
    )
    <span style="color: magenta; font-weight: 600;">return</span> results

This is critical for multi-project isolation: a plan targeting local/api-service never sees resources from local/frontend, even if both projects share the same physical database indices.

Backend Abstraction Layer (BAL)

The BAL provides a uniform interface to heterogeneous data stores. Each backend type has a protocol:

Backend Protocols
class TextBackend(Protocol):
    def search(self, query: str, *, resource_filter: set[str],
               limit: int = 20) -> list[TextResult]: ...
    def index(self, resource_ulid: str, content: str,
              metadata: dict) -> None: ...

class VectorBackend(Protocol): def search(self, embedding: list[float], *, resource_filter: set[str], limit: int = 20, threshold: float = 0.3) -> list[VectorResult]: ... def index(self, resource_ulid: str, chunks: list[EmbeddingChunk]) -> None: ...

class GraphBackend(Protocol): def query_sparql(self, sparql: str, *, resource_filter: set[str]) -> list[dict]: ... def add_triples(self, triples: list[Triple]) -> None: ... def subgraph_by_resources(self, resource_ulids: list[str], include_temporal: TemporalScope) -> ProjectedGraph: ... def traverse(self, start_uri: str, max_hops: int, edge_types: list[str], resource_filter: set[str]) -> SubGraph: ...

Unified Result Types
@dataclass
class TextResult:
    resource_ulid: str
    path: str
    line_range: tuple[int, int]
    content: str
    score: float

@dataclass class VectorResult: resource_ulid: str uko_node: str content: str similarity: float metadata: dict

Context Assembly Pipeline

The context assembly pipeline is the central orchestrator of context assembly. It receives a ContextRequest, processes it through a chain of pluggable components, and produces a budget-respecting AssembledContext. Every stage of the pipeline is a replaceable plugin — each defined by a Protocol interface, shipped with a default implementation, and configurable at the global, project, or plan scope.

Pipeline Component Model

The pipeline follows the Pipes and Filters architectural pattern, with each filter implemented as a Strategy (GoF) that can be swapped at runtime. Component resolution uses a Chain of Responsibility pattern: plan-level overrides take precedence over project-level, which take precedence over global defaults.


Pipeline Component Resolution Order:

plan scope ──► project scope ──► global scope ──► built-in default (most specific) (least specific)

There are ten pluggable components, grouped into three pipeline phases:

Phase 1 — Strategy Orchestration (selects and runs context strategies):

# Component Protocol Responsibility
1 StrategySelector StrategySelectorProtocol Decides which strategies to invoke and computes confidence scores
2 BudgetAllocator BudgetAllocatorProtocol Distributes the token budget across selected strategies
3 StrategyExecutor StrategyExecutorProtocol Controls how strategies are invoked (parallelism, timeouts, error handling)

Phase 2 — Fragment Fusion (merges strategy outputs into a coherent payload):

# Component Protocol Responsibility
4 FragmentDeduplicator FragmentDeduplicatorProtocol Removes duplicate fragments across strategy outputs
5 DetailDepthResolver DetailDepthResolverProtocol Resolves conflicts when the same UKO node appears at different depths
6 FragmentScorer FragmentScorerProtocol Computes composite relevance scores for ranking
7 BudgetPacker BudgetPackerProtocol Fits scored fragments into the token budget
8 FragmentOrderer FragmentOrdererProtocol Orders final fragments for coherence in the prompt

Phase 3 — Context Finalization (produces the final deliverable):

# Component Protocol Responsibility
9 PreambleGenerator PreambleGeneratorProtocol Generates the context preamble (provenance summary, structure map)
10 SkeletonCompressor SkeletonCompressorProtocol Compresses parent context into a skeleton for child plan inheritance
Component Protocol Definitions

Each component is defined as a @runtime_checkable Protocol, enabling both structural subtyping (duck typing) and explicit isinstance checks. The protocols use Generic type parameters where applicable, and employ the Template Method pattern — each protocol defines the contract while default implementations provide the algorithm skeleton with overridable hook methods.

# ── Phase 1: Strategy Orchestration ────────────────────────────────

@runtime_checkable class StrategySelectorProtocol(Protocol): """Decides which strategies to invoke and with what confidence. Applies request preferences and backend availability filtering."""

<span style="color: magenta; font-weight: 600;">def</span> <span style="color: cyan; font-weight: 600;">select</span>(
    <span style="color: cyan;">self</span>,
    strategies: <span style="color: cyan;">Sequence</span>[ContextStrategy],
    request: ContextRequest,
    backends: BackendSet,
) -> <span style="color: cyan;">list</span>[<span style="color: cyan;">tuple</span>[ContextStrategy, <span style="color: cyan;">float</span>]]:
    <span style="color: #888;">"""Returns (strategy, confidence) pairs, sorted by priority.
    Confidence is 0.0-1.0. Strategies with 0.0 are excluded."""</span>
    ...

@runtime_checkable class BudgetAllocatorProtocol(Protocol): """Distributes the token budget across selected strategies. May use proportional, priority-weighted, or custom allocation schemes."""

<span style="color: magenta; font-weight: 600;">def</span> <span style="color: cyan; font-weight: 600;">allocate</span>(
    <span style="color: cyan;">self</span>,
    candidates: <span style="color: cyan;">list</span>[<span style="color: cyan;">tuple</span>[ContextStrategy, <span style="color: cyan;">float</span>]],
    total_budget: <span style="color: cyan;">int</span>,
    request: ContextRequest,
) -> <span style="color: cyan;">list</span>[<span style="color: cyan;">tuple</span>[ContextStrategy, <span style="color: cyan;">float</span>, <span style="color: cyan;">int</span>]]:
    <span style="color: #888;">"""Returns (strategy, confidence, allocated_tokens) triples.
    Sum of allocated_tokens must not exceed total_budget."""</span>
    ...

@runtime_checkable class StrategyExecutorProtocol(Protocol): """Controls how strategies are invoked — parallelism model, timeout handling, circuit breaking, and error recovery."""

<span style="color: magenta; font-weight: 600;">def</span> <span style="color: cyan; font-weight: 600;">execute</span>(
    <span style="color: cyan;">self</span>,
    allocations: <span style="color: cyan;">list</span>[<span style="color: cyan;">tuple</span>[ContextStrategy, <span style="color: cyan;">float</span>, <span style="color: cyan;">int</span>]],
    request: ContextRequest,
    backends: BackendSet,
    plan_context: PlanContext,
) -> <span style="color: cyan;">list</span>[ContextFragment]:
    <span style="color: #888;">"""Executes strategies and collects all fragments.
    Must handle strategy failures gracefully (log and continue)."""</span>
    ...

# ── Phase 2: Fragment Fusion ────────────────────────────────────────

@runtime_checkable class FragmentDeduplicatorProtocol(Protocol): """Removes duplicate fragments from the merged strategy output. Deduplication can be identity-based, content-hash-based, or semantic."""

<span style="color: magenta; font-weight: 600;">def</span> <span style="color: cyan; font-weight: 600;">deduplicate</span>(
    <span style="color: cyan;">self</span>,
    fragments: <span style="color: cyan;">list</span>[ContextFragment],
) -> <span style="color: cyan;">list</span>[ContextFragment]:
    <span style="color: #888;">"""Returns deduplicated fragments. When duplicates are found,
    the fragment with the highest relevance_score is retained."""</span>
    ...

@runtime_checkable class DetailDepthResolverProtocol(Protocol): """Resolves conflicts when the same UKO node appears at different detail depths across strategy outputs."""

<span style="color: magenta; font-weight: 600;">def</span> <span style="color: cyan; font-weight: 600;">resolve</span>(
    <span style="color: cyan;">self</span>,
    fragments: <span style="color: cyan;">list</span>[ContextFragment],
    budget: <span style="color: cyan;">int</span>,
) -> <span style="color: cyan;">list</span>[ContextFragment]:
    <span style="color: #888;">"""Returns fragments with depth conflicts resolved.
    Default: keep highest depth that fits within budget."""</span>
    ...

@runtime_checkable class FragmentScorerProtocol(Protocol): """Computes a composite score for each fragment, used for ranking during budget packing. Applies hierarchical weighting, strategy quality weighting, and recency bonuses."""

<span style="color: magenta; font-weight: 600;">def</span> <span style="color: cyan; font-weight: 600;">score</span>(
    <span style="color: cyan;">self</span>,
    fragments: <span style="color: cyan;">list</span>[ContextFragment],
    plan_context: PlanContext,
) -> <span style="color: cyan;">list</span>[ScoredFragment]:
    <span style="color: #888;">"""Returns fragments annotated with composite scores.
    ScoredFragment adds a `composite_score: float` field."""</span>
    ...

@runtime_checkable class BudgetPackerProtocol(Protocol): """Fits scored fragments into the token budget using a packing algorithm. Supports depth fallback — if a fragment doesn't fit at its current depth, it can be re-rendered at a lower depth."""

<span style="color: magenta; font-weight: 600;">def</span> <span style="color: cyan; font-weight: 600;">pack</span>(
    <span style="color: cyan;">self</span>,
    scored_fragments: <span style="color: cyan;">list</span>[ScoredFragment],
    budget: <span style="color: cyan;">int</span>,
    detail_level_maps: DetailLevelMapRegistry,
) -> <span style="color: cyan;">list</span>[ContextFragment]:
    <span style="color: #888;">"""Returns the subset of fragments that fit within budget,
    possibly re-rendered at lower depths. Ordered by score."""</span>
    ...

@runtime_checkable class FragmentOrdererProtocol(Protocol): """Orders the packed fragments for optimal coherence in the actor's context window. Ordering may be by relevance, by topological dependency, or by a hybrid coherence heuristic."""

<span style="color: magenta; font-weight: 600;">def</span> <span style="color: cyan; font-weight: 600;">order</span>(
    <span style="color: cyan;">self</span>,
    fragments: <span style="color: cyan;">list</span>[ContextFragment],
) -> <span style="color: cyan;">list</span>[ContextFragment]:
    <span style="color: #888;">"""Returns fragments in the final presentation order."""</span>
    ...

# ── Phase 3: Context Finalization ──────────────────────────────────

@runtime_checkable class PreambleGeneratorProtocol(Protocol): """Generates the preamble prepended to assembled context — a structured summary of what's included, why, and from where."""

<span style="color: magenta; font-weight: 600;">def</span> <span style="color: cyan; font-weight: 600;">generate</span>(
    <span style="color: cyan;">self</span>,
    fragments: <span style="color: cyan;">list</span>[ContextFragment],
    strategies_used: <span style="color: cyan;">list</span>[<span style="color: cyan;">str</span>],
    budget_used: <span style="color: cyan;">float</span>,
    max_tokens: <span style="color: cyan;">int</span>,
) -> <span style="color: cyan;">str</span> | <span style="color: magenta; font-weight: 600;">None</span>:
    <span style="color: #888;">"""Returns the preamble string, or None if preamble is disabled."""</span>
    ...

@runtime_checkable class SkeletonCompressorProtocol(Protocol): """Compresses a parent plan's assembled context into a compact skeleton representation for inheritance by child plans."""

<span style="color: magenta; font-weight: 600;">def</span> <span style="color: cyan; font-weight: 600;">compress</span>(
    <span style="color: cyan;">self</span>,
    parent_context: AssembledContext,
    child_focus: <span style="color: cyan;">list</span>[<span style="color: cyan;">str</span>],
    skeleton_budget: <span style="color: cyan;">int</span>,
) -> AssembledContext:
    <span style="color: #888;">"""Returns a compressed version of the parent context that
    fits within skeleton_budget tokens. Typically reduces all
    fragments to depth 0-1 (e.g., MODULE_LISTING / TITLE_ONLY)."""</span>
    ...

Supporting Types
@dataclass(frozen=True)
class ScoredFragment:
    """A ContextFragment annotated with a composite score by the FragmentScorer."""
    fragment: ContextFragment
    composite_score: float        # 0.0-1.0 composite ranking score
    score_components: dict[str, float]  # Breakdown: {"relevance": 0.9, "hierarchy": 0.8, ...}

@dataclass class PipelineConfig: """Resolved configuration for the context assembly pipeline. Each field holds the concrete plugin instance to use for that stage. Resolved via the scope chain: plan > project > global > built-in."""

strategy_selector: StrategySelectorProtocol
budget_allocator: BudgetAllocatorProtocol
strategy_executor: StrategyExecutorProtocol
fragment_deduplicator: FragmentDeduplicatorProtocol
detail_depth_resolver: DetailDepthResolverProtocol
fragment_scorer: FragmentScorerProtocol
budget_packer: BudgetPackerProtocol
fragment_orderer: FragmentOrdererProtocol
preamble_generator: PreambleGeneratorProtocol
skeleton_compressor: SkeletonCompressorProtocol

Pipeline Orchestrator

The ContextAssemblyPipeline is the top-level orchestrator that wires the ten components together. It implements the Mediator pattern — components do not communicate directly with each other; the pipeline mediates all data flow. The pipeline itself is stateless; all state is carried through the data flowing between stages.

class ContextAssemblyPipeline:
    """Mediator that orchestrates the 10-stage context assembly pipeline.
    Each stage is a pluggable component resolved from the scope chain."""
<span style="color: magenta; font-weight: 600;">def</span> <span style="color: cyan; font-weight: 600;">__init__</span>(<span style="color: cyan;">self</span>, config: PipelineConfig, strategies: <span style="color: cyan;">Sequence</span>[ContextStrategy]):
    <span style="color: cyan;">self</span>._config = config
    <span style="color: cyan;">self</span>._strategies = strategies

<span style="color: magenta; font-weight: 600;">def</span> <span style="color: cyan; font-weight: 600;">assemble</span>(
    <span style="color: cyan;">self</span>,
    request: ContextRequest,
    backends: BackendSet,
    budget: <span style="color: cyan;">int</span>,
    plan_context: PlanContext,
    skeleton_ratio: <span style="color: cyan;">float</span> = <span style="color: #66cc66;">0.15</span>,
    parent_fragments: <span style="color: cyan;">tuple</span>[ContextFragment, ...] | <span style="color: cyan;">None</span> = <span style="color: cyan;">None</span>,
) -> AssembledContext:
    <span style="color: #888;"># ── Phase 1: Strategy Orchestration ──────────────────────</span>
    <span style="color: #888;"># 1. Select strategies</span>
    candidates = <span style="color: cyan;">self</span>._config.strategy_selector.select(
        <span style="color: cyan;">self</span>._strategies, request, backends,
    )
    <span style="color: #888;"># 2. Allocate budget</span>
    allocations = <span style="color: cyan;">self</span>._config.budget_allocator.allocate(
        candidates, budget, request,
    )
    <span style="color: #888;"># 3. Execute strategies</span>
    raw_fragments = <span style="color: cyan;">self</span>._config.strategy_executor.execute(
        allocations, request, backends, plan_context,
    )

    <span style="color: #888;"># ── Phase 2: Fragment Fusion ──────────────────────────────</span>
    <span style="color: #888;"># 4. Deduplicate</span>
    deduped = <span style="color: cyan;">self</span>._config.fragment_deduplicator.deduplicate(raw_fragments)
    <span style="color: #888;"># 5. Resolve depth conflicts</span>
    resolved = <span style="color: cyan;">self</span>._config.detail_depth_resolver.resolve(deduped, budget)
    <span style="color: #888;"># 6. Score fragments</span>
    scored = <span style="color: cyan;">self</span>._config.fragment_scorer.score(resolved, plan_context)
    <span style="color: #888;"># 7. Pack into budget</span>
    packed = <span style="color: cyan;">self</span>._config.budget_packer.pack(
        scored, budget, <span style="color: cyan;">self</span>._detail_level_maps,
    )
    <span style="color: #888;"># 8. Order for coherence</span>
    ordered = <span style="color: cyan;">self</span>._config.fragment_orderer.order(packed)

    <span style="color: #888;"># ── Phase 3: Context Finalization ─────────────────────────</span>
    <span style="color: #888;"># 9. Generate preamble</span>
    strategies_used = [s.name <span style="color: magenta; font-weight: 600;">for</span> s, _, _ <span style="color: magenta; font-weight: 600;">in</span> allocations]
    preamble = <span style="color: cyan;">self</span>._config.preamble_generator.generate(
        ordered, strategies_used,
        budget_used=<span style="color: cyan;">sum</span>(f.token_count <span style="color: magenta; font-weight: 600;">for</span> f <span style="color: magenta; font-weight: 600;">in</span> ordered) / budget,
        max_tokens=<span style="color: cyan;">self</span>._preamble_max_tokens,
    )

    <span style="color: #888;"># 10. Compress parent context into skeleton for child plan inheritance</span>
    skeleton_budget = <span style="color: cyan;">int</span>(budget * skeleton_ratio)
    skeleton_frags = ()
    <span style="color: magenta; font-weight: 600;">if</span> parent_fragments:
        skeleton_frags = <span style="color: cyan;">self</span>._config.skeleton_compressor.compress(
            parent_fragments, skeleton_budget,
        )

    <span style="color: magenta; font-weight: 600;">return</span> AssembledContext(
        fragments=ordered,
        total_tokens=<span style="color: cyan;">sum</span>(f.token_count <span style="color: magenta; font-weight: 600;">for</span> f <span style="color: magenta; font-weight: 600;">in</span> ordered),
        budget_used=<span style="color: cyan;">sum</span>(f.token_count <span style="color: magenta; font-weight: 600;">for</span> f <span style="color: magenta; font-weight: 600;">in</span> ordered) / budget,
        strategies_used=strategies_used,
        context_hash=<span style="color: cyan;">self</span>._compute_hash(ordered),
        preamble=preamble,
        provenance_map={f.uko_node: f.provenance <span style="color: magenta; font-weight: 600;">for</span> f <span style="color: magenta; font-weight: 600;">in</span> ordered},
        skeleton_fragments=skeleton_frags,
    )

Built-in Default Implementations

Each component ships with a default implementation that provides the behavior described in the current specification. Custom implementations need only implement the relevant Protocol — they need not subclass the default.

Phase 1 defaults:

Component Default Class Algorithm
StrategySelector ConfidenceWeightedSelector Calls can_handle() on all registered strategies; filters to confidence > 0; applies ContextRequest.preferred_strategies preferences; sorts by confidence * quality_score descending. Uses the Observer pattern to emit StrategySelectionEvent for diagnostics.
BudgetAllocator ProportionalBudgetAllocator Allocates budget proportional to confidence * quality_score. Guarantees each strategy receives at least min_useful_budget tokens or is excluded. Implements Flyweight for allocation metadata reuse across re-assembly cycles.
StrategyExecutor ParallelStrategyExecutor Runs strategies concurrently via ThreadPoolExecutor. Per-strategy timeout (default: 30s). Uses Circuit Breaker pattern: after 3 consecutive failures, a strategy is temporarily disabled for that plan. Failed strategies log warnings and are excluded from results.

Phase 2 defaults:

Component Default Class Algorithm
FragmentDeduplicator ContentHashDeduplicator Groups fragments by UKO node URI; within each group, hashes content to detect duplicates; retains the fragment with the highest relevance_score. Configurable via context.fusion.dedup-strategy: content-hash (default), uko-identity (URI only), or semantic (embedding cosine similarity > 0.95). Implements Strategy (GoF) pattern to swap dedup algorithms.
DetailDepthResolver MaxDepthResolver When the same UKO node appears at multiple depths, retains the highest-depth rendering that fits within the remaining budget. Uses a Specification pattern: each fragment is tested against a budget specification, and the highest-depth satisfying fragment wins.
FragmentScorer WeightedCompositeScorer Computes composite = relevance_score * strategy_quality * hierarchy_weight * recency_bonus. Hierarchy weight is derived from the fragment's distance to the plan's focus nodes. Recency bonus is 1.0 for current nodes, decaying for older revisions. Scoring weights are configurable. Uses Decorator pattern to compose scoring dimensions.
BudgetPacker GreedyKnapsackPacker Sorts by composite score descending; greedily adds fragments until budget is exhausted. When a fragment doesn't fit at its current depth, attempts depth fallback: re-renders at progressively lower depths (e.g., depth 9 → 4 → 0) until it fits or is excluded. Implements Iterator pattern over the depth fallback sequence.
FragmentOrderer RelevanceCoherenceOrderer Groups fragments by resource, then orders within each group by containment depth and source position. Groups are ordered by maximum composite score within the group. Configurable via context.fusion.ordering: relevance (strict score order), topological (dependency-aware), relevance-coherence (default hybrid). Implements Template Method with overridable _group_key() and _intra_group_sort() hooks.

Phase 3 defaults:

Component Default Class Algorithm
PreambleGenerator ProvenancePreambleGenerator Generates a structured preamble listing: included resources, strategies used, budget utilization, and a compact structure map. Respects context.fusion.preamble-max-tokens limit. Can be disabled via context.fusion.preamble-enabled = false. Uses Builder pattern to construct preamble sections.
SkeletonCompressor DepthReductionCompressor Re-renders all parent context fragments at depth 0-1 (e.g., MODULE_LISTING for code, TABLE_OF_CONTENTS_L1 for documents). Prioritizes fragments closer to the child's focus area. Fits within skeleton_budget tokens (derived from skeleton_ratio). Uses Visitor pattern to traverse and re-render fragments by domain type.
Scope Resolution and Configuration

Pipeline components are resolved through a three-level scope chain. Each scope can override any subset of the ten components; unspecified components inherit from the next broader scope.

class PipelineConfigResolver:
    """Resolves the effective PipelineConfig for a given context assembly.
    Implements the Chain of Responsibility pattern across three scopes."""
<span style="color: magenta; font-weight: 600;">def</span> <span style="color: cyan; font-weight: 600;">resolve</span>(
    <span style="color: cyan;">self</span>,
    plan: Plan | <span style="color: magenta; font-weight: 600;">None</span>,
    project: Project | <span style="color: magenta; font-weight: 600;">None</span>,
    global_config: GlobalConfig,
) -> PipelineConfig:
    <span style="color: #888;"># 1. Start with built-in defaults</span>
    config = <span style="color: cyan;">self</span>._builtin_defaults()
    <span style="color: #888;"># 2. Apply global overrides from config.toml</span>
    config = <span style="color: cyan;">self</span>._apply_scope(config, global_config.pipeline_overrides)
    <span style="color: #888;"># 3. Apply project-level overrides (if any)</span>
    <span style="color: magenta; font-weight: 600;">if</span> project <span style="color: magenta; font-weight: 600;">and</span> project.pipeline_overrides:
        config = <span style="color: cyan;">self</span>._apply_scope(config, project.pipeline_overrides)
    <span style="color: #888;"># 4. Apply plan-level overrides (if any)</span>
    <span style="color: magenta; font-weight: 600;">if</span> plan <span style="color: magenta; font-weight: 600;">and</span> plan.pipeline_overrides:
        config = <span style="color: cyan;">self</span>._apply_scope(config, plan.pipeline_overrides)
    <span style="color: magenta; font-weight: 600;">return</span> config

<span style="color: magenta; font-weight: 600;">def</span> <span style="color: cyan; font-weight: 600;">_apply_scope</span>(<span style="color: cyan;">self</span>, base: PipelineConfig, overrides: <span style="color: cyan;">dict</span>) -> PipelineConfig:
    <span style="color: #888;">"""Merges overrides into the base config using the Prototype pattern.
    Only non-None overrides replace the base component."""</span>
    <span style="color: magenta; font-weight: 600;">return</span> PipelineConfig(**{
        field: overrides.get(field, getattr(base, field))
        <span style="color: magenta; font-weight: 600;">for</span> field <span style="color: magenta; font-weight: 600;">in</span> PipelineConfig.__dataclass_fields__
    })

Pipeline components are registered in TOML configuration:

# ── Global config.toml ────────────────────────────────────────────
[context.pipeline]
# Override any pipeline component globally (values are "module:ClassName")
strategy-selector     = "builtin:ConfidenceWeightedSelector"   # default
budget-allocator      = "builtin:ProportionalBudgetAllocator"   # default
strategy-executor     = "builtin:ParallelStrategyExecutor"      # default
fragment-deduplicator = "builtin:ContentHashDeduplicator"       # default
detail-depth-resolver = "builtin:MaxDepthResolver"              # default
fragment-scorer       = "builtin:WeightedCompositeScorer"       # default
budget-packer         = "builtin:GreedyKnapsackPacker"          # default
fragment-orderer      = "builtin:RelevanceCoherenceOrderer"     # default
preamble-generator   = "builtin:ProvenancePreambleGenerator"   # default
skeleton-compressor  = "builtin:DepthReductionCompressor"      # default

# ── Per-component configuration ──────────────────────────────────── [context.pipeline.strategy-executor] timeout-seconds = 30 max-workers = 4 circuit-breaker-threshold = 3

[context.pipeline.fragment-scorer] relevance-weight = 0.4 hierarchy-weight = 0.3 quality-weight = 0.2 recency-weight = 0.1

[context.pipeline.budget-packer] depth-fallback-steps = [9, 4, 2, 0] # depths to try during fallback min-fragment-tokens = 10 # fragments below this are excluded

Project-level and plan-level overrides use the same key structure, specified via the context view YAML or the agents project context set CLI:

# ── Project-level context view YAML ────────────────────────────────
project: local/api-service
view: strategize
pipeline:
  fragment-scorer: "my_extensions.scorers:DomainAwareScorer"
  budget-packer: "my_extensions.packers:PriorityPreservingPacker"
Fallback Degradation Path

The pipeline's ConfidenceWeightedSelector (default StrategySelector) implements automatic fallback:

  1. Try arce (requires all backends) — if unavailable:
  2. Try breadth-depth-navigator (requires graph) — if unavailable:
  3. Try semantic-embedding (requires vector) — if unavailable:
  4. Fall back to simple-keyword (requires only text search / ripgrep)

This ensures the system always produces context, even when advanced backends are not configured.

Initial Context Assembly

Before the actor's first turn, the system assembles initial context:

class InitialContextAssembler:
    """Produces the starting context for an actor invocation."""
<span style="color: magenta; font-weight: 600;">def</span> <span style="color: cyan; font-weight: 600;">assemble</span>(<span style="color: cyan;">self</span>, plan: <span style="color: cyan;">Plan</span>, actor: <span style="color: cyan;">Actor</span>,
             token_budget: <span style="color: cyan;">int</span>) -> <span style="color: cyan;">AssembledContext</span>:
    <span style="color: #888;"># 1. Resolve resource scope</span>
    scope = <span style="color: cyan;">self</span>.scope_resolver.resolve(plan)
    <span style="color: #888;"># 2. Get inherited context from parent plan (if any)</span>
    inherited = <span style="color: cyan;">self</span>._get_inherited_context(plan)
    <span style="color: #888;"># 3. Create scoped backend views</span>
    backends = <span style="color: cyan;">self</span>.bal.create_scoped_views(scope)
    <span style="color: #888;"># 4. Build the initial context request from plan metadata</span>
    initial_request = <span style="color: cyan;">self</span>._build_initial_request(plan, actor, inherited)
    <span style="color: #888;"># 5. Run through the Context Assembly Pipeline</span>
    result = <span style="color: cyan;">self</span>.pipeline.assemble(
        request=initial_request,
        backends=backends,
        budget=token_budget,
        plan_context=PlanContext(
            plan=plan,
            parent_context=inherited,
            depth_in_tree=plan.depth_in_tree,
        ),
    )
    <span style="color: magenta; font-weight: 600;">return</span> result

Context Inheritance Mechanism

class PlanContextInheritance:
    """Manages how child plans inherit and refine parent context.
    Uses the pipeline's SkeletonCompressor component to produce
    compact parent context for child plan inheritance."""
<span style="color: magenta; font-weight: 600;">def</span> <span style="color: cyan; font-weight: 600;">compute_child_context</span>(<span style="color: cyan;">self</span>, parent_plan, child_plan, parent_context):
    <span style="color: #888;"># 1. Determine the child's focus from the parent's decisions</span>
    child_focus = <span style="color: cyan;">self</span>._extract_child_focus(parent_plan, child_plan)
    <span style="color: #888;"># 2. Compute depth/breadth adjustments</span>
    depth_delta = child_plan.depth_in_tree - parent_plan.depth_in_tree
    child_detail = <span style="color: cyan;">self</span>._increase_detail(<span style="color: cyan;">self</span>._avg_detail(parent_context), depth_delta)
    child_breadth = max(<span style="color: yellow;">1</span>, parent_context.avg_breadth - depth_delta)
    <span style="color: #888;"># 3. Compute skeleton budget from the child's token budget</span>
    skeleton_budget = <span style="color: cyan;">int</span>(child_plan.token_budget * <span style="color: cyan;">self</span>.config.skeleton_ratio)
    <span style="color: #888;"># 4. Use the pipeline's SkeletonCompressor to build the parent skeleton</span>
    skeleton = <span style="color: cyan;">self</span>.pipeline.skeleton_compressor.compress(
        parent_context=parent_context,
        child_focus=child_focus,
        skeleton_budget=skeleton_budget,
    )
    <span style="color: #888;"># 5. Build the inherited context request with parent skeleton</span>
    <span style="color: magenta; font-weight: 600;">return</span> ContextRequest(
        focus=child_focus,
        breadth=child_breadth,
        depth=child_detail,
        depth_gradient=<span style="color: magenta; font-weight: 600;">True</span>,
        metadata={
            <span style="color: #66cc66;">"parent_context_hash"</span>: parent_context.context_hash,
            <span style="color: #66cc66;">"parent_skeleton"</span>: skeleton,
            <span style="color: #66cc66;">"inherited_decisions"</span>: <span style="color: cyan;">self</span>._relevant_parent_decisions(parent_plan, child_focus),
        },
    )

Skeleton Context Propagation

The skeleton is a compact, low-depth representation (depth 0-1) of the parent's context window that is passed to every child. This ensures children always have the "big picture" available, even though they focus on a narrow area. The skeleton typically consumes 5-15% of the child's token budget.


Parent's hot context (32K tokens):
  ┌─ Module: src/auth/          [depth 3/MEMBER_SUMMARY: 2K tokens]
  ├─ Module: src/core/          [depth 3/MEMBER_SUMMARY: 2K tokens]
  ├─ Module: src/api/           [depth 3/MEMBER_SUMMARY: 2K tokens]
  ├─ Module: src/services/      [depth 3/MEMBER_SUMMARY: 2K tokens]
  └─ ... 12 more modules        [depth 3/MEMBER_SUMMARY: 24K tokens]

Child's hot context (32K tokens): ┌─ [SKELETON from parent] [depth 0/MODULE_LISTING: 3K tokens] │ Module: src/auth/ [just name + member names] │ Module: src/core/ [just name + member names] │ Module: src/api/ [just name + member names] │ ... etc. ├─ [CHILD FOCUS AREA] [depth 9/FULL_SOURCE: 25K tokens] │ Class: AuthManager [depth 9: all methods with bodies] │ Class: TokenValidator [depth 9: referenced by AuthManager] │ Class: User [depth 4/SIGNATURES: used as parameter type] └─ [INHERITED DECISIONS] [4K tokens] Decision: "Use async patterns for all auth operations" Decision: "Maintain backward compat with sync callers"

Dynamic Context Window Adaptation

The context window size may change between invocations. Different LLM providers, different models, different configurations, and even dynamic adjustments during a session can alter the available budget. The ACMS handles this seamlessly.

Budget Computation
class ContextBudgetCalculator:
    """Computes the effective context budget for each assembly cycle."""
<span style="color: magenta; font-weight: 600;">def</span> <span style="color: cyan; font-weight: 600;">compute</span>(<span style="color: cyan;">self</span>, actor: <span style="color: cyan;">Actor</span>, plan: <span style="color: cyan;">Plan</span>,
            conversation_history_tokens: <span style="color: cyan;">int</span>) -> <span style="color: cyan;">int</span>:
    <span style="color: #888;"># 1. Get the actor's hard context window limit</span>
    model_limit = actor.model_config.context_window_tokens
    <span style="color: #888;"># 2. Reserve space for system prompt, history, tools, response</span>
    reserved = (<span style="color: cyan;">self</span>._count_tokens(actor.system_prompt) +
                conversation_history_tokens +
                <span style="color: cyan;">self</span>._estimate_tool_tokens(actor.skills) +
                actor.model_config.max_output_tokens)
    <span style="color: #888;"># 3. Apply the soft cap from context view policy</span>
    soft_cap = <span style="color: cyan;">self</span>._resolve_view(plan).hot_max_tokens <span style="color: magenta; font-weight: 600;">or</span> <span style="color: cyan;">float</span>(<span style="color: #66cc66;">'inf'</span>)
    <span style="color: #888;"># 4. Calculate available budget</span>
    effective_budget = max(<span style="color: yellow;">0</span>, min(model_limit - reserved, soft_cap))
    <span style="color: magenta; font-weight: 600;">return</span> effective_budget

Adaptive Behavior

When the budget changes between context assembly cycles:

  1. Budget increase: The pipeline re-runs strategy orchestration (Phase 1) with the expanded budget. The BudgetAllocator distributes the larger budget across strategies, and the BudgetPacker may promote fragments previously rendered at a low depth (e.g., depth 0) to a higher depth (e.g., depth 4 or 9).
  2. Budget decrease: The pipeline's BudgetPacker re-applies the knapsack with the new budget, potentially reducing fragment depths via its depth fallback mechanism or dropping low-relevance items as ranked by the FragmentScorer.
  3. Strategies are stateless: Each assembly cycle is independent. The pipeline runs all 10 components from scratch, and strategies receive the current budget and must respect it.
Progressive Refinement Within a Session

As an actor makes tool calls during a session, the conversation grows and the available budget shrinks. The ACMS handles this through a context refresh cycle that triggers re-assembly when the budget changes by more than 30% (configurable via context.budget.refresh_threshold).

Built-in Strategy Catalogue

The ACMS ships with several built-in strategies. The ARCE pipeline is one of them, not the only one.

Strategy: simple-keyword

Basic keyword/regex text search. Works with any backend, any resource type. No graph or vector required. This is the universal fallback. Quality score: 0.3.

Strategy: semantic-embedding

Vector similarity search. Finds semantically related content even without exact keyword matches. Requires a vector backend. Quality score: 0.6.

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.

Strategy: arce (Analyze, Retrieve, Contextualize, Execute)

The sophisticated multi-modal pipeline. Combines text, vector, and graph search with intent analysis and actor-type-aware patterns. This is the "kitchen sink" strategy that produces the highest quality results but requires all backends. Quality score: 0.95.

Actor-type-specific graph traversal patterns:

  • Strategy actors: Broader, shallower traversal. Emphasis on module boundaries and dependency structures. Prioritizes uko:Container and uko:Boundary nodes.
  • Execution actors: Narrower, deeper traversal. Emphasis on implementation details, function bodies, and test coverage. Prioritizes uko:Atom nodes.
  • Estimation actors: Focus on size metrics, complexity indicators, and historical data. Prioritizes temporal data.
Strategy: temporal-archaeology

Searches the cold tier for historical patterns. Useful for understanding "what changed last time we touched this area" or "what decisions were made about this module in the past." Requires graph + cold tier access. Quality score: 0.5.

Strategy: plan-decision-context

Retrieves context from parent and ancestor plan decisions. This is how child plans "remember" what their parent decided and why. Operates on the warm/cold tiers. Quality score: 0.7.

ACMS Extensibility

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.

UKO Runtime Services

The UKO runtime is operationalized through three service classes:

  • UKOQueryInterface — Typed interface for ACMS context strategies to query UKO classification data. Provides classify_resource(uri) returning a ClassificationResult with layer (03), primary_type, source_resource_id, and relationships. Strategies use this to discover which ontology layer a resource belongs to and what relationships it has.

  • UKOInferenceEngine — Semantic analysis service that infers implicit relationships from UKO triples produced by domain analyzers. Infers three relationship types: uko:implicitSiblingOf (co-occurrence), uko:implicitContains (URI prefix containment), and uko:implicitDependsOn (URI reference in object values). Inferred triples are stored with confidence 0.7 to distinguish them from deterministic extractions.

  • UKOGraphPersistence — Serializes and restores UKO graph state via JSON or in-memory backends. Used for graph persistence across sessions and for test isolation.

The UKOIndexer.index_graph() method runs inference via UKOInferenceEngine and populates uko:layer triples for all four ontology layers during indexing.

Real-time Index Synchronization

The system maintains index freshness through immediate, proactive updates. The UKOIndexer produces UKO triples from resources using pluggable analyzers, and simultaneously indexes into text and vector backends:

class UKOIndexer:
    """Produces UKO triples from resources using pluggable analyzers."""
<span style="color: magenta; font-weight: 600;">def</span> <span style="color: cyan; font-weight: 600;">index_resource</span>(<span style="color: cyan;">self</span>, resource: <span style="color: cyan;">Resource</span>):
    <span style="color: #888;"># 1. Determine the best analyzer for this resource</span>
    analyzer = <span style="color: cyan;">self</span>.analyzers.get_for_resource(resource)
    <span style="color: #888;"># 2. Produce UKO triples using the most specific vocabulary</span>
    triples = analyzer.analyze(resource)
    <span style="color: #888;"># 3. Add provenance</span>
    <span style="color: magenta; font-weight: 600;">for</span> triple <span style="color: magenta; font-weight: 600;">in</span> triples:
        <span style="color: cyan;">self</span>._add_provenance(triple, resource)
    <span style="color: #888;"># 4. Store in graph backend</span>
    <span style="color: cyan;">self</span>.graph_backend.add_triples(triples)
    <span style="color: #888;"># 5. Also index into text and vector backends</span>
    <span style="color: cyan;">self</span>.text_indexer.index(resource, triples)
    <span style="color: cyan;">self</span>.vector_indexer.index(resource, triples)

Index Lifecycle
Stage Trigger Action Result
Resource added agents resource add / agents project link-resource Immediate full indexing All indices ready for instant search
Code changed File modification detected Immediate incremental update Indices reflect latest state
Resource removed agents resource remove / agents project unlink-resource Immediate cleanup Stale data removed, UKO nodes marked historical
Maintenance Scheduled Full reindex, consistency check Indices verified and optimized

When advanced features are unavailable, the system gracefully degrades. The pipeline's ConfidenceWeightedSelector (default StrategySelector component) automatically routes to simpler strategies:

  1. Try arce (requires all backends) -> if unavailable:
  2. Try breadth-depth-navigator (requires graph) -> if unavailable:
  3. Try semantic-embedding (requires vector) -> if unavailable:
  4. Fall back to simple-keyword (requires only text search / ripgrep)

ACMS Performance Characteristics

Assembly Latency
Operation Target Latency Notes
Resource scope resolution < 10ms In-memory set operations
Scoped backend view creation < 5ms Filter injection
Strategy can_handle check (all) < 20ms Simple capability checks
simple-keyword assembly < 100ms Text search
semantic-embedding assembly < 200ms Vector search
breadth-depth-navigator assembly < 500ms Graph traversal + materialization
arce assembly < 1s Multi-modal search + ranking
temporal-archaeology assembly < 2s Cold tier queries
Fragment fusion pipeline (phases 2-3) < 100ms Dedup + depth resolution + scoring + knapsack + ordering + preamble
Total initial assembly < 2s Parallel strategy execution
Incremental refresh < 500ms Re-compression only
Indexing and Query Performance
Metric Performance
Text indexing speed 10,000 files/minute
Vector indexing speed 1,000 files/minute (with GPU)
Graph indexing speed 5,000 files/minute
Text search query < 100ms for 1M files
Vector search query < 200ms for 10M embeddings
Graph traversal query < 500ms for 3-hop queries
Scalability Guarantees
  • 100K+ file projects: The scoping mechanism ensures strategies never see the full dataset. Scoped views filter at the backend level.
  • Deep plan hierarchies (10+ levels): Skeleton compression ensures parent context overhead stays bounded (10% per level, multiplicatively compressed).
  • Concurrent plans: Each plan gets independent ScopedBackendViews. No cross-plan interference.
  • Dynamic budgets: The knapsack algorithm is O(n) in the number of fragments. Re-compression is O(1) per fragment.
  • Maximum scale tested: 10M files, 1B+ triples, 100M+ embeddings.
Progressive Enhancement Path

Organizations can adopt ACMS features progressively. At each stage, existing resources are reindexed to take advantage of new capabilities:

Stage Features Requirements Benefit
Basic Text search, file watching ripgrep, sqlite Instant exact-match search
Semantic Vector embeddings, similarity search Embedding model, vector DB Instant semantic similarity search
Structural UKO graph, relationship queries Graph database, language analyzers Instant relationship queries
Intelligent ML-driven ranking, automated analysis GPU, training data Instant intelligent suggestions
Custom Domain-specific ontologies, custom analyzers Domain expertise Instant domain-aware intelligence

Summary of Timing:

  • Indexing: Eager (happens immediately when resources are added/changed)
  • Searching: Instant (because indices are pre-computed and ready)
  • Sandboxing: Lazy (only when execution needs to modify a resource)
  • Context Assembly: Real-time (but fast because it queries ready indices)

This design ensures agents never wait for index building during execution, providing a responsive and predictable experience even on massive codebases.

Storage and Persistence

!!! adr "Architecture Decision" The hybrid storage model, database strategy, and migration approach are defined in ADR-019: Storage and Persistence.

This section defines the complete storage strategy for every persistent concept in CleverAgents, specifying the storage backend, data format, lifecycle management, and migration strategy for each entity type.

Storage Architecture Overview

CleverAgents uses a hybrid storage model combining a relational database for structured data, the filesystem for configuration files and artifacts, and specialized indexes for search operations. The storage layer is accessed exclusively through repository interfaces defined in the domain layer, ensuring that storage backends can be swapped without affecting application logic.

@startuml
skinparam componentStyle rectangle
skinparam defaultFontSize 12
skinparam packageFontSize 13
skinparam packageFontStyle bold

package "Storage Architecture" as SA {
  package "Relational Database" as RDB #E3F2FD {
    component [Plans\nDecisions\nProjects\nResources\nSessions\nActors (db)\nInvariants\nChanges\nCheckpoint Metadata\nCorrection Attempts\nAudit Log] as Tables
  }

  package "Filesystem" as FS #C8E6C9 {
    component [Config YAML Files] as Cfg
    component [Sandbox Worktree Files] as Sbx
    component [Artifact Storage] as Art
  }

  package "Search Indexes" as IDX #F3E5F5 {
    component [Text (Tantivy)] as TI
    component [Vector (FAISS)] as VI
    component [Graph (Neo4j)] as GI
    component [Checkpoint Store\n(filesystem)] as CS
  }
}
@enduml

Entity Storage Map

The following table specifies where each CleverAgents concept is persisted, in both local and server modes:

Entity Local Storage Server Storage Format Identifier
Plans SQLite plans table Server PostgreSQL Relational rows ULID (plan_id)
Decisions SQLite decisions table Server PostgreSQL Relational rows + JSON context_snapshot ULID (decision_id)
Projects SQLite projects table Server PostgreSQL (for non-local/ namespaces) Relational rows Namespaced name
Resources SQLite resources table + resource_links table Server PostgreSQL Relational rows + DAG edges ULID (resource_id)
Resource Types SQLite resource_types table + YAML config files Server PostgreSQL + synced YAML Relational rows Namespaced name (built-ins unnamespaced)
Actions SQLite actions table + YAML config files Server PostgreSQL + synced YAML Relational rows Namespaced name
Actors SQLite actors table + YAML config files Server PostgreSQL + synced YAML Relational rows + file reference Namespaced name
Skills SQLite skills table + YAML config files Server PostgreSQL + synced YAML Relational rows + file reference Namespaced name
Tools SQLite tools table + YAML config files Server PostgreSQL + synced YAML Relational rows + file reference Namespaced name
Sessions SQLite sessions table + session_messages table Server PostgreSQL Relational rows ULID (session_id)
Invariants SQLite invariants table Server PostgreSQL Relational rows Auto-generated ID
Automation Profiles SQLite automation_profiles table + YAML config files Server PostgreSQL + synced YAML Relational rows Namespaced name (built-ins unnamespaced)
Changes / ChangeSets SQLite changes table Server PostgreSQL Relational rows ULID per change
Checkpoints Filesystem (<data-dir>/checkpoints/<plan_id>/) + SQLite checkpoint_metadata table Server object storage + PostgreSQL metadata Git refs or filesystem snapshots + metadata rows ULID (checkpoint_id)
Correction Attempts SQLite correction_attempts table + archived artifacts on filesystem Server PostgreSQL + object storage Relational rows + file archives ULID (correction_attempt_id)
Artifacts Filesystem (<data-dir>/artifacts/<plan_id>/) Server object storage (S3-compatible) Raw files (code, documents, images, etc.) Filesystem path relative to plan
Context Indexes (Text) Filesystem (<data-dir>/index/text/) Server-managed index cluster Tantivy index files or SQLite FTS5 Index directory per project
Context Indexes (Vector) Filesystem (<data-dir>/index/vector/) Server-managed FAISS/Qdrant cluster FAISS index files or Qdrant collections Index directory per project
Context Indexes (Graph) Neo4j database or rdflib files Server-managed Neo4j instance Graph triples Graph database per project
Configuration TOML file (<data-dir>/config.toml) N/A (server has own config) TOML Single file
Logs Filesystem (<data-dir>/logs/) Server-managed log aggregation Structured JSON (structlog) Rotated log files
Audit Logs SQLite audit_log table Server PostgreSQL Relational rows Auto-incrementing ID

Database Schema Design

The relational database follows a normalized design with foreign key constraints enforcing referential integrity. The schema is version-controlled through Alembic migrations.

Key design decisions:

  1. ULID primary keys: All time-series entities (plans, decisions, checkpoints, correction attempts) use ULID primary keys. This provides natural time-ordering without separate timestamp indexes, collision-free generation without coordination, and URL-safe string representation.

  2. JSON columns for semi-structured data: Fields like context_snapshot, alternatives_considered, artifacts_produced, and resource_bindings are stored as JSON text columns. This avoids over-normalization for data that is always read and written as a unit, while still being queryable via SQLite's json_extract() or PostgreSQL's jsonb operators.

  3. Soft delete pattern: Entities that support archival (actions, actors, skills) use a state column with values like available, archived. No rows are physically deleted except during explicit agents init reset or retention policy enforcement.

  4. Optimistic concurrency control: The updated_at timestamp column on mutable entities serves as a version check. Update operations include WHERE updated_at = <previous_value> to detect concurrent modifications. On conflict, the operation fails with an explicit error rather than silently overwriting.

  5. Datetime handling contract: All timestamps stored in the database are UTC ISO 8601 strings (format: YYYY-MM-DDTHH:MM:SS.ffffff). All Python datetime comparisons involving stored timestamps MUST parse the stored string back to a timezone-aware datetime object before comparing — never compare ISO timestamp strings lexicographically. String comparison is incorrect because different timezone offset formats (+00:00 vs Z vs +05:30) produce strings that are not lexicographically comparable even when representing the same moment. The canonical pattern is:

    from datetime import datetime, UTC
    
    def parse_utc_ts(ts_str: str) -> datetime:
        """Parse a stored ISO timestamp string to a UTC-aware datetime."""
        dt = datetime.fromisoformat(str(ts_str))
        return dt if dt.tzinfo is not None else dt.replace(tzinfo=UTC)
    
    # Correct: compare datetime objects
    if parse_utc_ts(stored_expires_at) >= datetime.now(UTC):
        ...
    
    # Wrong: string comparison (do not do this)
    # if stored_expires_at >= datetime.now(UTC).isoformat():  # BUG
    

    For database-level comparisons (e.g., DELETE WHERE expires_at < ?), pass the ISO string produced by datetime.now(UTC).isoformat() only when the database column was also written using the same Python UTC isoformat — ensuring consistent format. Prefer Python-side datetime parsing over relying on lexicographic string ordering.

Core tables (SQLite DDL):

-- Plans table: the fundamental unit of orchestration
CREATE TABLE plans (
    plan_id TEXT PRIMARY KEY,                    -- ULID
    parent_plan_id TEXT REFERENCES plans(plan_id),
    root_plan_id TEXT NOT NULL REFERENCES plans(plan_id),
    action_name TEXT NOT NULL,                    -- namespaced action name
    phase TEXT NOT NULL DEFAULT 'strategize',     -- action|strategize|execute|apply
    state TEXT NOT NULL DEFAULT 'queued',         -- queued|processing|errored|complete|cancelled
    attempt INTEGER NOT NULL DEFAULT 1,
    automation_profile_name TEXT NOT NULL,
    effective_profile_snapshot TEXT NOT NULL,      -- JSON: frozen profile at creation
    arguments TEXT,                                -- JSON: action arguments
    project_names TEXT NOT NULL,                   -- JSON array of project names
    strategy_actor_name TEXT,
    execution_actor_name TEXT,
    estimation_actor_name TEXT,
    invariant_actor_name TEXT,
    created_by TEXT,                               -- session ID or user identity
    created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%f', 'now')),
    updated_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%f', 'now')),
    completed_at TEXT,
    error_message TEXT,
    error_traceback TEXT,
    cost_estimate_usd REAL,
    cost_actual_usd REAL,
    token_count_input INTEGER DEFAULT 0,
    token_count_output INTEGER DEFAULT 0
);

CREATE INDEX idx_plans_parent ON plans(parent_plan_id); CREATE INDEX idx_plans_root ON plans(root_plan_id); CREATE INDEX idx_plans_phase_state ON plans(phase, state); CREATE INDEX idx_plans_action ON plans(action_name); CREATE INDEX idx_plans_created ON plans(created_at);

-- Decisions table: the decision tree CREATE TABLE decisions ( decision_id TEXT PRIMARY KEY, -- ULID plan_id TEXT NOT NULL REFERENCES plans(plan_id), parent_decision_id TEXT REFERENCES decisions(decision_id), decision_type TEXT NOT NULL, -- prompt_definition|invariant_enforced| -- strategy_choice|subplan_spawn| -- subplan_parallel_spawn|implementation_choice question TEXT NOT NULL, chosen_option TEXT NOT NULL, alternatives_considered TEXT, -- JSON array of alternative options confidence_score REAL, -- 0.0-1.0 rationale TEXT, context_snapshot TEXT NOT NULL, -- JSON: hot_context_hash, relevant_resources, -- actor_state_ref downstream_plan_ids TEXT, -- JSON array of child plan ULIDs artifacts_produced TEXT, -- JSON array of artifact references is_correction BOOLEAN DEFAULT FALSE, corrects_decision_id TEXT REFERENCES decisions(decision_id), superseded_by TEXT REFERENCES decisions(decision_id), sequence_number INTEGER NOT NULL, -- ordering within this plan created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%f', 'now')) );

CREATE INDEX idx_decisions_plan ON decisions(plan_id); CREATE INDEX idx_decisions_parent ON decisions(parent_decision_id); CREATE INDEX idx_decisions_type ON decisions(decision_type); CREATE INDEX idx_decisions_superseded ON decisions(superseded_by) WHERE superseded_by IS NOT NULL;

-- Resources table: the resource registry CREATE TABLE resources ( resource_id TEXT PRIMARY KEY, -- ULID name TEXT, -- namespaced name (NULL for auto-discovered children) resource_type_name TEXT NOT NULL, -- namespaced type name classification TEXT NOT NULL, -- physical|virtual description TEXT, properties TEXT, -- JSON: type-specific properties location TEXT, -- physical resources only content_hash TEXT, -- for equivalence tracking sandbox_strategy TEXT, -- override per resource created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%f', 'now')), updated_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%f', 'now')) );

CREATE UNIQUE INDEX idx_resources_name ON resources(name) WHERE name IS NOT NULL; CREATE INDEX idx_resources_type ON resources(resource_type_name); CREATE INDEX idx_resources_classification ON resources(classification);

-- Resource DAG: parent/child relationships CREATE TABLE resource_links ( parent_id TEXT NOT NULL REFERENCES resources(resource_id), child_id TEXT NOT NULL REFERENCES resources(resource_id), link_type TEXT DEFAULT 'contains', -- contains|references|derived_from created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%f', 'now')), PRIMARY KEY (parent_id, child_id) );

CREATE INDEX idx_resource_links_child ON resource_links(child_id);

-- Project-resource linkage CREATE TABLE project_resources ( project_name TEXT NOT NULL, resource_id TEXT NOT NULL REFERENCES resources(resource_id), read_only BOOLEAN DEFAULT FALSE, linked_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%f', 'now')), PRIMARY KEY (project_name, resource_id) );

-- Checkpoint metadata (actual data on filesystem) CREATE TABLE checkpoint_metadata ( checkpoint_id TEXT PRIMARY KEY, -- ULID plan_id TEXT NOT NULL REFERENCES plans(plan_id), decision_id TEXT REFERENCES decisions(decision_id), checkpoint_type TEXT NOT NULL, -- pre_write|post_step|manual resource_id TEXT REFERENCES resources(resource_id), filesystem_path TEXT NOT NULL, -- relative path within checkpoint dir size_bytes INTEGER, created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%f', 'now')) );

CREATE INDEX idx_checkpoints_plan ON checkpoint_metadata(plan_id);

-- Correction attempts CREATE TABLE correction_attempts ( correction_attempt_id TEXT PRIMARY KEY, -- ULID plan_id TEXT NOT NULL REFERENCES plans(plan_id), original_decision_id TEXT NOT NULL REFERENCES decisions(decision_id), new_decision_id TEXT REFERENCES decisions(decision_id), mode TEXT NOT NULL, -- revert|append guidance TEXT NOT NULL, archived_artifacts_path TEXT, -- filesystem path to archived originals state TEXT NOT NULL DEFAULT 'pending', -- pending|executing|complete|failed created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%f', 'now')), completed_at TEXT );

CREATE INDEX idx_corrections_plan ON correction_attempts(plan_id);

-- Audit log for apply operations CREATE TABLE audit_log ( id INTEGER PRIMARY KEY AUTOINCREMENT, event_type TEXT NOT NULL, -- plan_applied|plan_cancelled|resource_modified| -- correction_applied|config_changed plan_id TEXT, project_name TEXT, actor_name TEXT, user_identity TEXT, details TEXT NOT NULL, -- JSON: event-specific details created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%f', 'now')) );

CREATE INDEX idx_audit_event ON audit_log(event_type); CREATE INDEX idx_audit_plan ON audit_log(plan_id) WHERE plan_id IS NOT NULL; CREATE INDEX idx_audit_created ON audit_log(created_at);

Filesystem Layout

The data directory follows a deterministic layout. All paths are relative to core.data-dir (default: ~/.cleveragents):


~/.cleveragents/
├── config.toml                    # Global configuration
├── cleveragents.db                # SQLite database (WAL mode)
├── cleveragents.db-wal            # WAL file (auto-managed by SQLite)
├── cleveragents.db-shm            # Shared memory file (auto-managed)
├── logs/
│   ├── cleveragents.log           # Current log file (structured JSON)
│   ├── cleveragents.log.1         # Rotated log files
│   └── ...
├── cache/
│   ├── models/                    # Cached model artifacts
│   ├── tools/                     # Cached tool downloads
│   └── templates/                 # Compiled Jinja2 templates
├── backups/
│   ├── 2026-02-08T12-30-00/       # Timestamped backup snapshots
│   └── ...
├── checkpoints/
│   ├── 01HXR1C1D2E3F4G5H6I7.../  # Per-plan checkpoint directories
│   │   ├── chk_01HXR1E1.tar.gz   # Compressed checkpoint snapshots
│   │   └── ...
│   └── ...
├── artifacts/
│   ├── 01HXR1C1D2E3F4G5H6I7.../  # Per-plan artifact directories
│   │   ├── src/routes/health.py   # Generated/modified artifacts
│   │   └── ...
│   └── ...
├── index/
│   ├── text/
│   │   ├── local_api-service/     # Per-project Tantivy indexes
│   │   └── ...
│   ├── vector/
│   │   ├── local_api-service/     # Per-project FAISS indexes
│   │   └── ...
│   └── graph/
│       └── ...                     # rdflib stores or Neo4j connection info
├── sessions/
│   └── ...                         # Session state files (if needed beyond DB)
└── contexts/
    └── ...                         # Exported/cached context snapshots

Data Lifecycle and Retention

Data Type Retention Policy Cleanup Trigger
Plans (active) Indefinite until the plan reaches applied or cancelled state Manual deletion
Plans (terminal) Governed by core.backup.retention-days for archives Automatic cleanup daemon
Decisions Retained as long as parent plan exists Cascade delete with plan
Checkpoints sandbox.checkpoint.max-per-plan per plan; oldest pruned first (keeping first + most recent) After apply or on retention limit
Correction archives core.backup.retention-days after plan reaches applied state Automatic cleanup daemon
Logs core.log.retention-days Daily rotation + age-based cleanup
Backups core.backup.retention-days Age-based cleanup
Cache No automatic retention; manual cleanup via agents diagnostics --cleanup User-initiated
Search indexes Rebuilt on resource add/remove; no explicit retention Re-index on change
Audit logs Indefinite (critical for compliance) Manual archival only

Migration Strategy

Database schema changes are managed through Alembic migrations with the following rules:

  1. Forward-only in production: Downgrade scripts are provided but only used in development. Production deployments only move forward.
  2. Auto-apply on startup: When CLEVERAGENTS_AUTO_APPLY_MIGRATIONS is set (default in development), migrations run automatically on first database access. In production, migrations are triggered by agents init or explicit upgrade commands.
  3. Backward compatibility window: Each migration maintains backward compatibility with the previous schema version for at least one minor release cycle, allowing rolling upgrades in server mode.
  4. Data migration separation: Schema DDL changes and data transformations are kept in separate migration steps to enable rollback of data changes independently.

Observability

!!! adr "Architecture Decision" The observability strategy, structured logging, metrics, and distributed tracing are defined in ADR-025: Observability and Logging.

CleverAgents implements a comprehensive observability strategy across three pillars — logging, metrics, and tracing — designed to provide full visibility into plan execution, actor behavior, tool invocations, and resource access. Every significant operation in the system is observable, enabling debugging of complex multi-plan hierarchies and performance optimization.

Structured Logging

All logging uses structlog with JSON output format. Every log entry includes contextual fields that enable correlation across plan hierarchies:

import structlog

logger = structlog.get_logger()

# Every log entry automatically includes bound context logger = logger.bind( plan_id="01HXR1C1D2E3F4G5H6I7J8K9L0", phase="execute", actor_name="anthropic/claude-3.5-sonnet", session_id="01HXM2A6K1P2E9Q9D4GQ7J4S7Z", )

# Example: tool invocation logging logger.info( "tool_invoked", tool_name="local/write-file", skill_name="local/file-ops", resource_id="01HXR1A1B2C3D4E5F6G7H8J9K0", resource_type="git-checkout", input_schema_hash="sha256:a1b2c3...", duration_ms=142, writes=True, checkpoint_created=True, )

Log levels and their semantics:

Level Usage Examples
DEBUG Internal state transitions, context building details, LLM prompt construction "Building hot context: 12 files, 8,420 tokens", "Decision confidence computed: 0.87"
INFO Significant lifecycle events, phase transitions, tool completions "Plan entered execute phase", "Tool write-file completed in 142ms", "Validation passed: pytest"
WARNING Degraded operations, approaching limits, non-fatal issues "Budget at 82% ($4.10/$5.00)", "Checkpoint pruned: exceeded max-per-plan", "Provider fallback: openai -> anthropic"
ERROR Operation failures, invariant violations, unrecoverable tool errors "Validation failed: pytest exited 1", "Invariant violated: API compatibility", "LLM provider error: rate limited"

Log correlation: Every log entry within a plan execution context includes plan_id, root_plan_id, parent_plan_id, phase, state, and attempt. This enables tracing a single decision through the entire plan hierarchy using standard log querying tools (e.g., jq '.plan_id == "01HXR1C1..."').

Event System

CleverAgents emits domain events for every significant state change. Events flow through two channels:

  1. RxPY Reactive Stream: Real-time event stream for in-process subscribers (TUI updates, progress bars, streaming output). Uses Subject for fan-out distribution.
  2. Persistent Event Log: All events are written to the audit_log table for post-hoc analysis and compliance.

Event taxonomy:

from enum import Enum
from pydantic import BaseModel
from datetime import datetime

class EventType(StrEnum): # Plan lifecycle events PLAN_CREATED = "plan.created" PLAN_PHASE_CHANGED = "plan.phase_changed" PLAN_STATE_CHANGED = "plan.state_changed" PLAN_APPLIED = "plan.applied" PLAN_CANCELLED = "plan.cancelled" PLAN_ERRORED = "plan.errored" PLAN_ESTIMATION_COMPLETE = "plan.estimation_complete" # emitted after Strategize when estimation_actor is set

<span style="color: #888;"># Decision events</span>
DECISION_CREATED = <span style="color: #66cc66;">"decision.created"</span>
DECISION_APPROVED = <span style="color: #66cc66;">"decision.approved"</span>
DECISION_CORRECTED = <span style="color: #66cc66;">"decision.corrected"</span>
DECISION_SUPERSEDED = <span style="color: #66cc66;">"decision.superseded"</span>

<span style="color: #888;"># Invariant events</span>
INVARIANT_RECONCILED = <span style="color: #66cc66;">"invariant.reconciled"</span>
INVARIANT_VIOLATED = <span style="color: #66cc66;">"invariant.violated"</span>
INVARIANT_ENFORCED = <span style="color: #66cc66;">"invariant.enforced"</span>

<span style="color: #888;"># Actor events</span>
ACTOR_INVOKED = <span style="color: #66cc66;">"actor.invoked"</span>
ACTOR_COMPLETED = <span style="color: #66cc66;">"actor.completed"</span>
ACTOR_ERRORED = <span style="color: #66cc66;">"actor.errored"</span>
ACTOR_ESCALATED = <span style="color: #66cc66;">"actor.escalated"</span>       <span style="color: #888;"># confidence below threshold</span>

<span style="color: #888;"># Tool events</span>
TOOL_INVOKED = <span style="color: #66cc66;">"tool.invoked"</span>
TOOL_COMPLETED = <span style="color: #66cc66;">"tool.completed"</span>
TOOL_ERRORED = <span style="color: #66cc66;">"tool.errored"</span>
TOOL_RETRIED = <span style="color: #66cc66;">"tool.retried"</span>

<span style="color: #888;"># Resource events</span>
RESOURCE_ACCESSED = <span style="color: #66cc66;">"resource.accessed"</span>
RESOURCE_MODIFIED = <span style="color: #66cc66;">"resource.modified"</span>
RESOURCE_INDEXED = <span style="color: #66cc66;">"resource.indexed"</span>

<span style="color: #888;"># Correction events</span>
CORRECTION_APPLIED = <span style="color: #66cc66;">"correction.applied"</span>

<span style="color: #888;"># Configuration events</span>
CONFIG_CHANGED = <span style="color: #66cc66;">"config.changed"</span>

<span style="color: #888;"># Entity lifecycle events</span>
ENTITY_DELETED = <span style="color: #66cc66;">"entity.deleted"</span>

<span style="color: #888;"># Authentication events (server-mode only)</span>
AUTH_SUCCESS = <span style="color: #66cc66;">"auth.success"</span>
AUTH_FAILURE = <span style="color: #66cc66;">"auth.failure"</span>

<span style="color: #888;"># Sandbox events</span>
SANDBOX_CREATED = <span style="color: #66cc66;">"sandbox.created"</span>
SANDBOX_COMMITTED = <span style="color: #66cc66;">"sandbox.committed"</span>
SANDBOX_ROLLED_BACK = <span style="color: #66cc66;">"sandbox.rolled_back"</span>
CHECKPOINT_CREATED = <span style="color: #66cc66;">"checkpoint.created"</span>
CHECKPOINT_RESTORED = <span style="color: #66cc66;">"checkpoint.restored"</span>

<span style="color: #888;"># Context events</span>
CONTEXT_BUILT = <span style="color: #66cc66;">"context.built"</span>
CONTEXT_QUERY_EXECUTED = <span style="color: #66cc66;">"context.query_executed"</span>

<span style="color: #888;"># Context tier lifecycle events</span>
TIER_PROMOTED = <span style="color: #66cc66;">"tier.promoted"</span>
TIER_DEMOTED = <span style="color: #66cc66;">"tier.demoted"</span>
TIER_EVICTED = <span style="color: #66cc66;">"tier.evicted"</span>

<span style="color: #888;"># Validation events</span>
VALIDATION_STARTED = <span style="color: #66cc66;">"validation.started"</span>
VALIDATION_PASSED = <span style="color: #66cc66;">"validation.passed"</span>
VALIDATION_FAILED = <span style="color: #66cc66;">"validation.failed"</span>
VALIDATION_FIX_ATTEMPTED = <span style="color: #66cc66;">"validation.fix_attempted"</span>
VALIDATION_FIX_SUCCEEDED = <span style="color: #66cc66;">"validation.fix_succeeded"</span>
VALIDATION_FIX_EXHAUSTED = <span style="color: #66cc66;">"validation.fix_exhausted"</span>

<span style="color: #888;"># Session events</span>
SESSION_CREATED = <span style="color: #66cc66;">"session.created"</span>
SESSION_MESSAGE_SENT = <span style="color: #66cc66;">"session.message_sent"</span>

<span style="color: #888;"># Cost events</span>
BUDGET_WARNING = <span style="color: #66cc66;">"budget.warning"</span>
BUDGET_EXCEEDED = <span style="color: #66cc66;">"budget.exceeded"</span>

class DomainEvent(BaseModel): """Base event model emitted by all domain operations.""" event_type: EventType timestamp: datetime # UTC; defaults to now(UTC) correlation_id: str # ULID linking related events in one request chain plan_id: str | None = None root_plan_id: str | None = None session_id: str | None = None actor_name: str | None = None project_name: str | None = None user_identity: str | None = None # Authenticated user; None in local (unauthenticated) mode details: dict # Event-type-specific payload

Event emission pattern: Domain services emit events through an injected EventBus interface. The infrastructure layer provides two implementations:

from typing import Protocol
import rx
from rx.subject import Subject

class EventBus(Protocol): def emit(self, event: DomainEvent) -> None: ... def subscribe(self, event_type: EventType, handler: Callable) -> None: ...

class ReactiveEventBus: """In-process event bus using RxPY for real-time distribution.""" def init(self): self._subject = Subject() self._subscriptions: dict[EventType, list[Callable]] = {}

<span style="color: magenta; font-weight: 600;">def</span> <span style="color: cyan; font-weight: 600;">emit</span>(<span style="color: cyan;">self</span>, event: DomainEvent) -> <span style="color: cyan;">None</span>:
    <span style="color: #888;"># Push to reactive stream for real-time subscribers</span>
    <span style="color: cyan;">self</span>._subject.on_next(event)
    <span style="color: #888;"># Persist to audit log</span>
    <span style="color: cyan;">self</span>._persist_audit(event)
    <span style="color: #888;"># Dispatch to type-specific handlers</span>
    <span style="color: magenta; font-weight: 600;">for</span> handler <span style="color: magenta; font-weight: 600;">in</span> <span style="color: cyan;">self</span>._subscriptions.get(event.event_type, []):
        handler(event)

<span style="color: magenta; font-weight: 600;">def</span> <span style="color: cyan; font-weight: 600;">subscribe</span>(<span style="color: cyan;">self</span>, event_type: <span style="color: cyan;">EventType</span>, handler: <span style="color: cyan;">Callable</span>) -> <span style="color: cyan;">None</span>:
    <span style="color: cyan;">self</span>._subscriptions.setdefault(event_type, []).append(handler)

<span style="color: cyan;">@property</span>
<span style="color: magenta; font-weight: 600;">def</span> <span style="color: cyan; font-weight: 600;">stream</span>(<span style="color: cyan;">self</span>) -> rx.Observable:
    <span style="color: #888;">"""Raw observable stream for advanced RxPY operators."""</span>
    <span style="color: magenta; font-weight: 600;">return</span> <span style="color: cyan;">self</span>._subject

LLM Call Tracing

Every LLM invocation is traced with full context for debugging and cost tracking:

# Trace record for each LLM call
class LLMTrace(BaseModel):
    trace_id: str                     # ULID
    plan_id: str
    decision_id: str | None
    actor_name: str
    provider: str                     # openai, anthropic, google, etc.
    model: str                        # gpt-4o, claude-3.5-sonnet, etc.
    prompt_tokens: int
    completion_tokens: int
    total_tokens: int
    cost_usd: float
    latency_ms: int
    temperature: float
    tool_calls: list[str]             # tool names invoked by the LLM
    context_hash: str                 # hash of the context window contents
    context_refs: list[str]           # resource IDs referenced in context
    streaming: bool
    retry_count: int
    error: str | None
    timestamp: datetime

LangSmith integration: When CLEVERAGENTS_LANGSMITH_ENABLED is true, all LLM traces are additionally forwarded to LangSmith for visualization, comparison, and cost analysis. The integration maps CleverAgents plan IDs to LangSmith run tags, enabling filtering by plan hierarchy in the LangSmith UI.

Metrics Collection

CleverAgents collects operational metrics for performance monitoring and capacity planning. Metrics are emitted as structured log entries (for local mode) and optionally exported to Prometheus (for server mode):

Metric Type Description
plan.duration_seconds Histogram Total wall-clock time per plan, labeled by phase
plan.cost_usd Counter Cumulative API cost per plan
plan.decisions_count Counter Number of decisions per plan
plan.child_plans_count Counter Number of child plans spawned
actor.invocation_duration_ms Histogram Per-actor invocation latency
actor.token_usage Counter Token counts by provider and model
tool.invocation_duration_ms Histogram Per-tool invocation latency
tool.error_rate Counter Tool invocation failures by tool name
context.build_duration_ms Histogram Context building time by tier (hot/warm/cold)
context.tokens_used Gauge Current token usage in hot context
index.query_duration_ms Histogram Index query latency by backend (text/vector/graph)
sandbox.operation_duration_ms Histogram Sandbox create/commit/rollback time
validation.duration_seconds Histogram Per-validation tool invocation execution time
validation.pass_rate Counter Validation pass/fail counts (from passed field in return JSON)

Diagnostic Dashboard

The agents diagnostics command provides a real-time health check combining all observability data:

  1. System health: Config file readability, database accessibility, disk space, provider API key validation.
  2. Index health: Text index status, vector index dimensionality and count, graph store connectivity.
  3. Active plan overview: Running plans, queued plans, resource utilization.
  4. Cost summary: Current session cost, budget utilization, provider-level breakdown.
  5. Performance summary: Average plan duration, tool call latency percentiles, context build times.

Security Model

!!! adr "Architecture Decision" Sandbox isolation is covered in ADR-015: Sandbox and Checkpoint. The server-mode security model is defined in ADR-023: Server Mode.

CleverAgents implements a defense-in-depth security model addressing five concerns: sandbox isolation, access control, prompt injection mitigation, secret management, and audit logging. The security model differs between local mode (single-user, trusted environment) and server mode (multi-user, potentially untrusted environment).

Sandbox Isolation

The sandbox is the primary safety mechanism preventing untested changes from reaching production resources. Every plan's Execute phase runs within an isolated sandbox unless explicitly disabled by the automation profile (require_sandbox: false).

Sandbox strategies and their security properties:

Strategy Isolation Level Resource Type Mechanism Rollback
git_worktree Process-level filesystem isolation Git repositories Creates a separate git worktree on a plan-specific branch. Changes are confined to the worktree directory. Merge back to main requires explicit apply. git checkout -- . or worktree deletion
copy_on_write Process-level filesystem isolation Filesystems with native CoW support (e.g., BTRFS, ZFS) Leverages the filesystem's native copy-on-write capability to create a lightweight snapshot. The filesystem preserves original blocks when edits occur, requiring no explicit copy. Only available on CoW-capable filesystems. Restore from snapshot
filesystem_copy Process-level filesystem isolation All writable filesystems Creates an explicit full copy of the resource directory (using cp or equivalent). Works on any writable filesystem regardless of CoW support, at the cost of duplicating data upfront. Original files are never modified during execution. Delete the copy directory
overlay Process-level filesystem isolation Filesystems supporting overlay mounts Uses an overlay filesystem (e.g., OverlayFS) to layer changes on top of the original. Writes go to the upper layer; the lower layer remains untouched. Remove the overlay mount
transaction_rollback Database transaction isolation Databases Opens a database transaction with SERIALIZABLE isolation level. All changes are staged within the transaction. Commit on apply; rollback on cancel/error. ROLLBACK
none No isolation Any Changes are applied directly. Requires require_sandbox: false in the automation profile. Only appropriate when external safety mechanisms exist. No automatic rollback

Sandbox security invariants:

  1. No cross-sandbox access: Each plan's sandbox is isolated. Plan A cannot read or write Plan B's sandbox contents. The sandbox path is derived deterministically from the plan ULID, preventing path traversal.
  2. Read-only source guarantee during Execute: The original project resources are never modified during Execute. All tool write operations target the sandbox copy.
  3. Atomic apply: The Apply phase is an atomic operation — either all sandbox changes are committed to the real resources, or none are. For git-based resources, this uses git merge. For database resources, this uses transaction commit.
  4. Sandbox cleanup: Sandbox directories are cleaned up according to sandbox.cleanup policy. Sensitive data (API responses, LLM outputs) is purged from the sandbox on cleanup.

Path containment implementation contract:

All path containment checks (verifying that a resolved path is inside a sandbox root) MUST use Path.is_relative_to(root) (Python 3.9+) or equivalent path-semantic comparison — never string prefix comparison (str(path).startswith(str(root))). String prefix comparison is incorrect: a sandbox root of /tmp/sandbox would incorrectly pass /tmp/sandboxmalicious/file.txt because the string starts with /tmp/sandbox. The canonical implementation is:

def validate_path(path_str: str, sandbox_root: str | None = None) -> Path:
    root = Path(sandbox_root).resolve() if sandbox_root else Path.cwd().resolve()
    target = (root / path_str).resolve()
    if not (target == root or target.is_relative_to(root)):
        raise ValueError(f"Path traversal detected: '{path_str}' escapes sandbox root '{root}'")
    return target

This invariant applies to all built-in tools, MCP tool adapters, and any infrastructure code that validates file paths against sandbox boundaries.

Prompt Injection Mitigation

In server mode, where user-provided content may flow into LLM prompts, CleverAgents implements several mitigations:

  1. Input sanitization: User-provided text in action arguments, invariant text, and session prompts is sanitized before inclusion in LLM prompts. HTML entities, control characters, and known injection patterns are escaped or rejected.

  2. Prompt boundary markers: System prompts and user content are separated by clear boundary markers that the LLM is instructed to recognize. The system prompt explicitly states that content between [USER_CONTENT_START] and [USER_CONTENT_END] markers is user-provided and should not be treated as system instructions.

  3. Output validation: LLM outputs that are used as tool invocations are validated against the tool's JSON Schema before execution. This prevents the LLM from being tricked into invoking tools with malicious parameters.

  4. Tool capability restrictions: Tools declare their capabilities (read_only, writes, checkpointable, side_effects). The execution engine enforces these declarations — a tool declared as read_only cannot invoke write operations even if the LLM requests it.

  5. Unsafe tool gating: Tools marked as unsafe (e.g., arbitrary shell execution, network access) are blocked unless the automation profile explicitly sets allow_unsafe_tools: true. In server mode, only administrators can create automation profiles that allow unsafe tools.

Secret Management

Secrets (API keys, database credentials, authentication tokens) are managed through a layered approach:

  1. Environment variables (preferred): Provider API keys (OPENAI_API_KEY, ANTHROPIC_API_KEY, etc.) and the server token (CLEVERAGENTS_SERVER_TOKEN) are read from environment variables. This is the recommended approach for both local and CI environments.

  2. Configuration file (fallback): API keys can be stored in config.toml under the provider.* section. The config file should have restrictive permissions (chmod 600). The agents diagnostics command warns if config file permissions are too permissive.

  3. Secret masking in logs: All log output and CLI display automatically masks values that match known secret patterns (keys starting with sk-, sk-ant-, tok_, etc.). Masked values are replaced with ***REDACTED***. The masking is applied at the structlog processor level, ensuring no secret leaks through any log path.

  4. Secret masking in LLM context: Before constructing LLM prompts, the context builder scans for patterns matching known secret formats and replaces them with [REDACTED]. This prevents accidental exposure of secrets in LLM training data (for providers that use customer data for training, which CleverAgents discourages).

  5. Server mode credential isolation: In server mode, each user's API keys are stored encrypted in the server database using AES-256-GCM with a per-user key derived from the server's master secret. Keys are decrypted only at the moment of LLM invocation and are never transmitted to the client CLI.

Exception Handling and Error Classification

All exceptions surfacing through the CLI are processed by the cleveragents.core.error_handling module, which provides three guarantees: classification, redaction, and safe wrapping.

Error classification: Every exception is mapped to an HTTP-like numeric error code via the exception class hierarchy. The mapping uses the exception's MRO (Method Resolution Order) to find the most specific match:

Range Category Examples
400-range Client / input errors ValidationError → 422, ResourceNotFoundError → 404, AuthenticationError → 401, RateLimitError → 429
500-range Server / infrastructure errors DatabaseError → 522, NetworkError → 524, ProviderError → 520, ConfigurationError → 525

Unknown exceptions (not inheriting from CleverAgentsError) default to code 500 (INTERNAL).

Secret redaction in error output: Both the exception message and its details dict are redacted before display. Redaction delegates to cleveragents.shared.redaction (the single redaction implementation shared with structlog processors and CLI output). This ensures:

  • Key names matching sensitive substrings (api_key, password, secret, token, credential, auth, etc.) have values fully replaced with ***REDACTED***.
  • String values are scanned for known secret patterns (OpenAI keys, Anthropic keys, JWT tokens, GitHub PATs, GitLab PATs, Bearer tokens).
  • Nested dicts and lists are recursively redacted.
  • Error details always enforce redaction (show_secrets=False) regardless of the global show_secrets flag.

Safe wrapping of unexpected exceptions: The wrap_unexpected function converts bare Exception instances into CleverAgentsError with a generic, user-safe message ("An unexpected error occurred"). Internal stack details are captured in the error's details dict for diagnostics but are not shown to the user. If the exception is already a CleverAgentsError, it is returned unchanged with optional context merged into a copy of its details.

CLI integration: The top-level error handlers in cli/main.py use classify_error for CleverAgentsError exceptions and wrap_unexpected + classify_error for bare exceptions. CLI output includes the error code and category name for consistent, machine-parseable error reporting:

Error [422] VALIDATION_FAILED: name is required
Error [500] INTERNAL: An unexpected error occurred

See docs/reference/error_handling.md for the complete API reference and exception hierarchy.

Audit Logging

All security-relevant operations are recorded in the audit_log table with the following events:

Event Type Trigger Details Captured
plan_applied agents plan apply Plan ID, project names, files changed, lines added/removed, resources modified, apply duration, user identity
plan_cancelled agents plan cancel Plan ID, reason, project names, cancelled phase, cancelled processing state, last completed step, subplan count, sandbox refs, changeset ID, resources pending cleanup
resource_modified Tool write operations during Execute Resource ID, modification type, sandbox path, tool name
correction_applied agents plan correct Correction attempt ID, original decision ID, mode, guidance
config_changed agents config set Key, old value (masked if secret), new value (masked if secret), user identity
entity_deleted agents <entity> delete/remove Entity type, entity name, cascade effects
session_created agents session create Session ID, actor name, user identity
auth_success Server mode login User identity, IP address, token prefix
auth_failure Server mode failed login Attempted identity, IP address, failure reason

Audit log retention: Audit logs are never automatically deleted. In server mode, audit logs can be exported for compliance archival using server administration commands. In local mode, audit logs are preserved in the SQLite database indefinitely and are included in backup snapshots.

Extensibility

!!! adr "Architecture Decision" The extensibility model and plugin architecture are grounded in the layered design defined in ADR-001: Layered Architecture and the configuration system in ADR-024: Configuration System.

CleverAgents is designed as an extensible platform where every major subsystem supports custom implementations. The extensibility model follows the Open/Closed Principle — the system is open for extension through well-defined interfaces, but closed for modification of core behavior.

Plugin Architecture Overview

@startuml
skinparam componentStyle rectangle
skinparam defaultFontSize 12
skinparam packageFontSize 13
skinparam packageFontStyle bold
skinparam componentFontSize 11

package "Extension Points" as EP {
  package "User-Facing Extensions" as UFE #C8E6C9 {
    component [Custom Tools\n(YAML/Python)] as CT
    component [Custom Skills\n(YAML)] as CS
    component [Custom Actors\n(YAML/Graph)] as CA
  }

  package "Integration Extensions" as IE #E3F2FD {
    component [MCP Servers\n(stdio/SSE)] as MCP
    component [Agent Skills\nStandard] as ASS
    component [Custom LLM\nProviders] as CLP
  }

  package "Core Registries" as CR #F3E5F5 {
    component [Tool Registry\nSkill Registry\nActor Registry\nProvider Registry] as REG
  }

  package "Infrastructure Extensions" as INF #FFF9C4 {
    component [Custom Resource\nTypes (YAML)] as CRT
    component [Custom Sandbox\nStrategies (Python)] as CSS
    component [Custom Index\nBackends] as CIB
  }

  package "ACMS Pipeline Extensions" as ACMS #E8EAF6 {
    component [Custom UKO\nAnalyzers / Vocabularies] as UKO
    component [Custom Context\nStrategies] as CCS
    component [Custom Pipeline\nComponents (10 slots)] as CPC
  }

  CT -down-> REG
  CS -down-> REG
  CA -down-> REG
  MCP -down-> REG
  ASS -down-> REG
  CLP -down-> REG
  REG -down-> CRT
  REG -down-> CSS
  REG -down-> CIB
  REG -down-> UKO
  REG -down-> CCS
  REG -down-> CPC
}
@enduml

Tool Extensibility

Tools are the atomic unit of execution and the primary extension point. There are five ways to add tools:

  1. Custom Python tools: Define a tool with inline Python code in a YAML configuration file. The code runs in a sandboxed execution context with access to the plan context, resource bindings, and checkpoint API.

    # File: tools/my-custom-tool.yaml
    tool:
      name: local/analyze-complexity
      description: "Compute cyclomatic complexity for Python files"
      source: custom
    

    input_schema: type: object properties: file_path: { type: string } required: [file_path]

    output_schema: type: object properties: complexity: { type: integer } functions: { type: array }

    capability: writes: false checkpointable: false

    resource_slots: - name: source type: git-checkout binding: contextual

    code: | import ast content = ctx.resources["source"].read(params["file_path"]) tree = ast.parse(content) # ... complexity analysis ... return {"complexity": total, "functions": results}

  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"]
    
  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/
    
  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.

  5. Anonymous inline tools: Tools can be defined inline within actor graph nodes or skill definitions for one-off operations that don't need global registration.

Tool lifecycle hooks: Every tool follows a four-phase lifecycle — discover (find available capabilities), activate (prepare for use, acquire connections), execute (perform the operation), deactivate (release resources). The tool registry manages these lifecycle transitions.

Skill Extensibility

Skills compose tools into capability collections. Skills support:

  • Tool aggregation: Reference named tools from the Tool Registry
  • Inline anonymous tools: Define tools directly in the skill YAML
  • Skill inclusion: Include other skills by reference (composability)
  • MCP server aggregation: Expose tools from one or more MCP servers
  • Agent Skills Standard: Expose tools from Agent Skills Standard directories

Actor Extensibility

Actors can be extended through:

  1. YAML-defined agents: Single LLM actors with custom system prompts, temperature settings, tool bindings, and capability constraints.
  2. YAML-defined graphs: LangGraph topologies with multiple actors and tool nodes connected by edges, conditional routing, and parallel execution groups.
  3. Provider extension: New LLM providers can be added by implementing the AIProviderInterface protocol and registering with the ProviderRegistry. The registry uses auto-discovery to detect installed langchain-* packages.
from typing import Protocol

class AIProviderInterface(Protocol): """Protocol for LLM provider implementations."""

<span style="color: cyan;">@property</span>
<span style="color: magenta; font-weight: 600;">def</span> <span style="color: cyan; font-weight: 600;">provider_name</span>(<span style="color: cyan;">self</span>) -> <span style="color: cyan;">str</span>: ...

<span style="color: cyan;">@property</span>
<span style="color: magenta; font-weight: 600;">def</span> <span style="color: cyan; font-weight: 600;">capabilities</span>(<span style="color: cyan;">self</span>) -> ProviderCapabilities: ...

<span style="color: magenta; font-weight: 600;">def</span> <span style="color: cyan; font-weight: 600;">create_chat_model</span>(
    <span style="color: cyan;">self</span>,
    model: <span style="color: cyan;">str</span>,
    temperature: <span style="color: cyan;">float</span> = <span style="color: yellow;">0.7</span>,
    **kwargs,
) -> BaseChatModel: ...

<span style="color: magenta; font-weight: 600;">def</span> <span style="color: cyan; font-weight: 600;">create_embedding_model</span>(
    <span style="color: cyan;">self</span>,
    model: <span style="color: cyan;">str</span>,
    **kwargs,
) -> BaseEmbeddings: ...

Custom Resource Types

New resource types extend CleverAgents to manage any kind of resource:

# File: resource-types/docker-registry.yaml
resource_type:
  name: local/docker-registry
  description: "Docker container registry"
  classification: physical
  user_addable: true

cli_args: - name: registry_url type: string required: true - name: repository type: string required: true - name: tag type: string default: "latest"

sandbox_strategy: none # Docker images are immutable handler: docker_registry_handler

child_types: - type: local/docker-image relationship: contains auto_discover: true

auto_discovery: enabled: true strategy: list_tags

Custom Index Backends

The indexing layer is abstracted behind three interfaces (text, vector, graph). Custom implementations can be registered for each:

class TextIndexBackend(Protocol):
    """Interface for full-text search backends."""
    def index_document(self, project: str, doc_id: str, content: str, metadata: dict) -> None: ...
    def search(self, project: str, query: str, limit: int) -> list[SearchResult]: ...
    def remove_document(self, project: str, doc_id: str) -> None: ...
    def rebuild_index(self, project: str) -> None: ...

class VectorIndexBackend(Protocol): """Interface for vector similarity search backends.""" def index_embedding(self, project: str, doc_id: str, embedding: list[float], metadata: dict) -> None: ... def search_similar(self, project: str, query_embedding: list[float], limit: int, min_relevance: float) -> list[VectorResult]: ... def remove_embedding(self, project: str, doc_id: str) -> None: ...

class GraphIndexBackend(Protocol): """Interface for knowledge graph backends.""" def add_triple(self, project: str, subject: str, predicate: str, obj: str) -> None: ... def query(self, project: str, sparql: str) -> list[dict]: ... def remove_triples(self, project: str, subject: str | None, predicate: str | None, obj: str | None) -> None: ...

New backends are registered via configuration:

# config.toml
[index.text]
backend = "custom"
custom_module = "my_extensions.elasticsearch_backend"
custom_class = "ElasticsearchTextIndex"

[index.text.custom_options] hosts = ["http://localhost:9200"] index_prefix = "cleveragents"

Custom Sandbox Strategies

The sandbox layer supports custom isolation strategies for specialized resource types:

class SandboxStrategy(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 diff(self, ref: SandboxRef) -> DiffView: ...
    def commit(self, ref: SandboxRef) -> None: ...
    def rollback(self, ref: SandboxRef) -> None: ...
    def checkpoint(self, ref: SandboxRef, checkpoint_id: str) -> None: ...
    def restore_checkpoint(self, ref: SandboxRef, checkpoint_id: str) -> None: ...
    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.

Plugin Security Contract

The plugin loading system enforces a security boundary: module imports are restricted to a configurable prefix allowlist to prevent arbitrary code execution from untrusted configuration. However, protocol validation must never instantiate the plugin class to perform type checking. Instantiation during validation runs __init__ side effects (network connections, file I/O, subprocess spawning) before the plugin is approved for use.

Protocol compliance MUST be checked structurally using issubclass() for @runtime_checkable protocols, or by inspecting declared methods — never by creating a live instance:

# Correct: structural check only — no instantiation
@staticmethod
def validate_protocol(klass: type[Any], protocol: type[Any]) -> bool:
    try:
        return issubclass(klass, protocol)
    except TypeError:
        return False

# Wrong: instantiates the class, running __init__ side effects (do not do this)
# instance = klass()  # BUG: arbitrary code execution during validation
# return isinstance(instance, protocol)

This contract applies to all plugin loaders in the Infrastructure layer (provider plugins, custom tool plugins, custom index backend plugins, custom sandbox strategy plugins, and ACMS extension plugins).

ACMS Extensions

The ACMS provides five categories of extension points: UKO analyzers, index backends, context strategies, UKO vocabularies, and Context Assembly Pipeline components.

Domain and Language-Specific Analyzers

Analyzers are pluggable components that parse resources and produce UKO triples. They are organized by domain (code, documents, data, infrastructure) and then by specific technology within each domain:

PluginSystem:
  analyzers:
    # --- Source Code Analyzers (Layer 1: uko-code + Layer 2/3) ---
    python:
      class: "PythonAnalyzer"
      features: [AST parsing, Type inference via mypy, Import resolution, Docstring extraction]
    typescript:
      class: "TypeScriptAnalyzer"
      features: [TSC-based parsing, Type extraction, Module resolution, JSDoc parsing]
    rust:
      class: "RustAnalyzer"
      features: [rust-analyzer integration, Lifetime analysis, Trait resolution, Macro expansion]
    custom_dsl:
      class: "CustomDSLAnalyzer"
      config:
        grammar: "path/to/grammar.peg"
        semantic_rules: "path/to/rules.yaml"
<span style="color: #888;"># --- Document Analyzers (Layer 1: uko-doc) ---</span>
<span style="color: cyan; font-weight: 600;">markdown</span>:
  <span style="color: cyan; font-weight: 600;">class</span>: <span style="color: #66cc66;">"MarkdownAnalyzer"</span>
  <span style="color: cyan; font-weight: 600;">features</span>: [<span style="color: #66cc66;">Heading hierarchy, Link extraction, Code block detection, Topic inference, Cross-reference resolution</span>]
<span style="color: cyan; font-weight: 600;">restructuredtext</span>:
  <span style="color: cyan; font-weight: 600;">class</span>: <span style="color: #66cc66;">"ReStructuredTextAnalyzer"</span>
  <span style="color: cyan; font-weight: 600;">features</span>: [<span style="color: #66cc66;">Directive parsing, Role resolution, Cross-reference resolution, Index extraction</span>]
<span style="color: cyan; font-weight: 600;">html</span>:
  <span style="color: cyan; font-weight: 600;">class</span>: <span style="color: #66cc66;">"HTMLDocumentAnalyzer"</span>
  <span style="color: cyan; font-weight: 600;">features</span>: [<span style="color: #66cc66;">Semantic HTML parsing, Heading extraction, Link graph, Readability scoring</span>]

<span style="color: #888;"># --- Data Schema Analyzers (Layer 1: uko-data) ---</span>
<span style="color: cyan; font-weight: 600;">postgresql</span>:
  <span style="color: cyan; font-weight: 600;">class</span>: <span style="color: #66cc66;">"PostgreSQLAnalyzer"</span>
  <span style="color: cyan; font-weight: 600;">features</span>: [<span style="color: #66cc66;">Schema introspection, DDL extraction, Foreign key graph, View dependency analysis, Stored procedure parsing, Statistics collection</span>]
<span style="color: cyan; font-weight: 600;">mysql</span>:
  <span style="color: cyan; font-weight: 600;">class</span>: <span style="color: #66cc66;">"MySQLAnalyzer"</span>
  <span style="color: cyan; font-weight: 600;">features</span>: [<span style="color: #66cc66;">Schema introspection, DDL extraction, Foreign key graph, Trigger parsing</span>]
<span style="color: cyan; font-weight: 600;">sqlite</span>:
  <span style="color: cyan; font-weight: 600;">class</span>: <span style="color: #66cc66;">"SQLiteAnalyzer"</span>
  <span style="color: cyan; font-weight: 600;">features</span>: [<span style="color: #66cc66;">Schema introspection, DDL extraction, Virtual table detection</span>]

<span style="color: #888;"># --- Infrastructure Analyzers (Layer 1: uko-infra) ---</span>
<span style="color: cyan; font-weight: 600;">docker_compose</span>:
  <span style="color: cyan; font-weight: 600;">class</span>: <span style="color: #66cc66;">"DockerComposeAnalyzer"</span>
  <span style="color: cyan; font-weight: 600;">features</span>: [<span style="color: #66cc66;">Service graph, Port mapping, Volume resolution, Environment variable extraction</span>]
<span style="color: cyan; font-weight: 600;">kubernetes</span>:
  <span style="color: cyan; font-weight: 600;">class</span>: <span style="color: #66cc66;">"KubernetesAnalyzer"</span>
  <span style="color: cyan; font-weight: 600;">features</span>: [<span style="color: #66cc66;">Resource parsing, Service dependency graph, ConfigMap/Secret references, Ingress routing</span>]

Index Backend Providers
  backends:
    graph:
      - { name: "blazegraph", class: "BlazegraphBackend", features: ["SPARQL", "reasoning"] }
      - { name: "neo4j", class: "Neo4jBackend", features: ["Cypher", "APOC", "GDS"] }
    vector:
      - { name: "faiss", class: "FaissBackend", features: ["GPU acceleration", "HNSW"] }
      - { name: "qdrant", class: "QdrantBackend", features: ["filtering", "payloads"] }
  embedders:
    - { name: "openai", class: "OpenAIEmbedder", models: ["text-embedding-3-large"] }
    - { name: "local", class: "LocalEmbedder", models: ["all-MiniLM-L6-v2"] }
Adding a New UKO Vocabulary (e.g., for Terraform)
@prefix uko-tf: <https://cleveragents.ai/ontology/uko/infra/terraform#> .

uko-tf:TerraformModule a owl:Class ; rdfs:subClassOf uko-infra:ConfigBlock, uko:Container . uko-tf:Resource a owl:Class ; rdfs:subClassOf uko:Container . uko-tf:Variable a owl:Class ; rdfs:subClassOf uko-infra:ConfigKey . uko-tf:Output a owl:Class ; rdfs:subClassOf uko:Boundary . uko-tf:provisionsOn a owl:ObjectProperty ; rdfs:subPropertyOf uko:dependsOn ; rdfs:domain uko-tf:Resource ; rdfs:range uko-infra:Service .

Adding a New Context Strategy
class MyDomainStrategy:
    name = "my-namespace/domain-strategy"
    capabilities = StrategyCapabilities(
        uses_graph=True, uses_text=True,
        uko_levels=["uko:", "uko-tf:"],
        resource_types=["*"],
        supports_depth_breadth=True,
        quality_score=0.8,
    )
<span style="color: magenta; font-weight: 600;">def</span> <span style="color: cyan; font-weight: 600;">can_handle</span>(<span style="color: cyan;">self</span>, request, backends):
    <span style="color: magenta; font-weight: 600;">if</span> any(<span style="color: #66cc66;">"terraform"</span> <span style="color: magenta; font-weight: 600;">in</span> f <span style="color: magenta; font-weight: 600;">for</span> f <span style="color: magenta; font-weight: 600;">in</span> request.focus):
        <span style="color: magenta; font-weight: 600;">return</span> <span style="color: yellow;">0.9</span>
    <span style="color: magenta; font-weight: 600;">return</span> <span style="color: yellow;">0.0</span>

<span style="color: magenta; font-weight: 600;">def</span> <span style="color: cyan; font-weight: 600;">assemble</span>(<span style="color: cyan;">self</span>, request, backends, budget, plan_context):
    <span style="color: #888;"># Custom logic...</span>
    ...

Register via config:

[context.strategies.custom]
"my-namespace/domain-strategy" = "my_package.strategies.MyDomainStrategy"
Replacing Pipeline Components

Any of the 10 Context Assembly Pipeline components can be replaced by implementing the corresponding Protocol and registering the implementation. Components can be overridden at three scopes (plan > project > global), with more specific scopes taking precedence.

class MyCustomScorer:
    """A domain-aware fragment scorer that boosts infrastructure fragments
    when the plan focuses on deployment tasks."""
<span style="color: magenta; font-weight: 600;">def</span> <span style="color: cyan; font-weight: 600;">score</span>(<span style="color: cyan;">self</span>, fragments: <span style="color: cyan;">list</span>[ContextFragment],
          plan_context: PlanContext) -> <span style="color: cyan;">list</span>[ScoredFragment]:
    scored = []
    is_deploy = <span style="color: #66cc66;">"deploy"</span> <span style="color: magenta; font-weight: 600;">in</span> plan_context.plan.description.lower()
    <span style="color: magenta; font-weight: 600;">for</span> f <span style="color: magenta; font-weight: 600;">in</span> fragments:
        base_score = f.relevance_score * <span style="color: yellow;">0.4</span> + f.hierarchy_weight * <span style="color: yellow;">0.3</span>
        <span style="color: magenta; font-weight: 600;">if</span> is_deploy <span style="color: magenta; font-weight: 600;">and</span> <span style="color: #66cc66;">"uko-infra:"</span> <span style="color: magenta; font-weight: 600;">in</span> f.uko_uri:
            base_score *= <span style="color: yellow;">1.5</span>  <span style="color: #888;"># Boost infrastructure fragments</span>
        scored.append(ScoredFragment(**vars(f), composite_score=base_score))
    <span style="color: magenta; font-weight: 600;">return</span> sorted(scored, key=<span style="color: magenta; font-weight: 600;">lambda</span> s: s.composite_score, reverse=<span style="color: magenta; font-weight: 600;">True</span>)

Register via TOML config (global scope):

[context.pipeline]
fragment-scorer = "my_extensions.scorers:MyCustomScorer"

Or via project-level context view YAML:

project: local/api-service
view: strategize
pipeline:
  fragment-scorer: "my_extensions.scorers:MyCustomScorer"

The following table shows which Protocol each pipeline slot implements and what the default implementation provides:

Pipeline Slot Protocol Default Implementation Design Pattern
strategy-selector StrategySelectorProtocol ConfidenceWeightedSelector Strategy
budget-allocator BudgetAllocatorProtocol ProportionalBudgetAllocator Strategy
strategy-executor StrategyExecutorProtocol ParallelStrategyExecutor Circuit Breaker
fragment-deduplicator FragmentDeduplicatorProtocol ContentHashDeduplicator Strategy
detail-depth-resolver DetailDepthResolverProtocol MaxDepthResolver Specification
fragment-scorer FragmentScorerProtocol WeightedCompositeScorer Decorator
budget-packer BudgetPackerProtocol GreedyKnapsackPacker Iterator
fragment-orderer FragmentOrdererProtocol RelevanceCoherenceOrderer Template Method
preamble-generator PreambleGeneratorProtocol ProvenancePreambleGenerator Builder
skeleton-compressor SkeletonCompressorProtocol DepthReductionCompressor Visitor

Extension Points Summary

Extension Point Registration Mechanism Configuration Format Discovery
Custom Tools agents tool add --config <file> YAML Manual registration
MCP Server Tools Referenced in skill YAML mcp_servers section YAML Auto-discovered from MCP server
Agent Skills Standard Referenced in skill YAML agent_skills_dirs section Folder structure Auto-discovered from directory
Custom Skills agents skill add --config <file> YAML Manual registration
Custom Actors agents actor add --config <file> YAML Manual registration
Custom Resource Types agents resource type add --config <file> YAML Manual registration
Custom LLM Providers Python package installation + ProviderRegistry auto-discovery Python module Auto-discovered via langchain-* package naming
Custom Index Backends config.toml index.*.custom_module TOML + Python module Configuration-driven
Custom Sandbox Strategies Resource type sandbox_strategy field YAML + Python module Configuration-driven
Custom Automation Profiles agents automation-profile add --config <file> YAML Manual registration
Custom Invariant Actors Standard actor registration + assignment via --invariant-actor YAML Manual assignment
Custom UKO Analyzers config.toml context.uko.analyzers.custom.* TOML + Python module Configuration-driven
Custom UKO Vocabularies Turtle/RDF files + analyzer registration RDF/Turtle + TOML Configuration-driven
Custom Context Strategies config.toml context.strategies.custom.* TOML + Python module Configuration-driven
Pipeline: StrategySelector config.toml context.pipeline.strategy-selector or project/plan YAML TOML/YAML + Python module Configuration-driven (scope chain: plan > project > global > built-in)
Pipeline: BudgetAllocator config.toml context.pipeline.budget-allocator or project/plan YAML TOML/YAML + Python module Configuration-driven (scope chain)
Pipeline: StrategyExecutor config.toml context.pipeline.strategy-executor or project/plan YAML TOML/YAML + Python module Configuration-driven (scope chain)
Pipeline: FragmentDeduplicator config.toml context.pipeline.fragment-deduplicator or project/plan YAML TOML/YAML + Python module Configuration-driven (scope chain)
Pipeline: DetailDepthResolver config.toml context.pipeline.detail-depth-resolver or project/plan YAML TOML/YAML + Python module Configuration-driven (scope chain)
Pipeline: FragmentScorer config.toml context.pipeline.fragment-scorer or project/plan YAML TOML/YAML + Python module Configuration-driven (scope chain)
Pipeline: BudgetPacker config.toml context.pipeline.budget-packer or project/plan YAML TOML/YAML + Python module Configuration-driven (scope chain)
Pipeline: FragmentOrderer config.toml context.pipeline.fragment-orderer or project/plan YAML TOML/YAML + Python module Configuration-driven (scope chain)
Pipeline: PreambleGenerator config.toml context.pipeline.preamble-generator or project/plan YAML TOML/YAML + Python module Configuration-driven (scope chain)
Pipeline: SkeletonCompressor config.toml context.pipeline.skeleton-compressor or project/plan YAML TOML/YAML + Python module Configuration-driven (scope chain)

Milestone Plan

This section defines the ordered milestone plan for CleverAgents v3.x, mapping architectural features to verifiable deliverables. Each milestone builds on the previous and is independently testable. Milestones v3.0.0 and v3.1.0 are complete. This plan covers v3.2.0 through v3.8.0 — the production-ready target including server mode.

!!! note "Milestone Status (as of 2026-04-11)" - v3.0.0 CLOSED — Minimal Local Source-Code Workflow - v3.1.0 CLOSED — Actor Compiler + Full LLM Integration - v3.2.0 🔄 IN PROGRESS — Decisions + Validations + Invariants (571 open / 257 closed) - v3.3.0 🔄 IN PROGRESS — Corrections + Subplans + Checkpoints (122 open / 108 closed) - v3.4.0 🔄 IN PROGRESS — ACMS v1 + Context Scaling (200 open / 137 closed) - v3.5.0 🔄 IN PROGRESS — Autonomy Hardening (971 open / 201 closed) - v3.6.0 🔄 IN PROGRESS — Advanced Concepts & Deferred Features (282 open / 150 closed) - v3.7.0 🔄 IN PROGRESS — TUI Implementation (526 open / 427 closed) - v3.8.0 🔄 IN PROGRESS — Server Implementation (352 open / 132 closed)


v3.2.0 — Decisions + Validations + Invariants

Goal: Decisions are recorded during Strategize and Execute phases and persisted to the database. Users can view the decision tree, inspect individual decisions, manage invariants, and correct decisions with selective subtree recomputation.

Spec Coverage: Decision Tree and Correction, Invariant System, Validation Abstraction

Deliverables

# Deliverable Spec Reference Verifiable Check
1 Decision recording during Strategize with full context snapshots §Decision Tree — Recording Protocol agents plan tree <plan_id> renders non-empty tree after strategize
2 Decision recording during Execute phase §Decision Tree — Execute Phase Execute-phase decisions appear in plan tree output
3 agents plan tree renders the decision tree with correct hierarchy §CLI Commands — plan tree Command exits 0, shows tree structure with decision IDs
4 agents plan explain <decision_id> shows decision details including alternatives §CLI Commands — plan explain Output includes alternatives, confidence_score, rationale fields
5 agents invariant add creates invariants; agents invariant list displays them §CLI Commands — invariant Round-trip: add then list shows the invariant
6 Invariants enforced during Strategize phase §Invariant System — Enforcement invariant_enforced decision type appears in tree when invariant applies
7 agents plan correct --mode=revert re-executes from targeted decision point §Correction Model — Revert Mode Correction creates new attempt, affected subtree recomputed
8 agents plan correct --mode=append adds guidance without recomputing §Correction Model — Append Mode Append correction recorded; no subtree recomputation triggered
9 agents plan correct --dry-run shows warnings, recompute time, and hint §Correction Model — Dry Run Output includes warnings, estimated_recompute_time_seconds, and "remove --dry-run" hint
10 plan.attempt incremented on each correction §Correction Safety Guarantees plan show displays Attempt: N where N > 1 after correction
11 Validation runner executes required and informational validations §Validation Abstraction agents validation attach + plan execute triggers validation; required failure blocks apply
12 Output validation is flexible — structural checks, not exact character matching §Testing Strategy Tests use structural assertions, not string equality
13 Test coverage ≥ 97% §Quality Gates nox -s coverage_report passes

Key Architectural Constraints

  • Decision persistence: All decisions use ULID identifiers; stored in v3_decisions table with context_snapshot JSONB column.
  • Correction atomicity: CorrectionService.execute_revert() MUST increment plan.attempt via PlanLifecycleService before recomputing the subtree.
  • Dry-run purity: generate_dry_run_report() MUST be used for --dry-run (not analyze_impact()); it preserves correction status at PENDING.
  • Invariant precedence: plan > action > project > global; non_overridable global invariants always win.
  • Validation read-only: Validations are always writes=False, checkpointable=False.

Definition of Done

  • All 13 deliverables verified by automated tests
  • nox passes with coverage ≥ 97%
  • No open P0/P1 bugs in v3.2.0 milestone
  • agents plan tree, plan explain, plan correct, invariant add/list all functional end-to-end

v3.3.0 — Corrections + Subplans + Checkpoints

Goal: Plans can spawn child plans (subplans) during execution. Subplans execute in parallel with configurable concurrency limits. Results are merged back using three-way merge strategies. Checkpointing enables rollback to previous plan states.

Spec Coverage: Subplan Architecture, Checkpoint and Rollback, Merge Strategies, Correction Model

Deliverables

# Deliverable Spec Reference Verifiable Check
1 Plans spawn child subplans during Execute via subplan_spawn decision type §Subplan Architecture plan tree shows subplan_spawn decisions with child plan IDs
2 Sequential subplan execution functional §Subplan Execution — Sequential Child plan completes before parent resumes
3 Parallel subplan execution with max_parallel limit §Subplan Execution — Parallel Multiple child plans execute concurrently up to max_parallel
4 Parent plan tracks all subplan statuses §Subplan Status Tracking plan show <parent_id> lists child plan statuses
5 Three-way merge combines non-conflicting changes §Merge Strategies — Three-Way Non-conflicting file edits from two subplans both appear in merged result
6 Merge conflicts surfaced to user for resolution §Merge Strategies — Conflict Resolution Conflicting changes produce CONFLICT markers; user prompted
7 Checkpoint creation during Execute §Checkpoint System plan show lists checkpoints; each has a ULID and timestamp
8 agents plan rollback <plan_id> --checkpoint <id> restores plan state §CLI Commands — plan rollback Plan state reverts to checkpoint; subsequent apply uses checkpoint state
9 Correction flow functional (plan correct --mode revert and --mode append) §Correction Model Both modes functional end-to-end (builds on v3.2.0)
10 Correction rejects decisions whose subtree includes applied child plans §Correction Safety Guarantees Attempting to correct such a decision returns error with explanation
11 Test coverage ≥ 97% §Quality Gates nox -s coverage_report passes

Key Architectural Constraints

  • Subplan identity: Child plans use ULID identifiers only (no namespaced name); referenced by plan ID in all operations.
  • Sandbox isolation: Each subplan operates in its own sandbox; parent sandbox merges results via MergeWorkflow.
  • Checkpoint granularity: Checkpoints capture the full plan state including decision tree, sandbox state hash, and resource versions.
  • Parallel limit enforcement: max_parallel is enforced by the PlanLifecycleService; excess subplans queue until a slot opens.
  • Merge strategy selection: Strategy selected per resource type; git-checkout uses git three-way merge; fs-mount uses file-level merge.

Definition of Done

  • All 11 deliverables verified by automated tests
  • nox passes with coverage ≥ 97%
  • No open P0/P1 bugs in v3.3.0 milestone
  • Subplan spawning, parallel execution, checkpoint/rollback, and merge all functional end-to-end

v3.4.0 — ACMS v1 + Context Scaling

Goal: The Advanced Context Management System v1 is operational. Projects with 10,000+ files can be indexed and queried. The context assembly pipeline produces scoped, budget-constrained context views for actors. Hot/warm/cold storage tiers manage context lifecycle.

Spec Coverage: ACMS Architecture, Context Assembly Pipeline, UKO Ontology, Hot/Warm/Cold Tiers

Deliverables

# Deliverable Spec Reference Verifiable Check
1 Context policies configurable with view-specific settings (strategize/execute/apply/default) §Context Policy Configuration agents project context set --view strategize persists and is retrievable
2 Budget enforcement: max_file_size and max_total_size constraints respected §Context Budget Enforcement Files exceeding max_file_size excluded; total context ≤ max_total_size
3 agents project context show displays current context policy §CLI Commands — project context show Command exits 0, shows all configured parameters
4 agents project context inspect runs context assembly and shows result §CLI Commands — project context inspect Returns assembled context fragments with UKO URIs
5 agents project context simulate shows budget simulation without executing §CLI Commands — project context simulate Returns token budget breakdown without modifying state
6 Context assembly pipeline produces scoped context output for actors §Context Assembly Pipeline Plan execution uses ACMS context; actor receives assembled fragments
7 Hot/warm/cold tier management functional §Context Storage Tiers Hot tier bounded by hot_max_tokens; overflow moves to warm tier
8 Projects with 10,000+ files index without timeout §ACMS Scalability Indexing a 10k-file project completes within 5 minutes
9 UKO ontology indexing for Python and common resource types §UKO — Technology-Specific Layer Python files indexed with uko-py: URIs; relationships inferred
10 Pluggable context strategies execute in parallel §Context Strategy Execution Multiple strategies run concurrently; results fused by FragmentFusionCoordinator
11 Skeleton compression for child plan context inheritance §Skeleton Compressor Child plans receive compressed parent context via skeleton_ratio budget
12 Test coverage ≥ 97% §Quality Gates nox -s coverage_report passes

Key Architectural Constraints

  • Pipeline composability: All 10 Context Assembly Pipeline slots are overridable at plan > project > global scope.
  • UKO URI stability: UKO URIs are stable identifiers; changing a file's content does not change its URI.
  • Strategy parallelism: ParallelStrategyExecutor runs strategies concurrently with circuit-breaker protection per strategy.
  • Budget packing: GreedyKnapsackPacker fills context up to token budget; fragments ordered by RelevanceCoherenceOrderer.
  • Tier eviction: Hot tier evicts to warm on overflow; warm evicts to cold on age threshold; cold is persistent storage.

Definition of Done

  • All 12 deliverables verified by automated tests
  • nox passes with coverage ≥ 97%
  • No open P0/P1 bugs in v3.4.0 milestone
  • ACMS pipeline functional end-to-end; 10k-file project indexes and queries successfully

v3.5.0 — Autonomy Hardening

Goal: The system can autonomously execute a large-scale task using hierarchical plan decomposition with 4+ levels of subplans, decision correction with selective subtree recomputation, parallel execution scaling to 10+ concurrent subplans, and validation-gated apply.

Spec Coverage: Automation Profiles, Safety Profiles, A2A Facade, Guard Enforcement, Event Queue

Deliverables

# Deliverable Spec Reference Verifiable Check
1 A2A facade session and plan lifecycle operations functional via CLI §A2A Local Facade agents session create and agents plan use route through A2A facade
2 Event queue publish/subscribe operational §Event Queue Plan lifecycle events published; subscribers receive events
3 Guard enforcement: denylist, budget caps, tool call limits §Guard Enforcement Plan exceeding max_cost_per_plan is blocked; denylist tools rejected
4 Automation profile resolution precedence correct (plan > action > global) §Automation Profiles — Precedence Plan-level profile overrides action-level; action overrides global
5 Safety profile enforcement: require_sandbox, require_checkpoints, allow_unsafe_tools §Safety Profiles Plans with require_sandbox=true fail if sandbox unavailable
6 Hierarchical decomposition creates 4+ levels of subplans §Subplan Architecture — Hierarchy plan tree shows 4+ nesting levels for complex tasks
7 Parallel execution scales to 10+ concurrent subplans §Subplan Execution — Parallel 10 subplans execute concurrently without deadlock or resource starvation
8 Decision correction recomputes only affected subtree §Correction Model — Selective Recomputation Unaffected decisions preserved; only downstream decisions recomputed
9 Validation-gated apply: required validations must pass before apply §Validation Abstraction — Apply Gating Apply blocked when required validation fails; informational validation does not block
10 A realistic porting task completes autonomously §Autonomy Acceptance End-to-end task (e.g., port a Python module) completes without human intervention
11 agents automation-profile add/list/show functional §CLI Commands — automation-profile Round-trip: add profile, list shows it, show displays details
12 agents plan guard shows active guards for a plan §CLI Commands — plan guard Command shows denylist, budget caps, tool limits
13 Test coverage ≥ 97% §Quality Gates nox -s coverage_report passes

Key Architectural Constraints

  • A2A facade: In local mode, A2aLocalFacade resolves all A2A operations in-process; no subprocess spawning for local operations.
  • Guard evaluation order: Denylist checked first (fast reject), then budget caps, then tool call limits.
  • Autonomy threshold: 0.0 = always automatic; 1.0 = always manual; eight built-in profiles from manual to full-auto.
  • Subtree recomputation: Only decisions with depends_on edges to the corrected decision are recomputed; others preserved.
  • Validation gating: required validations block apply; informational validations log but do not block.

Definition of Done

  • All 13 deliverables verified by automated tests
  • nox passes with coverage ≥ 97%
  • No open P0/P1 bugs in v3.5.0 milestone
  • Full autonomy acceptance flow with 4+ subplan levels completes successfully

v3.6.0 — Advanced Concepts & Deferred Features

Goal: Advanced concepts not needed for basic MVP. Extends beyond core MVP but does not require TUI (v3.7.0) or Server (v3.8.0). Includes advanced context strategies, additional LLM backends, additional resource types, A2A module rename, container tool execution, pluggable scope chain extensions, cost/session budgets, and E2E workflow specification tests.

Spec Coverage: LSP Integration, Resource Type Inheritance, Devcontainer Integration, Container Resource Types, Advanced Context Strategies, Provider Registry

Deliverables

# Deliverable Spec Reference Verifiable Check
1 ACP → A2A module rename and symbol standardization complete §A2A Protocol — Module Structure No acp references in public API; all imports use a2a namespace
2 LSP Registry: agents lsp add/list/show/remove functional §LSP Integration — Registry Round-trip: add LSP server, list shows it, show displays capabilities
3 LSP server bound to actor graph node via YAML configuration §LSP Integration — Actor Binding Actor with lsp_bindings activates LSP server on startup
4 LSP capabilities exposed as tools via LSPToolAdapter §LSP Integration — Tool Adapter agents actor run with LSP-bound actor can invoke lsp/diagnostics tool
5 LSP diagnostics injected into ACMS hot context automatically §LSP Integration — Context Enrichment Plan execution with LSP-bound actor includes diagnostic fragments in context
6 Resource type inheritance: inherits field functional §Resource Type Inheritance Subtype resource inherits parent type's tools and sandbox strategy
7 Polymorphic tool matching: tools bound to parent type work with subtypes §Resource Type Inheritance — Polymorphism Tool registered for container-instance works with devcontainer-instance
8 Devcontainer auto-discovery from .devcontainer/devcontainer.json §Devcontainer Integration agents resource add git-checkout <url> auto-discovers devcontainer
9 Devcontainer lazy activation: container built only when first needed §Devcontainer Integration — Lazy Activation Container not started until plan execution requires it
10 Container tool execution: tools run inside devcontainer §Container Resource Types — Execution Tool invocation with execution_environment set runs in container
11 Additional LLM provider backends registered via ProviderRegistry §Provider Registry agents actor add with non-OpenAI/Anthropic provider works
12 Cloud resource types stubbed with NotImplementedError for future implementation §Cloud Resource Types agents resource add cloud-* validates config but raises NotImplementedError for sandbox
13 Pluggable scope chain resolution extensions §Scope Chain Resolution Custom scope resolver registered and invoked during name resolution
14 Cost and session budget enforcement §Safety Profiles — Budget Caps Plan exceeding max_cost_per_plan or max_total_cost is blocked
15 E2E workflow specification tests covering all major user workflows §Testing Strategy — E2E Robot Framework E2E tests cover: plan lifecycle, correction, subplans, ACMS, invariants
16 Code review tool examples in examples/ directory §Workflow Examples examples/code-review/ contains working action YAML and actor YAML
17 Test coverage ≥ 97% §Quality Gates nox -s coverage_report passes

Key Architectural Constraints

  • ACP deprecation: The acp module is deprecated; all new code uses a2a. The acp module may remain as a compatibility shim but must not be used in new implementations.
  • LSP server lifecycle: LSP servers start when actor activates and stop when actor deactivates; managed by LSPRuntime.
  • Resource type inheritance depth: Maximum chain depth of 5 levels; circular inheritance rejected at registration time.
  • Devcontainer resource type: devcontainer-instance inherits from container-instance; uses lazy activation pattern.
  • Cloud resource stubs: Cloud SDK integration is planned for v3.8.0+; stubs validate configuration but raise NotImplementedError for sandbox provisioning.

Definition of Done

  • All 17 deliverables verified by automated tests
  • nox passes with coverage ≥ 97%
  • No open P0/P1 bugs in v3.6.0 milestone
  • LSP integration, resource type inheritance, devcontainer, and container execution all functional end-to-end

v3.7.0 — TUI Implementation

Goal: Implement the comprehensive Text User Interface (TUI) and all TUI-dependent features using Textual ≥ 1.0.

Spec Coverage: TUI Architecture, Persona System, Reference and Command System, TUI Materializer

Key ADRs: ADR-044, ADR-045, ADR-046

Deliverables

# Deliverable Spec Reference Verifiable Check
1 agents tui launches Textual-based MainScreen §TUI — MainScreen Command launches TUI; MainScreen renders without errors
2 3 sidebar states: hidden / visible / fullscreen §TUI — Sidebar States Keyboard shortcuts cycle through all 3 sidebar states
3 Persona system: YAML-based actor + args + scope + presets §Persona System agents persona add --config persona.yaml creates persona; persona selectable in TUI
4 Reference input system (@ mode for resources/files) §Reference and Command System — @ Mode Typing @ in input shows resource completion popup
5 Command input system (/ mode for commands) §Reference and Command System — / Mode Typing / in input shows command completion popup
6 Shell execution mode (! mode) §Reference and Command System — ! Mode Typing ! in input executes shell command; output shown in TUI
7 TuiMaterializer A2A integration layer functional §TUI Materializer TUI operations route through A2A facade; same operations as CLI
8 Session persistence in SQLite at ~/.local/state/cleveragents/tui.db §TUI — Session Persistence TUI state persists across restarts; previous sessions resumable
9 Dracula theme applied §TUI — Theme TUI renders with Dracula color scheme
10 Block cursor navigation §TUI — Navigation Block cursor moves through interactive elements
11 Notification system for async events §TUI — Notifications Plan phase changes trigger toast notifications
12 Multi-session tabs with independent A2A bindings §TUI — Multi-Session Multiple tabs open simultaneously; each has independent session
13 Settings screen functional §TUI — Settings Screen Settings screen opens; configuration changes persist
14 Session management screen functional §TUI — Session Management Screen Session list shows all sessions; sessions can be created/deleted
15 Content pruning for long conversations §TUI — Content Pruning Long conversations pruned to fit display; older messages summarized
16 Safety behaviors: confirmation dialogs for destructive operations §TUI — Safety Behaviors Destructive operations (delete, apply) show confirmation dialog
17 Loading states for async operations §TUI — Loading States Spinner shown during plan execution; progress updates streamed
18 agents tui web launches Textual Web mode §TUI — Web Mode Web mode accessible via browser at configured port
19 Test coverage ≥ 97% §Quality Gates nox -s coverage_report passes

Key Architectural Constraints

  • Textual version: Textual ≥ 1.0 required; no compatibility with pre-1.0 API.
  • TUI/CLI parity: All operations available in CLI must be available in TUI via TuiMaterializer; no TUI-exclusive features.
  • A2A routing: TUI operations route through A2aLocalFacade in local mode; same A2A operations as CLI.
  • Session database: TUI session state stored in ~/.local/state/cleveragents/tui.db (SQLite); separate from main ~/.local/share/cleveragents/data.db.
  • Persona scope: Personas are scoped to TUI only; they are not visible to CLI or server mode.

Definition of Done

  • All 19 deliverables verified by automated tests
  • nox passes with coverage ≥ 97%
  • No open P0/P1 bugs in v3.7.0 milestone
  • Full TUI functional end-to-end: launch, persona, reference/command input, multi-session, settings, session management

v3.8.0 — Server Implementation

Goal: Implement the CleverAgents server and all server-dependent capabilities using the A2A (Agent-to-Agent) protocol standard. The server enables multi-device collaboration, shared namespaces, team RBAC, and cloud-hosted actor execution via LangGraph Platform.

Spec Coverage: Server and Client Architecture, A2A Protocol, Authentication and Authorization

Key ADRs: ADR-047, ADR-048

!!! note "Sequencing" v3.8.0 has no deadline. Development effort should focus on M1M6 (v3.2.0v3.7.0) first. v3.8.0 work may proceed in parallel but is not on the critical path for local-mode production readiness.

Deliverables

# Deliverable Spec Reference Verifiable Check
1 A2A JSON-RPC 2.0 wire format: standard operations (message/send, message/stream, task lifecycle) §Server and Client Architecture — Server Presentation Layer curl to A2A endpoint returns valid JSON-RPC 2.0 response
2 _cleveragents/ extension method routing: plan lifecycle, registry CRUD, entity sync, namespace management §Server and Client Architecture — Extension Methods _cleveragents/plan/use creates a plan via server
3 A2A stdio transport (local mode — agent as subprocess) §A2A Protocol — Local Mode agents plan use routes through A2A stdio transport in local mode
4 A2A HTTP transport (server mode — single JSON-RPC endpoint) §A2A Protocol — Server Mode CLI connects to remote server via HTTP; all operations functional
5 LangGraph Platform RemoteGraph integration for server-side actor execution §Server Infrastructure — LangGraph Platform Strategy and execution actors run on LangGraph Platform via RemoteGraph
6 Server application structure: FastAPI + A2A SDK server §Server Application Structure Server starts, serves Agent Card at discovery endpoint
7 API token authentication: Authorization: Bearer <token> header §Authentication and Authorization — API Tokens Requests without valid token return 401; valid token accepted
8 Team RBAC: namespace-scoped read/write/admin permissions §Authentication and Authorization — Team RBAC User without write permission cannot create plans in restricted namespace
9 Entity sync via _cleveragents/sync/* extension methods §Entity Sync _cleveragents/sync/pull fetches server entities to local registry
10 Multi-device experience: shared namespaces accessible from any device §Multi-Device Experience Plan created on device A visible from device B via shared namespace
11 Shared namespaces: team members access same actors, actions, projects §Shared Namespaces Actor in shared namespace usable by all team members
12 PostgreSQL backend for server persistence §Server Infrastructure — Persistence Server stores plans, decisions, and registry data in PostgreSQL
13 Server deployment: Docker container with docker compose up §Server Deployment — Docker docker compose up starts server; health check endpoint returns 200
14 Server deployment: Kubernetes Helm chart §Server Deployment — Kubernetes helm install cleveragents ./charts/cleveragents deploys to cluster
15 agents server add registers a remote server in local config §CLI Commands — server Round-trip: add server, list shows it, CLI connects to it
16 agents server status shows server health and version §CLI Commands — server status Command exits 0, shows server version and uptime
17 Multi-turn interactions forwarded to client: _cleveragents/fs/*, _cleveragents/terminal/* §Server Presentation Layer — Multi-Turn Server-hosted actor can read client-local files via forwarded fs operations
18 Test coverage ≥ 97% §Quality Gates nox -s coverage_report passes

Key Architectural Constraints

  • A2A exclusivity: The server exposes only an A2A JSON-RPC 2.0 endpoint. No REST API, no GraphQL, no admin endpoint.
  • Shared domain/application layers: Server and client share identical Domain and Application layer packages. Zero behavioral drift between local and server modes.
  • LangGraph Platform: Actor graphs are deployed to LangGraph Platform as separate deployments; server invokes them via RemoteGraph. The server does NOT run actor graphs in-process.
  • PostgreSQL schema: Server uses the same schema as the SQLite client database, with PostgreSQL-specific extensions for multi-tenancy (namespace isolation, team membership).
  • Authentication boundary: All A2A requests require a valid API token. Tokens are scoped to a namespace; team RBAC controls per-namespace permissions.
  • Extension method routing: _cleveragents/ methods are routed to Application-layer services. Standard A2A operations (message/send, etc.) are routed to SessionWorkflow and actor execution.
  • Multi-turn forwarding: When a server-hosted actor needs client-local resources (files, terminal), it sends a _cleveragents/fs/* or _cleveragents/terminal/* request back to the connected client via the A2A task input-required mechanism.

Definition of Done

  • All 18 deliverables verified by automated tests
  • nox passes with coverage ≥ 97%
  • No open P0/P1 bugs in v3.8.0 milestone
  • Server starts, accepts A2A connections, executes plans via LangGraph Platform, and persists to PostgreSQL
  • CLI connects to remote server; all plan lifecycle operations functional in server mode

Cross-Milestone Quality Gates

These quality gates apply to every milestone and must pass before a milestone is considered complete:

Gate Requirement Command
Unit Tests All BDD Behave scenarios pass nox -s unit_tests
Integration Tests All Robot Framework tests pass nox -s integration_tests
Type Checking Zero Pyright errors (strict mode) nox -s typecheck
Linting Zero Ruff violations nox -s lint
Coverage ≥ 97% line coverage nox -s coverage_report
Security Scan No high/critical Bandit findings nox -s security_scan
Performance No ASV benchmark regressions > 10% nox -s benchmarks
Documentation All public APIs documented nox -s docs

Cross-Milestone Architectural Invariants

These architectural invariants must be maintained across all milestones:

  1. Spec-first: No feature is implemented without spec coverage. If implementation discovers a better approach, the spec is updated first via PR.
  2. Layer boundaries: Presentation → Application → Domain → Infrastructure. No reverse dependencies.
  3. Type safety: Full Pyright strict compliance. No # type: ignore suppressions.
  4. Fail-fast: All argument validation at entry points. No silent failures.
  5. ULID identifiers: Plans, decisions, resources, correction attempts, and validation attachments use ULIDs. Projects, actions, skills, and tools use namespaced names.
  6. Namespace format: [[server:]namespace/]name. local/ reserved for local-only items.
  7. A2A exclusivity: All client-server communication uses A2A. No REST API.
  8. BDD tests: All unit-level tests expressed as Behave/Gherkin scenarios. No xUnit-style tests.
  9. File size limit: No source file exceeds 500 lines. Split into modules if approaching limit.
  10. Atomic commits: One logical change per commit. No mixed concerns.