Skip to content
Robotadocs

Architecture

Robota is a collection of TypeScript packages, not one framework you adopt whole. Each package owns one concern, and packages are stacked in layers so that a package only ever depends on the layers beneath it. This page explains what the layers are, why they are separate, and how a request moves through them. The repository-level summary is in ARCHITECTURE.md; each package's contract is in its docs/SPEC.md.

Why the layers exist

  • Use as little as you need. @robota-sdk/agent-core runs an agent on its own. You add the session runtime, the assembly layer, a transport or a UI only when you need them, and you install only the provider packages you actually call.
  • Swap parts without touching the core. Providers, tools, transports and user interfaces plug into contracts that the lower layers define. Adding a vendor or a protocol is a new package, not an edit to the engine.
  • Keep implementations independent. Shared types live in contract-only packages (agent-interface, one subpath per family). A transport can talk to a session through those contracts without depending on the package that builds sessions.
  • Keep product opinions at the edge. Library packages stay neutral: they read no ambient config and pick no file locations on their own. The robota CLI is one application built on top of them, and it is where product decisions (which settings files to read, which commands to offer) live.

The layers

Arrows point from a package to what it depends on. The diagram groups packages; the tables below list the exact members.

Foundation — agent-core

agent-core contains the ConversationAgent agent class (also exported as Robota) and its execution loop (model call, tool calls, repeat), the provider abstraction (IAIProvider, AbstractAIProvider), the tool registry (FunctionTool, ToolRegistry), the plugin base class (AbstractPlugin), permission evaluation, hook execution, context-window accounting, model metadata, and the typed error classes.

It has no dependency on any other Robota package. Everything else registers with it through its abstract contracts, so it can be used alone and nothing can form a dependency cycle through it. Its main entry is platform-neutral — it runs in a browser, a Web Worker, Node, Deno and Bun — and everything that needs a Node host is on the separate @robota-sdk/agent-core/node entry point, which a Node application passes in (hook executors, the process environment).

Contracts — agent-interface

PackageContracts it owns
agent-interface/sessionInteractive sessions, interaction channels, events, turns, session records
agent-interface/commandCommands, command results, plugin adapters, capability descriptors
agent-interface/executionBackground tasks, job groups, subagent jobs, execution workspaces
agent-interface/transportTransport adapters (ITransportAdapter), admission, access tokens, external events
agent-interface/session-mobilityPeer messaging and session handoff between sessions and devices
agent-interface/analyticsUsage snapshots, per-source totals, run-trace timelines
agent-interface/tuiTerminal command interactions (pickers, confirmations, missing-arg prompts)

This package holds type declarations and a few pure accessors over them, and does no I/O. A subpath depends only on agent-core and on lower subpaths, never on an implementation. That is what lets, for example, every transport accept a session without depending on agent-framework.

Capabilities — providers, tools, MCP, plugins

PackageWhat it provides
agent-provider/anthropicAnthropic Claude
agent-provider/openaiOpenAI
agent-provider/deepseek, /qwen, /gemmaDeepSeek, Qwen, and Gemma-family models behind OpenAI-compatible endpoints
agent-provider/geminiGoogle Gemini
agent-provider/bytedanceByteDance (ModelArk) video generation
agent-provider/openai-compatibleThe shared OpenAI-compatible protocol layer that openai, deepseek, qwen and gemma build on
agent-toolsTool factories (createZodFunctionTool, createFunctionTool) and the built-in tools
agent-mcpModel Context Protocol client: server definitions, activation, OAuth sign-in
agent-pluginEight ready-made plugins (logging, usage, limits, webhooks, and more)
agent-osNode host primitives, one subpath each: rooted file access (file-authority), a nonblocking operation lock (node-lock), process-tree termination (process)
agent-keyringKey custody and public-key sealing for secure conversations (node:crypto only)
agent-session-analyticsSession-log timing and usage analysis, reports and OTLP export
agent-telemetry-otelOpenTelemetry spans and metrics for agent runs and traces, GenAI or agent.* names
agent-remote-pairingPairing and channel binding for peer-to-peer connections (no Robota dependencies)
agent-roundtableCoordinates independent agents and people in sequential or parallel conversations (no Robota dependencies)
agent-organizationCanonical JSON and generic organization budget contracts (no Robota dependencies)
product-configExplicit product configuration contracts and their host loading (product-config/node)

agent-provider is a leaf over agent-core with one subpath per vendor and no root export; the vendor SDKs are optional peers, so an application installs only the SDKs of the providers it imports. agent-plugin depends only on agent-core; plugins attach to the engine and never reach into the layers above it.

Runtime — agent-session, agent-executor

agent-session wraps a ConversationAgent in a Session: tool calls pass through permission checks and hooks, context usage is tracked, the conversation is compacted when it grows too large, and records can be persisted through a store port. It receives its provider and tools already built; it never constructs them. Its main entry is platform-neutral; the file-backed store and log sink are on @robota-sdk/agent-session/node.

agent-executor provides background-task lifecycles (queueing, cancellation, snapshots) and the subagent job ports.

agent-roundtable-robota runs a Session or a ConversationAgent as a participant, or as the selector of the next speaker, in an agent-roundtable conversation.

Assembly — agent-framework

agent-framework puts the lower layers together into a ready-to-use session. Its entry points are InteractiveSession, createQuery() and createAgentRuntime(). It also owns config and context loading, workspace trust, the command registry, skills, subagent assembly, and the headless and programmatic runners.

It is provider-neutral — you construct the provider and pass it in — and it contains no React or Ink. Its main entry is platform-neutral and takes the platform as an injected host; a Node application builds one with createNodeFrameworkHost from @robota-sdk/agent-framework/node. It reads only the files and directories its host passes to it: without an explicit decision it loads no user settings, no project instructions and no project skills. See Using the SDK.

Composition — defaults, presets, commands, packs

PackageRole
agent-provider/builtincreateDefaultProviderDefinitions() — the built-in chat provider definitions
agent-tools/defaultscreateDefaultTools() — the default tool set, handed to the Node host
agent-product/commandThe slash-command modules (/help, /compact, /permissions, and the rest)
agent-product/presetNamed bundles of session options (persona, model, permission posture)
agent-product/capability-packICapabilityPack and mergeCapabilityPacks() for adding tools, commands, agents
agent-product/codingcreateCodingPack() — the coding tools, commands and subagents as one pack
agent-productassembleProduct() — builds a product from a declarative profile
agent-subagent-runnerOptional runner that executes subagents in child processes

These packages are meant to be imported where an application is put together (its "composition root"), not by libraries in the middle of the stack, so a library that only needs the mechanisms never drags in these defaults.

Transports

PackageProtocol
agent-transportCarrier-neutral wire messages, decoders and delivery helpers; ./client has the browser-safe decoders, ./node the access-token verification
agent-transport/httpHTTP, built on Hono
agent-transport/wsWebSocket
agent-transport/mcpExposes a session as an MCP server (stdio and Streamable HTTP)
agent-transport/acpServes a session to an editor as an ACP agent over stdio
agent-transport/webrtcPeer-to-peer WebRTC data channels (remote control, device mesh)
agent-transport/linkOwner link to a local runtime, admitted by proof

A transport is a thin adapter: it turns protocol messages into session calls (submit, abort, commands, permission answers) and sends session events back. Transports implement ITransportAdapter from agent-interface/transport and depend on the session contracts, not on agent-framework. agent-framework itself provides the non-interactive runner (text, json or stream-json output) and the TransportRegistry that starts and stops several transports together. See Deployment.

Surfaces

PackageWhat it is
agent-ui-terminalThe terminal UI (React + Ink), including renderApp and TuiInteractionChannel
agent-cliThe robota command — a maintained agent interface built from the packages above
agent-ui-webGUI components, a session reducer over the transport wire protocol and a WebSocket client
agent-gui-web (internal)The GUI web app served by robota --serve --open and loaded by the desktop app

agent-cli bundles the Robota packages it uses into its own build, so installing it does not install the SDK packages separately. The desktop app (apps/agent-app) is an Electron shell: it starts or reuses the workspace's robota daemon, connects to it over loopback, and loads agent-gui-web.

React and Ink appear only in these surface packages: Ink in agent-ui-terminal (and agent-cli, which bundles it), React in the terminal and web UI packages. Every other package is plain TypeScript.

Internal packages

Four workspace packages are private and not published: agent-gui-web (above), agent-local-host (Node host authority for owner-protected local runtime state), agent-organization-host (an operator-owned organization broker with durable budgets) and agent-provider-replay (a provider that replays a recorded session log, for offline tests). The CLI bundles the separately maintained @robota-sdk/workflows library for its /workflows command; it is not a workspace package.

Dependency rules

These rules keep the layering real rather than aspirational (the full list is in ARCHITECTURE.md):

  • Dependencies point one way, down the layers. There are no cycles.
  • agent-core depends on no other Robota package.
  • An agent-interface subpath depends only on agent-core and lower subpaths.
  • Plugin packages depend only on agent-core.
  • agent-framework and agent-core never depend on a transport or UI package.
  • No package re-exports another package wholesale; you import a symbol from the package that owns it.

How a turn flows

InteractiveSession records everything that happens in one ordered history of IHistoryEntry items. An entry's category is 'chat' for user and assistant messages and 'event' for things such as tool starts and ends. The same history drives display and persistence.

User input
  → InteractiveSession.submit()
  → history: IHistoryEntry (category 'chat', user message)
  → Session.run() → ConversationAgent.run()
  → the provider receives only the chat entries, converted to messages
  → streaming reply → text_delta events; tool calls → tool_start / tool_end events
  → history: assistant message (category 'chat') and tool entries (category 'event')
  → context_update, then complete (or interrupted / error)
  → every attached client (terminal, web GUI, HTTP, WebSocket, MCP) updates from the events

The provider never sees event entries; the session filters the history to chat messages before each model call. Background tasks are tracked beside the session as task snapshots and append-only transcripts, so a long-running task's output does not rewrite the session record on every chunk.

Session delegates to focused components: PermissionEnforcer wraps each tool with permission checks and hooks, ContextWindowTracker tracks token usage and the auto-compaction threshold, and CompactionOrchestrator summarizes the conversation when it is compacted. See Context Management.

Plugins

agent-core defines AbstractPlugin; @robota-sdk/agent-plugin provides eight implementations. A plugin overrides the lifecycle hooks it cares about, such as beforeRun, afterRun, beforeProviderCall, afterProviderCall and onError. A failing hook is logged and does not fail the run. The hook list and examples are in Building Agents.

Design patterns

PatternWherePurpose
FacadeRobota, Session, InteractiveSessionOne entry point over many internal parts
DecoratorPermissionEnforcer.wrapTools()Adds permission checks around each tool
StrategyIAIProvider, ISessionLoggerSwappable implementations
FactorycreateQuery(), createAgentRuntime(), createZodFunctionTool()Build configured objects
Null ObjectSilentLogger, DefaultEventServiceSafe do-nothing defaults
RegistryToolRegistry, CommandRegistry, TransportRegistryOne place to look up tools, commands, transports
CompositionInteractiveSession → Session → RobotaDelegation instead of inheritance