Appearance
Architecture
Molf Assistant is a monorepo with 10 packages under packages/, managed by pnpm workspaces. A central oRPC WebSocket server orchestrates LLM interactions while workers execute tool calls locally. Clients connect over WebSocket to drive sessions.
Package Overview
| Package | Description |
|---|---|
protocol | Shared types, Zod schemas, oRPC contract definition, plugin system, credentials, TLS trust, tool definitions, truncation, WebSocket helpers |
agent-core | Agent class (manual step loop with streamText), Session, context pruner, provider registry/catalog, system prompts |
server | WebSocket server (oRPC), SessionManager, AgentRunner, ServerBus, ToolDispatch, ApprovalGate, ConnectionRegistry, WorkspaceStore, PluginLoader, SubagentRunner |
worker | ToolExecutor, skill/agent loading, server connection with auto-reconnect, StateWatcher, SyncCoordinator, worker plugin loader |
client-tui | Ink 5 + React 18 terminal client |
client-telegram | Telegram bot client via grammY framework |
plugin-cron | Default server plugin: cron job scheduling with routes and session tool |
plugin-mcp | Default worker plugin: MCP client integration (stdio + HTTP transport) |
test-utils | Shared test helpers: mockStreamText, createTmpDir, createEnvGuard, getFreePort |
e2e | Integration and live test suites with server/worker test helpers |
Dependency Graph
protocol
├──▲── agent-core
│ ▲
│ │
│ server
│
├──▲── worker
├──▲── client-tui
├──▲── client-telegram
├──▲── plugin-cron
└──▲── plugin-mcpprotocol sits at the base. agent-core builds on protocol to provide the Agent class and provider system. server depends on both protocol and agent-core. All other packages (worker, clients, plugins) depend only on protocol.
Communication
All communication uses oRPC over WebSocket with TLS enabled by default. The server exposes 9 sub-routers:
| Router | Domain |
|---|---|
session | Session lifecycle (create, list, load, delete, rename) |
agent | LLM interaction (prompt, abort, events subscription) |
tool | Tool approval (approve, deny, list) |
worker | Worker registration, state sync, tool call dispatch |
fs | File system reads (tool output retrieval) |
provider | LLM provider and model listing, API key management, custom provider configuration |
workspace | Workspace management and config |
auth | Pairing codes, API key management |
plugin | Plugin route dispatch |
See Protocol for the full oRPC API reference.
Message Flow
Prompt Flow
Client Server Worker
│ │ │
├─ agent.prompt ─────────►│ │
│ ├─ AgentRunner.run() │
│ ├─ streamText (LLM) ──► │
│ │ ◄── tool_call │
│ ├─ ToolDispatch ──────────►│
│ │ ├─ ToolExecutor
│ │ ◄── toolResult ─────────┤
│ ├─ continue loop │
│ │ ... │
│ ◄── turn_complete ─────┤ │- Client sends
agent.promptwith session ID and text. - AgentRunner resolves the model (prompt-level > workspace config > server default), builds the system prompt, and calls
streamTextfrom Vercel AI SDK. - When the LLM emits tool calls, ToolDispatch routes them to the bound worker via promise queuing (120s timeout).
- The worker executes the tool via ToolExecutor and returns the result.
- The agent loop continues until the LLM produces a final response or
maxStepsis reached.
Event Flow
AgentRunner ──► ServerBus (session scope) ──► agent.onEvents subscription ──► Client
ProviderKeyStore ──► ServerBus (global scope) ──► provider_state_changed ──► All ClientsThe AgentRunner emits 9 event types per session through the ServerBus session channel: status_change, content_delta, tool_call_start, tool_call_end, turn_complete, error, tool_approval_required, context_compacted, and subagent_event. Clients subscribe via agent.onEvents to receive streaming updates.
Additionally, the ServerBus global channel broadcasts a provider_state_changed event to all connected clients when a provider API key is added or removed at runtime.
Key Abstractions
AgentRunner
Orchestrates LLM interactions. Maintains a cache of Agent instances per session (evicted after 30 minutes idle). Builds system prompts from default instructions, skill hints, task hints, workdir context, media references, and runtime context. Dispatches hooks at turn_start, before_prompt, after_prompt, and turn_end.
ToolDispatch
Routes tool calls from the server to the connected worker. Uses promise queuing with a 120-second timeout. If a worker disconnects, all pending dispatches are rejected immediately.
ServerBus
Scoped publish-subscribe event bus. Supports four channel scopes: global (provider state changes, broadcast to all clients), session (agent events per session), workspace (workspace-scoped events), and worker (worker-scoped events). AgentRunner publishes session events; clients consume them via oRPC subscriptions. Replaces the former EventBus and WorkspaceNotifier.
ConnectionRegistry
Tracks all connected WebSocket clients (workers and clients). Worker state persists across disconnects -- workers are marked offline rather than removed. Dispatches worker_connect and worker_disconnect hooks.
ApprovalGate
Three-layer tool approval evaluation: agent permissions (subagent sessions only), static rules from permissions.jsonc, and runtime "always approve" patterns. See Tool Approval.
PluginLoader
Loads server plugins from config, validates config against plugin schemas, and manages the plugin lifecycle. Tracks worker plugin specifiers to send to workers on connect. See Plugins.
SessionManager
In-memory session cache with JSON file persistence at {dataDir}/sessions/{id}.json. Uses atomic writes (tmp file + rename). Dispatches session_create, session_delete, and session_save hooks.
WorkspaceStore
Groups sessions into workspaces. Each workspace carries a per-workspace model config override. Persisted at {dataDir}/workers/{workerId}/workspaces/{workspaceId}/workspace.json.
Server Module Table
| Module | Description |
|---|---|
main.ts | Entry point: CLI parsing, config resolution, LogTape setup, server start |
config.ts | Config resolution (JSONC + CLI + env), defaults; JSONC in-place modification (modifyConfigFile) |
server.ts | WebSocket server initialization, TLS, component wiring |
router.ts | oRPC router composition (9 sub-routers) |
context.ts | oRPC context and middleware (auth) |
agent-runner.ts | LLM orchestration, system prompt building, model resolution |
session-mgr.ts | Session persistence and caching |
server-bus.ts | Scoped event bus (global, session, workspace, worker); replaces EventBus and WorkspaceNotifier |
secrets.ts | Unified secrets persistence (auth tokens + provider keys in secrets.json) |
provider-keys.ts | ProviderKeyStore: per-provider key CRUD backed by secrets.ts |
server-state.ts | ServerState container; provider registry reload without restart |
auth.ts | Token verification, API key management |
tls.ts | Self-signed certificate generation |
connection-registry.ts | WebSocket client tracking |
worker-store.ts | Worker state persistence |
worker-dispatch.ts | Tool call, upload, and FS read dispatch to workers |
workspace-store.ts | Workspace config and session grouping |
plugin-loader.ts | Server plugin loading and lifecycle |
plugin-api.ts | ServerPluginApi implementation |
plugin-routes.ts | Plugin route oRPC integration |
summarization.ts | Context summarization |
subagent-runner.ts | Subagent session lifecycle |
subagent-types.ts | Built-in agent definitions (explore, general) |
pairing.ts | Pairing code store |
approval/approval-gate.ts | Three-layer tool approval |
approval/evaluate.ts | Pattern matching and rule evaluation |
approval/ruleset-storage.ts | permissions.jsonc persistence |
approval/shell-parser.ts | Shell command parsing for approval |
Worker Module Table
| Module | Description |
|---|---|
index.ts | Entry point: CLI, identity, TLS, connection, plugin loading |
cli.ts | CLI argument parsing |
identity.ts | Persistent worker UUID |
connection.ts | Server connection with auto-reconnect |
tool-executor.ts | Tool registration and execution |
skills.ts | Skill and AGENTS.md loading |
agents.ts | Agent definition loading |
state-watcher.ts | File system watching for skills/agents changes |
sync-coordinator.ts | Serialized state sync to server |
plugin-loader.ts | Worker plugin loading |
plugin-api.ts | WorkerPluginApi implementation |
pair.ts | TOFU + pairing code exchange |
truncation.ts | Tool output truncation and storage |