Durable local inbox and shared-chat primitives for agents. The Python API, CLI, and stdio MCP use one SQLite store and remain available without Better Agent or another backend.
Standalone MCP identity is bound when the process starts:
AGENT_MESSAGING_IDENTITY=alice \
AGENT_MESSAGING_DB="$HOME/.agent-messaging/messages.sqlite3" \
agent-messaging-mcpMutations require a caller-known request_id. Repeating the same request returns its original receipt; reuse with different content fails. The standalone surface intentionally has no wake or orchestration option.
This library owns the durable inbox: its SQLite database, its state root, rooms, receipts, and the orchestration layer built on them.
Process spawning and the provider adapters live in
agent-runtime, which this
package depends on. agent_messaging.orchestration.spawn and
agent_messaging.orchestration.providers are import surfaces that re-export
the runtime's objects unchanged (the same function, registry, and
detached-process list objects — asserted in tests/test_runtime_boundary.py),
and RuntimeOperationError is one class shared with agent_runtime.errors.
The dependency points messaging -> runtime, and never the reverse: the runtime must be installable and reasonable about on its own, and the inbox database must never be owned by it.
A separate entrypoint adds wake and session orchestration on top of the same store. It is opt-in: agent-messaging-mcp is unchanged.
AGENT_MESSAGING_IDENTITY=alice \
AGENT_MESSAGING_DB="$HOME/.agent-messaging/messages.sqlite3" \
AGENT_COMMUNICATE_PROFILE=communicate \
agent-communicate-mcpTools are bound to a SessionRuntime (agent_messaging.orchestration.runtime_spec), which a host implements. The final tool set is profile ∩ runtime.capabilities() − AGENT_COMMUNICATE_DISABLED_TOOLS, so an operation the runtime cannot perform is absent rather than failing. Select a runtime with AGENT_MESSAGING_RUNTIME=module:factory; the default drives real native provider CLI sessions (claude, codex, agy).
Inbox delivery is always durable first and the wake is best-effort: a failed wake leaves the message delivered and the wake pending for a later retry sweep. Starting a turn in another session costs tokens, so it only happens when explicitly requested (queue_turn=true, ask, delegate_task).