Changelog · May 2026

The founding month

From zero to a production-grade peer messaging framework — 18 releases, 800+ tests, five language clients, and enterprise-grade encryption, routing, and audit in 10 days.

May 2026 was the founding month of a2a-skill. In 10 days the project went from nothing to a production-grade peer messaging framework: a zero-config SQLite bus, a 14-command CLI, five language clients, and over 800 automated tests. Every release this month was a hardening lap or a new capability layered on top of a stable core.

May 18 · v1.0-alpha

The Bus Launches

a2a-skill starts with a single Python file and a clear idea: AI agents should talk to each other directly, without an orchestrator. The initial release ships 14 CLI commands backed by a WAL-mode SQLite database that handles concurrent writes without a daemon or server process.

  • 14 commands: init, register, send, recv, peek, list, status, wait, clear, search, stats, thread, unregister, project
  • Per-agent read tracking — each agent sees only its own unread messages
  • Message TTL with automatic expiry on every recv/peek call
  • Agent presence tracking: status field, PID registration
  • 72 tests and a real-time bus dashboard on day one
May 19 · v1.1

Search, Threading, and the Python Client

Agents can now query the bus by keyword, follow conversations by thread ID, and check bus health with a2a stats. A native Python client library lands — direct SQLite access, no subprocess overhead.

  • Keyword search across all messages with a2a search
  • Thread views — full conversation in one command with a2a thread <id>
  • Bus statistics: agent count, message totals, unread counts
  • A2AClient Python library — sync and async, full CLI parity
May 19 · v1.2

Every Language on the Same Bus

Go, Node.js, and Rust clients ship the same day. A REST API server adds 10 HTTP endpoints. Python, Go, JS, and Rust agents can now collaborate in the same team — sharing one SQLite bus file, no protocol translation needed.

  • Go client library with full API parity
  • Node.js client using the built-in node:sqlite module (Node 22+)
  • Rust crate and binary — same methods, same behavior
  • REST API server: 10 JSON endpoints
May 19 · v1.3

Enterprise-Grade Capabilities

Six major features in a single release: encryption, FTS5 search, audit logging, priority queuing, smart routing, and async Python clients. Each ships with its own documentation guide.

  • End-to-End Encryption — Fernet (AES-128) + RSA-2048. Transparent to agents calling send/recv.
  • FTS5 Full-Text Search — Boolean AND/OR/NOT, phrase queries, prefix matching. LIKE fallback for older SQLite.
  • Audit Logging — Complete message lifecycle tracking. GDPR, HIPAA, SOC 2, PCI DSS aligned. Export to compliance reports.
  • Priority Queue — CRITICAL, HIGH, NORMAL, LOW. Incident alerts cut the line; background tasks wait their turn.
  • Smart Routing — Rules-based: Deliver, Forward, Discard, Queue, Escalate. Persistent rules, enable/disable without restart.
  • Async Clients — PriorityClientAsync and RoutingClientAsync. 10× throughput vs sync (1K → 10K msg/sec).
May 19–27 · v1.3.1–v1.3.9

Hardening Sprint — WAL, FTS5, Cross-Client Parity

After the v1.3 feature push, a week-long hardening sprint closes every known reliability gap. WAL mode is enforced on all connections. FTS5 search is fixed to not rebuild on every query. Missing methods, divergent error messages, and validation inconsistencies across all five clients are patched one by one. The test suite grows from 95 to 800+ tests.

  • WAL mode + busy timeout enforced across Python, Go, JS, and Rust — all 19 affected call sites patched
  • FTS5 rebuild-on-every-search bug fixed; boolean operators preserved in LIKE fallback path
  • Go and Rust clients receive register() and unregister() — previously absent, making send() impossible
  • a2a-spawn now uses nohup + disown — spawned agents survive parent shell exit
  • a2a-spawn shell quoting fix — kit prompts passed via file reference, not inline string
  • Async routing: all five message categories now marked as read after processing (not just discard)
  • Go Wait() connection churn fixed — was opening 120 SQLite connections for a 60s wait
  • Rust touch() method added; recv() connection reuse across poll loop
May 27–28 · v1.3.10–v1.3.17

Final Parity Pass — Validation, Stubs, wait_for_messages

The last stretch of May closes remaining cross-client gaps: empty body rejection standardized across all five clients, type stubs brought into sync with their implementations, and wait_for_messages() added to Go and Rust to complete the cross-language API surface.

  • Empty body rejection — all five clients refuse empty strings with a consistent error message
  • wait_for_messages() — Go and Rust gain this blocking method, matching Python and JS
  • Type stubs fixed — 8 missing public methods added to .pyi stubs; wait_for_messages return type corrected from List to bool
  • Go semantic drift closed — 10 behavioral gaps vs Python patched: GetStatus() nil-for-not-found, Recv() partial-read bug, Register() atomicity fix, RecvSimple() wait type to float64
  • Async routing limit fixrecv_with_routing(limit=N) now routes all messages then truncates per-category, matching sync behavior
18
Releases
800+
Tests (from 72)
5
Language clients
10d
v0 → v1.3.17
v1.3.17 2026-05-28 Cross-Client Parity
Fixed
  • All clients: reject empty message bodies — Python, Go, JS, Rust now validate non-empty bodies before sending. Consistent error messages across all four.
  • Go/JS/Rust: standardized error messages — aligned error text across language implementations.
Refactored
  • test_helpers.py: extracted make_connection() — consolidated SQLite setup logic across test modules.
Added
  • Go and Rust: wait_for_messages() — blocks until N unread messages arrive or timeout. Matches Python and JS signature.
v1.3.15 2026-05-28 Go Semantic Drift
Fixed
  • Go Wait() connection churn — was creating a new SQLite connection every 500ms (up to 120 for 60s timeout). Now reuses one connection.
  • Go GetStatus() return type — changed to (*string, error) returning nil, nil for not-found. Matches Python's None.
  • Go Recv() partial read-marking on scan error — scan phase separated from mark-read phase; failure no longer silently consumes prior messages.
  • Go Register() upsert not atomic — two db.Exec() calls wrapped in a transaction.
  • Go SearchFTS() missing validation — empty query and zero/negative limit now validated upfront.
  • Go Send() whitespace thread_id — whitespace-only IDs rejected to match Python.
  • Go RecvSimple() wait parameter — changed from int to float64 matching Python.
  • Go Stats() error swallowing — all five QueryRow/Scan calls now propagate errors.
  • Go connect() expirySetConnMaxLifetime(5s) caused mid-poll connection drops; set to 0.
  • Go Send() error hint — added "register them first" matching Python message.
v1.3.14 2026-05-28 Async Parity
Fixed
  • a2a_priority_async.py: missing input validation in __init__ — async skipped empty-string checks for project and agent_id.
  • a2a_priority_async.py: recv methods missing read tracking — all three priority recv methods re-delivered the same messages on every call with unread_only=True.
  • a2a_routing_async.py: limit applied before routingrecv_with_routing(limit=N) used SQL-level LIMIT, restricting routing decisions. Now fetches all, routes, truncates per-category.
  • a2a_routing_async.py: apply_routing read marking gaps — consolidated into single loop over all five routing categories.
v1.3.12 2026-05-27 Stub Expansion
Fixed
  • a2a_client.pyi: 8 public methods missingregister(), unregister(), list(), status(), wait(), init_project(), project_info(), clear() absent. All added.
  • a2a_client_async.pyi: same 8 methods missing — async variants added with matching signatures.
  • wait_for_messages return type — stub declared List[Dict], implementation returns bool. Corrected. timeout changed from int to float.
  • search() default limit drift — stub showed 100, implementation uses 50. Corrected.
v1.3.11 2026-05-27 Async Parity Audit
Fixed
  • a2a_client_async.py: __init__ missing agent_id guard — empty/whitespace accepted silently.
  • a2a_routing_async.py: zero input validation in __init__ — fields set with no guards; now calls all validation helpers matching sync.
  • a2a_routing_async.py: add_rule always appended — repeated calls created duplicates. Now upserts by rule name.
  • a2a_routing_async.py: disable_rule/enable_rule stale in-memory list — missing await get_rules() refresh after commit.
  • a2a_routing_async.py: apply_routing only marked discard read — all five categories now marked.
  • a2a_routing_async.py: non-existent m.priority column — would raise OperationalError at runtime; removed from SELECT.
  • a2a_routing_async.py: recv_with_routing never marked read — all returned messages reappeared on every call.
v1.3.9 2026-05-27 Shell Quoting Fix
Fixed
  • a2a-spawn: --append-system-prompt-file — multi-line kit prompts corrupted by shell quoting when passed inline via nohup. Now passed via temp file reference.
v1.3.8 2026-05-27 Go + Rust Hardening
Fixed
  • Go NewClient() — signature changed to (*Client, error) with input validation: empty/non-printable project/agentID, path separators, length limits.
  • Go Send()MaxBodyLength check moved before c.connect() to fail fast.
  • Go Peek() + Recv() — TTL cleanup inlined; no longer opens separate connection.
  • Go Register() — INSERT OR IGNORE + UPDATE cross-client upsert pattern applied.
  • Rust recv()connect() moved outside poll loop; one connection per call.
  • Rust recv()last_seen update added inside poll loop, matching Go/Python Touch.
  • Rust touch() — new public method matching Python/Go API.
v1.3.3 – v1.3.7 2026-05-25–27 Multi-Client Audit
Added
  • JS + Rust: register() and unregister() — both clients had no way to register an agent, making send() impossible (sender validation rejects unknown agents).
  • Rust: project_info() — missing method added matching Python/Go API.
Fixed
  • a2a-spawn: nohup + disown — spawned agents survive parent shell exit.
  • a2a_client.py: TTL cleanup now commits — DELETE was silently rolled back on conn.close().
  • a2a_client_async.py: register(upsert=True) — changed from INSERT OR REPLACE (destroys created_at) to INSERT OR IGNORE + UPDATE.
  • Rust search() — changed to WHERE LOWER(body) LIKE ?; all other clients already used lower().
  • Rust TTL cleanuprecv() and peek() now delete expired messages before fetching. Rust was the only client skipping this.
  • Rust TTL precision — uses float SystemTime instead of strftime('%s','now') for sub-second accuracy, matching Go/Python.
  • WAL invariant: 19 call sites — PRAGMA journal_mode=WAL + busy_timeout=5000 added to remaining direct sqlite3.connect() calls in test files.
  • All clients: MAX_ROLE_LENGTH constant — replaced hardcoded 512 with named constant.
  • All clients: NULLIF upsertCOALESCE(NULLIF(?,'')) preserves non-empty fields on overwrite.
  • Python sync/async: sender + recipient validation in send() — unknown senders and recipients now rejected before INSERT.
v1.3.0 – v1.3.2 2026-05-19 – 2026-05-25 Enterprise Features + Hardening
Added
  • End-to-End Encryption — Fernet (AES-128) + RSA-2048 module. Transparent encryption/decryption. Key rotation support.
  • FTS5 Full-Text Search — Boolean AND/OR/NOT, phrase queries, prefix matching. SearchQueryBuilder. LIKE fallback.
  • Audit Logging — Operations: send, receive, encrypt, decrypt, route, search. GDPR/HIPAA/SOC 2/PCI DSS aligned.
  • Priority Queue — CRITICAL/HIGH/NORMAL/LOW ordered delivery. Sync + async (PriorityClientAsync).
  • Smart Routing — Pattern/sender/priority rules. Deliver/Forward/Discard/Queue/Escalate. Persistent. SmartRouter for custom logic.
  • Async ClientsPriorityClientAsync, RoutingClientAsync. aiosqlite backend. 10× throughput.
  • Validation Hardening (v1.3.2) — NaN/Inf rejection, max lengths (agent_id 256, thread_id 256, body 100K), cross-client enforcement. REST API TTL/status/limit validation.
  • Test count: 95 → 303 (v1.3.1) → 616 (v1.3.2). FTS5 regression tests, WAL invariant coverage, REST API tests, async tests.
v1.0-alpha – v1.2 2026-05-18 – 2026-05-19 Initial Release
Added
  • Core CLI (v1.0) — 14 commands, SQLite WAL bus, per-agent read tracking, TTL, agent presence. 72 tests. dashboard.py + benchmark.py.
  • a2a-spawn (v1.0) — per-CLI launcher (claude, opencode, pi). Kit prompt integration. Global skills at ~/.agents/skills.
  • Bash wrapper a2a — auto-detects Python 3, cached interpreter selection.
  • Python client library (v1.1)A2AClient sync + async, full CLI parity, direct SQLite.
  • Search + threading (v1.1)a2a search, a2a thread, a2a stats, all with --json.
  • Go client (v1.2)a2a_client.go, WAL + MkdirAll, full API parity.
  • Node.js client (v1.2)a2a_client.js, built-in node:sqlite (Node 22+).
  • Rust client (v1.2)src/lib.rs crate + binary, WAL + create_dir_all.
  • REST API server (v1.2)a2a_server.py, 10 JSON endpoints.