Skip to content

Latest commit

 

History

History
85 lines (54 loc) · 7.71 KB

ARCHITECTURE.md

File metadata and controls

85 lines (54 loc) · 7.71 KB

Architecture

日本語

This document describes the internal shape of agmsg — the mental model a contributor needs before reading the code or the interface spec. For what to implement, see docs/spec/. For why a decision was made, see docs/adr/.

Goal

A cross-agent messaging primitive that works between any combination of Claude Code, Codex, Gemini CLI, Antigravity, and future agent runtimes — with no required daemon, no network, no shared cloud. agmsg itself runs without a daemon; the optional agmsgd (1.6.0 beta) is a resident service that is installed only when the user picks it. Messages move through local files on a single machine; receivers are notified through whatever hook or streaming mechanism their host runtime provides.

The default install must work with bash + sqlite3 only. Any feature beyond that is opt-in and may require additional dependencies that the user agrees to install.

The 4-axis driver model

agmsg is built around four orthogonal axes, each of which has exactly one driver active at a time (per member, for terminal — see below). A driver is a swappable implementation behind a fixed protocol.

Axis What it abstracts Bundled drivers
storage Where messages and team state live, and how they are queried sqlite (default), jsonl, sqlite-sync
agent Per-runtime differences (hook formats, settings file locations, monitor tool availability) claude-code, codex, gemini, antigravity, copilot, opencode, grok-build, cursor, devin, hermes (plus agmsg-app and ext-tool for non-CLI members)
delivery How a recipient is notified that a message arrived monitor (legacy), turn, both, off; each agent type declares which of these it supports (delivery_modes= in its type.conf). The optional agmsgd daemon is a further way to deliver to Claude Code and Codex sessions
terminal The pane, window, or process a member's host-agent CLI runs under, and how to create, read, and signal it herdr, tmux, plain, orca, claude-desktop, codex-app

The three configuration axes are independent: any storage driver can be paired with any agent driver and any delivery mode that agent driver supports. They share a common discovery/config/dependency-check protocol (see the spec) but expose axis-specific operations.

terminal is detected, not configured — a session determines which terminal driver it is running under (or falls back to plain, the driver of last resort, if none matches) rather than being told. It is otherwise the same kind of driver: a fixed protocol (terminal_* functions, see docs/spec/driver-interface.md §6) behind which herdr, tmux, orca, and a plain OS terminal are interchangeable, and it shares the same discovery/trust protocol as the other three. Unlike them, a member can function with no terminal capability at all — plain degrades to "no addressable pane," and messaging keeps working; terminal only adds the ability to locate, read, or type into another member's pane.

Driver vs plugin

  • driver is the architectural unit. Both bundled implementations and user-installed ones are drivers.
  • plugin is a distribution distinction: a driver that ships outside agmsg core, dropped into a user-controlled directory.

Bundled drivers live under scripts/drivers/<axis>/. Inside scripts/, the top-level .sh files are directly-invokable commands; subdirectories group implementation code by category (drivers/ for axis drivers, lib/ for shared helpers).

External drivers are dropped into <skill>/plugins/<axis>/<name>/ or into a directory listed in $AGMSG_PLUGIN_DIRS, and are ignored until the user opts in with agmsg plugin trust <axis>/<name>. Today only the types axis loads external drivers. Bundled and external drivers are discovered and loaded through the same protocol; see docs/plugins.md and ADR 0002.

agmsg does not use the terms backend, extension, provider, or adapter. storage, when written in user-facing documentation and CLI, is a synonym for "storage driver".

Dependency management

Drivers may require external tools (e.g. the jsonl-duckdb storage driver needs duckdb on $PATH). agmsg never installs those tools itself. Instead:

  1. When a driver is activated or its check subcommand is invoked, it inspects its environment.
  2. If a dependency is missing, the driver emits a machine-readable AGMSG-DIRECTIVE line on stdout describing what to install and how.
  3. The host agent (Claude Code, Codex, etc.) reads the directive and runs the install command, with the user's consent, using its own tools.
  4. On the next agmsg invocation, the dependency check passes and the driver activates.

This keeps agmsg itself minimal and platform-agnostic. The host agent is already trusted with file and shell access, so dependency installation is naturally its responsibility.

The same AGMSG-DIRECTIVE mechanism is used for other host-agent-coordinated actions (e.g. telling Claude Code to invoke its Monitor tool when delivery mode is set to monitor (legacy); a session that agmsgd delivers to directly needs no such directive).

Failure semantics

Switching a storage driver is the most consequential action agmsg takes. The protocol prioritizes leaving the system in a recoverable state:

  • A dep-check failure during agmsg storage switch does not change the active driver. The user is prompted with the directive and given a fallback.
  • A convert always writes to a staging store first, verifies it, then performs the atomic config flip. The previous driver remains active and intact until the final step.
  • Status codes are structured (ok, missing_deps, incompatible_core, corrupt_state, runtime_error) so the host agent can react appropriately rather than treating every non-zero exit identically.

Storage shape

Storage drivers expose CRUD-like operations to the rest of agmsg (insert a message, list unread for an agent, mark read, query history). Internally, the bundled drivers represent state as an append-only event log (message_sent, message_read, etc.). Mutable status — most importantly the read flag — is derived by projection over the event stream rather than by in-place updates.

The sqlite driver retains a backward-compatible read path for the legacy messages table with an inline read column, so existing installs continue to work without migration. New writes go to the event log; reads union both sources.

Message identifiers are UUIDv7 strings. The interface treats them as opaque so existing sqlite databases with integer autoincrement IDs remain readable.

Vocabulary

Term Meaning
driver Swappable implementation of a fixed protocol on one axis
plugin A driver distributed outside agmsg core
bundled driver A driver shipped inside agmsg core
storage The storage axis, or its active driver, in user-facing contexts
AGMSG-DIRECTIVE A JSON line emitted on stdout instructing the host agent to take a specific action
host agent The runtime invoking agmsg scripts (Claude Code, Codex, Gemini CLI, Antigravity, …)
event log The append-only record of message lifecycle events that bundled storage drivers project queries over
placement A <terminal>:<id> reference to one member's pane/window (e.g. tmux:/path/to/socket:%3)

See also