@mono-agent/operator-adapter

Loopback operator adapter for mono-agent structured TUI and web NDJSON turns.


License
GPL-3.0-only
Install
npm install @mono-agent/operator-adapter@0.20.14

Documentation

mono-agent

This repository is a config-first pnpm workspace of reusable npm packages under the @mono-agent scope. The framework is built around @mono-agent/agent-runtime as the single shipped runtime implementation layer, while sandboxing, communication adapters, skills, memory, observability, and operator surfaces stay modular. @mono-agent/agent-app composes them from one shareable config file so an agent can be built, validated, and moved as configuration instead of host glue.

Documentation

Full documentation and end-to-end playbooks: https://mono-agent-docs.vercel.app/ (authored as markdown under docs/, built with Astro Starlight in website/ and deployed on Vercel — see website/README.md for the build/sync/deploy workflow and version-pin notes). Start with the package directory when you need to find the owner of a capability. docs/reference/feature-registry.md remains the canonical feature reference, and docs/playbooks/ holds copy-paste recipes for every channel and memory tier.

Maintainers should begin with ARCHITECTURE.md and CONTRIBUTING.md. Security boundaries, vulnerability reporting, and the manual local-secret cleanup checklist live in SECURITY.md.

Quickstart: An Agent Folder From One Config File

Any folder — empty or already holding knowledge (AGENTS.md, CLAUDE.md, docs) — can become a running agent from one mono-agent.config.json. You need Node.js 22.19.0 or newer and credentials for the model you choose.

Choose the wall-clock path up front: flags or non-TTY input use the fast scaffold-only path (unless explicit --auth adds provider setup) and never claim readiness. Bare mono-agent init on a TTY makes one real no-tool model call per selected route before committing the scaffold, with timeouts of 90s for each cloud route and 240s for each local route.

1. Install the CLI

Install the convenience package to put mono-agent on your PATH:

npm i -g create-mono-agent

The scoped @mono-agent/agent-app package provides the same CLI. For one-shot scaffolding with no global install, use npm create mono-agent@latest init. See Install & Prerequisites for those alternatives and the unreleased-source workflow.

2. Create the agent folder

mkdir my-agent
cd my-agent
mono-agent init

Bare init on a TTY opens the guided wizard: name the agent, write its Role, choose a model and capabilities, review the result, and complete any provider setup. On macOS, a successful guided run proves the selected routes, starts the background agent, and opens a dedicated [SELF-CONFIG] session that maps the framework and helps build the user's workflow conversationally. Approval, rejection, and no-change turns keep that session active; only quitting it exits self-configuration. Any flag or non-TTY invocation is scaffold-only; on other platforms, continue with the foreground start below.

The complete wizard, non-interactive flags, generated files, and provider-specific setup are documented in Your First Agent.

3. Validate and start

Check the generated config:

mono-agent validate

If guided macOS init already started the agent, confirm it is live:

mono-agent status

Otherwise start the scaffold now. On macOS this backgrounds the process; elsewhere keep the foreground command running in Terminal 1:

mono-agent start                  # macOS
# or:
mono-agent start --foreground     # Linux and other platforms

start prints one status line per channel. Copy the loopback webhook invoke URL it prints; the default scaffold enables that credential-free smoke channel.

4. Send the first request

In Terminal 2, set PORT to the port from the printed invoke URL:

PORT=3000 # Replace 3000 with the printed port.
curl -s "http://127.0.0.1:${PORT}/webhook/invoke" \
  -H 'content-type: application/json' \
  -d '{"text": "Say hello and tell me what you are."}'

A reply proves the runtime, model, identity, and webhook channel are wired together. Provider authentication or local-model availability still determines whether the model can answer; failures are reported honestly rather than replaced with a fake reply.

For the low-level trust model behind guided secret persistence and managed macOS startup, see Setup security and managed runtime. For command details and recovery paths, see the CLI reference.

Presets & the setup wizard

mono-agent init composes an agent from capability modules (channels, built-in memory tiers, sandbox, observability) and walks you through the tool allowlist so the agent can actually do something. Presets are saved answer-sets for five built-in shapes — starter (webhook smoke agent), telegram-assistant (BuJo memory), slack-bot, local-private (Ollama), and code-sandbox. Optional packages such as Supermemory ship their own setup skill instead of making an unavailable service look built in. Each core preset prints its generated config with secrets externalized to .env.example, and mirrors a copy-paste playbook in docs/playbooks/:

mono-agent presets list
mono-agent presets show telegram-assistant
mono-agent init --preset telegram-assistant --yes

The code-sandbox preset uses Pi plus sandbox.mode: "native". On macOS, mono-agent sandbox setup installs the exact pinned SRT dependency tree into a private per-user cache; it never changes PATH, global npm packages, or system packages. The installed tree must match an independently pinned digest, so a locally rewritten marker cannot bless different bytes. Choosing managed SRT in guided setup always runs that idempotent managed install/verification, even when a compatible external srt is already on PATH. sandbox status inspects the install and sandbox check proves allowed workspace access, protected/out-of-root denial, localhost access, and non-allowlisted-domain denial. Runtime launches revalidate a present managed tree and fail closed on corruption or when no effective engine exists. A compatible external srt remains a fallback only when the managed install is absent, never when a managed install is corrupt; its canonical absolute file identity and content are pinned after the functional proof and rechecked before use.

The old mono-agent recipes … / --recipe <id> surface was removed — use mono-agent presets … / --preset <id> instead (the removed commands now exit with a replacement message). See docs/reference/presets.md for the presets, capability modules, tools/no-tools guardrail, and migration map, and docs/reference/deprecations.md for the canonical deadline and permanent-compatibility decisions.

Skill-Based Composition Guide

The repo includes a composer skill that walks an agent (in mono-agent itself, Claude Code, or another harness that reads SKILL.md files) through constructing an agent folder with the flow above:

The skill asks discovery questions (runtime + backup models, channels incl. crons and webhooks, skills, MCP, memory strategy incl. semantic search, sandbox, observability), maps each answer to config keys, then runs mono-agent initvalidatestart and a channel-matched smoke test. docs/reference/feature-registry.md is the source of truth mapping every framework feature to its config/CLI/programmatic surface; the skill ships a condensed copy as references/feature-coverage.md. Its version-matched @mono-agent/docs-mcp companion gives coding harnesses semantic and exact-identifier search over the full public docs plus those authoritative references. The skill ships with @mono-agent/agent-app; install and pair it for Claude Code and Codex with:

mono-agent install-skill   # copies the skill and pairs mono-agent-docs for available CLIs

Use --no-docs-mcp only for an intentional file-only install. An unmanaged MCP entry named mono-agent-docs is never overwritten, including with --force.

This authoring-oriented composer is not auto-selected inside generated agents. New agents instead select the narrower mono-agent-configure and mono-agent-memory project skills with index disclosure. Check or safely refresh their managed copies with mono-agent install-skill --project --check / --update; canonical non-symlink parent checks, an owner lock, compare-and-swap activation, and guarded rollback never write outside the agent or overwrite modified/concurrently edited copies.

To use it as a selected mono-agent skill instead, point context.skillsRoot at ./packages/agent-app/skills and add mono-agent-composer to context.selectedSkills.

Always-on web console

Start the browser operator console once, then open it from this computer, the local network, or Tailscale:

mono-agent web start --theme ocean
mono-agent web                 # status and exact URLs; does not start or change anything

It binds 0.0.0.0:5050 by default, auto-discovers running agents, and keeps separate persistent conversations for each selected agent. The header, tab title, and installed PWA use the operating-system hostname. Pick evergreen (default), ocean, plum, or terracotta with --theme; managed restarts retain that selection and web status reports it. The console has no application login: anyone who can reach the port can operate the discovered agents and see retained conversations, so expose it only on a trusted LAN or tailnet. When Tailscale Serve is available it adds an HTTPS route without replacing an existing handler; otherwise direct LAN/Tailscale HTTP remains available. Use --loopback when the service must stay on this computer.

Attachments come from the browser device's native file picker, not a browser over the host filesystem. The web transport uses the same AgentAttachment contract, MIME allowlist, 20 MiB per-file limit, image/document classification, text decoding, and harness persistence as Telegram; each turn additionally allows at most 10 files and 64 MiB total. See the web console guide for lifecycle, security, retention, and current scope. The mono-agent sessions Session Recorder command was removed; use mono-agent tui (recorded-run replay) or mono-agent web (live console).

Replies can also carry generated files and MCP Apps without embedding local paths, capability URLs, or payload bytes in the message stream. Slack and Telegram confirm native file uploads before suppressing a safe human fallback; machine and verbatim transports leave answer text unchanged. The web console provides integrity-checked downloads and a confirmation-gated, double-frame sandbox for supported Pi-native MCP Apps. See Reply files and MCP Apps for route support, limits, retention, and the browser security boundary.

Use the header bell to enable durable Web Push for completed responses, AskUser questions, and terminal failures. The server stores its VAPID identity, browser subscriptions, and retry outbox under the owner-private web state; notification previews are plain-text, redacted, and bounded. The web service is self-hosted, while encrypted delivery uses the browser vendor's standards-mandated push service. On iPhone or iPad, open the console over HTTPS and add it to the Home Screen first.

Package Architecture

Package categories are catalog metadata, documentation, and architecture-guard inputs. Core packages live under packages/<package-name> and optional plugin-tier extras live under extras/<package-name>. Both use @mono-agent/<package-name> names and both are publishable: true (released together on the npm lockstep tag); the extras are marked tier: "plugin" and are loaded only through explicit composition, channels.plugins[], an explicitly selected backend, or companion MCP pairing.

See the generated PACKAGES.md dependency graph and directory for the exact current package set, static workspace edges, npm pages, and authoritative package READMEs. The same directory is published in the website reference.

Before adding new capability surface area, use the Capability ladder to decide whether the work belongs in an existing package, config/skills, a new package, an MCP tool boundary, or a shared core contract.

The catalog groups packages by ownership boundary:

  • app composes the config-first host and CLI.
  • communication owns channel-specific ingress, delivery, authentication, and transport policy.
  • execution assembles one request and optional collaborator tools.
  • runtime owns provider bridges and the sandboxed runtime facade.
  • core defines adapter-neutral contracts and configuration.
  • context owns optional memory and documentation retrieval.
  • observability records and reads run artifacts and exports traces.
  • operator-surface presents local terminal and browser experiences without hosting the agent runtime.

Dependency Direction

Static manifest dependencies (abridged; see PACKAGES.md for every edge)

@mono-agent/agent-app
  ├─ config + agent-contracts
  ├─ agent-harness
  ├─ runtime-adapter ── agent-runtime
  ├─ memory + observability
  ├─ built-in channel adapters
  ├─ operator-adapter
  └─ tui + web

agent-harness ── agent-contracts + runtime-adapter + observability
tui / web ── agent-contracts + config + observability

Runtime-only composition (not manifest dependency edges)

tui / web ── HTTP operator protocol ──> operator-adapter
agent-app ── channels.plugins[] ──> a2a-adapter / whatsapp-adapter
agent-app ── selected memory backend ──> memory-supermemory
custom host ── request-scoped extension ──> agent-orchestrator
authoring harness ── explicit MCP companion ──> docs-mcp

Rules for future packages:

  • New publishable packages live under packages/<package-name> and publish as @mono-agent/<package-name>.
  • Optional plugin-tier add-ons may live under extras/<package-name> when cataloged with publishable: true and tier: "plugin" (published in the lockstep but outside the core app closure).
  • Add every workspace package to scripts/package-catalog.mjs with category, responsibility, and allowed dependency categories.
  • Communication packages use *-adapter naming and must not depend on other adapters, the harness, or operator surfaces.
  • Core config stays adapter-neutral; adapter credentials and allowlists live with the adapter package.
  • Operator surfaces register field groups from other packages; they do not hardcode adapter settings.

Host Traceability

The workspace now has a local host traceability path. Each running host registers an agent-runtime.trace-source.v1 manifest in a registry directory such as ~/.mono-agent/trace-sources; each manifest points at that source's artifact directory, where run summaries and event JSONL files remain. mono-agent status reads the registry, marks stale sources when their heartbeat ages out, and aggregates recent runs across sources by (sourceId, runId) so duplicate run ids do not collide.

This is local-first. It is not a LangSmith dependency, database, or cloud collector.

Phoenix is the recommended trace viewer for local development. When an observability.exporters entry (currently the phoenix preset) is configured, the host additively exports each run lifecycle to Phoenix's OTLP HTTP traces endpoint as binary protobuf (application/x-protobuf) via @mono-agent/observability/otel. Spans use OpenInference semantics (AGENT/LLM/TOOL/CHAIN kinds with input/output) and land in a named project (projectName, defaulting to the trace source label/id). Export is best-effort and bounded by a timeout — it never changes the run outcome and never suppresses JSONL writes. Raw prompts, reasoning, and tool I/O are metadata-only by default (includeSensitiveData: false). Setting includeSensitiveData: true exports those payloads: non-numeric values under sensitive-looking object keys are redacted; numeric values under matched keys are retained; free text is not content-scanned by default. Set contentPatternRedaction: true to replace a closed set of high-confidence OpenAI, GitHub, AWS, and Slack credential shapes in retained outbound text. Strings are capped, and the scan remains defense in depth rather than a substitute for trusting the collector.

Local JSONL artifacts are the completed-run fallback only after the terminal write succeeds; before then the on-disk record is only the start snapshot. At start(), the recorder independently replaces an empty events file and a running summary, then buffers later events in RAM after sensitive-key redaction, a closed high-confidence credential-shape scan, and a 4,096-byte default cap per string. Terminal finish()/fail() independently replaces that bounded events snapshot first and the summary second. A crash before terminal persistence can lose buffered events, and stale reconciliation can report only the data already on disk; the artifacts are not an in-flight, full-payload, or crash-safe source of truth.

mono-agent start, mono-agent status, and mono-agent validate report the configured exporter endpoint (validate POSTs an empty protobuf to confirm Phoenix will accept exports, not just that the port is open). Use mono-agent backfill --all to retroactively export already-recorded runs with their historical timestamps; deterministic per-run ids make re-exports idempotent.

A2A Inter-Agent Discovery

@mono-agent/a2a-adapter exposes a Mono responder over the A2A v1 protocol using the pinned @a2a-js/sdk@1.0.0-alpha.0. Provider mode serves the public Agent Card at /.well-known/agent-card.json and message/task endpoints under /a2a/json-rpc and /a2a/rest. Consumer mode discovers direct Agent Card URLs and sends text messages to remote agents.

The A2A adapter remains deliberately text/task only: no central registry, gRPC hosting, push notifications, signed cards, file exchange, or adapter-owned delegation policy. Dynamic collaborator selection is composed above A2A by @mono-agent/agent-orchestrator. Provider binds to loopback by default; non-loopback bind or advertised public URLs require explicit config and should be deployed behind HTTPS with bearer auth.

Local Providers

Hosts can pass local OpenAI-compatible providers into @mono-agent/agent-runtime through the Pi adapter. Ollama is the primary supported local path:

{
  "runtime": {
    "model": "ollama:qwen3:8b",
    "workspace": "."
  },
  "providers": {
    "local": [
      { "id": "ollama", "type": "ollama", "baseUrl": "http://localhost:11434", "enabled": true }
    ]
  }
}

Run Ollama locally and pull the model first, for example ollama pull qwen3:8b. Standard local Ollama needs no provider API key. LM Studio and other OpenAI-compatible local gateways use the same providers.local shape with type: "lmstudio" or type: "openai_compat"; public URLs must be explicitly trusted and use HTTPS. runtime.maxTurns is optional; omit it or set 0 for unlimited runs, or set 1-100 for a hard cap.

Built-in Pi credentialed providers use the Pi auth file instead of providers.local. Core config defaults providers.piAuthPath to ~/.pi/agent/auth.json and exposes MONO_AGENT_PI_AUTH_PATH for hosts that keep credentials elsewhere. Subscription/account-backed providers include openai-codex:*, anthropic:*, github-copilot:*, and opencode-go:*. OpenAI-Codex, Anthropic, and GitHub Copilot use Pi OAuth/account flows where supported; OpenCode-Go uses an API key (OPENCODE_API_KEY) that guided setup can save into the Pi auth store. Recover a Pi OAuth provider with mono-agent auth login <provider>. Anthropic keeps the localhost callback active while accepting a pasted final redirect URL; Pi validates its code and OAuth state before exchange. Standalone OpenCode-Go login prompts for the key with masked TTY input; on a headless host, opt in to redirected input:

printf '%s\n' "$OPENCODE_API_KEY" | mono-agent auth login opencode-go --api-key-stdin

The command never copies an ambient key implicitly. It uses the configured auth path (--pi-auth-pathMONO_AGENT_PI_AUTH_PATHproviders.piAuthPath → default), validates the requested credential and unchanged siblings in the app-owned terminal wrapper's staged Pi OAuth output, then promotes it under a durable lock with owner-only, pathname no-clobber semantics. The canonical parent must be current-user-owned and not group/world-writable; source, staged, and recovery credential inodes must also be current-user-owned with exactly the expected link identity. An owned existing store may start read-permissive (for example 0644) and is tightened to 0600, but group/world-writable, foreign-owned, or multiply linked stores fail closed. Pathname races remain at the target. A claimed-inode write detected after install is retained at the reported recovery path; a non-cooperative open-descriptor write after the final check is outside the POSIX guarantee. Malformed/unsafe output, concurrent changes, Windows persistence, and auth paths inside Git worktrees fail closed without silently discarding detected bytes.

Development Verification

Use the combined repository and golden-consumer gate when you need one final verdict:

pnpm install --frozen-lockfile
pnpm run verify:all

pnpm run verify:all runs the repository gate, then validates the committed golden consumer fixtures for local-agent-alpha and local-agent-beta. The consumer checks use redacted fixtures, liveness:false, no network probes, and no secrets by default.

To run only the consumer fixture contracts:

pnpm run verify:consumers

To add a deeper read-only audit of a downstream checkout's run artifacts:

pnpm run verify:consumers -- --consumer /path/to/downstream-agent

Focused checks remain useful while debugging a specific failure:

pnpm install --frozen-lockfile
pnpm run check:architecture
pnpm run build
pnpm run typecheck
pnpm test
git diff --check

For package-level work:

pnpm --filter @mono-agent/agent-runtime run build
pnpm --filter @mono-agent/agent-runtime run typecheck
pnpm --filter @mono-agent/agent-runtime run test

Replace @mono-agent/agent-runtime with the package under test.

Safety Model

  • No secrets, .env*, OAuth files, provider keys, OpenAI API adapter keys, Telegram tokens, WhatsApp auth state, or transcripts are committed.
  • Interactive secret persistence fails closed unless the canonical agent directory is current-user-owned and not group/world-writable, .env/.gitignore are current-user single-link regular paths, .env is untracked valid dotenv text, exact ignore rules are protected from group/world writes, and promotion can prove owner-only permissions plus pathname no-clobber identity. Windows gets manual instructions instead of an automatic secret write.
  • Settings JSON is local, schema-validated, and written with restrictive file permissions where the settings helper writes it.
  • Secret fields are redacted in diagnostics and status output.
  • Tool policy is allow-all by default (omit tools.allowedTools, or include "*" in the list, for every tool). Narrow it with an explicit list, or go chat-only with []. Unsupported policies fail before provider startup. See the runtime enforcement table. The programmatic harness safety net with no policy is fail-closed (failClosedToolPolicy()).
  • Mono-agent sandbox policy is explicit and fail-closed for Pi-owned commands. Every route is Pi-native, so the srt contract — mono-agent roots, deny-write globs, and network policy — applies uniformly; validation and runtime reject a route that cannot represent it.
  • Memory writes are host-owned and optional.
  • Fixtures and fake runtimes are for tests only, not product-runtime substitutes.

Layered Workflow

Diagram summary: The app composes adapter-neutral config, request execution, runtime bridges, optional context and observability, communication adapters, and operator surfaces; arrows show the intended high-level dependency direction.

flowchart TB
  Host["Config-first app host<br/>mono-agent CLI or custom host"]

  subgraph Surfaces["Operator-surface choices"]
    Tui["@mono-agent/tui<br/>Terminal chat + read-only config"]
  end

  subgraph Communication["Communication adapter choices"]
    A2A["@mono-agent/a2a-adapter<br/>extra plugin: Agent Card discovery + text tasks"]
    Cron["@mono-agent/cron-adapter<br/>Scheduled invocations"]
    OpenAIApi["@mono-agent/openai-api-adapter<br/>OpenAI Chat Completions"]
    Slack["@mono-agent/slack-adapter<br/>Socket Mode + Web API"]
    Telegram["@mono-agent/telegram-adapter<br/>Bot API + long polling"]
    Webhook["@mono-agent/webhook-adapter<br/>HTTP sync/async invocation"]
    WhatsApp["@mono-agent/whatsapp-adapter<br/>extra plugin: Baileys socket + group trigger policy"]
  end

  subgraph Core["Core contracts and config"]
    Contracts["@mono-agent/agent-contracts<br/>request/response/stream/settings helpers"]
    Config["@mono-agent/config<br/>core runtime/context settings"]
  end

  subgraph PromptContext["Context layer"]
    Memory["@mono-agent/memory<br/>./store SQLite, ./search embeddings, ./bujo engine"]
    MemorySupermemory["@mono-agent/memory-supermemory<br/>extra plugin: Supermemory-backed store"]
  end

  subgraph AppLayer["App layer"]
    AgentApp["@mono-agent/agent-app<br/>config to channels + responder"]
  end

  subgraph Execution["Execution layer"]
    Harness["@mono-agent/agent-harness<br/>request to runtime run<br/>context + skills + tool policy"]
    Orchestrator["@mono-agent/agent-orchestrator<br/>extra: collaborator MCP tool"]
    Observability["@mono-agent/observability<br/>JSONL events + summaries + trace registry"]
  end

  subgraph Runtime["Pi runtime"]
    RuntimeAdapter["@mono-agent/runtime-adapter<br/>model refs + sandbox policy"]
    AgentRuntime["@mono-agent/agent-runtime<br/>Pi implementation"]
    PiSdk["Pi providers<br/>&lt;provider&gt;:&lt;model&gt;"]
  end

  Host -. optional .-> Tui
  Host --> Telegram
  Host -. plugin .-> A2A
  Host --> Webhook
  Host --> OpenAIApi
  Host --> Cron
  Host -. optional package .-> Slack
  Host -. plugin .-> WhatsApp
  Host -. runtime extension .-> Orchestrator
  Host --> Config
  Host --> AgentApp

  Tui --> Contracts
  Tui --> Config
  Telegram --> Contracts
  A2A --> Contracts
  Cron --> Contracts
  OpenAIApi --> Contracts
  Slack --> Contracts
  Webhook --> Contracts
  WhatsApp --> Contracts

  Orchestrator --> Contracts
  Orchestrator -.->|runtime extension| Harness
  AgentApp --> Config
  AgentApp --> Harness
  AgentApp --> Memory
  AgentApp -. optional backend .-> MemorySupermemory
  AgentApp --> RuntimeAdapter
  AgentApp --> Observability
  Config --> Contracts
  Config --> RuntimeAdapter
  Harness --> Contracts
  MemorySupermemory --> Contracts
  Harness --> RuntimeAdapter
  Harness --> Observability

  RuntimeAdapter --> AgentRuntime
  RuntimeAdapter --> Contracts
  AgentRuntime --> PiSdk
Loading

License

Mono-agent and every publishable package in this workspace are licensed under GPL-3.0-only. See LICENSE for the complete terms.