docs: architecture — add Provider Registry and TUI Materializer spec sections; fix anchor mismatches #7839
+335
-16
@@ -18383,7 +18383,7 @@ Every plan should have:
|
||||
| `completed_at` | Timestamp? | When the plan reached a terminal state |
|
||||
| `created_by` | Identity | User or session identity |
|
||||
|
||||
#### Plan Hierarchy and Parallelism
|
||||
#### Plan Hierarchy and Parallelism { #subplan-architecture }
|
||||
|
||||
!!! adr "Architecture Decision"
|
||||
Plan hierarchy, child plan spawning, and parallel execution are covered in [ADR-006: Plan Lifecycle](adr/ADR-006-plan-lifecycle.md).
|
||||
@@ -18528,7 +18528,7 @@ The `local/plan-tools` skill references this tool (and others) by name:
|
||||
!!! 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
|
||||
##### Child Plan Result Merging { #merge-strategies }
|
||||
|
||||
The way child plan results are merged depends on the resource type:
|
||||
|
||||
@@ -18537,7 +18537,7 @@ The way child plan results are merged depends on the resource type:
|
||||
* **Other resources**: Pluggable merge strategies based on resource type
|
||||
* **Non-mergeable resources**: May require sequential execution only
|
||||
|
||||
#### The Plan "Decision Tree" and Visualization
|
||||
#### The Plan "Decision Tree" and Visualization { #decision-tree-and-correction }
|
||||
|
||||
!!! 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](adr/ADR-034-decision-tree-versioning-and-history.md).
|
||||
@@ -19393,7 +19393,7 @@ A record of checkpoints:
|
||||
* resources affected
|
||||
* rollback instructions / metadata
|
||||
|
||||
#### Checkpointing in Execute (Core Safety Mechanism)
|
||||
#### Checkpointing in Execute (Core Safety Mechanism) { #checkpoint-and-rollback }
|
||||
|
||||
!!! adr "Architecture Decision"
|
||||
Checkpointing, rollback, and transaction safety are defined in [ADR-015: Sandbox and Checkpoint](adr/ADR-015-sandbox-and-checkpoint.md).
|
||||
@@ -19707,7 +19707,7 @@ The `local/semantic-validators` skill references this tool by name:
|
||||
- local/validate-api-compat
|
||||
</code></pre></div>
|
||||
|
||||
#### Layer 3: Invariant Enforcement
|
||||
#### Layer 3: Invariant Enforcement { #invariant-system }
|
||||
|
||||
!!! adr "Architecture Decision"
|
||||
Invariant scoping, enforcement mechanisms, and inheritance rules are defined in [ADR-016: Invariant System](adr/ADR-016-invariant-system.md).
|
||||
@@ -22412,7 +22412,7 @@ Checkpointing is easier when tool scope is constrained (e.g., "only files within
|
||||
|
||||
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
|
||||
### Validation { #validation-abstraction }
|
||||
|
||||
!!! adr "Architecture Decision"
|
||||
The validation system, gate types, and validation lifecycle are defined in [ADR-013: Validation Abstraction](adr/ADR-013-validation-abstraction.md).
|
||||
@@ -28523,7 +28523,7 @@ A `SafetyProfile` may also be attached directly to an `Action` (via the `safety_
|
||||
|
||||
**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
|
||||
#### Automation Guard Sub-Model { #guard-enforcement }
|
||||
|
||||
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.
|
||||
|
||||
@@ -28735,7 +28735,7 @@ Runtime guardrails to prevent runaway resource consumption:
|
||||
|
||||
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)
|
||||
### Correcting Plans (Core Feature) { #correction-model }
|
||||
|
||||
!!! adr "Architecture Decision"
|
||||
Plan correction, decision revision, and the edit-replay model are defined in [ADR-007: Decision Tree and Correction](adr/ADR-007-decision-tree-and-correction.md).
|
||||
@@ -29219,6 +29219,145 @@ Hidden ──shift+tab──► Visible ──shift+tab──► Fullscreen
|
||||
```
|
||||
|
||||
|
||||
### TUI Materializer { #tui-materializer }
|
||||
|
||||
!!! adr "Architecture Decision"
|
||||
The `TuiMaterializer` integration layer, its role in the Output Rendering Framework, and its A2A event subscription model are defined in [ADR-044: TUI Architecture and Framework](adr/ADR-044-tui-architecture-and-framework.md) and [ADR-021: CLI and Output Rendering](adr/ADR-021-cli-and-output-rendering.md).
|
||||
|
||||
The **`TuiMaterializer`** is the bridge between the Output Rendering Framework (ADR-021) and the Textual widget tree. It implements the `Materializer` protocol defined in the Output Rendering Framework, receiving `ElementHandle` events from all CLI command producers and translating them into Textual widget operations — enabling every CLI command to render its output in the TUI without modification.
|
||||
|
||||
#### Responsibility
|
||||
|
||||
The `TuiMaterializer` owns:
|
||||
|
||||
- **ElementHandle → Textual widget mapping** — translating `ElementHandle` events (text, table, tree, progress, code block, etc.) to the appropriate Textual widget operations
|
||||
- **A2A event subscription** — subscribing to the A2A event bus for real-time plan lifecycle events (phase changes, decision recording, tool invocations, cost updates)
|
||||
- **Conversation stream management** — appending new `ConversationBlock` widgets to the active session's `ConversationStream`
|
||||
- **Sidebar panel updates** — pushing plan status and project updates to the sidebar panels in response to A2A events
|
||||
- **Notification dispatch** — routing plan phase change events to the `NotificationSystem` for toast display
|
||||
|
||||
The `TuiMaterializer` does **NOT** own:
|
||||
- Session management (owned by `SessionManager`)
|
||||
- A2A communication (owned by `A2aLocalFacade`)
|
||||
- Widget layout and styling (owned by the Textual widget classes themselves)
|
||||
- Persona management (owned by `PersonaManager`)
|
||||
|
||||
#### Public Interface
|
||||
|
||||
```python
|
||||
from typing import Protocol
|
||||
from cleveragents.rendering import ElementHandle, MaterializerProtocol
|
||||
from cleveragents.a2a import A2aEvent
|
||||
|
||||
|
||||
class TuiMaterializer:
|
||||
"""Bridges the Output Rendering Framework to the Textual widget tree.
|
||||
|
||||
Implements MaterializerProtocol so all CLI command producers can render
|
||||
in the TUI without modification.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
conversation_stream: "ConversationStream",
|
||||
sidebar: "Sidebar",
|
||||
notification_system: "NotificationSystem",
|
||||
a2a_facade: "A2aLocalFacade",
|
||||
) -> None:
|
||||
"""Inject dependencies. Called once per TUI session."""
|
||||
...
|
||||
|
||||
# ── MaterializerProtocol implementation ──────────────────────────────
|
||||
|
||||
def begin_block(self, block_type: str, metadata: dict[str, object]) -> str:
|
||||
"""Start a new ConversationBlock. Returns block_id for subsequent calls."""
|
||||
...
|
||||
|
||||
def append_element(self, block_id: str, element: ElementHandle) -> None:
|
||||
"""Append an ElementHandle to the active ConversationBlock."""
|
||||
...
|
||||
|
||||
def end_block(self, block_id: str) -> None:
|
||||
"""Finalize a ConversationBlock (mark as complete, stop spinner)."""
|
||||
...
|
||||
|
||||
def flush(self) -> None:
|
||||
"""Force any buffered elements to be rendered immediately."""
|
||||
...
|
||||
|
||||
# ── A2A event subscription ────────────────────────────────────────────
|
||||
|
||||
def subscribe_to_session(self, session_id: str) -> None:
|
||||
"""Subscribe to A2A events for the given session.
|
||||
Registers handlers for: plan.phase_changed, plan.decision_recorded,
|
||||
plan.tool_invoked, plan.cost_updated, plan.subplan_spawned."""
|
||||
...
|
||||
|
||||
def unsubscribe_from_session(self, session_id: str) -> None:
|
||||
"""Unsubscribe from A2A events for the given session."""
|
||||
...
|
||||
|
||||
def on_a2a_event(self, event: A2aEvent) -> None:
|
||||
"""Dispatch an incoming A2A event to the appropriate handler."""
|
||||
...
|
||||
```
|
||||
|
||||
#### ElementHandle → Widget Mapping
|
||||
|
||||
Each `ElementHandle` type produced by the Output Rendering Framework maps to a specific Textual widget or operation:
|
||||
|
||||
| ElementHandle Type | Textual Widget / Operation | Notes |
|
||||
|---|---|---|
|
||||
| `TextElement` | `Static` widget with Markup | Supports Rich markup for colors/bold |
|
||||
| `TableElement` | `DataTable` widget | Columns auto-sized; sortable |
|
||||
| `TreeElement` | `Tree` widget | Collapsible nodes; cursor navigation |
|
||||
| `ProgressElement` | `ProgressBar` widget | Updated in-place via `block_id` |
|
||||
| `CodeBlockElement` | `Static` with `Syntax` | Syntax highlighting via Rich |
|
||||
| `ErrorElement` | `Static` with error style | Red border; error icon prefix |
|
||||
| `WarningElement` | `Static` with warning style | Yellow border; warning icon prefix |
|
||||
| `SpinnerElement` | `LoadingIndicator` widget | Shown during async operations |
|
||||
| `DiffElement` | `Static` with diff Syntax | Green/red line coloring |
|
||||
| `JsonElement` | `Static` with JSON Syntax | Pretty-printed with syntax highlighting |
|
||||
|
||||
#### A2A Event Subscriptions
|
||||
|
||||
The `TuiMaterializer` subscribes to the following A2A event types for each active session:
|
||||
|
||||
| A2A Event | Handler Action |
|
||||
|---|---|
|
||||
| `plan.phase_changed` | Update plan card in sidebar; dispatch toast notification via `NotificationSystem` |
|
||||
| `plan.decision_recorded` | Append decision summary to active `ConversationBlock` |
|
||||
| `plan.tool_invoked` | Append tool invocation block (name, status, duration) to `ConversationBlock` |
|
||||
| `plan.tool_completed` | Update tool invocation block with result/error |
|
||||
| `plan.cost_updated` | Update cost display in `PersonaBar` |
|
||||
| `plan.subplan_spawned` | Add child plan entry to sidebar plan card; update depth indicator |
|
||||
| `plan.subplan_completed` | Update child plan status in sidebar |
|
||||
| `plan.checkpoint_created` | Append checkpoint notification to `ConversationBlock` |
|
||||
| `session.actor_thinking` | Show/hide `ThinkingIndicator` in active `ConversationBlock` |
|
||||
| `session.message_received` | Append actor message block to `ConversationStream` |
|
||||
|
||||
#### Integration with CLI Command Producers
|
||||
|
||||
All CLI command producers (e.g., `PlanTreeCommand`, `PlanExplainCommand`, `InvariantListCommand`) use the Output Rendering Framework's `Renderer` abstraction, which accepts any `MaterializerProtocol` implementation. When running in TUI mode, the `TuiMaterializer` is injected in place of the `CliMaterializer`:
|
||||
|
||||
```
|
||||
CLI mode: Command → Renderer(CliMaterializer) → stdout
|
||||
TUI mode: Command → Renderer(TuiMaterializer) → ConversationStream widgets
|
||||
```
|
||||
|
||||
This ensures **TUI/CLI parity**: every operation available in the CLI is available in the TUI, and the same command logic produces output in both surfaces without duplication.
|
||||
|
||||
#### Session Binding
|
||||
|
||||
Each TUI session has its own `TuiMaterializer` instance, bound to that session's `ConversationStream` and A2A session ID. When the user switches tabs, the active `TuiMaterializer` changes accordingly. The `SessionManager` is responsible for creating and destroying `TuiMaterializer` instances as sessions are opened and closed.
|
||||
|
||||
#### Dependencies
|
||||
|
||||
- **Depends on**: `A2aLocalFacade` (for event subscriptions), `ConversationStream` (for widget operations), `Sidebar` (for plan/project updates), `NotificationSystem` (for toast dispatch)
|
||||
- **Depended on by**: `SessionManager` (creates/destroys instances), all CLI command producers (via `MaterializerProtocol`)
|
||||
|
||||
---
|
||||
|
||||
### Persona System
|
||||
|
||||
!!! adr "Architecture Decision"
|
||||
@@ -45971,7 +46110,7 @@ logger.info(
|
||||
|
||||
**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
|
||||
#### Event System { #event-queue }
|
||||
|
||||
CleverAgents emits domain events for every significant state change. Events flow through two channels:
|
||||
|
||||
@@ -46455,6 +46594,186 @@ Actors can be extended through:
|
||||
) -> BaseEmbeddings: ...
|
||||
</code></pre></div>
|
||||
|
||||
#### Provider Registry { #provider-registry }
|
||||
|
||||
!!! adr "Architecture Decision"
|
||||
The provider extension model, `AIProviderInterface` protocol, and `ProviderRegistry` auto-discovery mechanism are grounded in the actor architecture defined in [ADR-010: Actor and Agent Architecture](adr/ADR-010-actor-and-agent-architecture.md) and the extensibility model in [ADR-024: Configuration System](adr/ADR-024-configuration-system.md).
|
||||
|
||||
The **`ProviderRegistry`** is the central registry for all LLM provider implementations. It manages the lifecycle of provider instances, handles auto-discovery of installed `langchain-*` packages, and resolves provider names to `BaseChatModel` and `BaseEmbeddings` instances at actor activation time.
|
||||
|
||||
##### Responsibility
|
||||
|
||||
The `ProviderRegistry` owns:
|
||||
|
||||
- **Provider registration** — mapping provider names to `AIProviderInterface` implementations
|
||||
- **Auto-discovery** — scanning installed packages for `langchain-*` naming convention
|
||||
- **Instance creation** — delegating to the appropriate provider to create `BaseChatModel` and `BaseEmbeddings` instances
|
||||
- **Capability querying** — exposing what each provider supports (streaming, function calling, vision, embeddings)
|
||||
|
||||
The `ProviderRegistry` does **NOT** own:
|
||||
- Actor lifecycle management (owned by `ActorRegistry`)
|
||||
- Model selection logic (owned by the actor's configuration)
|
||||
- Cost tracking (owned by `CostTracker` in the Application layer)
|
||||
|
||||
##### Public Interface
|
||||
|
||||
```python
|
||||
from typing import Protocol
|
||||
from langchain_core.language_models import BaseChatModel
|
||||
from langchain_core.embeddings import BaseEmbeddings
|
||||
|
||||
|
||||
class ProviderCapabilities(Protocol):
|
||||
"""Capabilities advertised by a provider."""
|
||||
supports_streaming: bool
|
||||
supports_function_calling: bool
|
||||
supports_vision: bool
|
||||
supports_embeddings: bool
|
||||
supports_thinking: bool # Extended reasoning / thinking tokens
|
||||
context_window_tokens: int # Maximum context window size
|
||||
|
||||
|
||||
class AIProviderInterface(Protocol):
|
||||
"""Protocol that all LLM provider implementations must satisfy."""
|
||||
|
||||
@property
|
||||
def provider_name(self) -> str:
|
||||
"""Canonical provider name (e.g., 'anthropic', 'openai', 'google')."""
|
||||
...
|
||||
|
||||
@property
|
||||
def capabilities(self) -> ProviderCapabilities:
|
||||
"""Capabilities supported by this provider."""
|
||||
...
|
||||
|
||||
def create_chat_model(
|
||||
self,
|
||||
model: str,
|
||||
temperature: float = 0.7,
|
||||
**kwargs: object,
|
||||
) -> BaseChatModel:
|
||||
"""Create a chat model instance for the given model name."""
|
||||
...
|
||||
|
||||
def create_embedding_model(
|
||||
self,
|
||||
model: str,
|
||||
**kwargs: object,
|
||||
) -> BaseEmbeddings:
|
||||
"""Create an embedding model instance for the given model name."""
|
||||
...
|
||||
|
||||
|
||||
class ProviderRegistry:
|
||||
"""Central registry for LLM provider implementations."""
|
||||
|
||||
def register(self, provider: AIProviderInterface) -> None:
|
||||
"""Register a provider implementation. Raises if name already registered."""
|
||||
...
|
||||
|
||||
def get(self, provider_name: str) -> AIProviderInterface:
|
||||
"""Retrieve a registered provider by name. Raises ProviderNotFoundError if absent."""
|
||||
...
|
||||
|
||||
def list_providers(self) -> list[str]:
|
||||
"""Return all registered provider names."""
|
||||
...
|
||||
|
||||
def get_capabilities(self, provider_name: str) -> ProviderCapabilities:
|
||||
"""Return capabilities for a registered provider."""
|
||||
...
|
||||
|
||||
def discover(self) -> list[str]:
|
||||
"""Scan installed packages for langchain-* providers and auto-register them.
|
||||
Returns list of newly discovered provider names."""
|
||||
...
|
||||
```
|
||||
|
||||
##### Auto-Discovery Mechanism
|
||||
|
||||
The `ProviderRegistry` uses Python package introspection to discover providers at startup:
|
||||
|
||||
1. **Package scan**: Enumerate all installed packages matching `langchain-*` (e.g., `langchain-anthropic`, `langchain-openai`, `langchain-google-genai`).
|
||||
2. **Entry point lookup**: Check each package for a `cleveragents.providers` entry point group. If present, load the declared `AIProviderInterface` implementation.
|
||||
3. **Convention fallback**: If no entry point is declared, attempt to import `<package_module>.CleverAgentsProvider` (e.g., `langchain_anthropic.CleverAgentsProvider`). This allows third-party packages to opt in without modifying their package metadata.
|
||||
4. **Built-in providers**: The following providers are always registered regardless of installed packages:
|
||||
- `anthropic` — via `langchain-anthropic`
|
||||
- `openai` — via `langchain-openai`
|
||||
- `google` — via `langchain-google-genai`
|
||||
|
||||
##### Registration Flow
|
||||
|
||||
```
|
||||
Application startup
|
||||
└─► ProviderRegistry.discover()
|
||||
├─► Scan installed packages for langchain-*
|
||||
├─► Load entry points from cleveragents.providers group
|
||||
├─► Fallback: import <module>.CleverAgentsProvider
|
||||
└─► Register each discovered provider
|
||||
|
||||
Actor activation (ActorRegistry.activate)
|
||||
└─► ProviderRegistry.get(actor.provider)
|
||||
└─► provider.create_chat_model(actor.model, temperature=actor.temperature)
|
||||
```
|
||||
|
||||
##### Adding a Custom Provider
|
||||
|
||||
To add a new LLM provider (e.g., a local Ollama instance):
|
||||
|
||||
```python
|
||||
# my_providers/ollama_provider.py
|
||||
from langchain_ollama import ChatOllama, OllamaEmbeddings
|
||||
from cleveragents.providers import AIProviderInterface, ProviderCapabilities
|
||||
|
||||
|
||||
class OllamaProvider:
|
||||
provider_name = "ollama"
|
||||
capabilities = ProviderCapabilities(
|
||||
supports_streaming=True,
|
||||
supports_function_calling=True,
|
||||
supports_vision=False,
|
||||
supports_embeddings=True,
|
||||
supports_thinking=False,
|
||||
context_window_tokens=128_000,
|
||||
)
|
||||
|
||||
def create_chat_model(self, model: str, temperature: float = 0.7, **kwargs):
|
||||
return ChatOllama(model=model, temperature=temperature, **kwargs)
|
||||
|
||||
def create_embedding_model(self, model: str, **kwargs):
|
||||
return OllamaEmbeddings(model=model, **kwargs)
|
||||
```
|
||||
|
||||
Register via `pyproject.toml` entry point:
|
||||
|
||||
```toml
|
||||
[project.entry-points."cleveragents.providers"]
|
||||
ollama = "my_providers.ollama_provider:OllamaProvider"
|
||||
```
|
||||
|
||||
Or register programmatically at startup:
|
||||
|
||||
```python
|
||||
from cleveragents.providers import ProviderRegistry
|
||||
from my_providers.ollama_provider import OllamaProvider
|
||||
|
||||
registry = ProviderRegistry.instance()
|
||||
registry.register(OllamaProvider())
|
||||
```
|
||||
|
||||
##### Error Handling
|
||||
|
||||
- **`ProviderNotFoundError`**: Raised by `ProviderRegistry.get()` when the requested provider name is not registered. Message includes the provider name and a list of registered providers.
|
||||
- **`ProviderRegistrationError`**: Raised by `ProviderRegistry.register()` when a provider with the same name is already registered.
|
||||
- **`ModelCreationError`**: Raised by `AIProviderInterface.create_chat_model()` when the model name is not supported by the provider. Wraps the underlying LangChain exception with additional context.
|
||||
|
||||
##### Dependencies
|
||||
|
||||
- **Depends on**: None (leaf node in the dependency graph; no other CleverAgents modules)
|
||||
- **Depended on by**: `ActorRegistry` (for actor activation), `ActorCompiler` (for graph node instantiation)
|
||||
|
||||
---
|
||||
|
||||
#### Custom Resource Types
|
||||
|
||||
New resource types extend CleverAgents to manage any kind of resource:
|
||||
@@ -46742,15 +47061,15 @@ The following table shows which Protocol each pipeline slot implements and what
|
||||
|
||||
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.7.0 — the production-ready target.
|
||||
|
||||
!!! note "Milestone Status (as of 2026-04-08)"
|
||||
!!! note "Milestone Status (as of 2026-04-12)"
|
||||
- **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 (85 open / 235 closed)
|
||||
- **v3.3.0** 🔄 IN PROGRESS — Corrections + Subplans + Checkpoints (65 open / 108 closed)
|
||||
- **v3.4.0** 🔄 IN PROGRESS — ACMS v1 + Context Scaling (55 open / 130 closed)
|
||||
- **v3.5.0** 🔄 IN PROGRESS — Autonomy Hardening (486 open / 188 closed)
|
||||
- **v3.6.0** 🔄 IN PROGRESS — Advanced Concepts & Deferred Features (162 open / 150 closed)
|
||||
- **v3.7.0** 🔄 IN PROGRESS — TUI Implementation (461 open / 403 closed)
|
||||
- **v3.2.0** 🔄 IN PROGRESS — Decisions + Validations + Invariants (666 open / 258 closed)
|
||||
- **v3.3.0** 🔄 IN PROGRESS — Corrections + Subplans + Checkpoints (122 open / 108 closed)
|
||||
- **v3.4.0** 🔄 IN PROGRESS — ACMS v1 + Context Scaling (204 open / 137 closed)
|
||||
- **v3.5.0** 🔄 IN PROGRESS — Autonomy Hardening (977 open / 201 closed)
|
||||
- **v3.6.0** 🔄 IN PROGRESS — Advanced Concepts & Deferred Features (280 open / 152 closed)
|
||||
- **v3.7.0** 🔄 IN PROGRESS — TUI Implementation (526 open / 427 closed)
|
||||
|
||||
---
|
||||
|
||||
|
||||
Reference in New Issue
Block a user