Skip to content

Commit bd7f413

Browse files
authored
Revert "refactor: four-layer architecture (connect / conv / biz)" (#351)
Reverts #349
1 parent 064ddc6 commit bd7f413

89 files changed

Lines changed: 3538 additions & 6572 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

AGENTS.md

Lines changed: 0 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -30,62 +30,6 @@ When changing a critical path, explicitly evaluate whether logs are needed for d
3030

3131
Production-visible logs must not include sensitive payloads such as prompts, tool input/output, file contents, command bodies, tokens, secrets, or raw provider requests/responses. If such payloads are needed for local debugging, they must be behind explicit development-only guards and never enabled by default.
3232

33-
### Four-Layer Architecture
34-
35-
The conversation/agent system is organized into four layers with strictly
36-
downward dependencies:
37-
38-
```
39-
biz layer aionui-team / aionui-cron / aionui-assistant
40-
41-
▼ uses IConversationService
42-
conv layer aionui-conversation
43-
44-
▼ uses IAgentConnector / IAgentConnectorFactory
45-
connect layer aionui-ai-agent
46-
47-
▼ spawns and talks to
48-
agent layer aionrs / acp engine processes
49-
```
50-
51-
**Rules:**
52-
- Biz layer talks to the conv layer ONLY through `IConversationService`.
53-
Never reach around it to call `IAgentConnectorFactory`, `IAgentConnector`,
54-
or any connect-layer type directly.
55-
- Conv layer talks to the connect layer ONLY through `IAgentConnector`
56-
and `IAgentConnectorFactory`. The conv layer is the sole owner of
57-
per-conversation runtime state (idle/running, last_activity).
58-
- Connect layer is a stateless adapter: each `IAgentConnector` impl
59-
spawns the corresponding agent process and translates events. It
60-
MUST NOT carry conversation-level state (status, history) — that's
61-
conv-layer territory.
62-
63-
**Why this exists:** the ELECTRON-1KB Sentry issue family
64-
(1FZ / 1J4 / 1MJ / 1EV / 1E9 / 1P0 / 1KB) was caused by state
65-
fragmentation across five holders (DB.status, AgentRuntime.status,
66-
OnceCell, ACP session state, OS subprocess) with no single source of
67-
truth. The race window between user cancel and the next user send
68-
produced 409 Conflict bursts that confused the UI and corrupted
69-
agent sessions. The four-layer split exists to prevent that class of
70-
bug at the type level.
71-
72-
**Enforcement:** `scripts/check_layer_deps.sh` (wired into `just push`)
73-
greps for forbidden cross-layer imports and Phase-5-deleted types.
74-
Each crate has a `crates/<crate>/AGENTS.md` with the per-layer hard
75-
rules. If you find yourself adding state to bridge two layers, stop —
76-
it belongs to one of them, not in between.
77-
78-
### Per-crate AGENTS.md
79-
80-
Each crate that participates in the four-layer architecture has its own
81-
AGENTS.md with hard rules:
82-
83-
- [`crates/aionui-conversation/AGENTS.md`](crates/aionui-conversation/AGENTS.md) — conv layer
84-
- [`crates/aionui-team/AGENTS.md`](crates/aionui-team/AGENTS.md) — biz layer (team)
85-
- [`crates/aionui-ai-agent/AGENTS.md`](crates/aionui-ai-agent/AGENTS.md) — connect layer
86-
87-
Read those before changing code in the corresponding crate.
88-
8933
## Architecture
9034

9135
> For detailed background and design decisions, see [ARCHITECTURE.md](./ARCHITECTURE.md).

ARCHITECTURE.md

Lines changed: 3 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ Each crate owns an independent business domain. They remain loosely coupled from
7676
| `aionui-office` | Office document handling (Excel, PPT, Word), preview, conversion |
7777
| `aionui-system` | System settings, provider management, version checking, model fetching |
7878
| `aionui-mcp` | MCP protocol integration, OAuth, multi-platform adapters |
79-
| `aionui-ai-agent` | Connect layer for the conversation/agent runtime: `IAgentConnector` trait, per-protocol connectors (ACP, aionrs, remote, nanobot, openclaw), `IAgentConnectorFactory` cache (see [Conversation / Agent Runtime](#conversation--agent-runtime)) |
79+
| `aionui-ai-agent` | Agent lifecycle management, worker task queues, ACP/auxiliary skills |
8080
| `aionui-extension` | Extension registry, hub management, skill discovery and installation |
8181
| `aionui-shell` | Shell command execution, speech-to-text |
8282
| `aionui-assistant` | Assistant configuration and management |
@@ -95,7 +95,7 @@ Composition → Domain → Capability → Foundation
9595
```
9696

9797
- ✅ Upper layers may depend on lower layers
98-
- ✅ Same-layer interaction through trait abstractions (e.g., the conv layer talks to the connect layer via `IAgentConnector` / `IAgentConnectorFactory`; biz crates talk to the conv layer via `IConversationService`)
98+
- ✅ Same-layer interaction through trait abstractions (e.g., conversation uses ai-agent capability via IWorkerTaskManager trait)
9999
- ❌ Lower layers must not depend on upper layers
100100
- ❌ Circular dependencies are forbidden
101101

@@ -347,7 +347,7 @@ pub struct AppServices {
347347
pub qr_token_store: Arc<QrTokenStore>,
348348
pub ws_manager: Arc<WebSocketManager>,
349349
pub event_bus: Arc<BroadcastEventBus>,
350-
pub connector_factory: Arc<dyn IAgentConnectorFactory>,
350+
pub worker_task_manager: Arc<dyn IWorkerTaskManager>,
351351
pub agent_registry: Arc<AgentRegistry>,
352352
pub conversation_repo: Arc<dyn IConversationRepository>,
353353
pub acp_session_sync: Arc<AcpSessionSyncService>,
@@ -682,92 +682,6 @@ Before adding a new crate, confirm:
682682
- [ ] Includes corresponding test files
683683
- [ ] WebSocket events follow `domain.camelCaseAction` naming convention
684684

685-
## Conversation / Agent Runtime
686-
687-
### Layers
688-
689-
| Layer | Crates | Responsibility |
690-
|---|---|---|
691-
| Biz | `aionui-team`, `aionui-cron`, `aionui-assistant` | Domain orchestration: teammates, schedules, mailboxes |
692-
| Conv | `aionui-conversation` | Per-conversation runtime state (`ConvActor`), turn serialization, event fan-out, idle scanner |
693-
| Connect | `aionui-ai-agent` | Stateless adapter: `IAgentConnector` per agent type (ACP, aionrs, remote, nanobot, openclaw), `IAgentConnectorFactory` cache |
694-
| Agent | `aionrs`, ACP engines, remote agents | Process-level concerns: subprocess lifecycle, stdin/stdout JSON-RPC |
695-
696-
### Single Source of Truth
697-
698-
`ConvActor` (in `aionui-conversation/src/conv_actor.rs`) holds the
699-
per-conversation runtime state machine:
700-
701-
```
702-
NeverOpened ──mark_idle──▶ Idle
703-
704-
705-
begin_turn ──▶ Running { msg_id }
706-
▲ │
707-
│ TurnHandle dropped
708-
│ │
709-
└──── wait_for_idle ◀
710-
```
711-
712-
The `Mutex<ConvState>` serializes turns. A `tokio::sync::Notify`
713-
(`idle_notify`) makes `cancel()` block until the running turn has fully
714-
released its slot — `wait_for_idle()` parks here, and the `TurnHandle`
715-
RAII guard fires it on drop. Together they close the cancel→send race
716-
that motivated the refactor.
717-
718-
### Trait surface
719-
720-
- `IConversationService` (in `aionui-conversation/src/conv_service_trait.rs`)
721-
— biz-layer entry point. Methods: `create`, `delete`, `get`, `list`,
722-
`warmup`, `send`, `cancel`, `cancel_idle`, `status`, `subscribe`,
723-
`collect_idle`. Phase 2.
724-
- `IAgentConnector` (in `aionui-ai-agent/src/connector/trait_def.rs`)
725-
— connect-layer trait. Methods include `run_turn`,
726-
`cancel_current_turn`, `subscribe`, plus the additive task-manager
727-
surface (`status`, `send_message`, `cancel`, `kill`, `confirm`, mode
728-
/ model getters/setters, …) that survived the Phase 5 reshape. Phase 1.
729-
- `IAgentConnectorFactory` (in `aionui-ai-agent/src/connector_factory.rs`)
730-
— connect-layer cache. `build_or_get(opts) -> Arc<dyn IAgentConnector>`,
731-
plus `get`, `drop_connector`, `clear`, `active_count`. Single-flight
732-
per `conversation_id` via `OnceCell`. Phase 5.
733-
734-
### Historical context
735-
736-
The ELECTRON-1KB Sentry issue family (1FZ, 1J4, 1MJ, 1EV, 1E9, 1P0,
737-
1KB) was the trigger. State was fragmented across five holders
738-
(`conversations.status` in DB, `AgentRuntime.status`, the ACP session
739-
`OnceCell`, ACP protocol-level state, and the OS subprocess). When
740-
users cancelled a turn and immediately sent the next message, races
741-
between those holders produced 409 Conflict, lost turns, and corrupted
742-
ACP sessions.
743-
744-
The refactor (May 2026, Phases 1–5) replaced those five holders with
745-
two: `ConvActor::state` (runtime) and `ConversationRow` (history). The
746-
`#[deprecated] ConversationRow.status` column survives one more
747-
deprecation window for safety; Phase 6 schedules its removal.
748-
749-
### What lives where
750-
751-
| Concern | Crate / type |
752-
|---|---|
753-
| Is conversation X running? | `IConversationService::status(id) -> ConversationStatus` |
754-
| Cancel turn safely (user-initiated) | `IConversationService::cancel(user_id, id)` |
755-
| Cancel turn from a background task | `IConversationService::cancel_idle(id)` |
756-
| Stream of turn events | `IConversationService::subscribe(id) -> broadcast::Receiver<ConversationEvent>` |
757-
| Collect stale conversations for cleanup | `IConversationService::collect_idle(threshold_ms)` |
758-
| Idle-conversation scanner | `aionui-conversation::start_idle_scanner` (Phase 5) |
759-
| Cron continuation chaining | `aionui-cron::CronContinuationOrchestrator` (Phase 4) |
760-
| Agent process lifecycle | `aionui-ai-agent` connectors |
761-
762-
### Layer-dependency check
763-
764-
`scripts/check_layer_deps.sh` (wired into `just push`) greps for
765-
forbidden cross-layer imports and Phase-5-deleted symbols
766-
(`IWorkerTaskManager`, `WorkerTaskManagerImpl`, `AgentInstance`,
767-
`IMockAgent`, `aionui_common::ConversationStatus`). Each crate
768-
participating in this stack also has its own `crates/<crate>/AGENTS.md`
769-
with the per-layer hard rules.
770-
771685
## Runtime Infrastructure
772686

773687
### Bundled bun Runtime

Cargo.lock

Lines changed: 0 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/aionui-ai-agent/AGENTS.md

Lines changed: 0 additions & 83 deletions
This file was deleted.

crates/aionui-ai-agent/Cargo.toml

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -61,10 +61,3 @@ aionui-db.workspace = true
6161
[[test]]
6262
name = "agent_types_integration"
6363
required-features = ["test-support"]
64-
65-
# Integration tests for the connect-layer cancel/run_turn invariants
66-
# (Phase 1 of the conversation refactor). Reaches into test-only
67-
# helpers on `AionrsAgentManager`, so requires `test-support`.
68-
[[test]]
69-
name = "connector_cancel_invariants"
70-
required-features = ["test-support"]

crates/aionui-ai-agent/src/agent_runtime.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,7 @@ use std::sync::{Arc, RwLock};
1414

1515
use tokio::sync::broadcast;
1616

17-
use aionui_api_types::ConversationStatus;
18-
use aionui_common::{TimestampMs, now_ms};
17+
use aionui_common::{ConversationStatus, TimestampMs, now_ms};
1918

2019
use crate::protocol::events::{AgentStreamEvent, ErrorEventData, FinishEventData};
2120

0 commit comments

Comments
 (0)