Skip to content

Agent addressing

LumenFlow’s memory layer can route coordination traffic to a specific live agent session without inventing a pack-specific side channel.

Agent-to-agent coordination is defined by the exported A2A.V1 contract in @lumenflow/control-plane-sdk/a2a:

  • A2ASignalV1 carries schema_version, signal_id, sender_identity, recipients[], optional workspace_id, thread/reply fields, intent, interrupt class, ACK requirement, message, idempotency key, origin, and existing WU/initiative/lane/role axes.
  • A2AReceiptV1 carries signal_id, reader_identity, read_at, delivery_state, and optional idempotency key. Delivery state starts as delivered and is promoted to acked or rejected when the recipient posts a same-thread AGREE or REJECT.
  • The conformance suite is published from the same package, so local memory adapters and hosted buses can prove the same visibility, routing, receipt, and convergence behavior.

The local implementation stores signals and receipts in .lumenflow/memory/, surfaces events through generated Claude hooks, and provides mem:watch for clients without native hooks. Those are adapters. lumenflow.cloud consumes the same A2A.V1 schema over a hosted transport, where workspace_id is required as the tenant/workspace routing key even though local worktrees may omit it.

This release is a minor-compatible addition while target_agent remains a deprecated read-compatible alias for the first entry in recipients[]. Removing that alias would be a major-version change.

Use mem:roster to see active sessions, their ephemeral display_name, explicit agent_identity, role axes, current WU, lane, and most recent heartbeat.

pnpm mem:roster
pnpm mem:roster --json

display_name is a friendly handle for the current live session. It is not a stable semantic alias; those still belong to the role contract.

agent_identity is the stable A2A reader identity for sessions that should share receipt state across restarts or concurrent terminals. Set it explicitly with pnpm agent:session --agent-identity <owner>:<role>:<purpose> or LUMENFLOW_AGENT_IDENTITY. LumenFlow does not infer this value from PID, hostname, or process metadata; when it is absent, receipts use session_id.

Send directed coordination signals by session_id, display_name, or agent_identity. --to accepts a comma-separated recipient list:

pnpm mem:signal 'Please review the docs diff' --wu WU-123 --to Planck,codex:implementer:host42
pnpm mem:signal 'Take the next test slice' --wu WU-123 --to sess-1234

The CLI resolves live session IDs and display names to agent_identity when one is present. Signals persist canonical A2A recipients[]; target_agent remains only as the deprecated single-recipient compatibility alias. Broadcast signals without recipients continue to render for every session.

Use A2A thread and intent fields when a signal needs a machine-readable decision, not just a status note:

pnpm mem:signal 'Approve this boundary?' --wu WU-123 --to Planck,codex:implementer:host42 --intent PROPOSE --requires-ack
pnpm mem:signal 'Agreed' --reply-to sig-aaaa1111 --intent AGREE
pnpm mem:converged --thread thread-1234

Directed root signals get a generated thread_id and default to interrupt_class: priority. Broadcasts default to interrupt_class: advisory. Replies inherit the parent thread unless --thread is supplied explicitly.

Valid intents are INFO, PROPOSE, COUNTER, AGREE, and REJECT. mem:converged --thread <id> exits successfully only when every recipient on the thread root has replied with AGREE; REJECT replies are reported separately and keep the thread unconverged.

The local .lumenflow/memory/*.jsonl files are an adapter over the exported A2ASignalV1 and A2AReceiptV1 contract. Hosted transports should preserve the same fields and state transitions while replacing storage and delivery.

Read only the messages for one live session with --for:

pnpm mem:inbox --for Planck
pnpm mem:inbox --thread thread-1234 --intent PROPOSE,AGREE
pnpm mem:inbox --for sess-1234 --no-mark

This view still includes broadcast traffic, so an agent sees both direct messages and lane-wide coordination.

Claude Code projects with LumenFlow enforcement hooks enabled install .claude/hooks/signal-received.sh and a PostToolUse settings entry. When mem:signal --to ... appends an addressed A2ASignalV1 signal for an active Claude session, LumenFlow writes a per-session pending event under .lumenflow/state/signal-hooks/. The Claude hook drains that queue at the next tool boundary and surfaces a structured signal:received prompt prefix.

requires_ack: true events include explicit AGREE and REJECT reply commands on the same thread. The wu:prep directed-signal gate remains the enforcement backstop if a session ignores or misses the hook payload.

Clients without native hook delivery use mem:watch as a foreground daemon:

pnpm mem:watch --session <session_id|display_name|agent_identity>

The watcher tails the canonical local A2A signal log, applies the same live session and reader identity resolution as mem:inbox --for, dispatches signal:received events through the hook event queue, drains them to stdout, and records delivered receipts so the same signal is not emitted repeatedly. Keep the process running in a side terminal for Codex CLI, Cursor, Windsurf, Aider, Cline, Gemini CLI, or another client that cannot consume the generated Claude PostToolUse hook.

ClientDelivery status
Claude CodeSupported through generated PostToolUse hook delivery.
Codex CLISupported through foreground mem:watch daemon fallback.
CursorSupported through foreground mem:watch daemon fallback.
WindsurfSupported through foreground mem:watch daemon fallback.
Aider / Cline / generic clientsSupported through foreground mem:watch daemon fallback.

wu:prep fails before gates when the current reader has unread directed signals on the WU or its initiative. The failure lists signal IDs, sender, intent, and ACK requirement when present. Continue only after reading and responding, or use an audited override:

pnpm wu:prep --id WU-123 --allow-unread-signals --reason 'handled in paired review'

mem:context now auto-composes a roster from the delegation registry and active session records. Use --no-roster only when a test or fixture needs stable, non-live output.

pnpm mem:context --wu WU-123
pnpm mem:context --wu WU-123 --no-roster

Friendly names come from .lumenflow/agents/display-name-pool.yaml. A name is reserved when agent:session starts and returned to the pool when the session ends, so long-running orchestration stays readable without conflating session identity with role ownership.

Two live sessions may intentionally use the same agent_identity. That is the workstation reuse pattern: a restarted reviewer or a second terminal can treat signals already read by the first session as read without sharing the ephemeral session_id.