Magnitude Package Layering Hierarchy Explained: Why Clients Use `client-common → sdk` Instead of Direct `acn` Imports
Magnitude enforces a strict four-layer package hierarchy (clients → client-common → sdk → acn) to isolate UI code from server-side daemon internals, preventing bundle bloat and runtime coupling.
The magnitudedev/magnitude repository implements a meticulously designed package layering system that governs how code flows through its TypeScript monorepo. This architecture prevents client applications from directly importing the agent runtime (agent) or daemon (acn), instead routing all communication through a controlled chain of abstractions. Understanding this hierarchy is essential for anyone extending Magnitude's CLI, web interface, or agent capabilities.
The Four-Layer Package Architecture
Magnitude's monorepo is organized into distinct layers with strictly enforced import boundaries. As documented in AGENTS.md at the repository root, each layer serves a specific purpose and exposes only what the layer above requires.
| Layer | Package | Purpose | Import Rules |
|---|---|---|---|
| Clients | @magnitudedev/cli, @magnitudedev/web |
UI entry points | Import only from client-common and sdk |
| Client-Common | @magnitudedev/client-common |
Shared state, hooks, display sync | Owns Query/Mutation/Subscription definitions over SDK methods |
| SDK | @magnitudedev/sdk |
Portable RPC client | Re-exports pure protocol contracts from acn-protocol; contains no SQLite or process logic |
| ACN (daemon) | @magnitudedev/acn |
Server-side agent runtime | Implements sessions, file operations, display streams; imported only by privileged composition roots |
This structure is not merely conventional—it is mechanically enforced through the codebase's module boundaries and build configuration.
Why Clients Must Import Through client-common and sdk
Direct imports from acn or agent into client code violate Magnitude's architectural contract. The AGENTS.md specification explicitly prohibits this pattern for three critical reasons.
Dependency Isolation
The acn package contains heavy runtime dependencies including SQLite, process supervision, and OS-service code. The agent package implements the actual runtime that executes tool calls and manages browser automation. Pulling either into a client bundle would:
- Dramatically increase bundle size with server-only dependencies
- Make the client unusable in browser environments where these native dependencies cannot execute
- Tightly couple UI code to a specific host runtime
The SDK layer solves this by providing a pure, portable RPC client that knows only about wire contracts, not implementation details.
Stability Through Protocol Contracts
The sdk re-exports types from @magnitudedev/acn-protocol—a stable, versioned contract separate from the daemon's internal implementation. This ensures that:
- Client code depends only on protocol guarantees, not implementation details
- Version checks and replay policies (
replaySafe,atMostOnce) are enforced at the SDK boundary - Daemon upgrades don't break existing clients as long as protocol compatibility is maintained
Centralized State Management
The client-common layer owns all first-party Effect Query/Mutation/Subscription definitions. This centralization provides:
- A single source of truth for UI state derived from agent operations
- Consistent error handling and loading states across CLI and web interfaces
- Clean separation between connection-scoped concerns and presentation logic
Correct vs. Incorrect Import Patterns
The codebase demonstrates the proper layering through concrete examples in packages/client-common/src/operations/agents.ts and packages/sdk/src/index.ts.
Correct: Client-Layer Import
// CLI or web client code
import { useAgentClient } from '@magnitudedev/client-common';
import { listAgents } from '@magnitudedev/sdk';
// Hook operates over the SDK without knowing daemon internals
const { data: agents } = useAgentClient(listAgents);
This maintains the layering boundary: the client knows only about React hooks and SDK methods.
Correct: Client-Common Query Definition
// packages/client-common/src/operations/agents.ts
import { Rpc } from '@magnitudedev/sdk';
import { AgentQuery } from '@magnitudedev/acn-protocol';
// High-level query that UI can consume
// Encapsulates SDK interaction in a reusable Effect Query
export const fetchAgents = Rpc.make(AgentQuery.getAll);
client-common bridges the gap: it uses SDK primitives (Rpc.make) with protocol contracts (AgentQuery) to build UI-friendly abstractions.
Correct: SDK RPC Wrapper
// packages/sdk/src/index.ts
import { AgentQuery } from '@magnitudedev/acn-protocol';
import { rpc } from '@magnitudedev/sdk/internal';
// Pure function over protocol contract—no daemon implementation leaked
export const listAgents = () => rpc(AgentQuery.list);
The SDK remains agnostic to how AgentQuery.list is ultimately implemented by the daemon.
Incorrect: Direct Daemon Import
// ❌ NEVER import directly in client code
import { startSession } from '@magnitudedev/acn';
import { ToolRuntime } from '@magnitudedev/agent';
// This breaks layering, bloats bundle, and creates runtime coupling
These imports are reserved exclusively for privileged composition roots: the CLI bootstrap, desktop main process, and development server.
Where Layering Is Enforced in Source
The architectural rules are documented and implemented across several key files in the repository:
AGENTS.md– root-level specification defining the full package hierarchy and import rulespackages/client-common/AGENTS.md– details Query/Mutation patterns over SDK methodspackages/sdk/AGENTS.md– explains the private RPC client and protocol re-export strategypackages/acn-protocol/AGENTS.md– documents the wire contracts shared between SDK and daemon
These files collectively serve as the source of truth for maintaining boundary discipline as the codebase evolves.
Summary
Magnitude's package layering hierarchy (clients → client-common → sdk → acn) enforces strict separation of concerns across its monorepo:
- Clients import only from
client-commonandsdkto remain lightweight and environment-agnostic - Client-common centralizes state management through Effect Queries that wrap SDK methods
- SDK provides a portable, pure RPC layer that re-exports stable protocol contracts
- ACN implements server-side behavior accessible only to privileged host composition roots
This design prevents bundle bloat, maintains protocol stability, and allows each layer to evolve independently as long as contract compatibility is preserved.
Frequently Asked Questions
What happens if I import directly from acn in a web client?
Direct imports from @magnitudedev/acn in browser-facing code will cause build failures or runtime errors. The acn package contains Node.js-specific dependencies (SQLite native bindings, process supervisors, OS service integrations) that cannot execute in browser environments. Even if tree-shaking removes some dead code, the bundler will likely fail to resolve these platform-specific modules.
Why does sdk re-export from acn-protocol instead of acn?
The acn-protocol package contains pure TypeScript contracts—interfaces, type definitions, and Effect schemas that describe the wire format. The acn package contains the actual implementation—servers, session managers, and runtime logic. By re-exporting from acn-protocol, the SDK maintains zero runtime dependency on server-specific code while still speaking the same language on the wire.
Can I extend Magnitude by adding a new layer between sdk and acn?
Adding intermediate layers is possible but requires careful consideration of the composition root pattern. The AGENTS.md documentation suggests that only privileged bootstrap code should import acn directly. Any new intermediate layer would need to either become part of the SDK's public API (breaking the encapsulation) or remain server-side (leaving the SDK boundary unchanged). Most extensions should instead add functionality within the existing acn implementation or expose new protocol contracts through acn-protocol.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →